Linux内核有多少系统调用,linux内核--系统调用(四)

一. 概述

系统调用是应用程序与内核交互的一种方式。系统调用作为一种接口,通过系统调用,应用程序能够进入操作系统内核,从而使用内核提供的各种资源,比如操作硬件,开关中断,改变特权模式等等。首先,系统调用是一个软中断,既然是中断那么一般就具有中断号和中断处理程序两个属性,Linux使用0x80号中断作为系统调用的入口,而中断处理程序的地址放在中断向量表里。

二. 过程

基于linux-2.6.38,以read()系统调用函数为例进行说明。

在用户空间,read()函数的声明位于#include,原型为:ssize_t read(int fd, void *buf, size_t count)。下面是read()函数在用户空间的定义的伪代码:

69c5a8ac3fa60e0848d784a6dd461da6.png

1 ssize_t read(int fd, void *buf, size_t count)2 {3 longres;4 %eax =__NR_read5 %ebx =fd6 %ecx = (long)buf7 %edx=count8 int $0x80

9 res = %eax10 returnres;11 }

69c5a8ac3fa60e0848d784a6dd461da6.png

第4行,用eax寄存器保存read()的系统调用号,在/arch/x86/include/asm/unistd_32.h里定义(#define __NR_read  3);第5~7行,分别将三个参数放入三个寄存器(通过寄存器来传递参数);第8行,执行系统调用,进入内核;第9行,获取eax寄存器所保存的函数返回值。

执行第8行后已经进入了系统内核,由于这是一个中断,因此程序进入到中断向量表中记录0x80号的中断处理程序,中断向量表的初始化在/arch/x86/kernel/traps.c中定义:

69c5a8ac3fa60e0848d784a6dd461da6.png

1 void __init trap_init(void)2 {3 ...................4

5 #ifdef CONFIG_X86_326 set_system_trap_gate(SYSCALL_VECTOR, &system_call);7 set_bit(SYSCALL_VECTOR, used_vectors);8 #endif

9 ...................10 }

69c5a8ac3fa60e0848d784a6dd461da6.png

如第6行所示。SYSCALL_VECTOR是系统调用的中断号,在/arch/x86/include/asm/irq_vectors.h中定义:

1 #ifdef CONFIG_X86_322 # define SYSCALL_VECTOR 0x80

3 #endif

正好是0x80。而system_call是系统调用的中断处理函数指针,用户执行int $0x80后会执行到这个函数,它在/arch/x86/kernel/entry_32.S中定义:

69c5a8ac3fa60e0848d784a6dd461da6.png

1 ENTRY(system_call)2 RING0_INT_FRAME # can't unwind into user space anyway3 pushl_cfi %eax # save orig_eax4 SAVE_ALL5 GET_THREAD_INFO(%ebp)6 # system call tracing in operation / emulation7 testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)8 jnz syscall_trace_entry9 cmpl $(nr_syscalls), %eax10 jae syscall_badsys11 syscall_call:12 call *sys_call_table(,%eax,4)13 movl %eax,PT_EAX(%esp) # store the return value

...........

69c5a8ac3fa60e0848d784a6dd461da6.png

第4行,SAVE_ALL是一个宏,也在这个文件里定义:

69c5a8ac3fa60e0848d784a6dd461da6.png

1 .macro SAVE_ALL2 cld

3 PUSH_GS4 pushl_cfi %fs5 /*CFI_REL_OFFSET fs, 0;*/

6 pushl_cfi %es7 /*CFI_REL_OFFSET es, 0;*/

8 pushl_cfi %ds9 /*CFI_REL_OFFSET ds, 0;*/

10 pushl_cfi %eax11 CFI_REL_OFFSET eax, 0

12 pushl_cfi %ebp13 CFI_REL_OFFSET ebp, 0

14 pushl_cfi %edi15 CFI_REL_OFFSET edi, 0

16 pushl_cfi %esi17 CFI_REL_OFFSET esi, 0

18 pushl_cfi %edx19 CFI_REL_OFFSET edx, 0

20 pushl_cfi %ecx21 CFI_REL_OFFSET ecx, 0

22 pushl_cfi %ebx23 CFI_REL_OFFSET ebx, 0

24 movl $(__USER_DS), %edx25 movl %edx, %ds26 movl %edx, %es27 movl $(__KERNEL_PERCPU), %edx28 movl %edx, %fs29 SET_KERNEL_GS %edx30 .endm

69c5a8ac3fa60e0848d784a6dd461da6.png

主要作用就是将各个寄存器压入栈中。

第9行,比较eax的值是否大于等于nr_syscalls,nr_syscalls是比最大有效系统调用号大1的值,在/arch/x86/kernel/entry_32.S中定义:

1 #define nr_syscalls ((syscall_table_size)/4)

其中syscall_table_size就是系统调用表的大小(单位:字节),syscall_table_size其实是一个数组,数组里存放的是各个系统调用函数的地址,元素类型是long型,除以4刚好是系统调用函数的个数。

如果从eax寄存器传进来的系统调用号有效,那么就执行第12行,在系统调用表里找到相应的系统调用服务程序,sys_call_table在/arch/x86/kernel/syscall_table_32.S中定义:

69c5a8ac3fa60e0848d784a6dd461da6.png

1 ENTRY(sys_call_table)2 .long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */3 .long sys_exit4 .long ptregs_fork5 .long sys_read6 .long sys_write7 .long sys_open /* 5*/8 .long sys_close9 .................

69c5a8ac3fa60e0848d784a6dd461da6.png

*sys_call_table(,%eax,4)指的是sys_call_table里偏移量为%eax*4上的那个值指向的函数,这里%eax=3,那么第5行的sys_read()函数就会被调用。sys_read()在/fs/read_write.c中定义:

69c5a8ac3fa60e0848d784a6dd461da6.png

1 SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)2 {3 struct file *file;4 ssize_t ret = -EBADF;5 intfput_needed;6

7 file = fget_light(fd, &fput_needed);8 if(file) {9 loff_t pos =file_pos_read(file);10 ret = vfs_read(file, buf, count, &pos);11 file_pos_write(file, pos);12 fput_light(file, fput_needed);13 }14

15 returnret;16 }

