Linux内核中添加系统调用接口简单示例

Posted 99度的水

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux内核中添加系统调用接口简单示例相关的知识,希望对你有一定的参考价值。

1. Linux体系结构

  Linux系统的地址空间分为用户空间和内核空间,通过系统调用和硬件中断能够完成从用户空间到内核空间的转移。

 

2. 系统调用接口

① 一般情况下,用户进程不能访问内核空间。Linux内核中提供了一组用于实现各种系统功能的子程序,用户可以调用它们访问Linux内核的数据和函数,这些子程序称为系统调用接口(SCI)。

② 系统调用和普通函数的区别:系统调用由操作系统内核实现,运行于内核态;普通函数调用由函数库或用户自己提供,运行于用户态。

 

3. 系统调用分类:主要分3大类

① 进程控制类

  * fork

  * clone

  * execve

  ...

② 文件控制类

  * fcntl

  * open

  * read

  ...

③ 系统控制类

  * ioctl

  * reboot

  ...

 

4.  系统调用工作原理

  系统调用工作原理是(1)进程先用适当的值填充寄存器(R7),(2)然后调用一个特殊的指令(swi),(3)这个指令会让用户程序跳转到一个事先定义好的内核中的一个位置(vector_swi),(4)这位置的代码会根据寄存器(R7)的值从表sys_call_table中查找相应的函数。

(1)适当的值:系统调用号,定义于文件arch\\arm\\include\\asm\\unistd.h中。

#define __NR_restart_syscall        (__NR_SYSCALL_BASE+  0)
#define __NR_exit            (__NR_SYSCALL_BASE+  1)
#define __NR_fork            (__NR_SYSCALL_BASE+  2)
#define __NR_read            (__NR_SYSCALL_BASE+  3)
#define __NR_write            (__NR_SYSCALL_BASE+  4)
#define __NR_open            (__NR_SYSCALL_BASE+  5)
#define __NR_close            (__NR_SYSCALL_BASE+  6)

(2)特殊的指令
  * 在X86 CPU中,这个指令由中断0x80实现

  * 在ARM中,这个指令是SWI(Software interrupt:软中断指令),现在重命名为SVC

(3)固定的位置:在ARM体系中,这个固定位置为ENTRY(vector_swi)(arch\\arm\\kernel\\entry-common.S)

(4)相应的函数:内核根据应用程序传递来的系统调用号,从系统调用表sys_call_table(sys_call_table中表项定于文件:arch\\arm\\kernel\\calls.S)找到相应的内核函数。

 

5. 参数传递

① 系统调用参数传递是通过寄存器传递的,寄存器传递参数,需要满足两个条件

  * 每个参数长度不能超过寄存器长度,即32位

  * 参数个数不能超过6个(少于等于6个的参数用r0~r5传递),处理器的寄存器并不是无限多的

② 多于6个的参数,会用一个寄存器指向进程地址空间中这些参数值所在的一个内存区域(不必过多深究)

 

6. 向Linux内核中添加新的系统调用

① 在内核代码的某一位置添加函数,如:向kernel/printk.c中添加

void sys_print(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5)
{
    printk("Hello Kevin, this is a new system call\\n");
    printk("arg0 = %d\\n", arg0);
    printk("arg1 = %d\\n", arg1);
    printk("arg2 = %d\\n", arg2);
    printk("arg3 = %d\\n", arg3);
    printk("arg4 = %d\\n", arg4);
    printk("arg5 = %d\\n", arg5);
}

 

② 把函数添加至sys_call_table,如:向arch\\arm\\kernel\\calls.S中373行添加

CALL(sys_print)

③ 添加系统调用号,如:向arch\\arm\\include\\asm\\unistd.h中添加

#define __NR_sys_print        (__NR_SYSCALL_BASE+361)


7. 新的系统调用测试

①应用层测试代码方法1:

#include <sys/syscall.h>

int main()
{
    syscall(361, 10, 11, 12, 13, 14, 15);
    
    return 0;
}


①应用层测试代码方法2:

void SystemCallTest()
{
    __asm__ (
        "ldr r7, =361 \\n"
        "mov r0, #0\\n"
        "mov r1, #1\\n"
        "mov r2, #2\\n"
        "mov r3, #3\\n"
        "mov r4, #4\\n"
        "mov r5, #5\\n"
        "swi \\n"
        :
        :
        :"memory"
    );
}

int main()
{
    SystemCallTest();
    
    return 0;
}

 

以上是关于Linux内核中添加系统调用接口简单示例的主要内容,如果未能解决你的问题,请参考以下文章

Linux 内核 内存管理内存管理系统调用 ⑤ ( 代码示例 | 多进程共享 mmap 内存映射示例 )

为Linux-3.10.1内核添加系统调用

Linux 系统调用

《linux内核设计与实现》第五章

系统调用

Linux 内核 内存管理内存管理系统调用 ④ ( 代码示例 | mmap 创建内存映射 | munmap 删除内存映射 )