如何在 SI 中找到系统调用源代码
回到开始的话题,既然不能直接通过系统调用声明跳转到定义的代码处,那么怎样在 SI 快速找到系统调用的源码呢?通过上面的sys_open 的展开,相信大家已经知道带有三个参数的系统调用展开的过程,由于系统调用中最多可以带有六个参数,那么Linux 内核中定义了一组宏用来展开带有不同参数的系统调用:
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) #define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__) #define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__) #define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
那么有了这些宏之后,系统调用定义处的部分就可以用宏来代替了,如:
fork系统调用,就可以定义为 SYSCALL_DEFINE0(fork)
brk系统调用,就可以定义为 SYSCALL_DEFINE1(brk, unsigned long)
creat 系统调用,就可以定义为 SYSCALL_DEFINE2(creat, const char __user *, pathname, umode_t, mode)
...
可以找出规律,SYSCALL_DEFINE 后面跟系统调用所带的参数个数n,第一个参数为系统调用的名字,然后接2*n个参数,每一对指明系统调用的参数类型及名字。那么下次我们想在 SI 中找某个系统调用的代码时,使用 SI 提供的全局搜索功能(快捷键 Ctrl-/),以open系统调用为例,输入 SYSCALL_DEFINE3(open ,如图1所示。有了这个方法以后就再不用担心找不到系统调用的内核代码了。
图1 SI中搜索sys_open函数代码
到此,我们学习到了宏定义的一些高级用法(如…、##等),还知道了如何在SI中通过搜索找系统调用代码,学习过程中还会不时感慨开发 Linux 内核这些大牛们怎能将宏运用得如此出神入化。如果只知道这些肯定还是不够的,我们试想一下为什么要用宏定义把系统调用搞得这么复杂?直接用展开的形式不好么?可以肯定的是内核开发者不是单纯地秀代码技巧,至于这样写带来的好处是什么?