【学习笔记06】深入了解系统调用

举报
王建峰 发表于 2021/11/19 00:36:11 2021/11/19
【摘要】 目的 进程访问核心资源通过系统调用。站在系统调用的角度,层层深入下去,就能从某个系统调用的场景出发,了解内核中各个模块的实现机制。 但是在实际开发中不会直接使用系统调用,因为Linux 还提供了glib...

目的

进程访问核心资源通过系统调用。站在系统调用的角度,层层深入下去,就能从某个系统调用的场景出发,了解内核中各个模块的实现机制。
但是在实际开发中不会直接使用系统调用,因为Linux 还提供了glibc这个中介。它更熟悉系统调用的细节,并且可以封装成更加友好的接口。

glibc 的官网:http://www.gnu.org/software/libc/
下载glibc源码: git clone git://sourceware.org/git/glibc.git

linux kernel官网: https://www.kernel.org/
linux源码获取: https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.14.175.tar.xz

本文目标:从glibc提供的open函数出发,剖析如何从glibc的open调用到内核的open!!!


glibc封装、系统实现

1、 glibc封装
我们用户进程调用的glibc库封装的open函数,在glibc的源代码中,有以下相关文件

./sysdeps/unix/syscalls.list   # 列出所有glibc的函数对应的系统调用
./sysdeps/unix/make-syscalls.sh # 根据上面的配置文件,对于每一个封装好的系统调用,生成一个文件
./sysdeps/unix/syscall-template.S  # 定义了这个系统调用的调用方式
./sysdeps/hppa/sysdep.h   # 通过 `vim -t PSEUDO` 找到 PSEUDO 这个宏的定义。

  
 
  • 1
  • 2
  • 3
  • 4

经过分析代码open函数的代码逻辑,得出结论: 对于任何的系统调用,会调用DO_CALL。这也是一个宏,这个宏 32 位和 64 位的定义是不一样的。

2、 系统调用过程
# 32位系统平台
继续分析glibc源码,发现宏DO_CALL定义处unix/sysv/linux/i386/sysdep.h。这里做了几件事情

  1. 请求参数放在寄存器里面
  2. 系统调用名转换成系统调用号,放在寄存器 eax 里面
  3. 执行 ENTER_KERNE,产生一个软中断

然后中断处理entry_INT80_32就被调用了

  1. 这里会将用户态的上下文保存在pt_regs结构中
  2. 然后调用do_syscall_32_irqs_on,它会将系统调用号取出,查找系统调用表,找到内核的系统调用执行,并取出寄存器中保存的参数。
  3. 当系统调用结束的时候,这时中断返回,iret 指令将原来用户态保存的现场恢复回来。进程恢复用户态继续执行。

# 64位系统平台
DO_CALL定义在源码位置unix/sysv/linux/x86_64/sysdep.h,还是将系统调用名称转换为系统调用号,放到寄存器 rax。和32位不同的是,

  • 这里是真正进行调用,不是用中断了,而是改用 syscall 指令了。
  • 而且传递参数的寄存器也变了

syscall指令通过一个叫做MSR的特殊模块寄存器,拿出函数地址来调用,也就是entry_SYSCALL_64。其中

  1. 保存了很多寄存器到 pt_regs 结构里面,例如用户态的代码段、数据段、保存参数的寄存器
  2. 调用链entry_SYSCALL64_slow_pat->do_syscall_64,其中拿到系统调用号,在系统调用表中找到对应的系统调用,取参数,执行(老方法)
  3. 系统调用结束,返回用户态的指令变成了 sysretq。将进程恢复为用户态

总结

结合之前对用户态、内核态模式转换的学习用户态 - 系统调用 - 保存寄存器 - 内核态执行系统调用 - 恢复寄存器 - 返回用户态。64位系统中一个完整的系统调用,专栏中的总结图如下


补充知识

1、 系统调用表
# kernel源码位置:
数据结构定义在arch/x86/entry/syscall_64.c,系统调用列表输出在arch/x86/entry/syscalls/syscall_64.tbl

#系统调用号 abi类型 函数名                      系统调用名
2       common  open                    sys_open

  
 
  • 1
  • 2

# 系统调用函数声明
声明在include/linux/syscalls.h,找到有sys_open 的声明

# 系统调用函数实现
内核系统调用实现和声明一致,其中fs/open.c



  
 
  • 1

# 编译规则
接下来,在编译的过程中,需要根据 syscall_32.tbl 和 syscall_64.tbl 生成自己的 unistd_32.h 和 unistd_64.h。在文件arch/x86/entry/syscalls/Makefile



  
 
  • 1

参考资料

文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/feit2417/article/details/105434281

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。