69c5a8ac3fa60e0848d784a6dd461da6.png

可见,参数的形式和用户空间的一样。SYSCALL_DEFINE3是一个宏,在/include/linux/syscalls.h中定义:

1 #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)

SYSCALL_DEFINEx也是一个宏,也在此文件中定义:

1 #define SYSCALL_DEFINEx(x, sname, ...) \

2 __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)3 .....4 #define __SYSCALL_DEFINEx(x, name, ...) \

5 asmlinkage longsys##name(__SC_DECL##x(__VA_ARGS__))6 ......

宏展开后,就是声明了这么一个函数:

asmlinkage long sys_read(unsigned int fd, char __user *buf, size_t count);

asmlingage是一个宏,定义为:__attribute__((regparm(0))),作用是让这个函数只从栈上获取参数(因为之前的SAVE_ALL将参数压到了栈里面)。

当执行完中断处理程序后,后面会调用RESTORE_REGS来恢复各个寄存器:

1 ..............2 CFI_REMEMBER_STATE3 jeldt_ss # returning to user-space with LDT SS4 restore_nocheck:

5 RESTORE_REGS 4# skip orig_eax/error_code6 ...............

第5行,RESTORE_REGS的定义:

69c5a8ac3fa60e0848d784a6dd461da6.png

1 .macro RESTORE_REGS pop=0

2 RESTORE_INT_REGS3 1: popl_cfi %ds4 /*CFI_RESTORE ds;*/

5 2: popl_cfi %es6 /*CFI_RESTORE es;*/

7 3: popl_cfi %fs8 /*CFI_RESTORE fs;*/

9 POP_GS \pop

10 .................

69c5a8ac3fa60e0848d784a6dd461da6.png

第2行,RESTORE_INT_REGS的定义:

69c5a8ac3fa60e0848d784a6dd461da6.png

1 .macro RESTORE_INT_REGS2 popl_cfi %ebx3 CFI_RESTORE ebx4 popl_cfi %ecx5 CFI_RESTORE ecx6 popl_cfi %edx7 CFI_RESTORE edx8 popl_cfi %esi9 CFI_RESTORE esi10 popl_cfi %edi11 CFI_RESTORE edi12 popl_cfi %ebp13 CFI_RESTORE ebp14 popl_cfi %eax15 CFI_RESTORE eax16 .endm

69c5a8ac3fa60e0848d784a6dd461da6.png        

到这里差不多了,再对read()跟踪的话就会涉及文件系统方面的内容,以后会说的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值