分类: LINUX
2010-05-19 15:21:39
1. 系统调用和函数库的关系。
系统调用通过软中断int 0x80从用户态进入内核态。
函数库中的某些函数调用了系统调用。
函数库中的函数可以没有调用系统调用,也可以调用多个系统调用。
编程人员可以通过函数库调用系统调用。
高级编程也可以直接采用int 0x80进入系统调用,而不必通过函数库作为中介。
如果是在核心编程,也可以通过int 0x80进入系统调用,此时不能使用函数库。因为函数库中的函数是内核访问不到的。
2. 从用户调用库函数到系统调用执行的流程。
1) 假设用户调用ssize_t write (int fields, cont void *buff, size_t nbytes);库函数。
2) 库函数会执行int 0x80中断。因为中断使得进程从用户态进入内核态,所以参数通过寄存器传送。
3) 0x80中断对应的中断例程被称为system call handler。其工作是:
i. 存储大多数寄存器到内核堆栈中。这是汇编代码写的。
ii. 执行真正的系统调用函数――system call service routine。这是C代码。
iii. 通过ret_from_sys_call ()返回,回到用户态的库函数。这是汇编代码。
3. 前面2.2)提到的的参数和返回值传输方法。
eax存放系统调用序号。因为所有系统调用都是通过0x80中断完成的,必须将系统调用序号作为传入以区别多个系统调用。
ebx,ecx,edx,esi和edi分别传送五个真正的参数。若多余5个参数,则将参数存放在内存中,而让某个寄存器存放指向这个内存地址。另外因为寄存器是32字节,所以参数不能超过4个bytes。
在2.3).i中,这些寄存器中包含的参数又被存储到内核堆栈中。之后的system call service roution可以像一般C函数那样使用堆栈来读取参数了。
返回值是通过eax传送的。
4. 前面2.3)中的system call handler在Linux中是system_call()函数,由汇编写成。
在i和ii之间以及ii和iii之间,system_call判断PT_TRACESYS标志是否设置。如果设置,则system_call会两次调用system_trace,一次在就要执行system call service routine之前,另一次是在刚刚离开system call service routine之后。
5. 前面2.3).ii中,system_call会根据不同的放在ax的系统调用序号来调用不同的system call service routine。Linux使用了一个系统调用表,其中按序号保存了所有系统调用服务例程(system call service routine)的起始地址。system_call中的语句
call *system_call_table (0, %eax, 4)
指明每个表项4bytes,内核通过(系统调用表头 + 系统调用号 × 4)的方法得到相应系统调用服务例程的起始地址。然后开始执行。
6. 在内核初始化期间, trap_init()来初始化中断描述表(Interrupt Description Table,IDT),trap_init通过调用set_system_gate (0x80, &system_call)添加了0x80中断对应system_call处理例程。
7. 为了方便执行2.2), Linux提供了一组预处理宏指令。它们可以用在程序中,也可以用在内核中。这些宏指令取一定的参数,然后扩展为2.2)的语句。
这些宏指令具有类似下面的名称格式:
_syscallN(parameters)
其中N 是系统调用所需的参数数目,而parameters则用一组参数代替。每个宏都包括2+N×2个参数。第一个参数表明返回类型,第二个表示函数名,其余参 数两个一组。每组第一个表示真正参数类型,第二个是真正参数名称。 Linux一共定义了6个不同的_syscallN()宏指令,从_syscall0()、_syscall1()直到_syscall5()。 一旦_syscallN()宏指令用特定系统调用的相应参数进行了扩展,得到的结果是一个与系统调用同名的函数,它可以在用户程序中或内核中执行这一系统 调用。