|
深入学习LINUX内核之七(图文讲解)
|
|
来源:http://www.unix5.com 作者:riechelr_hl 发布时间:2008-04-15
|
|
5.5 Linux的系统呼叫
5.5.1系统呼叫介面
系统呼叫(通常称为syscalls)是Linux內核与上层应用程式进行交互通信的唯一介面,参见图5-4所示。从对中断机制的說明可知,用戶程式透过直接或间接(透过程式库函数)呼叫中断int 0x80,並在eax寄存器中指定系统呼叫功能号,即可使用內核资源,包括系统硬件资源。 不过通常应用程式都是使用具有标批介面定义的 C 函数库中的函数间接地使用內核的系统呼叫,见图5-19所示。

通常系琉呼叫使用函数形式进行呼叫,因此可带有一个或多个参数。对於系统呼叫执行的结果,它会在返回值中表示出来。通常负值表示错误,而0则表示成功,在出错的情況下,错误的类型码被存放在全域变数errno中。透过呼叫程式库函数perror( ),我们可以列印出该错误码对应应的出错字串资讯。
在linux內核中,每个系统呼叫都具有唯一的一个系统呼叫功能号。这些功能号定义在当include/unistd.h中第62行开始处。例如,write系统呼叫的功能号是4,定义为符号--NR_write这些系统。这些系统呼叫功能号实际上对应於include/linux/sys.h中定义的系统呼叫处理程式指标阵列表sys_call_table[ ]中项的索引值。因此,write( )系统呼叫的处理程式指标就位于该阵列的项4处。
当我们想在自己的程式中使用这些系统呼叫符,需要像下面所示在包括进档“”之前定义符号“__LIBRARY__”。
#define__LIBRARY__ #include
另外,我们从sys_call_table[ ]中可以看出,內核中所有系统呼叫处理函数的名称基本上都是以符号‘sys_’开始的。例如系统呼叫read()在內核原始码中的实现函数就是sys_read( )。
5.5.2系统呼叫处理过程
当应用程式经过程式库函数向內核发出一个中断呼叫int 0x80时,就开始执行一个系统呼叫。其中寄存器eax中存放著系统呼叫号,而攜带的参数可依次存放在寄存器ebx、ecx和edx中。因此Linux 0.12内核中用戶程式能夠向內核最多直接传递三个参数,当然也可以不带参数。处理系统呼叫中断int 0x80的过程是程式kernel/system_call.s中的system_call。
为了方便执行系统呼叫,内核原始码在include/unistd.h档(150-200行)中定义了巨集函数_syscalln( ) ,其中n代表攜带的参数个数,可以分別0至3。因此最多可以直接传递3个参数。若需睪传递大块资料给內核,则可以传递这块资料的指标值。例如对於read()系统呼叫,其定义是:
int read(int fd,char *buf, int n );
若我们在用戶程式中直接执行对应的系统呼叫,那麼该系统呼叫的巨集的形式为:
#define__LIBRARY__ #include _syscall3(int, read, int, fd, char *, buf, int, n)
因此我们可以在用戶程式直接使用上面的_syscall3( )来执行一个系统呼叫read( ),而不用透过C函数库作仲介。实际上C函数库中函数最终呼叫系统呼叫的形式和这裡给出的完全一样。
对于include/unistd.h中给出的每个系统呼叫巨集,都有2+2*n个参数。其中第1个参数对应系统呼叫返回值的类型;第2个参数是系统呼叫的名称;随后是系统呼叫所攜带参数的类型 名称。这个巨集会被扩展成包含內嵌组合语句的C函数,见如下所示。
int read(int fd,char *buf, int n) { long__res; __asm__volatile ( “int$0x80” :“=a” ( __res) : “”(__NR_read),“b”((long) (fd)),“c”((1ong) (buf)),“d”((1ong) (n))); if ( __res>=0) return int __res; errno=- __res; return -1; }
可以看出,这个巨集经过展开就是一个读取作业系统呼叫的具体实现。其中使用了嵌入组合语句以功能号_ _NR_read (3)执行了Linux的系统中断呼叫0x80。该中断呼叫在eax(_ _res )寄存器中返回了实际读取的位元组数。若返回的值小于0,则表示此次读取操作出错,于是将出错号反转后存入全域变数errno中,并向呼叫程式返回-1值。
如果有某个系统呼叫需要多於3个参数,那么內核通常採用的方法是直接把这些参数作为一个参数缓冲区块,並把这个缓冲区块的指标作为一个参数传递给內核。因此对於多於3个参数的系统呼叫,我们只需要使用带一个参数的巨集_syscalll( ),把第一个参数的指标传递给內核即可。例如,select( )函数系统呼叫具有5个参数,但我们只需传递其第l个参熟的指标,参见对fs/select.c程式的說明。
当进入內核中的系统呼叫处理程式kernel/sys_call.s后,system_call的代码会写先检查eax中的系统呼叫功能号是否在有效系统呼叫号范围內,然后根据sys_call_table[ ]函数指标表呼叫执行相应的系统呼叫处理程式。
call_sys_call_table(, %eax, 4) //kernel/sys_call.s第99行。
这句组合语句运算元的含义是间接呼叫地址在_sys_call_table + %eax * 4处的函数。由於sys_call_table[ ]指标每项4 立元组,因此这里需要给系统呼叫功能号乘上4。然后用所得到的值从表中获取被呼叫处理函数的位址。
5.5.3Linux系统呼叫的参数传递方式
关于Linux用戶行程向系统中断呼叫过程传递参数方面,Linux系统使用了通用寄存器传递方法,例如寄存器ebx、ecx和edx。这种使用寄存器传递参数方法的一个明显优点就是:当进入系统中断服务程式而保存寄存器值时,这些传递参数的寄存器也被自动地放在了內核态堆栈上,因此用不著再专门对传递参数的寄存器进行特殊处理。这种方法是Linus 当时所知的最简单最快速的参数传递方法。另外还有一种使用Intel CPU提供的系统呼叫门(System Call gate)的参数专递方法,它在行程用戶态堆栈和內核态堆栈自动复制传递的参数。但这种 方法吏用起来步骤比较复杂。
另外,在每个系统呼叫处理函数中应该传递的参数进行验证,以保证所有参数都合法有效。尤其是用戶提供的指标,应该进行严格地审查。以保证指标所指的记忆体区域范围有效,並且具有相应的读写许可权。
5.6系统时间和定时
5.6.1系统时间
共12页: 上一页 1 [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] 下一页
|
| |
|
|
如果您对本文有任何疑问或者建议,请到论坛讨论区发表您的意见: >> 论坛入口 |
[ 收藏]
[ 推荐]
[ 评论(0条)]
[返回顶部] [打印本页]
[关闭窗口] |
|
|
|