rcu链表综合实践

基础知识

rcu-read copy update的缩写。和读写锁起到相同的效果。据说牛逼一点。对于我们普通程序员,要先学会使用,再探究其内部原理。

链表的数据结构:

cpp 复制代码
struct list_head {
	struct list_head *next, *prev;
};

还有一种:struct hlist_head,本文不做该链表的测试。

cpp 复制代码
struct hlist_head {
	struct hlist_node *first;
};

struct hlist_node {
	struct hlist_node *next, **pprev;
};

涉及的文件:include\linux\rculist.h

初始化链表:INIT_LIST_HEAD_RCU

cpp 复制代码
/*
 * INIT_LIST_HEAD_RCU - Initialize a list_head visible to RCU readers
 * @list: list to be initialized
 *
 * You should instead use INIT_LIST_HEAD() for normal initialization and
 * cleanup tasks, when readers have no access to the list being initialized.
 * However, if the list being initialized is visible to readers, you
 * need to keep the compiler from being too mischievous.
 */
static inline void INIT_LIST_HEAD_RCU(struct list_head *list)
{
	WRITE_ONCE(list->next, list);
	WRITE_ONCE(list->prev, list);
}

添加节点list_add_rcu(插入到头节点后面)

cpp 复制代码
/**
 * list_add_rcu - add a new entry to rcu-protected list
 * @new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 *
 * The caller must take whatever precautions are necessary
 * (such as holding appropriate locks) to avoid racing
 * with another list-mutation primitive, such as list_add_rcu()
 * or list_del_rcu(), running on this same list.
 * However, it is perfectly legal to run concurrently with
 * the _rcu list-traversal primitives, such as
 * list_for_each_entry_rcu().
 */
static inline void list_add_rcu(struct list_head *new, struct list_head *head)
{
	__list_add_rcu(new, head, head->next);
}

添加节点list_add_tail_rcu(插入到头节点前面,就是链尾)

cpp 复制代码
/**
 * list_add_tail_rcu - add a new entry to rcu-protected list
 * @new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 *
 * The caller must take whatever precautions are necessary
 * (such as holding appropriate locks) to avoid racing
 * with another list-mutation primitive, such as list_add_tail_rcu()
 * or list_del_rcu(), running on this same list.
 * However, it is perfectly legal to run concurrently with
 * the _rcu list-traversal primitives, such as
 * list_for_each_entry_rcu().
 */
static inline void list_add_tail_rcu(struct list_head *new,
					struct list_head *head)
{
	__list_add_rcu(new, head->prev, head);
}

删除节点list_del_rcu

cpp 复制代码
/**
 * list_del_rcu - deletes entry from list without re-initialization
 * @entry: the element to delete from the list.
 *
 * Note: list_empty() on entry does not return true after this,
 * the entry is in an undefined state. It is useful for RCU based
 * lockfree traversal.
 *
 * In particular, it means that we can not poison the forward
 * pointers that may still be used for walking the list.
 *
 * The caller must take whatever precautions are necessary
 * (such as holding appropriate locks) to avoid racing
 * with another list-mutation primitive, such as list_del_rcu()
 * or list_add_rcu(), running on this same list.
 * However, it is perfectly legal to run concurrently with
 * the _rcu list-traversal primitives, such as
 * list_for_each_entry_rcu().
 *
 * Note that the caller is not permitted to immediately free
 * the newly deleted entry.  Instead, either synchronize_rcu()
 * or call_rcu() must be used to defer freeing until an RCU
 * grace period has elapsed.
 */
static inline void list_del_rcu(struct list_head *entry)
{
	__list_del_entry(entry);
	entry->prev = LIST_POISON2;
}

删除尾节点hlist_del_init_rcu

