关于Linux内核源代码情景分析的点点滴滴
来源:互联网 发布:安卓系统源码网盘下载 编辑:程序博客网 时间:2024/05/22 00:07
写这文处于俩个原因:
(1) 备份,以便日后
(2) 分享
许多东西都是在这本书上看到的,也算是安利吧。
1. 宏定义好难
书上给的例子是:
#define DUMP_WRITE(addr, nr) do{ memcpy(bufp, addr, nr); bufp += nr;} while(0)
咋一看为什么不写成
#define DUMP_WRITE(addr, nr) memcpy(bufp, addr, nr); bufp += nr;
书上给的例子是
if (addr) DUMP_WRITE(addr, nr);else do_something_else();
如果是下面的话,就是:
if (addr) memcpy(bufp, addr, nr); bufp += nr;else do_something_else();
编译不过。
那么加个中括号呢
#define DUMP_WRIET(addr, nr) { memcpy(bufp, addr, nr); bufp += nr; }
那更不行了
if (addr) { memcpy(bufp, addr, nr); bufp += nr; };else do_something_else();
毕竟宏定义可以理解为直接替换。想想下面经典的例子就是了:
#define add(a, b) a+badd(1, 2) * 3 等价于 1 + 2 * 3
所以说写一个到处都能使用的宏定义代码,好难。共勉。
2. 链表的那些事
标准的链表结构如下(双向的)
typedef struct foo{ struct foo *prev; struct foo *next;}foo;
虽然声明这样的结构不费劲,但是这种结构只能用于foo类型中,对于新的类型bar的话,则必须再次声明(结构还是相类似!!)说到这里,我是想到了模板template。但是这个是C++才引入的。而linux里面的做法是什么呢?
linux里面的做法是这样的:
它定义一个
数据结构:
struct list_head { struct list_head *next, *prev;}
对于foo类型:
typedef struct foo { struct list_head list; ...}
至于怎么知道foo的next呢?毕竟我们所知道的是foo1 foo1.list 以及foo1.list.next 也就是foo2.list, 怎么反推到foo2呢? 看以下程序
1|#include "stdio.h" 2| 3|struct list_head { 4| struct list_head *next; 5|}; 6|typedef struct foo { 7| struct list_head list; 8| int num; 9|} foo;10|11|int main() {12| foo f1, f2;13| f1.num = 1;14| f2.num = 2;15| f1.list.next = &(f2.list);16| foo *pf2 = (foo *)f1.list.next;17| int answer = pf2->num;18| printf("f1: %d, f2: %d", f1.num, answer);19|20| return 0;21|}
首先由于在foo数据结构里面list_head是首位元素,所以list_head的地址就是foo的地址。但是:这个代码是我自己写的,连我自己都不敢用 = =(毕竟涉世不深,行差踏错。。。)只是提供一下思路(亲测有效,不过不知道这么写是否能够到处都能禁得住使用,求赐教)。
书上提供的方法是这个:
#define list_entry(ptr, type, member) \ ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
注意括号,使用例子如下:
1|#include "stdio.h" 2| 3|struct list_head { 4| struct list_head *next; 5|}; 6|typedef struct foo { 7| struct list_head list; 8| int num; 9|} foo;10|11|#define list_entry(ptr, type, member) \12| ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))13|14|int main() {15| foo f1, f2;16| f1.num = 1;17| f2.num = 2;18| f1.list.next = &(f2.list);19| foo *pf2 = list_entry(f1.list.next, foo, list);20| int answer = pf2->num;21| printf("f1: %d, f2: %d", f1.num, answer);22|23| return 0;24|}
首先得理解list_entry 做了什么事。
他做的事目的是:从list_head* next指针中获取其对应的foo指针。
适用性:list_head 不需要是foo结构的首个元素。
原因:因为它将list_head* next指针地址 减去 list_head*指针相对于foo的地址 = foo指针地址(P.S. 在foo结构中,foo指针地址 <= list_head指针地址)
这也是 (unsigned long)(&((type *)0)->member)的含义,取 member 相对于 type 结构的相对位置。
看起来是不是跟我的差不多,嘻嘻。不过确实在适用性方面增加不少。共勉。
P.S. 宏定义好难,看上面这个定义,各种括号。。。细心,细心,细心,可惜我是粗心的蓝孩子 = =
- 关于Linux内核源代码情景分析的点点滴滴
- linux内核源代码情景分析
- Linux内核源代码情景分析-外部设备存储空间的地址映射
- Linux内核源代码情景分析-文件系统的安装
- Linux内核源代码情景分析-文件系统安装后的访问
- Linux内核源代码情景分析-文件的打开
- Linux内核源代码情景分析-文件的写
- Linux内核源代码情景分析-内存管理
- Linux内核源代码情景分析-中断上半部
- Linux内核源代码情景分析-异常
- Linux内核源代码情景分析-系统调用
- Linux内核源代码情景分析-fork()
- Linux内核源代码情景分析-wait()、schedule()
- Linux内核源代码情景分析-execve()
- Linux内核源代码情景分析-exit()
- Linux内核源代码情景分析-强制性调度
- Linux内核源代码情景分析-虚拟文件系统
- Linux内核源代码情景分析-信号
- 推荐系统-文本相似性计算(3)
- js外部样式如何导入
- 内置CRC于hex程序中的方法
- php 中的heredoc与nowdoc的区别
- asp request response server 常用属性和方法
- 关于Linux内核源代码情景分析的点点滴滴
- s2sm搭建
- 关于JS对象的一些小问题
- JAVA之旅【第三天】 逻辑运算符 三元运算符 键盘录入 if switch
- javascript中函数的声明方式
- js实现正则表达式验证手机号码
- 下位机单片机c语言发送数据到串口,上位机pc机java语言获取端口数据
- js实现选中下拉框选项变换背景颜色
- js实现计算器