之前上《linux操作系统》课程的时候,陈老师安排的一个作业就是分析linux内核中的头文件list.h,但是之前我没有认真做,都是完成任务式的去做。
阅读(4211) | 评论(1) | 转发(8) |
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
今天比较伤心些,一个同学问我list.h中的
- #define LIST_HEAD_INIT(name) { &(name), &(name) }
- #define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
宏代表什么含义,一时没有给她解释清楚,于是下决心把这个list.h认真的分析一边,给她一个满意的答复。由于内容比较多,我把list.h头文件的分析分为了几个部分。
(一)结构体定义
- struct list_head {
- struct list_head *next, *prev;
- };
- #define LIST_HEAD_INIT(name) { &(name), &(name) }
- #define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
申请一个变量LIST_HEAD(temp)等价于这个:
struct list_head temp = {&(temp), &(temp)}
附带知识:
- 结构体赋值:
- 1、对成员赋值
- 例如结构体struct st1 {
- int a;
- int b;
- int c;
- }
- 1.1 用{}形式
- struct st1 st1 = {1,2,3);
- 1.2 linux kernel风格.
- struct st1 st1 = {
- .a = 1;
- .b = 2;
- };
- //注 此风格(即在成员变量之前加点“.”),可以不按成员变量的顺序进行赋值。如可以为
- struct st1 st1 = {
- .c = 3;
- .a = 1;
- .b = 2;
- };
- 2 对整体赋值.
- struct st1 a, b;
- b = a;
- 3 结构体作为函数返回值对另一个结构体赋值.
- struct st1 func1();
- struct st1 a = func1();
(二)结构体初始化
结构体初始化函数:
- static inline void INIT_LIST_HEAD(struct list_head *list)
- {
- list->next = list;
- list->prev = list;
- }
初始化结构体list指向自己本身。
对于(一)结构体定义和(二)结构体初始化来说,最终的效果是一样的,都是将一个
struct list_head变量指向自己本身。
可以写一个小的程序测试一下
- /*test.c*/
- #include <stdio.h>
- struct list_head {
- struct list_head *next, *prev;
- };
- #define LIST_HEAD_INIT(name) { &(name), &(name) }
- #define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
- static inline void INIT_LIST_HEAD(struct list_head *list)
- {
- list->next = list;
- list->prev = list;
- }
- int main()
- {
- LIST_HEAD(temp);
- printf("%p %p %p\n", (&temp)->prev, (&temp)->next, &temp);
- INIT_LIST_HEAD(&temp);
- printf("%p %p %p\n", (&temp)->prev, (&temp)->next, &temp);
- return 0;
- }
- 运行结果:
- ^_^[sunny@sunny-laptop ~/DS]11$ ./a.out
- 0xbf8191a8 0xbf8191a8 0xbf8191a8
- 0xbf8191a8 0xbf8191a8 0xbf8191a8
- ^_^[sunny@sunny-laptop ~/DS]12$
可以看出他们完毕之后的地址都是一样的。
(三)增加结点
附带知识:
- 内联函数 inline
- 在c 中,为了解决一些频繁调用的小函数而大量消耗栈空间或者是叫栈内存的问题,特别的引入了inline修饰符,表示为内联函数。
- 内联函数使用inline关键字定义,
- 并且函数体和声明必须结合在一起,
- 否则编译器将他作为普通函数对待。
- inline void function(int x); //仅仅是声明函数,没有任何效果
- inline void function(int x) //正确
- {
- return x;
- }
增加结点的话,有两种方式:头插法和尾插法。
我们调用的话就调用
static inline void list_add(struct list_head *new, struct list_head *head);
static inline void list_add_tail(struct list_head *new, struct list_head *head);
这两个接口。
头插法:
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
尾插法:
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
真正的实现插入:
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
__list_add(new, prev, next):表示在prev和next之间添加一个新的节点new
所以对于list_add()中的__list_add(new, head, head->next)表示的在head和
head->next之间加入一个新的节点,是头插法。
对于list_add_tail()中的__list_add(new, head->prev, head)表示在head->prev(双向循环链表的最后一个结点)和head之间添加一个新的结点。
相关热门文章