cpp 复制代码
/**
 * hlist_del_init_rcu - deletes entry from hash list with re-initialization
 * @n: the element to delete from the hash list.
 *
 * Note: list_unhashed() on the node return true after this. It is
 * useful for RCU based read lockfree traversal if the writer side
 * must know if the list entry is still hashed or already unhashed.
 *
 * In particular, it means that we can not poison the forward pointers
 * that may still be used for walking the hash list and we can only
 * zero the pprev pointer so list_unhashed() will return true after
 * this.
 *
 * The caller must take whatever precautions are necessary (such as
 * holding appropriate locks) to avoid racing with another
 * list-mutation primitive, such as hlist_add_head_rcu() or
 * hlist_del_rcu(), running on this same list.  However, it is
 * perfectly legal to run concurrently with the _rcu list-traversal
 * primitives, such as hlist_for_each_entry_rcu().
 */
static inline void hlist_del_init_rcu(struct hlist_node *n)
{
	if (!hlist_unhashed(n)) {
		__hlist_del(n);
		n->pprev = NULL;
	}
}

替换list_replace_rcu:

cpp 复制代码
/**
 * list_replace_rcu - replace old entry by new one
 * @old : the element to be replaced
 * @new : the new element to insert
 *
 * The @old entry will be replaced with the @new entry atomically.
 * Note: @old should not be empty.
 */
static inline void list_replace_rcu(struct list_head *old,
				struct list_head *new)
{
	new->next = old->next;
	new->prev = old->prev;
	rcu_assign_pointer(list_next_rcu(new->prev), new);
	new->next->prev = new;
	old->prev = LIST_POISON2;
}

计算长度

判空:list_empty

链表尾空时,返回值为1:链表不空时返回0。
下面这段注释的大体含义就是,当你想用list_empty_rcu的时候,list_empty就足够满足需要了。

cpp 复制代码
/*
 * Why is there no list_empty_rcu()?  Because list_empty() serves this
 * purpose.  The list_empty() function fetches the RCU-protected pointer
 * and compares it to the address of the list head, but neither dereferences
 * this pointer itself nor provides this pointer to the caller.  Therefore,
 * it is not necessary to use rcu_dereference(), so that list_empty() can
 * be used anywhere you would want to use a list_empty_rcu().
 */

获取节点对应的数据:list_entry_rcu

cpp 复制代码
/**
 * list_entry_rcu - get the struct for this entry
 * @ptr:        the &struct list_head pointer.
 * @type:       the type of the struct this is embedded in.
 * @member:     the name of the list_head within the struct.
 *
 * This primitive may safely run concurrently with the _rcu list-mutation
 * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock().
 */
#define list_entry_rcu(ptr, type, member) \
	container_of(READ_ONCE(ptr), type, member)

遍历 list_for_each_entry_rcu:

cpp 复制代码
/**
 * list_for_each_entry_rcu	-	iterate over rcu list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_head within the struct.
 * @cond:	optional lockdep expression if called from non-RCU protection.
 *
 * This list-traversal primitive may safely run concurrently with
 * the _rcu list-mutation primitives such as list_add_rcu()
 * as long as the traversal is guarded by rcu_read_lock().
 */
