前言
系统调用
系统调用的实现
系统调用过程描述
增加系统调用
第1步,添加新的系统调用函数源代码
第2步,连接新的系统调用
第3步,重建新的Linux内核
第4步,使用新的系统调用
系统调用
- 每个系统调用都是通过一个单一的入口点多路传入内核。eax 寄存器用来标识应当调用的某个系统调用,这在 C 库中做了指定(来自用户空间应用程序的每个调用)。
- 当加载了系统的 C 库调用索引和参数时,就会调用一个软件中断(0x80 中断),它将执行 system_call 函数(通过中断处理程序),这个函数会按照 eax 内容中的标识处理所有的系统调用。
- 在经过几个简单测试之后,使用 system_call_table 和 eax 中包含的索引来执行真正的系统调用了。从系统调用中返回后,最终执行 syscall_exit,并调用 resume_userspace 返回用户空间。然后继续在 C 库中执行,它将返回到用户应用程序中。
系统调用的实现
- 系统调用是操作系统向用户提供的程序一级的服务,用户程序借助于系统调用命令向操作系统提出各种资源要求和服务请求。
系统调用过程描述
- (1)用户程序调用C库中的API。
- (2)API里面有软中断int 0x80语句,这条指令的执行会让系统跳转到一个预设的内核空间地址,它指向系统调用处理程序,即system_call函数,同时把系统调用号放入eax寄存器中。
- (3)system_call系统调用处理程序是在执行系统调用服务例程之前的一个引导过程,针对int 0x80这条指令,面向所有的系统调用。system_call读取eax寄存器,获取系统调用号,将其乘以4,生成偏移地址,并以sys_call_table为基址,转到执行具体的系统调用服务例程。
(4)系统调用服务例程将从堆栈里获取参数并执行。由于system_call执行前,会先将参数存放在寄存器中,system_call执行时会首先将这些寄存器压入堆栈。system_call退出后,用户可以从寄存器中获得(被修改过的)参数。
注意:系统调用通过软中断INT 0x80陷入内核,跳转到系统调用处理程序system_call函数,然后执行相应的服务例程。但是由于是代表用户进程,所以这个执行过程并不属于中断上下文,而是进程上下文。因此,系统调用执行过程中,可以访问用户进程的许多信息,可以被其它进程抢占,可以休眠。
(5)系统调用完成后,把控制权交回到发起调用的用户进程前,内核会有一次调度。如果发现有优先级更高的进程或当前进程的时间片用完,那么会选择优先级更高的进程或重新选择进程执行
增加系统调用
如果用户在Linux中添加新的系统调用,应该遵循几个步骤才能添加成功。
第1步,添加新的系统调用函数源代码
- 第一个任务是编写加到内核中的源程序,即将要加到一个内核文件中的一个函数,该函数的名称应该是新的系统调用名称前面加上sys_标志。
- 假设新加的系统调用为mycall(int number),在/usr/src/Linux/kernel/sys.c文件中添加源代码,它对应的函数名称为sys_mycall。
第2步,连接新的系统调用
添加新的系统调用后,下一个任务是使Linux内核的其余部分知道该程序的存在。为了从已有的内核程序中增加到新的函数的连接,需要编辑两个文件:
- /usr/src/Linux/include/asm-i386/unistd.h:该文件中包含了系统调用清单,用来给每个系统调用分配一个唯一的号码。
- /usr/src/Linux/arch/i386/kernel/entry.S:该清单用来对sys_call_table[]数组进行初始化。该数组包含指向内核中每个系统调用的指针。这样就在数组中增加了新的内核函数的指针。
第3步,重建新的Linux内核
- 为使新的系统调用生效,需要重建Linux的内核。
- 这需要以超级用户身份登录。
- 具体方法可以参考内核编译步骤。
第4步,使用新的系统调用
- 在应用程序中使用新添加的系统调用。
- 由于使用了系统调用,编译和执行程序时,用户都应该是超级用户身份。
总结
系统调用
系统调用的实现
系统调用过程描述
增加系统调用
第1步,添加新的系统调用函数源代码
第2步,连接新的系统调用
第3步,重建新的Linux内核
第4步,使用新的系统调用