实验四
一、实验步骤
1.选取一个系统调用,这里选择的是getpid()
,其功能是取得进程识别码。
打开/usr/include/asm/unistd.h
查找系统调用号为20。
使用man getpid
查看参数个数,发现它没有参数
2.使用库函数编写
#include<unistd.h>
int main()
{
int a;
a=getpid();//调用库函数得到进程号
printf("pid=%d",a);
}
可以看到运行成功:
3.C代码中嵌入汇编代码编写
#include<stdio.h>
#include<unistd.h>
int main()
{
int a;
asm volatile(
"mov $0x14,%%eax
"//getpid()系统调用号为20,用16进制表示为0x14
"int $0x80
"//触发系统调用
"mov %%eax,%0
"//通过EAX寄存器返回系统调用值
:"=m"(a)
);
printf("pid=%d
",a);
return 0;
}
同样可以运行成功:
二、实验分析
两种方式得到的效果是一样的,但嵌入汇编代码能让我们更清楚地看到用户态进程通过系统调用的方式陷入内核态之前具体做了什么,具体来说它传递了系统调用号,还有通过EBX寄存器传递参数。具体分析:
1.由于getpid()没有参数,所以不需要给EBX寄存器赋值
2.“mov $0x14,%%eax”把0x14放到了EAX寄存器里面,EAX寄存器用于传递系统调用号,这里十六进制的14是20,即getpid()的系统调用号
3.“int $0x80”是触发系统调用陷入内核执行系统调用的内核处理函数
4.“mov %%eax,%0”系统调用会有一个返回值,通过EAX寄存器来返回,我们把EAX寄存器的值放到a变量中
三、实验总结
系统调用的工作机制:
应用程序在用户态调用API函数,API将封装的系统调用号保存到EAX寄存器中,将参数保存到EBX、ECX等寄存器,读取0x80中断向量触发中断,然后陷入内核态,中断服务程序根据系统调用号调用并执行对应的系统调用函数,系统调用函数执行完毕后将结果存放在EAX中并返回给程序,程序返回用户态。