实验过程
查看系统调用号
这里使用的是getpid函数与mkdir函数,对应的系统调用号为20与39.
实现系统调用
getpid函数
#include<stdio.h>
#include<unistd.h>
int main()
{
int pid;
pid=getpid();
printf("the pid is %d",pid);
return 0;
}
使用汇编代码实现:
#include<stdio.h>
#include<unistd.h>
int main()
{
int pid;
asm volatile(
"mov $0x14,%%eax
"
"int $0x80
"
"mov %%eax,%0
"
:"=m"(pid));
printf("the pid is %d
",pid);
return 0;
}
汇编代码细化了调用过程,getpid函数没有参数,首先将对应的系统调用号传入到ax寄存器中,int指令使系统内陷,执行相应的系统调用,最后返回值存入ax寄存器中,将ax中的返回值取出放入变量pid,打印输出。
mkdir函数
#include<stdio.h>
#include<unistd.h>
int main(){
int ret;
char *dir="dir";
asm volatile(
"movl %1,%%ebx
"
"movl $0x27,%%eax
"
"int $0x80"
:"=a"(ret)
:"b"(dir));
printf("the ret is %d
",ret);
return 0;
}
与上面的getpid函数相比,mkdir函数对应多了一个参数,其需要传入一个字符数组,这里以一个指针的形式传入到寄存器bx中,其他的操作与getpid函数无异。
小结
系统调用的三层皮:API、中断向量对应的system_call、中断服务程序sys_xyz
API:应用程序接口是一个函数定义,系统调用通过软中断向内核发出一个明确的请求。
系统调用:操作系统为用户态进程与硬件设备进行交互提供了一组接口。
当API中包含系统调用时,通过系统调用号进入相应的系统调用,然后使用软中断进入内核态。中断发生后会保护现场,将用户的当前栈顶地址,当时的状态字和cs:eip的值进行压栈。之后进入由用户态切换到内核态,处理完中断程序后还原现场,返回用户态。
xyz()函数,是系统调用对应的API,这个应用程序编程接口里面封装了一个系统调用,这个系统调用会触发一个int 0x80的中断,0x80这个中断向量对应着system_call这个内核代码的起点,这个内核代码里面会有SAVE_ALL,然后执行到sys_xyz()中断服务程序,进入程序里面处理,在中断服务程序执行完之后会ret_from_sys_call,在return的过程中可能会发生进程调度(这是一个进程调度的时机),如果没有进程调度,就会iret,回到用户态接着执行。