Linux自定义系统调用


添加系统调用


添加系统调用的头文件索引

我们要调用syscall是通过unistd提供的一系列系统接口,其中由C提供的接口需要通过修改unistd_64.h这个文件来指定。
打开是存放内核源码的地方,进入子目录arch/x86/include/generated/uapi/asm/

比如在我的Linux里此文件是位于 /usr/src/linux-4.10.8/arch/x86/include/generated/uapi/asm/ 里的
通过使用vim或者自带的gedit打开这个文件,因为打开这个文件需要管理员权限,所以需要在命令前加上sudo。在endif之前添加一个新的系统调用号:

1
#define __NR_qwq 333

333表示系统调用号,不一定要和前面的连续,只要不重复就行啦,qwq则是系统调用名。

修改系统调用向量表

/usr/src/linux-4.10.8/arch/x86/entry/syscalls中打开syscall_64.tbl文件,向里面添加:

333 64 qwq sys_qwq

1
2
//以内核版本为例,我的版本是4.10.8所以是进入linux-4.10.8文件夹
//此处的333与你之前添加的系统调用号一致,qwq是调用函数名,sys_qwq是实际的调用函数,这个名字和调用函数名没有关系哦。

添加调用实现

sino大佬博客里这个方法在内核版本4.0以上就不再使用了,都是使用宏定义,如果坚持使用函数声明的办法可能会出错
后就是添加通过调用号要调用到的程序了 打开/usr/src/linux-4.10.8/include/linux/syscalls.h添加一个函数声明

1
2
> asmlinkage long sys_qwq(void);
>

然后打开/usr/src/linux-4.10.8/kernel/sys.c,并在结尾添加函数:

1
2
3
4
5
SYSCALL_DEFINE2(qwq, long, b, const char __user *, a)
{
printk("Hi, this is %ld who wants to say %s to you!\n",b,a);
return 1;
}

DEFINE后面的数字是传入的参数个数,qwq为入口地址,注意这里参数类型和参数名字之间均要用,隔开
传入1个long型数据和一个字符串到内核空间,在内核空间打印完后返回一个1

重新编译

确定所有文件保存后,执行:

1
2
$ sudo make -j6 bzImage //内核数+2
$ sudo make install

然后要重启哦!


使用系统调用


编写一个小程序

新建一个文件 test.cpp 输入以下代码

1
2
3
4
5
6
7
8
9
10
#include <sys/syscall.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
long ret = syscall(333); //syscall 调用号 参数1 之后为系统调用的参数列表
printf("result is %ld\n", ret);
return 0;
}

编译程序

输入命令:

1
$ g++ test.cpp -o test

等待执行完之后,程序就编译成功啦

执行程序

输入命令:

1
$ sudo ./test

屏幕上会打印出 result is 1 因为我们的系统调用返回数字就是1

查看内核输出信息

输入命令:

1
$ dmesg | grep "hello"

即可看到内核中输出的printk信息

grep “hello” 为过滤出包含”hello”的信息
此处有坑…如果是在CB里面运行的,记得把运行弹出的shell关掉啊,不然看不到输出消息的…qwq

Printk()会依据日志级别将指定信息输出到控制台或日志文件中,其格式为:

printk(日志级别 “消息文本”);
如 printk(KERN_ALERT”hello,world\n”);
一般情况下,优先级高于控制台日志级别的消息将被打印到控制台,优先级低于控制台日志级别的消息将被打印到messages日志文件中,而在伪终端下不打印任何的信息。
加载模块后,用户可使用 dmesg 命令查看模块初始化函数中的输出信息,如使用 “ dmesg | tail -20 ” 来输出“dmesg”命令的最后 20 行日志。

文章目录
  1. 1. 添加系统调用
    1. 1.1. 添加系统调用的头文件索引
    2. 1.2. 修改系统调用向量表
    3. 1.3. 添加调用实现
    4. 1.4. 重新编译
  2. 2. 使用系统调用
    1. 2.1. 编写一个小程序
    2. 2.2. 编译程序
    3. 2.3. 执行程序
    4. 2.4. 查看内核输出信息
|