rcu链表综合实践

news2024/12/23 9:47:00

基础知识 

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

链表的数据结构:

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

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

struct hlist_head {
	struct hlist_node *first;
};

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

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

初始化链表:INIT_LIST_HEAD_RCU

/*
 * 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(插入到头节点后面)

/**
 * 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(插入到头节点前面,就是链尾) 

/**
 * 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

/**
 * 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 

/**
 * 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:

/**
 * 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就足够满足需要了。

/*
 * 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

/**
 * 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:

/**
 * 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))

链表综合实验代码

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

#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");

测试

加载模块

卸载模块 

小结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/801071.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自建纯内网iot平台服务,软硬件服务器全栈实践

基于以下几个考虑&#xff0c;自制硬件设备&#xff0c;mqtt内网服务器。 1.米家app不稳定&#xff0c;逻辑在云端或xiaomi中枢网关只支持少部分在本地计算。 2.监控homeassistant官方服务有大量数据交互。可能与hass安装小米账户有关。 3.硬件&#xff1a;原理图&#xff0c;l…

apifox 调用camunda engine-rest接口报错“type“: “NotFoundException“

官方文档在这&#xff1a; https://docs.camunda.org/rest/camunda-bpm-platform/7.19/ 现象 engine-rest本是可以直接请求的&#xff0c;我把openapi导入到apifox之中了&#xff0c;我测试一下接口没有能请求成功的&#xff0c;基本都报以下的错。 报错如下 {"type&qu…

【iOS】Frame与Bounds的区别详解

iOS的坐标系 iOS特有的坐标是&#xff0c;是在iOS坐标系的左上角为坐标原点&#xff0c;往右为X正方向&#xff0c;向下为Y正方向。 bounds和frame都是属于CGRect类型的结构体&#xff0c;系统的定义如下&#xff0c;包含一个CGPoint&#xff08;起点&#xff09;和一个CGSiz…

《向量数据库指南》:向量数据库Pinecone如何集成Haystack库

目录 安装Haystack库 初始化PineconeDocumentStore 数据准备 初始化检索器 检查文档和嵌入 初始化提取式问答管道 提问 在这个指南中,我们将看到如何集成Pinecone和流行的Haystack库进行问答。 安装Haystack库 我们首先安装最新版本的Haystack,其中包括PineconeDocum…

【爬虫案例】用Python爬取iPhone14的电商平台评论

用python爬取某电商网站的iPhone14评论数据&#xff0c; 爬取目标&#xff1a; 核心代码如下&#xff1a; 爬取到的5分好评&#xff1a; 爬取到的3分中评&#xff1a; 爬取到的1分差评&#xff1a; 所以说&#xff0c;用python开发爬虫真的很方面&#xff01; 您好&…

Shell ❀ 一键配置Iptables规则脚本 (HW推荐)

文章目录 注意事项1. 地址列表填写规范2. 代码块3. 执行结果4. 地址与端口获取方法4.1 tcpdump抓包分析&#xff08;推荐使用&#xff09;4.2 TCP连接分析&#xff08;仅能识别TCP连接&#xff09; 注意事项 请务必按照格式填写具体参数&#xff0c;否则会影响到匹配规则的创建…

【腾讯云 Cloud Studio】构建基于 React 的实时聊天应用

关于腾讯云 Cloud Studio构建基于 Cloud Studio 的聊天应用&#xff08;项目实战&#xff09;1. 注册并登录 Cloud Studio2. 配置 Git 环境2.1 复制 SSH 公钥2.2 添加 SSH 公钥至 GIt 平台 3. 创建项目4. 项目开发4.1 安装依赖4.2 集成 tailwind css4.3 编写代码4.4 项目运行示…

windows中文界面乱码问题

我的便携是内部返修机&#xff0c;买来时就是英文版&#xff0c;在设置中改成简体中文就可以了&#xff0c;与中文版没有什么区别&#xff0c;已经升级成win11。windows自身的应用、360之类的界面都能正常显示&#xff0c;但是个别应用总是乱码&#xff0c;根据客服的提示设置一…

【lesson5】linux vim介绍及使用

文章目录 vim的基本介绍vim的基本操作vim常见的命令命令模式下的命令yypnyynpuctrlrGggnG$^wbh,j,k,lddnddnddp~shiftrrnrxnx 底行模式下的命令set nuset nonuvs 源文件wq!command&#xff08;命令&#xff09; vim配置解决无法使用sudo问题 vim的基本介绍 首先vim是linux下的…

企业服务器数据库被360后缀勒索病毒攻击后采取的措施

近期&#xff0c;360后缀勒索病毒的攻击事件频发&#xff0c;造成很多企业的服务器数据库遭受严重损失。360后缀勒索病毒是Beijingcrypt勒索家族中的一种病毒&#xff0c;该病毒的加密形式较为复杂&#xff0c;目前网络上没有解密工具&#xff0c;只有通过专业的技术人员对其进…

vlan 绑定端口号

<s2>system-view Enter system view, return user view with CtrlZ. # 创建 vlan 10 和 20 [s2]vlan 10 [s2-vlan10]vlan 20# display vlan# 删除 vlan 10 # [s2-vlan20]quit # [s2]undo vlan 10# 设置 interface 为 access port [s2]interface Eth0/0/1 [s2-Ethernet0/…

【雕爷学编程】MicroPython动手做(02)——尝试搭建K210开发板的IDE环境3

4、下载MaixPy IDE&#xff0c;MaixPy 使用Micropython 脚本语法&#xff0c;所以不像 C语言 一样需要编译&#xff0c;要使用MaixPy IDE , 开发板固件必须是V0.3.1 版本以上&#xff08;这里使用V0.5.0&#xff09;, 否则MaixPy IDE上会连接不上&#xff0c; 使用前尽量检查固…

diffusion model(五)stable diffusion底层原理(latent diffusion model, LDM)

LDM: 在隐空间用diffusion model合成高质量的图片&#xff01; [论文地址] High-Resolution Image Synthesis with Latent Diffusion Models [github] https://github.com/compvis/latent-diffusion 文章目录 LDM: 在隐空间用diffusion model合成高质量的图片&#xff01;系列…

3D工厂模拟仿真 FACTORY I/O 2.55 Crack

FACTORY I/O 提供超过20个典型的工业应用场景让您如身临其境般地练习控制任务。选择一种场景直接使用或以其作为一个新项目的开端。学生可以利用内嵌的可编辑的典型工业系统模板&#xff0c;也可以自由搭建并编辑工业系统。同时该系统具有全方位3D视觉漫游&#xff0c;可随意放…

存储重启后,ceph挂载信息没了,手动定位osd序号并挂载到对应磁盘操作流程、ceph查看不到osd信息处理方法

文章目录 故障说明处理流程定位硬盘中的osd序号挂载osd到ceph上验证并拉起osd重复上面操作故障说明 我们的一个存储节点莫名其妙的重启了,不知道咋回事 但这样的问题就是,所有osd都down了 因为挂载信息没有写到fstab里面,所以不会自动up,并且没有挂载信息,并且也看不到o…

如何用Java代码写出二维码!!!

什么你说你不会&#xff1a; 1.首先加入二维码需要的架包。&#xff08;认真看了&#xff0c;我只教一遍&#xff09;安装包已经放上来了&#xff0c;需要的直接下载。 2.将架包接入项目。 3.编写代码。 //支持中文格式Map<EncodeHintType,String> hintsnew HashMap<&…

适配器模式——不兼容结构的协调

1、简介 有的笔记本电脑的工作电压是20V&#xff0c;而我国的家庭用电是220V&#xff0c;如何让20V的笔记本电脑能够在220V的电压下工作&#xff1f;答案是引入一个电源适配器&#xff08;AC Adapter&#xff09;&#xff0c;俗称充电器&#xff0f;变压器。有了这个电源适配器…

【JAVA】你可知JAVA中的运算符|重温运算符

作者主页&#xff1a;paper jie的博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《JAVASE语法系列》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精…

MySQL之深入InnoDB存储引擎——Checkpoint机制

文章目录 一、引入二、LSN三、触发时机 一、引入 由于页的操作首先都是在缓冲池中完成的&#xff0c;那么如果一条DML语句改变了页中的记录&#xff0c;那么此时页就是脏的&#xff0c;即缓冲池中页的版本要比磁盘的新。那么数据库需要将新版本的页刷新到磁盘。倘若每次一个页…

地图应用构建平台:助力小程序开发者快速构建地图应用

地图应用构建平台&#xff08;也称Wemap Builder&#xff09;是地图低代码开发平台&#xff0c;在微信开发者工具中提供了丰富的小程序模板&#xff0c;开发者能够选择模板快速创建地图应用&#xff0c;同时在微信开发者工具中可直接使用低代码编辑器&#xff0c;更高效的开发小…