网站首页 > 技术文章 正文
Linux系统调用是内核提供服务的接口函数,进程通过它完成自身所需的全部功能,每个平台都有自己的实现方式。我们以ARMv8为例讲解此调用的过程,包括程序调用C库的open函数,C库执行svc进入CPU异常模式,然后内核找到系统调用函数并执行它,最后返回到用户空间的一个过程。它就好比我们要建造一个房子,程序代码就是设计图纸,内核就是建筑工人,我们委托建筑工人根据图纸建好整个房子的过程。
一、程序调用C库执行open系统调用
假设程序已经创建并运行了一段时间,这时,程序要打开一个文件进行读写就需要执行open系统调用。系统调用的代码已经封装在C库里,程序只需调用C库的接口函数就可以实现全部功能。我们先来写一个模拟程序,看看如何实现的,如下图:
执行编译命令:
- aarch64-none-linux-gnu-gcc sys_call_open.c -o sys_call_open --static
执行反汇编命令:
- aarch64-none-linux-gnu-objdump -D sys_call_open > asm
得到汇编指令如下:
从上图得知,C库把fopen自动转换成了对openat的系统调用(前文讲了openat与open的区别),并把系统调用的参数写在x1,x2,x3寄存器里。
24160行,设置x8寄存器为系统调用号0x38(openat的系统调用号0x38)。
24161行,程序执行svc #immed指令将触发CPU进入异常模式,svc的立即数并没有被Linux使用,而是把系统调用号放到了x8寄存器里。执行完系统调用后,程序将会回到执行svc的下一行代码处继续执行。接下来,我们看一下CPU进入异常模式后是如何运行的。
二、CPU进入异常模式
程序执行C库svc指令后,CPU进入异常模式。内核根据异常类型及中断向量表里的地址,调用同步异常处理例程(中断设置我们另文再说),现在看看同步异常处理例程el0_sync的代码。
592行, kernel_entry是一个汇编宏代码,做进入系统调用前的准备工作,包括保存程序执行的现场,载入与CPU核相关的线程数据,保存异常返回地址等。打个比方就像建筑工人建房子到一半,需要回家办点事情,这时需要把图纸放下,把工衣脱掉,换好西装体面的回去。
593行,读取系统寄存器esr_el1的值。异常不单单只有系统调用会触发,内存缺页、指令错误等也会触发,因此,esr_el1[26:31]就保存了异常发生的原因。
594行,取出esr_el1中产生异常的原因,保存在x24里。
595行,ARM定义系统调用的原因为ESR_ELx_EC_SVC64,把它与x24与比较,如果相等则执行系统调用的代码,如果不相等则需继续往下走,表明异常是由其他原因触发的,比如内存缺页等。
596行,执行el0_svc系统调用的代码。
三、找到系统调用函数并执行它
el0_syc函数是用来处理系统调用的,首先从系统全局唯一的系统调用表里,取得C库调用svc时准备的系统调用号,然后根据调用号索引找到系统调用表里相应的函数地址,最后执行它。
892行,载入全局系统调用列表sys_call_table的地址。
893行,取出系统调用号(w8即是x8的低32位寄存器),保存到scno(x26)里。系统调用号是C库代码在每个系统调用前写入x8寄存器里的。
894行,获得全部系统调用的最大值并保存到sc_nr(x25)里。
907行,比较系统调用号与最大值,结果保存在状态寄存器里。
908行,比较的结果大于最大值,则跳到ni_sys处执行错误处理。
909行,把系统调用号作为索引,取出sys_call_table表中相应的函数地址,这里保存的就是sys_open的地址。sys_open函数地址如何设置到sys_call_table表里请参考前一篇文章。
910行,调用sys_open函数,完成此系统调用的功能。
四、返回用户空间
程序调用完sys_open函数后,系统调用的功能就执行完毕了,剩下的任务就是返回到程序的用户空间里,继续执行剩下的代码。就像建筑工人从家里回来继续工作时,要穿回工衣,拿回图纸,继续砌砖了。
ret_fast_syscall函数,执行流返回到的用户空间里去。
826行,关闭中断
827行,系统调用的返回值保存进堆栈里。
838行,kernel_exit是一个汇编宏与kernel_entry相对应,恢复之前程序执行的现场,设置异常返回地址,执行eret指令,返回到用户空间里去。
以上就是系统调用流程的代码分析。
相关文章连接:
最后放一张好看的风景照,表示想到那里去休息一下。梦想还是要有的,万一实现了呢!
猜你喜欢
- 2024-11-22 正点原子I.MX6U嵌入式Linux C应用编程:第二章《文件I/O基础》
- 2024-11-22 如何将Python函数输出内容同时打印到屏幕和文件
- 2024-11-22 3个重点,20个函数分析,浅析FFmpeg转码过程
- 2024-11-22 Linux驱动基础篇:hello驱动
- 2024-11-22 Python自带的库(open函数)读写txt、csv、json、XML、Excel文件
- 2024-11-22 UG NX OPEN二次开发实例:UF,C语言编程,创建圆柱体,API文档翻译
- 2024-11-22 openGauss SEQUENCE函数
- 2024-11-22 Python文件操作的步骤
- 2024-11-22 Python读取与写入Excel模块:openpyxl
- 2024-11-22 PHP imap_open函数任意命令执行漏洞
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)