Linux系统调用及示例

来源:互联网 发布:nodejs调用java api 编辑:程序博客网 时间:2024/06/02 03:43

学习linux系统调用时先明确一个概念:一般情况下,应用程序通过应用编程接口API而不是直接通过系统调用来编程


应用编程接口API与系统调用的关系如下:(应用程序编程接口实际上并不需要和内核提供的系统调用对应)

(1)一个API可以实现成一个系统调用

(2)一个API可以通过调用多个系统调用来实现

(3)一个API可以完全不使用任何系统调用



来说说系统调用的一些共性吧:

1.系统调用(在linux中常称作syscalls)通常通过函数进行调用

2.它们通常都需要定义一个或几个参数

3.系统调用通常会通过一个long类型的返回值来表示成功或者错误

注意:用一个负的返回值来表明错误;用一个0值通常(不绝对)表明成功


系统调用号很重要:

每个系统调用被赋予一个系统调用号系统调用号关联系统调用,当用户空间的进程执行一个系统调用的时候,这个系统调用号就被用来指明到底是要执行哪个系统调用,进程不会提及系统调用的名称。注意:系统调用号相当关键,一旦分配就不能再有任何变更,否则编译好的应用程序就会崩溃。此外,如果一个系统调用被删除,它所占用的系统调用号也不允许被回收利用,否则,以前编译过的代码会调用这个系统调用,造成混乱。(linux有一个“未实现”系统调用sys_ni_syscall(),它除了返回-ENOSYS外不做任何其他工作,如果一个系统调用被删除,或者变得不可用,这个函数就要负责“填补空位”


系统调用表(sys_call_table):

表中存放每个的系统调用的入口地址(函数指针);

每一个体系结构都有对应的一个系统调用表;

如x86平台的系统调用表arch/x86/kernel/syscall_table_32.S


如ARM平台的系统调用表arch/arm/kernel/entry-common.S

ENTRY(sys_call_table)

#include"calls.S"//arch/arm/kernel/calls.S文件内容如下:

/*0 */  CALL(sys_restart_syscall)
CALL(sys_exit)
CALL(sys_fork_wrapper)
CALL(sys_read)
CALL(sys_write)
/*5 */  CALL(sys_open)
CALL(sys_close)
..........

系统调用处理过程:
对于ARM来说,系统软中断的入口处理程序是vector_swi(arch/arm/kernel/entry-common.S)
ENTRY(vector_swi)
@获取系统调用号到scno(r7)中;
@加载sys_call_table表到tbl变量中
@调用系统调用
ldrcc pc,[tbl,scno,lsl#2]



基于tiny4412开发板的系统调用流程及示例:

Linux内核的唯一访问接口是系统调用,不同平台下的系统调用数目不一样,在ARM中,通用的系统调用有390个。通过修改内核的源码,增加第391个系统调用到内核中。

第一步:往内核添加一个自定义系统调用

在内核源码的arch/arm/kernel/sys_arm.c 文件中添加一个系统调用的实现:

gedit arch/arm/kernel/sys_arm.c 

在文件最后添加如下代码,这个就是系统调用的实现代码:

asmlinkage long sys_foo(void)

{

       printk("my syscallsys_foo!\n");

       return THREAD_SIZE;

}

第二步:将系统调用添加到系统调用表中

gedit arch/arm/kernel/calls.S

在系统调用号为390后面添加,如下蓝色部分

/* 385 */  CALL(sys_memfd_create)

              CALL(sys_bpf)

              CALL(sys_execveat)

              CALL(sys_userfaultfd)

              CALL(sys_membarrier)

              CALL(sys_mlock2)

              CALL(sys_foo)

第三步:添加系统调用号

gedit arch/arm/include/uapi/asm/unistd.h

在系统调用号为390后面添加,如下蓝色部分

#define __NR_bpf                (__NR_SYSCALL_BASE+386)

#define __NR_execveat                (__NR_SYSCALL_BASE+387)

#define __NR_userfaultfd             (__NR_SYSCALL_BASE+388)

#define __NR_membarrier                  (__NR_SYSCALL_BASE+389)

#define __NR_mlock2                 (__NR_SYSCALL_BASE+390)

#define __NR_foo               (__NR_SYSCALL_BASE+391)

第四步:修改系统调用数目

gedit arch/arm/include/asm/unistd.h

#include <uapi/asm/unistd.h>

/*

 *This may need to be greater than __NR_last_syscall+1 in order to

 *account for the padding in the syscall table

 */

#define __NR_syscalls  (392) //该值要大于等于最大的系统调用号+1并且为4的整数倍,对齐

第五步: 添加系统调用函数的声明到头文件中

gedit include/linux/syscalls.h

在文件的最后添加如下蓝色部分;

asmlinkage long sys_open_by_handle_at(int mountdirfd,

                                  struct file_handle __user *handle,

                                  int flags);

asmlinkage long sys_setns(int fd, int nstype);

asmlinkage long sys_foo(void);

#endif

第六步:添加完毕后,重新编译内核,并且使用重新编译的内核来启动开发板

make  uImage  LOADADDR=0x40008000  -j2



以上就是添加一个系统调用的过程,可通过一下应用程序做测试:

#include <linux/unistd.h>

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

       long stack_size;

       stack_size= syscall(391);//系统一般会通过c库来调用系统调用,但是自己定义的系统调用缺少c库的支持,只能使用宏来调用系统调用

       printf("Thekernel stack size is %ld\n",stack_size);

       return0;

}

交叉编译上面的测试程序,在开发板上运行查看效果






















0 0
原创粉丝点击