添加一个简单的系统调用——内核模块法
操作系统作业:添加一个简单的系统调用(内核模块法)操作系统作业:添加一个简单的系统调用一、实验目的二、实验内容三、实验环境四、操作方法和步骤(1)准备工作(2)修改源程序,增加系统调用(3)编译内核(4)测试系统调用五、总结一、实验目的学习Linux的内核的系统调用。理解、掌握Linux系统调用的实现框架、用户界面、参数传递、进入/返回过程。二、实验内容在系统调用中添加一个不用传...
·
操作系统作业:添加一个简单的系统调用(内核模块法)
操作系统作业:添加一个简单的系统调用
一、实验目的
- 学习Linux的内核的系统调用。
- 理解、掌握Linux系统调用的实现框架、用户界面、参数传递、进入/返回过程。
二、实验内容
在系统调用中添加一个不用传递参数的系统调用,实现一个简单的系统调用的添加。执行这个系统调用,在屏幕上显示输出“Hello World! ”。
三、实验环境
虚拟机软件:Vmware Workstation
Linux发行版:CentOS 7 内置内核:linux 3.10.0
待编译内核:linux 4.15.10
四、操作方法和步骤
(1)准备工作
1.查询syscall_table的地址,执行命令:
sudo cat /proc/kallsyms | grep sys_call_table
2. 查询可用的系统调用号,使用vim编辑器打开unistd_32.h,执行命令:
vim /usr/include/asm/unistd_32.h
在普通模式下,输入G
或者shift+g
定位到文尾,可以看到最后一个是383,因此384是可用的系统调用号
(2)创建hello.c文件
- sys_call_table为上面查询到的系统调用表的地址,NUM为待添加的系统调用号
- 使用
vim
命令创建hello.c
,并添加以下内容:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/unistd.h>
#include <linux/sched.h>
MODULE_LICENSE("Dual BSD/GPL");
#define SYS_CALL_TABLE_ADDRESS 0xffffffffa8400160 //sys_call_table对应的地址
#define NUM 384 //系统调用号为384
int orig_cr0; //用来存储cr0寄存器原来的值
unsigned long *sys_call_table_my=0;
static int(*anything_saved)(void); //定义一个函数指针,用来保存一个系统调用
static int clear_cr0(void) //使cr0寄存器的第17位设置为0(内核空间可写)
{
unsigned int cr0=0;
unsigned int ret;
asm volatile("movq %%cr0,%%rax":"=a"(cr0));//将cr0寄存器的值移动到eax寄存器中,同时输出到cr0变量中
ret=cr0;
cr0&=0xfffffffffffeffff;//将cr0变量值中的第17位清0,将修改后的值写入cr0寄存器
asm volatile("movq %%rax,%%cr0"::"a"(cr0));//将cr0变量的值作为输入,输入到寄存器eax中,同时移动到寄存器cr0中
return ret;
}
static void setback_cr0(int val) //使cr0寄存器设置为内核不可写
{
asm volatile("movq %%rax,%%cr0"::"a"(val));
}
asmlinkage long sys_mycall(void) //定义自己的系统调用
{
printk("模块系统调用-当前pid:%d,当前comm:%s\n",current->pid,current->comm);
printk("hello,world!\n");
return current->pid;
}
static int __init call_init(void)
{
sys_call_table_my=(unsigned long*)(SYS_CALL_TABLE_ADDRESS);
printk("call_init......\n");
anything_saved=(int(*)(void))(sys_call_table_my[NUM]);//保存系统调用表中的NUM位置上的系统调用
orig_cr0=clear_cr0();//使内核地址空间可写
sys_call_table_my[NUM]=(unsigned long) &sys_mycall;//用自己的系统调用替换NUM位置上的系统调用
setback_cr0(orig_cr0);//使内核地址空间不可写
return 0;
}
static void __exit call_exit(void)
{
printk("call_exit......\n");
orig_cr0=clear_cr0();
sys_call_table_my[NUM]=(unsigned long)anything_saved;//将系统调用恢复
setback_cr0(orig_cr0);
}
module_init(call_init);
module_exit(call_exit);
(3)创建Makefile文件
- 使用
vim
命令创建Makefile文件,添加以下内容
obj-m:=hello.o
CURRENT_PATH:=$(shell pwd)
LINUX_KERNEL_PATH:=/usr/src/linux-4.15.10
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
- 注意:
LINUX_KERNEL_PATH
是内核代码的位置
(4)安装内核模块
1.执行命令:
make
2.查看make是否成功,执行命令:
ls | grep 'hello.*'
看到有ko
,mod.c
,mod.o
文件就说明成功了
3.使用insmod插入模块,执行命令:
insmod hello.ko
4.使用lsmod查看模块是否插入成功,执行命令:
lsmod
Module
出现hello,表示模块插入成功
(5)测试系统调用
1.使用vim创建test.c文件,执行命令:
#include<stdio.h>
#include<stdlib.h>
#include<linux/kernel.h>
#include<sys/syscall.h>
#include<unistd.h>
int main()
{
unsigned long x = 0;
x = syscall(483); //测试483号系统调用
printf("17计科-1727405169 syscall result: %ld\n", x);
return 0;
}
2.使用gcc编译,执行命令:
gcc test.c
3.运行a.out,执行命令:
./a.out
出现以下结果就成功了。
五、总结
- 两个字高度概括,简单!
- 剩下的你们自己慢慢写吧,有问题欢迎在留言区交
流,谢谢观看。
更多推荐
已为社区贡献2条内容
所有评论(0)