8第八章Linux下的系统调用
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第八章 Linux下的系统调用
8.1 系统调用介绍
8.1.1 引言
系统调用是内核提供的、功能十分强大的一系列函数。它们在内核中实现,然后通过一定的方式(库、陷入等)呈现给用户,是用户程序与内核交互的一个接口。如果没有系统调用,则不可能编写出十分强大的用户程序,因为失去了内核的支持。由此可见系统调用的地位举足轻重。内核的主体可以归结为:
系统调用的集合;
实现系统调用的算法。
8.1.2 系统调用的实现流程
这里我们通过getuid()这个简单的系统调用来分析一下系统调用的实现流程。在分析这个程序时并不考虑它的底层是如何实现的,而只需知道每一步执行的功能。
首先来看一个例子:
#include
int main()
{
int i=getuid();
printf(“Hello World! This is my uid: %d\n”,i);
}
#include
arch/i386/kernel/traps.c
set_system_gate(SYSCALL_VECTOR,&system_call);
从这行程序我们可以看出,系统规定的系统调用的处理程序就是system_call。控制转移到内核之前,硬件会自动进行模式和堆栈的切换。现在控制转移到了system_call,保留系统调用号的最初拷贝之后,由SAVE_ALL来保存上下文,得到该进程结构的指针,放在ebx里面,然后检查系统调用号,如果__NR_getuid(24)是合法的,则根据这个系统调用号,索引sys_call_table,得到相应的内核处理程序:sys_getuid。执行完sys_getuid之后,保存返回值,从eax移到堆栈中的eax处,假设没有
意外发生,于是ret_from_sys_call直接到RESTORE_ALL恢复上下文,从堆栈中弹出保存的寄存器,堆栈切换,iret。执行完iret后,正如我们所分析的,进程回到用户态,返回值保存在eax中,于是得到返回值,打印:
Hello World! This is my uid: 551
这时这个最简单的调用系统调用的程序到这里就结束了,系统调用的流程也理了一遍。跟系统调用相关的内核代码文件主要有:arch/i386/kernel/entry.S
include/linux/unistd.h
下面将分别介绍这两个文件。
8.1.3 entry.S文件相关说明
这个文件中包含了系统调用和异常的底层处理程序,信号量程序。
一.关于SAVE_ALL,RESTORE_ALL
arch/i386/kernel/entry.S
#define SAVE_ALL
cld;
pushl %es;
pushl %ds;
pushl %eax;
pushl %ebp;
pushl %edi;
pushl %esi;
pushl %edx;
pushl %ecx;
pushl %ebx;
movl $(__KERNEL_DS),%edx;
movl %edx,%ds;
movl %edx,%es;
#define RESTORE_ALL
popl %ebx;
popl %ecx;
popl %edx;
popl %esi;
popl %edi;
popl %ebp;
popl %eax;
1: popl %ds;
2: popl %es;
addl $4,%esp;
3: iret;
这一部分程序主要执行的任务就是中断时保存进程的上下文以及执行中断后恢复进程上下文的环境。
二.系统调用表(sys_call_table)
在这个文件中还有一个重要的地方就是维护整个系统调用的一张表――系统调用表。系统调用表一次保存着所有系统调用的函数指针,以方便总的系统调用处理程序(system_call)进行索引调用。
arch/i386/kernel/entry.S
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_syscall)
.long SYMBOL_NAME(sys_exit)
.long SYMBOL_NAME(sys_fork)
.long SYMBOL_NAME(sys_read)
…
.long SYMBOL_NAME(sys_ni_syscall)
.long SYMBOL_NAME(sys_ni_syscall)
1 .rept NR_syscalls-(.-sys_call_table)/4
.long SYMBOL_NAME(sys_ni_syscall)
.endr
1行中的‘.’代表当前地址,sys_call_table代表数组首地址,所以1行中两个变量相减,得到差值表示这个系统调用表的大小(两个地址之间相差的byte数),除以4,得到现在的系统调用个数。用NR_syscalls 减去系统调用个数,得到的是没有定义的系统调用。然后用
.rept …
.long SYMBOL_NAME(sys_ni_syscall)
.endr
往数组的剩余空间里填充sys_ni_syscall。
三.system_call和ret_from_sys_call
arch/i386/kernel/entry.S
ENTRY(system_call)
pushl %eax # save orig_eax