Linux Kernel Development 3rd Edition 读书笔记(3)

第六章: Kernel Data Structures
1. Linked Lists
(1) singly list
/* an element in a linked list */
struct list_element { 
void *data;                   /* the payload */ 
struct list_element *next;    /* pointer to the next element */
};


(2) doubly list
/* an element in a linked list */ 
struct list_element {
void *data;                   /* the payload */ 
struct list_element *next;    /* pointer to the next element */ 
struct list_element *prev;    /* pointer to the previous element */
};

(3) Circular Linked Lists

(4) 链表定义在, <linux/list.h>:
struct list_head { 
struct list_head *next 
struct list_head *prev;
};

(5) 使用container_of()可以定义一个函数来获得实际的数据结构.
#define list_entry(ptr, type, member) \ 
container_of(ptr, type, member)

(6) LIST_HEAD可以设置链表的头节点,如:static LIST_HEAD(fox_list);

(7) 添加节点: list_add(struct list_head *new, struct list_head *head);
删除节点: list_del(struct list_head *entry);
删除节点并重新初始化(与list_del()相同,出了重新初始化list_head,这样可以继续使用该节点数据):
list_del_init():
list_del_init(struct list_head *entry);
移动节点到另一个list:
移到head之后: list_move(struct list_head *list, struct list_head *head);
移到list末尾,head之前: list_move_tail(struct list_head *list, struct list_head *head);
检查list是否为空: list_empty(struct list_head *head);
合并两个list: list_splice(struct list_head *list, struct list_head *head);
合并两个list并重新初始化原list: list_splice_init(struct list_head *list, struct list_head *head);

(8) 遍历list:  list_for_each()宏,参数为2个list_head结构,第一个指向当前entry,是提供的临时变量,第二个指向head node.在遍历过程中,第一个参数指向下一个entry.
struct list_head *p;
struct fox *f;
list_for_each(p, &fox_list) {
/* f points to the structure in which the list is embedded */
f = list_entry(p, struct fox, list);
}

(9) list_for_each_entry(pos, head, member)宏常用来遍历list,pos指向包含list_head的对象,head是要遍历的list的头节点list_head,member是list_head结构的变量名.
struct fox *f;
list_for_each_entry(f, &fox_list, list) {
/* on each iteration, ‘f’ points to the next fox structure ... */
}
list_for_each_entry_reverse(pos, head, member) :反向遍历list

(10) 遍历时删除节点
list_for_each_entry_safe(pos, next, head, member), next用来保存下一个list节点.
反向遍历时对应list_for_each_entry_safe_reverse(pos, n, head, member).

(11) list相关函数定义在<linux/list.h>

2. Queues
(1) Linux内核队列实现叫kfifo,实现在kernel/kfifo.c, 声明在<linux/kfifo.h>.Thi本节讨论的API是基于2.6,33内核.

(2) Linux’s kfifo提供2种操作,入队列(in),出队列(out),两个offsets值,in offset是队列中下一个入队列位置,out offset是下一个出队列位置.out offset总是小于或等于in offset
out offset等于in offset, 队列为空; in offset等于队列长度时,队列为满.

(3) 动态创建队列:
int kfifo_alloc(struct kfifo *fifo, unsigned int size, gfp_t gfp_mask);返回0成功,负值失败.
示例:
struct kfifo fifo;
int ret;
ret = kfifo_alloc(&kifo, PAGE_SIZE, GFP_KERNEL);
if (ret)
return ret;

手动分配buffer:
void kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size);
size必须是2的指数.

静态创建队列:
DECLARE_KFIFO(name, size);
INIT_KFIFO(name);

