张建帮 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
关于linux的系统调用,以下这张图片可以解释得比较清楚:
这幅图片以fork()函数为例,详细阐述了系统调用的具体的步骤与流程:
- 在源文件中使用的
fork()
函数会调用函数库中的fork()
函数- 函数库中的
fork()
会将sys_fork
的系统调用号2
保存在eax
寄存器中,然后调用int $0x80
指令自陷,进入内核态- 系统根据立即数
$0x80
,到IDT(Interrupt Descriptor Table,中断描述符表)中找到对应的中断处理程序的入口地址- 开始执行中断处理程序
- 中断处理程序根据
eax
寄存器中存储的系统调用号,到SCT
(Systerm call table,系统调用表)中找到sys_fork()
的入口地址- 执行
sys_fork
系统调用明白了整个系统调用的流程后,我们就可以通过嵌入式汇编直接调用想调用的系统调用了,具体流程如下:
- 查询相应的系统调用号
- 将系统调用号保存到
eax
寄存器中- 通过
int $0x80
指令自陷- 函数调用完成,自动将结果(返回值)保存在
eax
寄存器中- 将
eax
寄存器的内容进行输出
整体的流程相当于把函数库中的工作实现了一遍。
现在以 getpid
为例,分别用C函数库和嵌入式汇编(查看嵌入式汇编相关知识)实现其系统调用:
(getpid的系统调用号是 20
,点这里查看完整的系统调用列表)
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
printf("pid is %d\n",getpid());
int pidAsm = 0;
asm volatile(
"mov $0, %%ebx\n\t" //系统调用第一个参数,清零,不清零好像也没影响
"mov $20, %%eax\n\t" //保存系统调用号
"int $0x80\n\t" //自陷
"mov %%eax, %0\n\t" //将结果保存到变量 pidAsm中
: "=m"(pidAsm)
);
printf("using asm,pid is %d\n", pidAsm);
}
运行结果如下:
可以看到,两种方式得到的结果是一样的,这也印证了我们的猜想。