对于日常使用的应用也不是脱离了硬件进行执行的,为了方便使用,就出现了操作系统,如果操作系统不是开放的,那就失去了操作系统的意义,为了方便使用操作系统,操作系统预留出了一些接口,这些接口就是系统调用函数。
当然系统调用函数肯定不同于库函数,接下来我将讲解Linux中的系统调用过程。
下图是软硬件的简单关系。
库函数:调用在用户态,执行在用户态
系统调用函数,调用在用户态,执行在内核态。
操作系统:管理软硬件资源,为用户提供人机交互的平台。
本文使用了系统调用里的文件函数open,read,write,close(可以查看我的博客,文件函数)
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
int main()
{
int fdr = open("passwd", O_RDONLY);
assert(fdr != -1);
int fdw = open("./newpasswd", O_WRONLY | O_CREAT, 0600);
assert(fdw != -1);
char buff[256] = {0};
int num = 0;
while((num = read(fdr, buff, 256)) > 0)
{
write(fdw, buff, num);
}
close(fdr);
close(fdw);
exit(0);
}
当程序遇到第一个系统调用函数open时,发生系统调用,接下来我将从程序的执行方面讲述:
第一步:程序进入操作系统查找系统调用号,并将系统调用号保存到eax寄存器中。
一般情况下Linux中open函数的系统调用号为5
第二步:将eax中的值作为系统调用表的下标执行系统内核函数
第三步:系统内核返回文件描述符,并将文件描述符保存给eax寄存器带出,赋值给fdr
一般情况下第一次打开的文件所返回的文件描述符为3,第二个为4(具体可以参考我的博客,文件描述符)
第四步:程序继续执行
大概过程如下所示
上面是对程序运行方面进行理解的,理解比较粗糙。
当发生系统调用时具体发生了以下事情:
1.将程序上下文保存
2.将函数对应的系统调用号保存到eax寄存器
3.出发0x80中断,切换到内核态执行中断处理
ps:因为每当发生系统调用都会执行上面的步骤,所以输入输出函数都会带有缓冲区,以减少发生系统调用的次数