内核klist结构分析

进来学习块设备驱动时候偶然遇到在genhd_device_init中看到class_register调用中有关klist链表有关的问题,经过今天一天业余时间研究终于发现klist的重要作用,下面介绍有关klist有关的数据结构,函数,以及klist申请有关的一些函数。

数据结构

klist主要有一个链表头结点以及若干个链表节点组成的,主要数据结构如下:

struct klist {
        spinlock_t              k_lock;    //链表锁
        struct list_head        k_list;		//klist头结点
        void                    (*get)(struct klist_node *);   //klist结点n_kref加一操作
        void                    (*put)(struct klist_node *);	//klist链表节点减一操作
}
struct klist_node {
        void                    *n_klist;       /* never access directly 在class_register中此指针指向klist头部*/
        struct list_head        n_node;      //klist链表节点
        struct kref             n_ref;			//klist链表节点的计数
};

klist 是klist链表头结点,主要任务为:给链表加锁,负责klist_node中的原子kref加一减一操作,还有一个struct list_head结构负责管理链表头
klist_node主要有三个数据组成:list_head链表节点,n_kref节点的持有个数,n_private私有化指针;

klist链表相关函数

klist_init / KLIST_INIT第一个为初始化链表相关的函数,第二个为初始化链表节点相应的宏

void klist_init(struct klist *k, void (*get)(struct klist_node *),
                void (*put)(struct klist_node *))
{
        INIT_LIST_HEAD(&k->k_list);
        spin_lock_init(&k->k_lock);
        k->get = get;
        k->put = put;
}

主要完成klist->k_list的初始化,并且完成klist->k_lock的自旋锁初始化。最重要的是把klist相关的get/put私有化函数赋值。

klist_add* 为一系列函数操作,主要把klist_node节点加入相应的klist链表相应的位置(增加链表操作之前会吧klist_node节点初始化并且把knode->n_klist赋予klist头节点,并且调用klist->get()函数初始化计数值)

void klist_add_tail(struct klist_node *n, struct klist *k)
{
        klist_node_init(k, n);   //初始化klist相应的节点,并且把n->k_list赋予klist
        add_tail(k, n);   //把klist_node加入到klist->list_head后面
}
static void klist_node_init(struct klist *k, struct klist_node *n)
{
        INIT_LIST_HEAD(&n->n_node);
        kref_init(&n->n_ref);
        knode_set_klist(n, k);
        if (k->get)
                k->get(n);
}

static void add_tail(struct klist *k, struct klist_node *n)
{
        spin_lock(&k->k_lock);
        list_add_tail(&n->n_node, &k->k_list);
        spin_unlock(&k->k_lock);
}

klist_del是调用klist_put使klist_node->n_kref计数减一
klist_remove这个函数有点复杂,包含了一个回收机制,实际上把klist_del删除的链表节点重新删除了一下。下面一张着重讲解klist_del和klist_remove函数

klist的回收机制

klist函数有一个主要的回收机制,引入了一个通用链表,此链表可以装下所有的klist_node类型的节点(不管这个klist_node节点关联的数据结构有哪些)。

内核klist结构分析
以上klist_remove_waters链表在klist释放过程中主要有两步:
1.klist_del调用,此函数调用时候只是把klist_node->n_kref计数减一,直到这个计数为0的时候才会把klist_node转化成klist_water并把klist_water->woken调用赋值为1,并且把water_list从klist_remove_waters链表里面删除。
2.klist_remove调用时候虽然也调用了klist_del操作,更重要的是会把含有要删除的klist_node的water_list加入到klist_remove_waters里,但是会等待klist_water->woken计数变为1
也就是说klist_remove加入了一个等待klist_node删除的机制。

一下为klist节点的回收机制:
以下假设被回收的klist_node.n_kref计数为3。

1.在klist_remove调用中会根t据klist_node生成一个water_list,然后把water_list假如到klist_removed_water全局链表中并且把klist_node->woken赋予0,然后等待water_list->woken变为1.
并且把klist_water->process置为当前进程。
2.另外一方面在调用kref_put过程中,最后一次调用kref_put才会调用klist_release,这个函数的作用是遍历klist_removed_water链表,找到与要删除的klist_node相同的节点。并把klist_water的woken置为1,唤醒klist_water->process等待进程.

内核klist结构分析