linux 内核基础(1)

在linux内核中,有一种通用的双向循环链表,构成了各种队列的基础。链表的结构定义和相关函数均在include/linux/list.h中,下面就来全面的介绍这一链表的各种API。

1.链表初始化

循环链表,表头和表中节点都是下面元素结构。有prev,next两个指针分指向链表中前一个节点和后一个节点。

struct list_head{
    struct list_head *next,*prev;
}

链表初始化的时候,链表头的prev和next都是指向自身的.

#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;
}

2.链表操作

双向循环链表实现,基本用公共方法处理实现。无论是新加一个节点还是其他操作,使用方法一样。另外iain,链表API实现大致都分为两层:

  • 一层外部的,如list_addlist_add_tail,用来消除一些例外情况,调用内部实现。
  • 另外一层是内部的,函数名前会加双下划线,如__list_add,往往几个操作公共的部分,或者排除例外的实现。

2.1链表添加

//pre和next 之间插入new
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;
}
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);
}

2.2 链表删除

list_del是链表中节点的删除。之所以在调用__list_del后又把被删除元素的next、prev指向特殊的LIST_POSITION1LIST_POSITION2,是为了调试未定义的指针。

static inline __list_del(struct list_head*prev,struct list_head*next){
    next->prev=prev;
    prev->next=next;
}
//list_del_init是删除节点后,随即把节点中指针再次初始化,这种删除方式更为实用。
static inline void list_del_init(struct list_head*entry){
    __list_del(entry->prev,entry->next);
    INIT_LIST_HAED(entry);//指向本身
}
//list_del是链表中节点的删除。之所以在调用__list_del后又把被删除元素的next、prev指向特殊的LIST_POSITION1和LIST_POSITION2,是为了调试未定义的指针。
static inline void list_del(struct list_head*entry){
    __list_del(entry->prev,entry->next);
    //LIST_POSITION1和LIST_POSITION2,是为了调试未定义的指针。
    entry->next=LIST_POSTION1;
    entry->next=LIST_POTIONS2;
}

2.3 链表的节点替换

list_replace是将链表中一个节点old,替换为另一个节点new。从实现来看,即使old所在地链表只有old一个节点,new也可以成功替换,这就是双向循环链表可怕的通用之处。

list_replace_init将被替换的old随即又初始化。

2.4链表的移动

将list节点从原链表中去除,并加入新的链表head中。

//list_move的作用是把list节点从原链表中去除,并加入新的链表head中。
static inline void list_move(struct list_head*list,struct list_head*head){
    __list_del(list->prev,list->next);
    list_add(list,head);
}
//list_move_tail只在加入新链表时与list_move有所不同,list_move是加到head之后的链表头部,而list_move_tail是加到head之前的链表尾部。
static inline void list_move_tail(struct list_head*list,struct list_head*head){
    __list_del(list->prev,list->next);
    list_add_tail(list,head);
}

list_is_last 判断list是否处于head链表的尾部。
list_empty判断head链表是否为空,为空的意思就是只有一个链表头head。
list_empty_careful同样是判断head链表是否为空,只是检查更为严格。
list_is_singular 判断head中是否只有一个节点,即除链表头head外只有一个节点。
list_cut_position用于把head链表分为两个部分。从head->next一直到entry被从head链表中删除,加入新的链表list。新链表list应该是空的,或者原来的节点都可以被忽略掉。可以看到,list_cut_position中排除了一些意外情况,保证调用__list_cut_position时至少有一个元素会被加入新链表。
list_splice的功能和list_cut_position正相反,它合并两个链表。list_splice把list链表中的节点加入head链表中。在实际操作之前,要先判断list链表是否为空。它保证调用__list_splice时list链表中至少有一个节点可以被合并到head链表中。

2.遍历

地址翻译技巧是Linux拿手好戏,container_of随处可见。链表节点多被封装在更复杂结构中,使用专门list_entry定义更为方便。

#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
#define container_of(ptr,type,memeber) ({ \
    const typeof(((type*)0)->member)* __mptr=(ptr); \
    (type*)((char*)__mptr - offsetof(type,member));})
//list_entry主要用于从list节点查找内嵌结构。比如定义一个结构struct A{struct list_head list;};如果知道结构中链表的地址,ptrList,进而可以获得整个结构体地址。struct A*ptrA=list_entry(ptrlist,struct A,list);
#define list_entry(ptr,type,member) \
    container_of(ptr,type,member)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值