系统调用的两种方式
一、库函数API调用
以系统函数rename为例,研究Linux系统的系统调用过程。
编写一个简单的API调用系统函数rename的c程序
#include <stdio.h>
int main(){
int ret;
char *oldname = "hello.c";
char *newname = "20209329_zbl";
ret = rename(oldname, newname);
if(ret == 0) //rename函数调用成功将返回0
printf("Renamed successfully
");
else
printf("Unable to rename the file
");
return 0;
}
编译运行,可见该程序已经将当前目录下的hello.c重命名为20209329_zbl。
接下来,通过c语言内嵌汇编的方式,深入了解系统函数的调用过程。
二、内嵌汇编调用
编写一个内嵌汇编的c语言程序,同样实现系统函数rename的调用。
#include <stdio.h>
int main(){
int ret;
char *oldname = "hello.c";
char *newname = "20209329_zbl";
asm volatile(
"movl %2,%%ecx
" //参数按顺序赋值给ebx,ecx
"movl %1,%%ebx
"
"movl $0x26,%%eax
"//实现将系统调用号38传递给eax
"int $0x80" //触发系统调用
:"=a"(ret)
:"b"(oldname), "c"(newname)
);
if(ret == 0)
printf("Renamed successfully
");
else
printf("Unable to rename the file
");
return 0;
}
注意在编译的时候要指定 -m32,另外将文件名还原为hello.c,执行程序。
可见,通过内嵌汇编实现了与API调用同样的功能。
三、总结
以rename函数为例,系统调用的三层机制分别为rename(),system_call(通过int $0x80触发系统调用),sys_rename(通过系统调用号38指定系统调用)。
系统函数的调用过程:从API函数,进入系统调用的封装,封装里的int指令会触发软中断,保存用户态的上下文,0x80对应中断服务程序的入口,通过系统调用号指定系统调用例程,执行例程,例程执行完毕后返回用户态。