#define list_for_each_entry_rcu(pos, head, member, cond...)		\
	for (__list_check_rcu(dummy, ## cond, 0),			\
	     pos = list_entry_rcu((head)->next, typeof(*pos), member);	\
		&pos->member != (head);					\
		pos = list_entry_rcu(pos->member.next, typeof(*pos), member))

链表综合实验代码

测试内容包括添加、计算长度、遍历数据、删除节点、替换节点。最后在卸载函数中释放链表资源。

cpp 复制代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rculist.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>

#define _DEBUG_INFO
#ifdef _DEBUG_INFO
    #define DEBUG_INFO(format,...)	\
        printk(KERN_ERR"%s:%d -- "format"\n",\
        __func__,__LINE__,##__VA_ARGS__)
#else
    #define DEBUG_INFO(format,...)
#endif

struct rcu_private_data{
    struct list_head list;
};

struct my_list_node{
    struct list_head node;
    int number;
};

static int list_size(struct rcu_private_data *p){
    struct my_list_node *pos;
    struct list_head *head = &p->list;
    int count = 0;

    if(list_empty(&p->list)){
        DEBUG_INFO("list is empty");
        return 0;
    }else{
        DEBUG_INFO("list is not empty");
    }

    list_for_each_entry_rcu(pos,head,node){
        count++;
    }
    return count;
}

//遍历链表
void show_list_nodes(struct rcu_private_data *p){
    struct my_list_node *pos;
    struct list_head *head = &p->list;

    if(list_empty(&p->list)){
        DEBUG_INFO("list is empty");
        return;
    }else{
        DEBUG_INFO("list is not empty");
    }

    list_for_each_entry_rcu(pos,head,node){
        DEBUG_INFO("pos->number = %d",pos->number);
    }
}


//清空链表
void del_list_nodes(struct rcu_private_data *p){
    struct my_list_node *pos;
    struct list_head *head = &p->list;

    if(list_empty(&p->list)){
        DEBUG_INFO("list is empty");
        return;
    }else{
        DEBUG_INFO("list is not empty");
    }

    list_for_each_entry_rcu(pos,head,node){
        DEBUG_INFO("pos->number = %d\n",pos->number);
        vfree(pos);
    }
}


struct rcu_private_data *prpd;

static int __init ch02_init(void){
    
    int i = 0;
    static struct my_list_node * new[6];
    struct rcu_private_data *p = (struct rcu_private_data*)vmalloc(sizeof(struct rcu_private_data));
    prpd = p;
    INIT_LIST_HEAD_RCU(&p->list);
    DEBUG_INFO("list_empty(&p->list) = %d",list_empty(&p->list));

    for(i = 0;i < 5;i++){
        new[i] = (struct my_list_node*)vmalloc(sizeof(struct my_list_node));
        INIT_LIST_HEAD_RCU(&new[i]->node);
        new[i]->number = i;
        list_add_rcu(&new[i]->node,&p->list);
    }
    DEBUG_INFO("list_size = %d",list_size(p));
    //添加后的结果,应该是5 4 3 2 1
    //遍历链表:
    show_list_nodes(p);
    //删除链表节点 new[3];
    list_del_rcu(&new[3]->node);
    vfree(new[3]);

    DEBUG_INFO("list_size = %d",list_size(p));
    //遍历链表:
    show_list_nodes(p);

    
    //替换一个链表节点
    new[5] = (struct my_list_node*)vmalloc(sizeof(struct my_list_node));
    INIT_LIST_HEAD_RCU(&new[5]->node);
    new[5]->number = i;

    list_replace_rcu(&new[1]->node,&new[5]->node);
    vfree(new[1]);
    //遍历链表:
    show_list_nodes(p);

    DEBUG_INFO("init");
    return 0;
}

static void __exit ch02_exit(void){

    del_list_nodes(prpd);
    vfree(prpd);
    DEBUG_INFO("exit");
}

module_init(ch02_init);
module_exit(ch02_exit);
MODULE_LICENSE("GPL");

测试

加载模块

卸载模块

小结

相关推荐
2035去旅行5 小时前
bootcmd 详细讲解
linux·嵌入式·arm·soc
陈希1236 小时前
Linux 防火墙配置实战:firewalld、iptables 与云安全组
linux·运维·服务器
女神下凡6 小时前
芯参谋(95):嵌入式文件系统_开发指南
linux·服务器·arm开发·驱动开发·设计规范
且从容.10 小时前
Ubuntu安装flume
linux·ubuntu·flume
承渊政道10 小时前
Linux网络学习【Reactor反应堆模式】
linux·网络·学习·ubuntu·reactor
且从容.13 小时前
ubuntu安装flink
linux·ubuntu·flink
爱和冰阔落14 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
程序员沉梦听雨14 小时前
WSL2的安装与使用
linux·wsl
微小的鱼15 小时前
Linux的渲染与显示系统链路
android·java·linux
lisanmengmeng15 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器