学习linux内核(二)

来源:互联网 发布:超星网络课答案 编辑:程序博客网 时间:2024/05/23 14:37

这次学习一下linux内核里如何构建单链表。

单链表定义在include/linux目录下的llist.h,llist.c文件定义在lib目录下。

llist.h文件里开头“Lock-less NULL terminated single linked list”,告诉我们这个单链表是以NULL结束的。

全部介绍:

/* * Lock-less NULL terminated single linked list * * If there are multiple producers and multiple consumers, llist_add * can be used in producers and llist_del_all can be used in * consumers.  They can work simultaneously without lock.  But * llist_del_first can not be used here.  Because llist_del_first * depends on list->first->next does not changed if list->first is not * changed during its operation, but llist_del_first, llist_add, * llist_add (or llist_del_all, llist_add, llist_add) sequence in * another consumer may violate that. * * If there are multiple producers and one consumer, llist_add can be * used in producers and llist_del_all or llist_del_first can be used * in the consumer. * * This can be summarized as follow: * *           |   add    | del_first |  del_all * add       |    -     |     -     |     - * del_first |          |     L     |     L * del_all   |          |           |     - * * Where "-" stands for no lock is needed, while "L" stands for lock * is needed. * * The list entries deleted via llist_del_all can be traversed with * traversing function such as llist_for_each etc.  But the list * entries can not be traversed safely before deleted from the list. * The order of deleted entries is from the newest to the oldest added * one.  If you want to traverse from the oldest to the newest, you * must reverse the order by yourself before traversing. * * The basic atomic operation of this list is cmpxchg on long.  On * architectures that don't have NMI-safe cmpxchg implementation, the * list can NOT be used in NMI handlers.  So code that uses the list in * an NMI handler should depend on CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG. * * Copyright 2010,2011 Intel Corp. *   Author: Huang Ying <ying.huang@intel.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation; * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
本人英语水平很差,上文就不翻译。


这次实现单链表的add与del_all。

实现代码

llist.h文件:

#ifndef LLIST_H#define LLIST_H#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))#define LLIST_HEAD_INIT(name) {NULL}#define LLIST_HEAD(name) struct llist_head name = LLIST_HEAD_INIT(name)#define container_of(ptr, type, member) ({                      \const typeof( ((type *)0)->member ) *__mptr = (ptr);    \(type *)( (char *)__mptr - offsetof(type, member) );})typedef _Bool bool;struct llist_head{struct llist_node *first;};struct llist_node{struct llist_node *next;};static unsigned long xchg(unsigned long *p, unsigned long val){return __atomic_exchange_n(p, val, __ATOMIC_SEQ_CST);}static unsigned long cmpxchg(unsigned long *p, unsigned long expected,unsigned long desired){unsigned long exp = expected;__atomic_compare_exchange_n(p, &exp, desired, 0,__ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);return exp;}bool llist_add_batch(struct llist_node *new_first, struct llist_node *new_last,struct llist_head *head){struct llist_node *first;do{new_last->next = first = ACCESS_ONCE(head->first);}while(cmpxchg(&head->first, first, new_first) != first);return !first;}static inline void init_llist_head(struct llist_head *list){list->first = NULL;}#endif

llist.c文件:

#include <stdio.h>#include <malloc.h>#include "llist.h" #define llist_entry(ptr, type, member)\container_of(ptr, type, member)struct student{int id;char *name;struct llist_node node;};extern bool llist_add_batch(struct llist_node *new_first,    struct llist_node *new_last,    struct llist_head *head);static inline bool llist_add(struct llist_node *new, struct llist_head *head){return llist_add_batch(new, new, head);}static inline struct llist_node *llist_del_all(struct llist_head *head){return xchg(&head->first, NULL);}int main(int argc, char *argv[]){LLIST_HEAD(head);init_llist_head(&head);for(int i = 0; i < 5; i++){struct student *data = (struct student *)malloc(sizeof(struct student));data->id = i + 1;printf("学生姓名:");scanf("%s", &data->name);llist_add(&data->node, &head);}struct llist_node *p = head.first;struct student *a;while(NULL != p){a = llist_entry(p, struct student, node);printf("学生id:%d,姓名:%s\n", a->id, &a->name);p = p->next;}printf("删除所有节点\n");llist_del_all(&head);p = head.first;while(NULL != p){a = llist_entry(p, struct student, node);printf("学生id:%d,姓名:%s\n", a->id, &a->name);p = p->next;}return 0;}

测试效果

学生姓名:z学生姓名:x学生姓名:c学生姓名:v学生姓名:b学生id:5,姓名:b学生id:4,姓名:v学生id:3,姓名:c学生id:2,姓名:x学生id:1,姓名:z删除所有节点


linux内核定义链表的方式跟我们定义的方式有很大的区别。
由于单链表与双链表形式差不多,所以用双链表的示例图文代替,体现区别在哪里。

list1

list2

传统的链表有个最大的缺点就是不好共通化,因为每个node中的data1,data2等等都是不确定的(无论是个数还是类型)。
linux中的链表巧妙的解决了这个问题,linux的链表不是将用户数据保存在链表节点中,而是将链表节点保存在用户数据中。



在后面的博客里再具体分析一下上面的代码。
注:编译代码需要gcc编译器(代码里包含了gcc的内嵌函数)。

 
原创粉丝点击