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) // 底层