Linux 的系统调用在内核中的入口函数都是 sys_... 但是实际上找不到 sys_ 开头的函数的定义, 因为Linux的系统调用对应的函数全部都是由 SYSCALL_DEFINE 相关的宏来定义的
在路径 /include/linux/syscalls.h 中可以看到对 SYSCALL_DEFINE 的宏定义,
1 2 3 4 5 6
| #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__)
|
这里的不同数字在 linux 内核中常用来封装不同数量的输入 (argc), 而对 SYSCALL_DEFINEx 的定义如下:
1 2 3
| #define SYSCALL_DEFINEx(x, sname, ...) \ SYSCALL_METADATA(sname, x, __VA_ARGS__) \ __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
|
也就是加了一层转载
__前缀: 在 linux 内核源码中, 或者说在 C 语言的习惯中, 如果变量有前缀 __ 往往指代底层功能函数, 即少封装, 少检查, 底层, 内部用的含义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #define __SYSCALL_DEFINEx(x, name, ...) \ __diag_push(); \ __diag_ignore(GCC, 8, "-Wattribute-alias", \ "Type aliasing is used to sanitize syscall arguments");\ asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ __attribute__((alias(__stringify(__se_sys##name)))); \ ALLOW_ERROR_INJECTION(sys##name, ERRNO); \ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ { \ long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ __MAP(x,__SC_TEST,__VA_ARGS__); \ __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ return ret; \ } \ __diag_pop(); \ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
|
宏的使用中,
## 是字符串连接符,
__VA_ARGS__ 用于在 可变参数宏(variadic macro)中表示"传入的全部可变参数"
... 表示宏函数可以接受任意数量的 arg,由 __VA_ARGS__ 进行捕获
__SC_DECL 用来把一组“类型 + 形参名”的成对参数,拼成函数形参列表
也就是通过第一个参数 x, 我们可以看到首先会生成一个接口 sys##name, 并且接受
以socket为例来说明; sys_socket 函数的声明在linux-2.6.34\include\linux\syscalls.h中, 如下所示:
1 2 3 4
| asmlinkage long sys_socket(int, int, int);
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) SYSCALL_DEFINEx(3, _socket, int, family, int, type, int, protocol)
|