盘点Linux内核源码中使用宏定义的若干技巧(1)

转载 2012年03月22日 16:52:38

在C中,宏定义的概念虽然简单,但是真要用好却并不那么容易,下面从Linux源码中抽取一些宏定义的使用方法,希望能从中得到点启发:

1. 类型检查
比如module_init的宏定义:
<include/linux/init.h>

点击(此处)折叠或打开

  1. #define module_init(initfn)                    \
  2.     static inline initcall_t __inittest(void)        \
  3.     { return initfn; }                    \
  4.     int init_module(void) __attribute__((alias(#initfn)));
module_init宏的关键点是在代码中的第4行,通过gcc别名的特性将init_module与initfn等同起来。这里宏定义的技巧出现在第2和3行,通过return initfn实际上是来对initfn做静态类型检查,以确保程序员不会提供一个原型不符合要求的模块初始化函数,后者要求是一个参数值为void,返回者为int类型的函数。所以,如果你定义了一个比如void my_module_init(void)或者是void my_module_init(int)这样的模块初始化函数作为module_init宏参数,那么编译时就会出现类似下面的warning:
GPIO/fsl-gpio.c: In function '__inittest':
GPIO/fsl-gpio.c:46: warning: return from incompatible pointer type

2. 变长参数列表,比如系统调用相关的定义:
<include/linux/syscalls.h>

点击(此处)折叠或打开

  1. #define __SYSCALL_DEFINEx(x, name, ...)                    \
  2.     asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))

点击(此处)折叠或打开

  1. #define SYSCALL_DEFINEx(x, sname, ...)                \
  2.     __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

点击(此处)折叠或打开

  1. #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
  2. #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
  3. #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
  4. #define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
  5. #define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
  6. #define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
所以源码中的SYSCALL_DEFINE1(close, unsigned int, fd)将展开成:
asmlinkage long sys_close(__SC_DECL1(unsigned int, fd))
可见,上述宏定义SYSCALL_DEFINE1中除第一个参数明确给出外,对于可变长的参数列表,采用__VA_ARGS__就可以圆满解决,换言之,__VA_ARGS__成了变长参数列表的容器了。

3.这条也不能算是技巧了,C中宏定义的一种很常见的用法,Linux内核源码中也大量使用:
  #define STR(x)  #x
#的使用将把宏参数x变成一个字符串,比如STR(my hub)将转化成"my hub"
另一个常见的符号是##,它用来将两个参数粘合到一起,比如
  #define STR1(a,b) a##b
那么STR1(my hub,   is good)将转换成my hubis good,可见##会自动把第2个参数前的空格给移除掉。

4. do...while(0)这个就不用多说了吧,不过有个问题是,如果使用{...}来代替do{...)while(0)行不行呢?

相关文章推荐

linux进程管理

提到程序的执行必不可少我们会想到的就是进程,那么进程到底是什么呢?         计算机系统为了使程序可以并发执行,引入了“进程”的概念。...

如何获得gcc/g++编译宏定义和头文件搜索目录的方法说明

/* co-gcc.lnt: This is the seed file for configuring Lint for use with GCC versions 2.95.3 and late...

Linux内核源码中使用宏定义的若干技巧

在C中,宏定义的概念虽然简单,但是真要用好却并不那么容易,下面从Linux源码中抽取一些宏定义的使用方法,希望能从中得到点启发: 1. 类型检查 比如module_init的宏定义: ...
  • sdulibh
  • sdulibh
  • 2016年04月19日 15:05
  • 445

Linux内核源码--min,swap宏定义

Linux3.5的部分宏定义在Linux-3.5/include/linux/kernel.h的头文件中有定义 一: 最大值和最小值相关的宏 [cpp] view ...

【C语言】Linux内核源码--min,swap宏定义

Linux3.5的部分宏定义在linux-3.5/include/linux/kernel.h的头文件中有定义 /* * min()/max()/clamp() macros that also d...

linux内核源码中常见宏定义

1. gcc的__attribute__编绎属性 要了解Linux Kernel代码的分段信息,需要了解一下gcc的__attribute__的编绎属性,__attribute__主要用于改变所声明...

Linux内核源码分析--内存管理(二、函数实现技巧)

仔细的分析了一下各个内存管理函数的实现,发现里面涉及到了几个技巧,如果知道了这几个技巧,那么阅读内存管理源码将会事半功倍(主要是这几个技巧在几个函数中都出现过),当然也会选择性的分析几个比较重要的函数...

Linux内核源码分析之文件系统(1) -- 三思而后行

经历过内核源码阅读的小伙伴,相信你在最初面对如此庞大的内核源码时,一定会有种“面对茫茫大海,无从下手”的感觉,即便幸运的找到了逻辑入口,却在繁琐的函数跳转中晕头转向,无法把握住整个控制路径上的重难点。...

Linux内核源码分析(1)——compiler.h分析

原文:http://www.jtben.com/document/17660 Linux的内核源码都会包含文件linux\compile.h,所以先分析该文件内的内容,作为开篇。 ...
  • mznd520
  • mznd520
  • 2014年03月29日 18:11
  • 553

linux内核源码总览之0000--------备分,未整理,文件访问1

一. 虚拟文件系统的open,只负责建立如下图所示的虚拟文件系统的数据结构     二. 虚拟文件系统open最后会调用文件系统或者设备或者fifo等的open,   根据深入linux内核P539...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:盘点Linux内核源码中使用宏定义的若干技巧(1)
举报原因:
原因补充:

(最多只允许输入30个字)