linux内核编程--3系统调用与宏参数字符串化

来源:互联网 发布:2016海外代购数据 编辑:程序博客网 时间:2024/06/16 13:23

系统调用

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;}

输出结果:

 


阅读全文
0 0
原创粉丝点击