系统调用是一个软中断,中断号是0x80,它是上层应用程序与Linux系统内核进行交互通信的唯一接口。通过int 0x80,就可使用内核资源。不过,通常应用程序都是使用具有标准接口定义的C函数库间接的使用内核的系统调用,即应用程序调用C函数库中的函数,C函数库中再通过int 0x80进行系统调用。
所以,系统调用过程是这样的:
应用程序调用libc中的函数->libc中的函数引用系统调用宏->系统调用宏中使用int 0x80完成系统调用并返回
下面是sys_call_table的定义文件
位于./include/sys.h
其中sys_call_table的类型是fn_ptr类型,其中sys_call_table[0]元素为sys_setup,它的类型是fn_ptr类型,它实际上是函数sys_setup的
入口地址。
它的定义如下:
typedef int (*fn_ptr) (); // 定义函数指针类型。
下面的实例代码有助于理解函数指针:
- #include<stdio.h>
- typedef int (*MyFunc)();
- MyFunc Func1;
- int Func2()
- {
- printf("This is a sample output!/n");
- return 0;
- }
- int main()
- {
- Func1=Func2;
- //Func2();
- //(*Func1)();
- printf("%x/n",(*Func1));
- printf("%x/n",Func2);
- return 0;
- }
system_call系统调用入口函数
- #### int 0x80 --linux 系统调用入口点(调用中断int 0x80,eax 中是调用号)。
- .align 2
- _system_call:
- cmpl $nr_system_calls-1,%eax # 调用号如果超出范围的话就在eax 中置-1 并退出。
- ja bad_sys_call
- push %ds # 保存原段寄存器值。
- push %es
- push %fs
- pushl %edx # ebx,ecx,edx 中放着系统调用相应的C 语言函数的调用参数。
- pushl %ecx # push %ebx,%ecx,%edx as parameters
- pushl %ebx # to the system call
- movl $0x10,%edx # set up ds,es to kernel space
- mov %dx,%ds # ds,es 指向内核数据段(全局描述符表中数据段描述符)。
- mov %dx,%es
- movl $0x17,%edx # fs points to local data space
- mov %dx,%fs # fs 指向局部数据段(局部描述符表中数据段描述符)。
- # 下面这句操作数的含义是:调用地址 = _sys_call_table + %eax * 4。参见列表后的说明。
- # 对应的C 程序中的sys_call_table 在include/linux/sys.h 中,其中定义了一个包括72 个
- # 系统调用C 处理函数的地址数组表。
- call _sys_call_table(,%eax,4)
- pushl %eax # 把系统调用号入栈。
- movl _current,%eax # 取当前任务(进程)数据结构地址??eax。
- # 下面97-100 行查看当前任务的运行状态。如果不在就绪状态(state 不等于0)就去执行调度程序。
- # 如果该任务在就绪状态但counter[??]值等于0,则也去执行调度程序。
- cmpl $0,state(%eax) # state
- jne reschedule
- cmpl $0,counter(%eax) # counter
- je reschedule
- # 以下这段代码执行从系统调用C 函数返回后,对信号量进行识别处理。
- ret_from_sys_call:
- # 首先判别当前任务是否是初始任务task0,如果是则不必对其进行信号量方面的处理,直接返回。
- # 103 行上的_task 对应C 程序中的task[]数组,直接引用task 相当于引用task[0]。
- movl _current,%eax # task[0] cannot have signals
- cmpl _task,%eax
- je 3f # 向前(forward)跳转到标号3。
- # 通过对原调用程序代码选择符的检查来判断调用程序是否是超级用户。如果是超级用户就直接
- # 退出中断,否则需进行信号量的处理。这里比较选择符是否为普通用户代码段的选择符0x000f
- # (RPL=3,局部表,第1 个段(代码段)),如果不是则跳转退出中断程序。
- cmpw $0x0f,CS(%esp) # was old code segment supervisor ?
- jne 3f
- # 如果原堆栈段选择符不为0x17(也即原堆栈不在用户数据段中),则也退出。
- cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ?
- jne 3f
- # 下面这段代码(109-120)的用途是首先取当前任务结构中的信号位图(32 位,每位代表1 种信号),
- # 然后用任务结构中的信号阻塞(屏蔽)码,阻塞不允许的信号位,取得数值最小的信号值,再把
- # 原信号位图中该信号对应的位复位(置0),最后将该信号值作为参数之一调用do_signal()。
- # do_signal()在(kernel/signal.c,82)中,其参数包括13 个入栈的信息。
- movl signal(%eax),%ebx # 取信号位图??ebx,每1 位代表1 种信号,共32 个信号。
- movl blocked(%eax),%ecx # 取阻塞(屏蔽)信号位图??ecx。
- notl %ecx # 每位取反。
- andl %ebx,%ecx # 获得许可的信号位图。
- bsfl %ecx,%ecx # 从低位(位0)开始扫描位图,看是否有1 的位,
- # 若有,则ecx 保留该位的偏移值(即第几位0-31)。
- je 3f # 如果没有信号则向前跳转退出。
- btrl %ecx,%ebx # 复位该信号(ebx 含有原signal 位图)。
- movl %ebx,signal(%eax) # 重新保存signal 位图信息??current->signal。
- incl %ecx # 将信号调整为从1 开始的数(1-32)。
- pushl %ecx # 信号值入栈作为调用do_signal 的参数之一。
- call _do_signal # 调用C 函数信号处理程序(kernel/signal.c,82)
- popl %eax # 弹出信号值。
- 3: popl %eax
- popl %ebx
- popl %ecx
- popl %edx
- pop %fs
- pop %es
- pop %ds
- iret
./include/unistd.h文件中系统调用符号和调用号的对应定义
- // 以下是内核实现的系统调用符号常数,用于作为系统调用函数表中的索引值。( include/linux/sys.h )
- #define __NR_setup 0 /* used only by init, to get system going */
- /* __NR_setup 仅用于初始化,以启动系统 */
- #define __NR_exit 1
- #define __NR_fork 2
- #define __NR_read 3
- #define __NR_write 4
- #define __NR_open 5
- #define __NR_close 6
- #define __NR_waitpid 7
- #define __NR_creat 8
- #define __NR_link 9
- #define __NR_unlink 10
- #define __NR_execve 11
- #define __NR_chdir 12
- #define __NR_time 13
- #define __NR_mknod 14
- #define __NR_chmod 15
- #define __NR_chown 16
- #define __NR_break 17
- #define __NR_stat 18
- #define __NR_lseek 19
- #define __NR_getpid 20
- #define __NR_mount 21
- #define __NR_umount 22
- #define __NR_setuid 23
- #define __NR_getuid 24
- #define __NR_stime 25
- #define __NR_ptrace 26
- #define __NR_alarm 27
- #define __NR_fstat 28
- #define __NR_pause 29
- #define __NR_utime 30
- #define __NR_stty 31
- #define __NR_gtty 32
- #define __NR_access 33
- #define __NR_nice 34
- #define __NR_ftime 35
- #define __NR_sync 36
- #define __NR_kill 37
- #define __NR_rename 38
- #define __NR_mkdir 39
- #define __NR_rmdir 40
- #define __NR_dup 41
- #define __NR_pipe 42
- #define __NR_times 43
- #define __NR_prof 44
- #define __NR_brk 45
- #define __NR_setgid 46
- #define __NR_getgid 47
- #define __NR_signal 48
- #define __NR_geteuid 49
- #define __NR_getegid 50
- #define __NR_acct 51
- #define __NR_phys 52
- #define __NR_lock 53
- #define __NR_ioctl 54
- #define __NR_fcntl 55
- #define __NR_mpx 56
- #define __NR_setpgid 57
- #define __NR_ulimit 58
- #define __NR_uname 59
- #define __NR_umask 60
- #define __NR_chroot 61
- #define __NR_ustat 62
- #define __NR_dup2 63
- #define __NR_getppid 64
- #define __NR_getpgrp 65
- #define __NR_setsid 66
- #define __NR_sigaction 67
- #define __NR_sgetmask 68
- #define __NR_ssetmask 69
- #define __NR_setreuid 70
- #define __NR_setregid 71
这是一系列宏,它们的定义在unistd.h中,基本形式为#define _NR_name value,name为系统函数名字,value是一个整数值,是name所对应的系统函数指针在sys_call_table中的偏移量。
系统调用宏也在本文件内定义,采用内联汇编,如下:
- // 以下定义系统调用嵌入式汇编宏函数。
- // 不带参数的系统调用宏函数。type name(void)。
- // %0 - eax(__res),%1 - eax(__NR_##name)。其中name 是系统调用的名称,与 __NR_ 组合形成上面
- // 的系统调用符号常数,从而用来对系统调用表中函数指针寻址。
- // 返回:如果返回值大于等于0,则返回该值,否则置出错号errno,并返回-1。
- #define _syscall0(type,name) /
- type name(void) /
- { /
- long __res; /
- __asm__ volatile ( "int $0x80" / // 调用系统中断0x80。
- :"=a" (__res) / // 返回值??eax(__res)。
- :"" (__NR_##name)); / // 输入为系统中断调用号__NR_name。
- if (__res >= 0) / // 如果返回值>=0,则直接返回该值。
- return (type) __res; errno = -__res; / // 否则置出错号,并返回-1。
- return -1;}
- // 有1 个参数的系统调用宏函数。type name(atype a)
- // %0 - eax(__res),%1 - eax(__NR_name),%2 - ebx(a)。
- #define _syscall1(type,name,atype,a) /
- type name(atype a) /
- { /
- long __res; /
- __asm__ volatile ( "int $0x80" /
- : "=a" (__res) /
- : "" (__NR_##name), "b" ((long)(a))); /
- if (__res >= 0) /
- return (type) __res; /
- errno = -__res; /
- return -1; /
- }
- // 有2 个参数的系统调用宏函数。type name(atype a, btype b)
- // %0 - eax(__res),%1 - eax(__NR_name),%2 - ebx(a),%3 - ecx(b)。
- #define _syscall2(type,name,atype,a,btype,b) /
- type name(atype a,btype b) /
- { /
- long __res; /
- __asm__ volatile ( "int $0x80" /
- : "=a" (__res) /
- : "" (__NR_##name), "b" ((long)(a)), "c" ((long)(b))); /
- if (__res >= 0) /
- return (type) __res; /
- errno = -__res; /
- return -1; /
- }
- // 有3 个参数的系统调用宏函数。type name(atype a, btype b, ctype c)
- // %0 - eax(__res),%1 - eax(__NR_name),%2 - ebx(a),%3 - ecx(b),%4 - edx(c)。
- #define _syscall3(type,name,atype,a,btype,b,ctype,c) /
- type name(atype a,btype b,ctype c) /
- { /
- long __res; /
- __asm__ volatile ( "int $0x80" /
- : "=a" (__res) /
- : "" (__NR_##name), "b" ((long)(a)), "c" ((long)(b)), "d" ((long)(c))); /
- if (__res>=0) /
- return (type) __res; /
- errno=-__res; /
- return -1; /
- }