如何将参数传递给Linux系统调用?

时间:2018-12-12 04:02:42

标签: c linux linux-kernel parameter-passing system-calls

我是一名学习操作系统的大学生。

我正在尝试在Linux内核中添加自己的系统调用,这出了问题。

我的环境如下:

  • Linux内核v.4.19.1
  • 在Oracle VirtualBox 5.2.18上具有Intel Core i5-4210M CPU的64位Ubuntu LTS 18.04.1
  • 64位Windows 10 Home 1803作为主机


由于我在x86_64机器上工作,因此我从 arch / x86 / entry / syscalls / syscall_64.tbl 开始。 在Linux内核v.4.19.1中,最后一项是

334     common     rseq             __x64_sys_rseq

所以我在下面添加了这三行。

335     common     my_syscall_0     sys_my_syscall_0
336     common     my_syscall_1     sys_my_syscall_1
337     common     my_syscall_2     sys_my_syscall_2


其次,我在 include / linux / syscalls.h 中添加了函数原型。

asmlinkage int sys_my_syscall_0(void);
asmlinkage int sys_my_syscall_1(int);
asmlinkage int sys_my_syscall_2(int, int);


第三,我创建了一个新文件 kernel / my_syscall.c 并添加了函数的实现。

asmlinkage int sys_my_syscall_0(void)
{
    printk("my_syscall_0\n");
    return 0;
}

asmlinkage int sys_my_syscall_1(int a)
{
    printk("my_syscall_1 : %d\n", a);
    return 0;
}

asmlinkage int sys_my_syscall_0(int a, int b)
{
    printk("my_syscall_2 : %d, %d\n", a, b);
    return b;
}


然后,我在 kernel / Makefile 中添加了my_syscall.o来编译 kernel / my_syscall.c

obj-y = fork.o exec_domain.o panic.o \
        cpu.o exit.o softirq.o resource.o \
        sysctl.o sysctl_binary.o capability.o ptrace.o user.o \
        signal.o sys.o umh.o workqueue.o pid.o task_work.o \
        extable.o params.o \
        kthread.o sys_ni.o nsproxy.o \
        notifier.o ksysfs.o cred.o reboot.o \
        async.o range.o smpboot.o ucount.o \
        my_syscall.o


make-kpkg dpkg 命令编译后,我制作了一个测试程序。

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
int main()
{
    printf("1 : %d\n", syscall(335));
    printf("2 : %d\n", syscall(336, 1));
    printf("3 : %d\n", syscall(337, 2, 3));
    return 0;
}


但是,结果很奇怪。 dmesg 向我展示了一些根本没有意义的数字。

# dmesg
...
my_syscall_0
my_syscall_1 : 1111490392
my_syscall_2 : 1111490392, 1111490392

每次执行程序时,它似乎都在改变。


显然,传递参数存在一些问题。但是, strace 命令告诉我这些值传递得很好。

# strace ./syscall
...
syscall_0x14F(0x7ffd21866538, 0x7ffd21866548, ....) = 0
...
syscall_0x150(0x1, 0, 0, 0, 0x7f9e1562f3a0, ....) = 0
...
syscall_0x14F(0x2, 0x3, 0x7f9e1562f3a0, ....) = 0
....

简介

  1. 我进行了简单的系统调用。

  2. 向他们传递参数的方式与预期不符。

  3. 问题:为了添加新的系统调用,上述步骤中是否存在任何问题?

  4. 问题:将参数传递给系统调用时,我应该注意什么问题?

先谢谢您。

2 个答案:

答案 0 :(得分:3)

您需要告诉构建系统您的系统调用需要2个参数,并且它们的类型为int。这样,作为构建系统一部分的脚本将生成适当的包装器,以将参数转换为所需的类型。而不是像您那样定义实际的处理程序,应该使用-

SYSCALL_DEFINE2(my_syscall_2, int, a, int, b) // Yes, there is a comma between the types and the argument names
{
    printk("my_syscall_2 : %d, %d\n", a, b);
    return b;
}

SYSCALL_DEFINExlinux/include/linux/syscalls.h中定义。

您可以在linux/fs/read_write.c

中查看示例

答案 1 :(得分:2)

我找到了解决方案。正如@Ajay Brahmakshatriya回答的那样,我应该使用SYSCALL_DEFINEx宏。而且,我也应该修改 arch / x86 / entry / syscalls / syscall_64.tbl

这是最终摘要。

如何添加新的系统调用

首先,修改 arch / x86 / entry / syscalls / syscall_64.tbl :在下面添加这些行。

335     common     my_syscall_0     __x64_sys_my_syscall_0
336     common     my_syscall_1     __x64_sys_my_syscall_1
337     common     my_syscall_2     __x64_sys_my_syscall_2

第二,修改 include / linux / syscalls.h :在下面添加这些行。

asmlinkage long sys_my_syscall_0(void);
asmlinkage long sys_my_syscall_1(int);
asmlinkage long sys_my_syscall_2(int, int);

第三,创建一个新文件用于实施。就我而言,是 kernel / my_syscall.c

#include <linux/syscalls.h>
#include <linux/kernel.h>

SYSCALL_DEFINE0(my_syscall_0)
{
    printk("my_syscall_0\n");
    return 0;
}

SYSCALL_DEFINE1(my_syscall_1, int, a)
{
    printk("my_syscall_1 : %d\n", a);
    return 0;
}

SYSCALL_DEFINE2(my_syscall_2, int, a, int, b)
{
    printk("my_syscall_2 : %d, %d\n", a, b);
    return b;
}

第四,将创建的文件添加到其目录中的Makefile中。就我而言,是内核/ Makefile

...
obj-y = fork.o exec_domain.o panic.o \
        cpu.o exit.o softirq.o resource.o \
        sysctl.o sysctl_binary.o capability.o ptrace.o user.o \
        signal.o sys.o umh.o workqueue.o pid.o task_work.o \
        extable.o params.o \
        kthread.o sys_ni.o nsproxy.o \
        notifier.o ksysfs.o cred.o reboot.o \
        async.o range.o smpboot.o ucount.o \
        my_syscall.o
...

最后,编译并安装内核。现在,您将可以看到新的系统调用运行良好。

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>

int main()
{
    printf("1 : %d\n", syscall(335));
    printf("2 : %d\n", syscall(336, 1));
    printf("3 : %d\n", syscall(337, 2, 3));
    return 0;
}

dmesg 命令显示系统调用运行良好。

# dmesg
my_syscall_0
my_syscall_1 : 1
my_syscall_2 : 2, 3