使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
潘俊洋
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用。 Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用。用户可以通过系统调用命令在自己的应用程序中调用它们。从某种角度来看,系统调用和普通的函数调用非常相似。区别仅仅在于,系统调用由操作系统核心提供,运行于核心态;而普通的函数调用由函数库或用户自己提供,运行于用户态。
fork函数
本次实验选择fork系统调用,其系统调用号为:
2 i386 fork sys_fork stub32_fork
以下参考jason314的博客。 一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
- 在父进程中,fork返回新创建子进程的进程ID;
- 在子进程中,fork返回0;
- 如果出现错误,fork返回一个负值;
在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。 引用一位网友的话来解释fpid的值为什么在父子进程中不同。“其实就相当于链表,进程形成了链表,父进程的fpid(p 意味point)指向子进程的进程id, 因为子进程没有子进程,所以其fpid为0。
嵌入式汇编:
volatile
关键字表示禁止编译器优化,然后把 ebx
寄存器清零,把第 2 号系统调用赋给 eax
寄存器,然后再调用0x80
号系统中断,最后把 eax 的值赋给第 0 个变量,即下面的 fpid
变量。
运行这段函数,并与使用 C 语言实现的系统调用进行对比。如上图所示,二者运行结果完全相同。
一般来说,完成系统调用需要3层,第一层是用户函数,第二层库函数,第三层是内核。三层之间的关系是,用户函数调用库函数提供的API API中封装了调用sysem_call的代码。最后才是执行内核。第二层会把系统调用号放在eax寄存器中,这时进程会跳到的内核叫做sysem_call的位置。这个过程检查系统调用号(系统调用号告诉内核进程请求哪种服务),ebx、ecx、edx、esi和edi 来依次传递服务所需要的参数(最多五个参数),当系统调用返回时,返回值存放在 eax 中。