首页 | Linux 基础 | 资讯动态 | Linux 应用 | Linux 服务器 | Linux 开发 | Linux 安全 | 专题 | 联盟论坛
  当前位置:主页>Linux 开发>linux 内核>文章内容
深入学习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条)] [返回顶部] [打印本页] [关闭窗口]  
  热点文章
·使用 Linux 系统调用的内核命令
·Linux 2.6.11内核文件IO系统调用
·Linux操作系统的源代码目录树结
·Linux用户态与内核态的交互讲解
·Linux内核对I/O端口的管理实现(
·深入分析 Linux操作系统的内核链
·Linux内核可装载模块对设备驱动
·概述Linux系统的驱动框架及驱动
·详解Linux 2.6内核新文件系统变
·Linux系统可卸载内核模块完全指
·FreeBSD手册讲解(一)--配置FreeB
·编译Linux操作系统的内核讲解
  相关文章
·深入学习LINUX内核之六(图文讲解
·深入学习LINUX内核之五(图文讲解
·深入学习LINUX内核之四(图文讲解
·Linux系统内核漏洞分析
·深入学习LINUX内核之三(图文讲解
·如何在Linux内核中的实现SYN Coo
·深入学习LINUX内核之二(图文讲解
·简析Linux与FreeBSD的syscall
·深入学习Linux内核文档一(图文讲
·Linux操作系统“警惕”内核汉化
·Linux操作系统核心的汉字显示机
·如何利用异常表处理Linux内核态

本站信息源至:互联网络,均为学习,交流所用,如有版权问题,请联系我们.
站长QQ:397422079 E_mail:riechelr_hl@unix5.com
转载本站内容请注明原作者名.谢谢!