WSL下添加系统调用

WSL下添加系统调用

由于WSL的内核是在原版基础之上有做修改的,所以要先去下载内核的源码。https://github.com/microsoft/WSL2-Linux-Kernel.

查看当前系统内核版本

1
uname -r

然后去源码中找到对应的版本并下载,下载完成后解压。

1
2
tar -zvxf linux-msft-wsl-5.15.90.1.tar.gz
cd WSL2-Linux-Kernel-linux-msft-wsl-5.15.90.1

新增系统调用号

在arch/x86/include/generated/uapi/asm/unistd_64.h中添加系统调用号

1
#define __NR_my_func 335

注意这里的系统调用号可以随便取,但一定要是唯一的,不然会报错。my_func是系统调用名。

修改系统调用向量表

修改arch/x86/entry/syscalls/syscall_64.tbl文件

1
335 64 my_oper sys_my_func

此处的系统调用号必须与之前声明的保持一致,my_func为调用函数名,sys_my_func为实际调用的函数。

添加系统调用的声明

在include/linux/syscalls.h中添加

1
asmlinkage long sys_my_func(int count);

其中asmlinkage是一个宏,用于声明函数的调用方式,long是返回值类型,sys_my_func为函数名,int count为函数参数。

添加具体实现

在kernel/sys.c中添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
long my_oper(int *result, int num1, int num2, char *op)
{
if(op)
{
if(*op == '+')
*result = num1 + num2;
else if(*op == '-')
*result = num1 - num2;
else if(*op == '*')
*result = num1 * num2;
else if(*op == '\\')
{
if(num2 != 0)
*result = num1 / num2;
else
printk("divided number can't bere zero.\n");
}
}
else
printk("operator is empty.\n");
return 0;
}


SYSCALL_DEFINE1(my_func, int, count)
{
printk("count is %d\n", count);
struct timespec64 tstart, tend;
ktime_get_real_ts64(&tstart);
int i, result, times = count / 4;
for(i = 0; i < times; ++i)
{
char op_add = '+';
my_oper(&result, 10, 10, &op_add);
}
printk("my_func op_add is ok. op_add count is :%d", i);

for(i = 0; i < times; ++i)
{
char op_sub = '-';
my_oper(&result, 20, 10, &op_sub);
}
printk("my_func op_sub is ok. op_sub count is :%d", i);

for(i = 0; i < times; ++i)
{
char op_mul = '*';
my_oper(&result, 10, 10, &op_mul);
}
printk("my_func op_mul is ok. op_mul count is :%d", i);

for(i = 0; i < times; ++i)
{
char op_div = '\\';
my_oper(&result, 20, 10, &op_div);
}
printk("my_func op_div is ok. op_div count is :%d", i);
ktime_get_real_ts64(&tend);
printk("my_func running time is %ld usec/n", 1000000000 * (tend.tv_sec-tstart.tv_sec) + (tend.tv_nsec-tstart.tv_nsec));
return 0;
}

DEFINE后面的数字是传入的参数个数,这里是1个,所以是1。my_func是系统调用名,count是传入的参数。

注意:函数实现不要写在条件编译的范围内。

编译内核

编译内核需要.config配置文件,可以通过make menuconfig生成,也可以直接复制现有的.config文件。这里使用make menuconfig生成。

1
make menuconfig

make menuconfig会打开一个配置界面,这里不需要修改别的配置,只需要修改内核版本号,以便与现在的内核区分开,然后保存退出。

编译内核

1
sudo make

编译完成后,生成的内核文件在项目目录arch/x86/boot下面,文件名为 bzImage。

替换内核

将内核文件bzImage从wsl系统中拷贝出来,放在windows文件系统中任意路径下,例如C:_usr_kernel

然后进入C:.wslconfig 的文件,添加如下内容:

1
2
[wsl2]
kernel=C:\\wsl_usr_kernel\\bzImage

注意双反斜杠,否则不生效。然后在powershell中执行

1
wsl --shutdown

重启wsl系统,查看内核版本

1
uname -r

可以看到内核版本已经变成了我们刚刚编译的版本。

如果要恢复原来的内核,只需要删除.wslconfig文件,然后执行wsl --shutdown重启wsl系统即可。


WSL下添加系统调用
https://gstarmin.github.io/2023/06/05/WSL下添加系统调用/
作者
Starmin
发布于
2023年6月5日
许可协议