(3) 入队列:
unsigned int kfifo_in(struct kfifo *fifo, const void *from, unsigned int len);
这个函数负值从from开始的len个字节数据到队列fifo,成功返回入队列的字节数.如果小于len,只复制可用的字节数.
出队列:
unsigned int kfifo_out(struct kfifo *fifo, void *to, unsigned int len);
这个函数复制队列fifo中最多len个字节数据到to地址.成功返回复制数据的字节数.如果小于len,则复制了小于要求的数目.
取完数据后,队列中将不在存在这些数据.如果要保留,可以使用kfifo_out_peek():
unsigned int kfifo_out_peek(struct kfifo *fifo, void *to, unsigned int len, unsigned offset);
与kfifo_out()操作相同,只是offet没有增加,参数offset指明队列的索引,设置为0从队列头开始读取.像kfifo_out()一样.

(4) 获取队列长度:
static inline unsigned int kfifo_size(struct kfifo *fifo);
static inline unsigned int kfifo_len(struct kfifo *fifo);
获取队列可用的空间:
static inline unsigned int kfifo_avail(struct kfifo *fifo);
判断队列是否为空或为满:
static inline int kfifo_is_empty(struct kfifo *fifo);
static inline int kfifo_is_full(struct kfifo *fifo);

(5) reset队列:
static inline void kfifo_reset(struct kfifo *fifo);
销毁队列:
void kfifo_free(struct kfifo *fifo);

3. Maps
(1) 映射支持至少3种操作:
Add (key, value)
Remove (key)
value = Lookup (key)
maps可以实现为hash表,也可以实现为二叉树.
Although a hash offers better average-case asymptotic complexity, a binary search tree has better worst-case behavior (logarithmic versus linear).

Linux提供简单而有效的map数据结构,仅仅用于一种情况: 映射 unique identification number(UID)到一个指针.
Linux在add操作之前还提供allocate操作,这不仅仅添加UID/value的映射,还生成UID.
idr结构用来映射用户空间的UID,如映射inotify watch descriptors or POSIX timer IDs到内核数据结构(inotify_watch  or k_itimer),这个map叫做idr.

(2)初始化idr
首先静态或者动态创建idr结构,然后调用idr_init():
void idr_init(struct idr *idp);
如:
struct idr id_huh;  /* statically define idr structure */
idr_init(&id_huh);  /* initialize provided idr structure */

(3)分配新的UID
第一步,重新分配backing tree的大小,使用 idr_pre_get():
int idr_pre_get(struct idr *idp, gfp_t gfp_mask);(返回1成功,0失败)
第二步,获取新的UID,添加到idr,使用idr_get_new():
int idr_get_new(struct idr *idp, void *ptr, int *id);(返回0成功,非0失败)
 idr_get_new_above() 能够申请获取最小的UID值:
int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id);

(4)查询UID
void *idr_find(struct idr *idp, int id);

(5)移除UID
void idr_remove(struct idr *idp, int id);

(6)销毁idr
void idr_destroy(struct idr *idp);(只释放违背使用的内存,不释放已经使用的)
强制移除所有UID
void idr_remove_all(struct idr *idp);

4. 二叉树(binary trees)
(1) it is an acyclic, connected, directed graph in which each vertex (called a node) has zero or
more outgoing edges and zero or one incoming edges.


(2) Binary Search Trees
二叉查找树是节点有特殊顺序的,一般遵循以下规则:
左孩子小于父节点;右孩子大于父节点;所有的子树都是二叉查找树.

(3) Self-Balancing Binary Search Trees
自平衡二叉查找树所有叶子的深度差最多为1.

(4) Red-Black Trees
Red-Black Trees是Self-Balancing Binary Search Tree的一种类型. 遵循以下规则:
所有的节点非红即黑;叶子节点为黑;叶子节点不包含数据;非叶子节点有2个孩子;如果节点是红,其孩子为黑;节点到其叶子的深度包含与到其他叶子相同数目的黑节点.

(5) rbtrees

Linux中Red-Black Trees的实现为rbtrees, 定义在lib/rbtree.c, 声明在<linux/rbtree.h>.
rbtree的根节点为rb_root,要创建一个新的rbtree,首先新建一个rb_root,初值为RB_ROOT.
struct rb_root root = RB_ROOT;
rbtress的节点为rb_node,通过其可以移动左右孩子.
rbtree的实现不提供查找和插入的例程.这些功能由用户实现.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值