第五章 系统调用
内核提供了用户进程与内核进行交互的一组接口。这些接口让应用程序受限地访问硬件设备,提供了创建进程并与已有进程进行通信的机制,也提供了申请操作系统其它资源的能力。
5.1 内核通信
中间层的作用:
1.它为用户空间提供了一种硬件的抽象接口
2.系统调用保证了系统的稳定和安全
3.使进程运行在虚拟系统中
5.2 API,POSIX和C库
应用程序通过在用户空间实现的API来编程,API并不需要与内核提供的系统调用对应。API可以在各种不同的操作系统上实现,给应用程序提供完全相同的接口,而它们本身在这些系统上的实现却可能迥异。
应用编程接口是基于POSIX标准的。
C库实现了Unix系统的主要API,包括标准C库函数和系统调用接口,C库提供了POSIX的绝大部分API
Unix的系统调用抽象出了用于完成某种确定的目的的函数,这些函数怎么使用不需要内核关心。
5.3 系统调用
要访问系统调用,通常通过C库中定义的函数调用来进行。系统调用最终有一种明确的操作。
如何定义系统调用:
- 编译指令 asmlinkage,通知编译器仅从栈中提取该函数的参数。所有的系统调用都需要这个限定词。
- 函数返回long
- sys_ 命名规则
系统调用号:在linux中,每个系统调用被赋予一个系统调用号,通过独一无二的号就可以关联系统调用。分配后不可变更,即使系统调用被删除,系统调用号也不能被回收利用。
系统调用列表:sys_call_table。
系统调用的性能:linux很短的上下文切换时间和简洁的系统调用
5.4系统调用处理程序
应用程序通过软中断的方式通知内核自己要执行一个系统调用,希望系统切换到内核态。
软中断:通过引发一个异常来促使系统切换到内核态去执行异常处理程序,此时的异常处理程序实质上就是系统调用处理程序。
软中断的中断号是128,通过int 0x80指令来触发该中断。这条指令会触发一个异常导致系统切换到内核态并执行128号异常处理程序,而该程序正是系统调用处理程序system_call()。
系统陷入内核空间时将系统调用号一并传给内核。在x86上,系统调用号是通过eax传给内核的。在陷入内核空间之前,用户空间就把相应系统调用所对应的号放入eax中,这样系统调用处理程序运行后可以从eax中得到数据。
大部分系统调用需要外部参数传入。在x86-32系统上,ebx,ecx,edx,esi,edi按照顺序存放前五个参数,六个或六个以上时,用一个单独的寄存器存放指向所有这些参数在用户空间地址的指针。返回值存在eax中。
5.5 系统调用的实现
注意可移植性和健壮性。
验证参数是否合法有效:
- 检查用户提供的指针是否有效
- 在用户空间读写数据时检查是否会引起堵塞
- 检查针对是否有合法权限
5.6系统调用上下文
内核在执行系统调用时处于进程上下文。Current指针指向当前任务,即引发系统调用的那个进程。
在进程上下文中,内核可以休眠并且可以被抢占。