glibc 对系统调用的封装

glibc 中的 open

  1. int open(const char *pathname, int flags, mode_t mode)

32 位系统调用过程

将请求参数放在寄存器里面,根据系统调用的名称,得到系统调用号,放在寄存器 eax 里面,然后执行 ENTER_KERNEL。

  1. # define ENTER_KERNEL int $0x80

int 就是 interrupt,也就是“中断”的意思。int $0x80 就是触发一个软中断,通过它就可以陷入(trap)内核。

进入内核之前,保存所有的寄存器,然后调用 do_syscall_32_irqs_on。

将系统调用号从 eax 里面取出来,然后根据系统调用号,在系统调用表中找到相应的函数进行调用,并将寄存器中保存的参数取出来,作为函数参数。

  1. #define INTERRUPT_RETURN iret

iret 指令将原来用户态保存的现场恢复回来,包含代码段、指令指针寄存器等。这时候用户态进程恢复执行。

image.png

调用流程

  1. 将系统调用参数放到寄存器里
  2. 将要调用的系统调用号放到 eax 里
  3. 执行 ENTER_KERNEL 进入内核
  4. 保存用户态寄存器 (貌似时内核负责保存用户寄存器)
  5. 取出系统调用号, 在系统调用表中找到相应的函数进行调用
  6. 系统调用结束后调用 INTERRUPT_RETURN, 将原来用户态保存的现场恢复回来
  7. 用户态恢复运行

64 位系统调用过程

image.png

系统调用表

sys_call_table:

  • 32 位的系统调用表定义在 arch/x86/entry/syscalls/syscall_32.tbl 文件里
  • 64 位的系统调用定义在另一个文件 arch/x86/entry/syscalls/syscall_64.tbl 里

第一列的数字是系统调用号。可以看出,32 位和 64 位的系统调用号是不一样的。第三列是系统调用的名字,第四列是系统调用在内核的实现函数。

总结

image.png