LinuxKernel(一)
首先,回顾一下基础的宏操作:
C语言宏
#与##
#的作用是字符串化:在一个宏中的参数前面使用一个#,预处理器会把这个参数转换为一个字符数组#define ERROR_LOG(info) fprintf(stderr,"error:"#info"\n");
则有:
ERROR_LOG("add"); ---> fprintf(stderr,"error: "add"\n");
ERROR_LOG(devied =0); ---> fprintf(stderr,"error: devied=0\n");
#是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。例如:
#define XNAME(n) x##n
那么
XNAME(4)就会展开为x4.
do{/*codes*/}while(0)
采用这种方式是为了防范在使用宏过程中出现错误,主要有如下几点:
(1)空的宏定义避免warning:
#define foo() do{}while(0)
(2)存在一个独立的block,可以用来进行变量定义,进行比较复杂的实现。
(3)如果出现在判断语句过后的宏,这样可以保证作为一个整体来是实现:
#define foo() \
action1(); \
action2();
在遇到分支语句时:
if(NULL == pPtr)
foo();
foo()中的两个语句就不会都被执行。
(4)为何不用单独的{}
#define switch(x,y) {int tmp; tmp=x;x=y;y=tmp;}
if(x>y)
switch(x,y);
else
op();
在把宏引入代码中,会多出一个分号,从而会报错。
变参宏: ··· 与__VA_ARGS__
某些函数接受可变的参数例如printf(),在头文件stdvar.h中有工具可以自定义变参宏。
把宏参数列表中最后的参数用···省略,而__VA_ARGS__可用在替换部分,表面省略号代表的东西。
#define PR(···) printf(__VA_ARGS__)
例如:
PR("THIS IS __VA_ARGS__");
会被展开为:
printf("THIS IS __VA_ARGS__");
预定义符号
| 符号 | 样例值 | 含义 |
|---|---|---|
__FILE__ |
"test.c" |
进行编译的文件名 |
__LINE__ |
25 |
当前行的行号 |
__DATE__ |
"Jan 31 2001" |
被编译的日期 |
__TIME__ |
"23:17:24" |
被编译的时间 |
__STDC__ |
1 |
是否遵循ANSI C |
__FUNCTION__ |
main |
所在函数名称 |
这些宏与编译器有关,有些支持有些不支持.
如下程序:

运行结果为:

注意到: 如果用函数或内联函数,每次的行号便都会相同。
下面是内核中,常见的两个宏:
Linux常用的两个宏
offsetof
该宏的定义如下:
#define __offsetof__(type, member) ( ( size_t ) & ( ( type * ) 0 )->member )
作用是获取结构体某成员变量的偏移量。
分析如下:
(type *)0将0转化为该类型的指针,即地址为0x00000000((type *)0)->member访问成员member&(((type *)0)->member)获取该成员地址(也就是其偏移量)(size_t)&(((type *)0)->member)将地址转化为size_t类型 即偏移量
这里访问0指针为何不会报错,取决于gcc对于该过程的优化,不会直接访问空间而是直接获得地址.
container_of
该宏的定义如下:
#define __container_of__(ptr, type, member) ({\
const typeof ( ( ( type * ) 0 ) -> member ) *__mptr=(ptr);\
( type * )( ( char * )__mptr - __offsetof__( type, member ) );\
})
要理解这段宏,需要知道几个GCC C EXTENSIONS,查阅GCC MANUAL:
1.Statements and Declarations in Expressions


2.Referring to a Type with typeof

手册中也给出了一个典型的用法示例:

