C

用户程序》C库API》系统调用》内核函数

2018-08-08  本文已影响151人  XDgbh
关系图总结

系统调用接口的主要任务是把进程从用户态切换到内核态。在具有保护机制的计算机系 统中,用户必须通过软件中断陷阱,才能使进程从用户态切换为内核态。系统调用通过软中断0x80陷入内核,跳转到系统调用处理程序system_call函数,并执行相应的服务例程(内核函数)。

用户态——内核态

用户态——内核态——底层硬件

《Linux内核修炼之道》第5章讲解系统调用,它是应用程序和内核间的桥梁,学习并理解它是我们走向内核的一个很好的过渡。

1、系统调用

1.1、用户程序如何进行系统调用
系统调用两种方式

syscall函数原型为:
int syscall(int number, ...);
其中number是系统调用号,number后面应顺序接上该系统调用的所有参数。
下面是gettid系统调用的调用实例。

00 #include <unistd.h> 
01 #include <sys/syscall.h> 
02 #include <sys/types.h> 
03   
04 #define __NR_gettid      224  
05   
06 int main(int argc, char *argv[])  
07 {  
08     pid_t tid;  
09   
10     tid = syscall(__NR_gettid);  //括号内参数直接写224也可以
11 } 

大部分系统调用都包括了一个SYS_符号常量来指定自己到系统调用号的映射,因此上面第10行可重写为:
tid = syscall(SYS_gettid);

1.2、为什么需要系统调用

(1)系统调用可以为用户空间提供访问硬件资源的统一接口,以至于应用程序不必去关注具体的硬件访问操作。比如,读写文件时,应用程序不用去管磁盘类型,甚至于不用关心是哪种文件系统。

(2)系统调用可以对系统进行保护,保证系统的稳定和安全。系统调用的存在规定了用户进程进入内核的具体方式,换句话说,用户访问内核的路径是事先规定好的,只能从规定位置进入内核,而不准许肆意跳入内核。有了这样的进入内核的统一访问路径限制才能保证内核的安全。

我们可以形象地描述这种机制:作为一个游客,你可以买票要求进入野生动物园,但你必须老老实实地坐在观光车上,按照规定的路线观光游览。当然,不准下车,因为那样太危险,不是让你丢掉小命,就是让你吓坏了野生动物。

1.3、系统调用执行过程——把进程从用户态切换到内核态

用户空间到内核空间的转换阶段
如图所示,系统调用的执行需要一个用户空间到内核空间的状态转换,不同的平台具有不同的指令可以完成这种转换,这种指令也被称作 操作系统陷入(operating system trap)指令。
Linux通过软中断来实现这种陷入,具体对于X86架构来说,是软中断0x80,也即int $0x80汇编指令。软中断和我们常说的中断(硬件中断)不同之处在于-它由软件指令触发而并非由硬件外设引发。
int 0x80指令被封装在C库中,对于用户应用来说,基于可移植性的考虑,不应该直接调用int $0x80指令。陷入指令的平台依赖性,也正是系统调用需要在C库进行封装的原因之一。
通过软中断0x80,系统会跳转到一个预设的内核空间地址,它指向了系统调用处理程序(不要和系统调用服务例程相混淆),即在arch/i386/kernel/entry.S文件中使用汇编语言编写的system_call函数

2)system_call函数到系统调用服务例程。


1、进入system_call函数前,用户应用将参数存放到对应寄存器中,system_call函数执行时会首先将这些寄存器压入堆栈。
2、软中断指令int 0x80执行时,系统调用号会被放入eax寄存器,同时,系统调用表sys_call_table每一项占用4个字节。这样,如上图所示,system_call函数可以读取eax寄存器获得当前系统调用的系统调用号,偏移地址=4x%eax,然后以sys_call_table为基址,基址加上偏移地址所指向的内容即是应该执行的系统调用服务例程(内核函数)的地址
3、对于系统调用服务例程,可以直接从system_call函数压入的堆栈中获得参数,对参数的修改也可以一直在堆栈中进行。在system_call函数退出后,用户应用可以直接从寄存器中获得被修改过的参数。

2、C库提供操作系统应用编程接口(API)

3、C库函数(API)内部封装系统调用(函数)

4、系统命令

……  
write(1, "/usr/src/linux-2.6.23\n", 22/usr/src/linux-2.6.23) = 22  
close(1)                                = 0  
munmap(0xb7f5a000, 4096)                = 0  
exit_group(0) 

5、系统调用——>内核函数

6、系统调用号——>系统调用表——>系统调用服务例程(内核函数)

6.1系统调用号
008 #define __NR_restart_syscall      0  
007 #define __NR_exit         1  
009 #define __NR_fork         2  
010 #define __NR_read         3  
011 #define __NR_write        4  
012 #define __NR_open         5 ——系统调用号
……  
326 #define __NR_getcpu     318  
327 #define __NR_epoll_pwait    319  
328 #define __NR_utimensat      320  ——系统调用号
329 #define __NR_signalfd       321  
330 #define __NR_timerfd        322  
331 #define __NR_eventfd        323  
332 #define __NR_fallocate      324 
6.2系统调用表sys_call_table
001 ENTRY(sys_call_table)  
002     .long sys_restart_syscall   /* 0 - old
"setup()" system call, used for restarting */  
003     .long sys_exit  
004     .long sys_fork  
005     .long sys_read  
006     .long sys_write  
007     .long sys_open      /* 5 ——系统调用号*/  
    ……  
320     .long sys_getcpu  
321     .long sys_epoll_pwait  
322     .long sys_utimensat     /* 320 ——系统调用号*/  
323     .long sys_signalfd  
324     .long sys_timerfd  
325     .long sys_eventfd  
326     .long sys_fallocate 
6.3系统调用服务例程
954 asmlinkage long sys_getpid(void)  
955 {  
956     return current->tgid;  
957 } 
上一篇 下一篇

猜你喜欢

热点阅读