系统调用
1. 简介
linux内核中设置了一组用于实现系统功能的子程序,称为系统调用。系统调用和普通库函数调用非常相似,只是系统调用由操作系统核心提供,运行于核心态,而普通的函数调用由函数库或用户自己提供,一般运行于用户态(也可能间接调用系统调用)。
2. 内核系统调用申明与实现
Linux内核提供的系统调用大多声明在文件:include\linux\syscalls.h,而具体函数实现通过宏定义定义在内核系统的各个子模块中。以下以最简单的系统调用:epoll_create(创建epoll句柄)为例,讲解系统调用的申明与实现。
1) 声明
在文件include\linux\syscalls.h中申明了系统调用:
asmlinkage long sys_epoll_create(int size);
函数sys_epoll_create接受一个入参size,自内核2.6.8后此入参其实已经没用,只要求大于0即可:
Since Linux 2.6.8, the size argument is ignored, but mustbe greater than zero;
2) 实现
在内核include\linux\eventpoll.c中,有如下实现:
SYSCALL_DEFINE1(epoll_create,int, size)
{
if (size <= 0)
return -EINVAL;
return sys_epoll_create1(0);
}
将宏SYSCALL_DEFINE1(epoll_create, int, size)展开其实就是asmlinkagelong sys_epoll_create(int size)。宏参数的第一个参数将扩展为系统调用函数:sys_epoll_create,其后所有参数都以type/value形式成对出现,将扩展成函数的入参定义。具体分析如下(SYSCALL_DEFINEX宏解析):
a. 文件include\linux\syscalls.h定义宏SYSCALL_DEFINE1为
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1,_##name, __VA_ARGS__)
表示此系统调用需要一个入参。
b. SYSCALL_DEFINEx为如下:
#defineSYSCALL_DEFINEx(x, sname, ...) \
SYSCALL_METADATA(sname, x, __VA_ARGS__) \
__SYSCALL_DEFINEx(x,sname, __VA_ARGS__)
c. 由于没有定义CONFIG_FTRACE_SYSCALLS预编译宏,上述第一个宏定义 SYSCALL_METADATA定义为空,第二个宏为:
#define__SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \
__attribute__((alias(__stringify(SyS##name)))); \
static inline longSYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \
asmlinkage longSyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \
asmlinkage longSyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \
{ \
long ret =SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \
__MAP(x,__SC_TEST,__VA_ARGS__); \
__PROTECT(x,ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \
return ret; \
} \
static inline longSYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))
d. sys##name会拼接上一层调用宏创建来的name,也就是sname,sname的再上一层是_##name,也就是_epoll_create,最后就变成sys_epoll_create(…),下面请看括号里面的宏参数转换;
e. 有__MAP定义如下:
#define __MAP(n,...) __MAP##n(__VA_ARGS__)
所以括号里面的参数就变成:__MAP1(__VA_ARGS__)
有__MAP1如下:
#define __MAP1(m,t,a) m(t,a)
所
以刚才_
_MAP(x,__SC_DECL,__VA_ARGS__)就被扩展为__SC_DECL(t,a)
再有__SC_DECL如下:
#define __SC_DECL(t, a) t a
最终括号里面的内容就扩展为:
int size
f. 最终整个__SYSCALL_DEFINEx的第一行就扩展称为------------即函数名定义:
asmlinkage long sys_epoll_create(int size)
也就是函数的实现。
g. __SYSCALL_DEFINEx宏的后面部分是利用__attribute__设置系统调用的别名。用兴趣的读者可以自己展开分析。
宏参数字符串化
在上面的宏内容解析中用到了##,其含义就是以字符串形式拼接后面的参数;还有一个符号:#,含义就是字符串化宏参数。
以下详细讲诉此(#、##)用法:
1. 宏参数字符串化
定义如下宏:
#define STRINGIFY(x)#x
#define TOSTRING(x) STRINGIFY(x)
分析:第一个宏是将参数字符串化,第二个宏是将参数宏化(也就是在字符串上加了个括号,使之成为一个独立的整体,相对于提高了优先级)。
2. 宏参数字符串拼接:
#define STR_APPEND2_STR(param1, param2) TOSTRING(param1##param2)
分析:先将两个参数以字符串形式拼接,在将整体字符串化。
测试:
源代码:
ULONG cvtest_Test4_Hong2Str()
{
ULONG ulRet = ERROR_SUCCESS;
printf("to_str = %s, append_str = %s\n", TOSTRING(1+1), STR_APPEND2_STR(ABC_, ABC));
return ulRet;
}
输出结果: