追踪系统调用流程

前言

   我不是专业的内核研读者,只因自己的好奇心,想一探(IO多路复用)select系统调用的实现原理,于是一路追踪到其内核的底层调用。特此记录这一段学习过程。

本机系统:ubuntu15.04
内核版本:3.19.0-29-generic


sys_select 哪去了?

   当用户态使用系统调用的时候,操作系统会根据该函数的系统调用号找到其对应的底层函数(前缀 sys_ )去执行。比如当用户调用下面的函数的时候:

系统调用 内核实际调用
open() sys_open()
select() sys_select()
poll() sys_poll()
sys_…

于是我在 /include/sys/select.h 库文件中找到了select() 函数的声明:

这里写图片描述

在下载的本系统的内核源代码中找到了 select() 函数的源文件 —- /fs/select.c
但是,在这个文件中并没有找到其对应的底层函数 sys_select() , 只找到了这个(下图),看起来是select()的实现源码
追踪系统调用流程_第1张图片

难道sys_select 调用没了? SYSCALL_DEFINE5 这个宏是什么东西?

SYSCALL_DEFINEx 宏

于是经过网上查阅,我找到了这个文件 — /include/linux/syscalls.h

瞬间顿悟了。。。下来根据文件内容,我们将这个宏一步步展开

1、SYSCALL_DEFINE5 变成了SYSCALL_DEFINEx 的形式,## 是连接符,比如 ##select 就变成了 _select , __VA_ARGS_ 表示可变参数,对应函数中的多个参数。
追踪系统调用流程_第2张图片

2、SYSCALL_DEFINEx转变为 __SYSCALL_DEFINEx 的形式
这里写图片描述

3、__SYSCALL_DEFINEx 转变为 asmlinkage long sys##name 的形式,
sys##name 即 sys_select , (惊喜!) 看来 sys_ 这个底层调用还是依然存在的。
下来分析括号里面的 __MAP、__SC_LONG ,只要继续找到这两个宏定义就行了。
追踪系统调用流程_第3张图片

4、这一步就可以把上面括号中的数字5 去除了,转变成这样的形式:
asmlinkage long sys_select(MAP5(__SC_DECL,__VA_ARGS))
追踪系统调用流程_第4张图片

5、结合上一步的MAP宏,括号中转变为 SC_DECL(int,__nfds) ,__MAP4(__SC_DECL,__VA_ARGS)
根据下图,进一步转变可以分离出select()的第一个参数:
asmlinkage long sys_select(int nfds, __MAP4(__SC_DECL,__VA_ARGS))

这里写图片描述

6、第4的图可以看出,MAP宏是一个递归展开的过程,所以继续展开下去,最终 select()调用会变为如下形式:

asmlinkage long sys_select(int n,fd_set __user*  inp, fd_set __user*  outp,fd_set __user*  exp,struct timeval __user*  tvp)

据查,2.6.28及其之后的内核源码里,系统调用的过程都是通过以上形式的宏定义实现的。

http://blog.chinaunix.net/uid-23069658-id-4106015.html

sys_底层调用

接下来就是底层常见的调用流程:

追踪系统调用流程_第5张图片

如何找到sys_调用

1、
当用户态在执行一个系统调用时,会首先确定对应的系统调用号。对应关系在下面的文件中:

/usr/include/asm/unistd_64.h

追踪系统调用流程_第6张图片
可知,select的系统调用号是23

2、
根据系统调用号在系统调用表中找到对应的内核调用,系统调用表在如下文件中:(需要下载内核源码)

linux-source-3.19.0/arch/x86/syscalls/syscall_64.tbl

追踪系统调用流程_第7张图片

所以,select的入口是sys_select,即前面那个宏展开后的函数名。

你可能感兴趣的:(操作系统,Linux编程,C/C++,内核,select,系统调用,系统调用号)