这段宏的分析如下:
typeof()为GNU C,获得变量类型typeof (((type *)0 )->member)起始地址为0再获取member最后返回member类型const typeof (((type *)0 )->member) * __mptr=(ptr)定义__mptr 指针,指向ptr指向的地址,并成为常量指针(char *)__mptr__mptr转化为字符型指针(运算以1个字节为单位)- __offsetof__(type,member))减去该成员的偏移量(type*)( ( char * )__mptr - __offsetof__(type,member))最后转化为指向该类型的指针(指向该类型的首地址)
关于上述两个宏的一段程序如下:
#include <stdio.h>
#include <string.h>
/**
* 获取结构体变量成员的偏移量
* @param type 类型(struct)
* @param member 成员
*/
#define __offsetof__(type, member) ( ( size_t ) & ( ( type * ) 0 )->member )
/**
* 获取指向整个结构体的指针
* @param ptr 指向成员(member)变量的指针
* @param type 类型(struct)
* @param member 成员变量
*/
#define __container_of__(ptr, type, member) ({\
const typeof ( ( ( type * ) 0 ) -> member ) *__mptr=(ptr);\
( type * )( ( char * )__mptr - __offsetof__( type, member ) );\
})
typedef struct Student {
char gender;
int id;
int age;
char name[20];
double score;
} Stu;
int main() {
int gender_offset,id_offset,age_offset,name_offset,score_offset;
gender_offset = __offsetof__(struct Student, gender);
id_offset = __offsetof__(struct Student, id);
age_offset = __offsetof__(struct Student, age);
name_offset = __offsetof__(struct Student, name);
score_offset = __offsetof__(struct Student, score);
printf("%d\t%d\t%d\t%d\t%d\n", gender_offset, id_offset, age_offset, name_offset, score_offset);
Stu stu;
Stu *pstu;
stu.gender = '1';
stu.id = 9527;
stu.age = 18;
stu.score = 98.2;
strcpy(stu.name, "elioyang");
pstu = __container_of__(&stu.id, Stu, id);
printf("gender=%c\n", pstu->gender);
printf("age=%d\n", pstu->age);
printf("name=%s\n", pstu->name);
printf("score=%lf", pstu->score);
return 0;
}
运行结果如下:
0 4 8 12 32
gender=1
age=18
name=elioyang
score=98.200000
LinuxKernel(一)的更多相关文章
- visual studio 阅读 linux-kernel
@2018-12-13 [小记] 使用 visual studio 阅读 linux-kernel 方法 a. 文件 ---> 新建 --->从现有代码创建项目 b. 指定项目存储位置,命 ...
- LinuxKernel优秀博客
1.vanbreaker的专栏 2.LinuxKernel Exploration 3.DroidPhone的专栏 4.Linux内核研究以及学习文档和ARM学习以及研究的开放文档 [力荐] 5. ...
- linux-kernel 学习计划
[资料] http://www.ibm.com/developerworks/cn/views/linux/libraryview.jsp http://www.kerneltravel.net/ [ ...
- linux-kernel/CodingStyle
https://www.kernel.org/doc/Documentation/zh_CN/CodingStyle Chinese translated version of Documentati ...
- 如何在LinuxKernel中操作file(set_fs與get_fs)
在Kernel 中,照理說能存取至 0 ~ 4GB.但是實作層面卻是只能讓我們使用到3GB ~ 4GB 這會導致我們無法使用open(),write()這些在user space下的function. ...
- 在阿里云中编译Linux4.5.0内核 - Ubuntu内核编译教程
实验环境:Ubnuntu 64位(推荐使用14.04)+Xshell 阿里云现在提供的云服务器很好用的,用来编译内核性能也不错.本文介绍最基本的内核编译方法,为了方便,所有操作均在root用户下进行. ...
- C语言位域
转载自 http://tonybai.com/2013/05/21/talk-about-bitfield-in-c-again/ 再谈C语言位域 五 21 bigwhite技术志 bitfield, ...
- linux I/O复用
转载自:哈维.dpkirin url:http://blog.csdn.NET/zhang_shuai_2011/article/details/7675797 http://blog.csdn.Ne ...
- Linux内核分析作业7:Linux内核如何装载和启动一个可执行程序
1.可执行文件的格式 在 Linux 平台下主要有以下三种可执行文件格式: 1.a.out(assembler and link editor output 汇编器和链接编辑器的输出) ...
随机推荐
- Java学习的第二十四天
1. 目录管理 2.文件方法太多记不清 3.明天学习流和流的分类
- MarkDown排版、多样的文本框
<战争与和平>一八一二年,俄.法两国再度交战,安德烈·保尔康斯基在战役中身受重伤,而俄军节节败退,眼见莫斯科将陷于敌人之手了.罗斯托夫将原本用来搬运家产的马车,改去运送伤兵,娜达莎方能于伤 ...
- How to using code post packingSlip on Quality Orders Form[AX2009]
For simple user operation posting packing slip with purchase order. we added a function button on Qu ...
- 测试php
/** * 测试guzzle * * @return void */ public function index() { $client = new GuzzleHttp\Client(); //12 ...
- 你还再为下载jar包慢而烦恼吗?Maven配置阿里云镜像
Maven配置阿里云镜像 为什么我们下载jar这么慢 maven默认会从中央仓库下载jar包,这个仓库在国外,而且全世界的人都会从这里下载,所以下载速度肯定是非常慢的. 解决方案使用镜像 什么是镜像? ...
- 【转】jps 命令使用
jps(Java Virtual Machine Process Status Tool)是JDK1.5提供的一个显示当前所有java进程pid的命令,简单实用,非常适合在linux/unix平台上简 ...
- 判断浏览器,还在用userAgent吗,你out了
以下内容摘自http://www.cnblogs.com/rubylouvre/archive/2009/10/14/1583362.html //2010 4 16日更新 ie678 = !+&qu ...
- 企业网络拓扑MSTP功能实例(二)
组网图形 MSTP简介 以太网交换网络中为了进行链路备份,提高网络可靠性,通常会使用冗余链路.但是使用冗余链路会在交换网络上产生环路,引发广播风暴以及MAC地址表不稳定等故障现象,从而导致用户通信质量 ...
- netfilter内核态与用户态 通信 之 sockopt
用户态与内核态交互通信的方法不止一种,sockopt是比较方便的一个,写法也简单.缺点就是使用 copy_from_user()/copy_to_user()完成内核和用户的通信, 效率其实不高, 多 ...
- epoll源码解析翻译------说使用了mmap的都是骗子
本文地址 //https://www.cnblogs.com/l2017/p/10830391.html //https://blog.csdn.net/li_haoren select poll e ...