linux 内核经典RCU

如果不关心使用的RCU是不可抢占RCU还是可抢占RCU,应该使用经典RCU的编程接口。最初的经典RCU是不可抢占RCU,后来实现了可抢占RCU,经典RCU的意思发生了变化:如果内核编译了可抢占RCU,那么经典RCU的编程接口被实现为可抢占RCU,否则被实现为不可抢占RCU。
读者使用函数rcu_read_lock()标记进入读端临界区,使用函数rcu_read_unlock()标记退出读端临界区。读端临界区可以嵌套。
在读端临界区里面应该使用宏rcu_dereference(p)访问指针,这个宏封装了数据依赖屏障,即只有阿尔法处理器需要的读内存屏障。
写者可以使用下面4个函数。
(1)使用函数synchronize_rcu()等待宽限期结束,即所有读者退出读端临界区,然后写者执行下一步操作。这个函数可能睡眠。
(2)使用函数synchronize_rcu_expedited()等待宽限期结束。和函数synchronize_rcu()的区别是:该函数会向其他处理器发送处理器间中断(Inter-Processor Interrupt,IPI)请求,强制宽限期快速结束。我们把强制快速结束的宽限期称为加速宽限期(expedited grace period),把没有强制快速结束的宽限期称为正常宽限期(normal grace period)。
(3)使用函数call_rcu()注册延后执行的回调函数,把回调函数添加到RCU回调函数链表中,立即返回,不会阻塞。函数原型如下:
void call_rcu(struct rcu_head *head, rcu_callback_t func);
struct callback_head {
     struct callback_head *next;
     void (*func)(struct callback_head *head);
} __attribute__((aligned(sizeof(void *))));
#define rcu_head callback_head
typedef void (*rcu_callback_t)(struct rcu_head *head);
(4)使用函数rcu_barrier()等待使用call_rcu注册的所有回调函数执行完。这个函数可能睡眠。
现在举例说明使用方法,假设链表节点和头节点如下:
typedef struct {
    struct list_head link;
    struct rcu_head rcu;
    int key;
    int val;
} test_entry;
struct list_head test_head;
成员“struct rcu_head rcu”:调用函数call_rcu把回调函数添加到RCU回调函数链表的时候需要使用。
读者访问链表的方法如下:
int test_read(int key, int *val_ptr)
{
     test_entry *entry;
     int found = 0;
     rcu_read_lock();
     list_for_each_entry_rcu(entry, &test_head, link) {
           if (entry->key == key) {
                *val_ptr = entry->val;
                found = 1;
                break;
           }
     }
     rcu_read_unlock();
     return found;
}
如果只有一个写者,写者不需要使用锁,添加、更新和删除3种操作的实现方法如下。
(1)写者添加一个节点到链表尾部。
void test_add_node(test_entry *entry)
{
     list_add_tail_rcu(&entry->link, &test_head);
}
(2)写者更新一个节点。
更新的过程是:首先把旧节点复制更新,然后使用新节点替换旧节点,最后使用函数call_rcu注册回调函数,延后释放旧节点。
int test_update_node(int key, int new_val)
{
     test_entry *entry, *new_entry;
     int ret;
     ret = -ENOENT;
     list_for_each_entry(entry, &test_head, link) {
           if (entry->key == key) {
                new_entry = kmalloc(sizeof(test_entry), GFP_ATOMIC);
                if (new_entry == NULL) {
                     ret = -ENOMEM;
                     break;
                }
                *new_entry = *entry;
                new_entry->val = new_val;
                list_replace_rcu(&entry->list, &new_entry->list);
                call_rcu(&entry->rcu, test_free_node);
                ret = 0;
                break;
           } 
     }
     return ret;
}
static void test_free_node(struct rcu_head *head)
{
     test_entry *entry = container_of(head, test_entry, rcu);
     kfree(entry);
}
(3)写者删除一个节点的第一种实现方法是:首先把节点从链表中删除,然后使用函数call_rcu注册回调函数,延后释放节点。
int test_del_node(int key)
{
     test_entry *entry;
     int found = 0;
     list_for_each_entry(entry, &test_head, link) {
           if (entry->key == key) {
                list_del_rcu(&entry->link);
                call_rcu(&entry->rcu, test_free_node);
                found = 1;
                break; 
           } 
     }
     return found;
}
static void test_free_node(struct rcu_head *head)
{
     test_entry *entry = container_of(head, test_entry, rcu);
     kfree(entry);
}
(4)写者删除一个节点的第二种实现方法是:首先把节点从链表中删除,然后使用函数synchronize_rcu()等待宽限期结束,最后释放节点。
int test_del_node(int key)
{
     test_entry *entry;
     int found = 0;
     list_for_each_entry(entry, &test_head, link) {
           if (entry->key == key) {
                list_del_rcu(&entry->link);
                synchronize_rcu();
                kfree(entry);
                found = 1;
                break; 
           } 
     }
     return found;
}
如果有多个写者,那么写者之间必须使用锁互斥,添加、更新和删除3种操作的实现方法如下。
(1)写者添加一个节点到链表尾部。假设使用自旋锁“test_lock”保护链表。
void test_add_node(test_entry *entry)
{
     spin_lock(&test_lock);
     list_add_tail_rcu(&entry->link, &test_head);
     spin_unlock(&test_lock);
}
(2)写者更新一个节点。
int test_update_node(int key, int new_val)
{
     test_entry *entry, *new_entry;
     int ret;
     ret = -ENOENT;
     spin_lock(&test_lock);
     list_for_each_entry(entry, &test_head, link) {
           if (entry->key == key) {
                new_entry = kmalloc(sizeof(test_entry), GFP_ATOMIC);
                if (new_entry == NULL) {
                     ret = -ENOMEM;
                     break;
                }
                *new_entry = *entry;
                new_entry->val = new_val;
                list_replace_rcu(&entry->list, &new_entry->list);
                call_rcu(&entry->rcu, test_free_node);
                ret = 0;
                break;
           } 
     }
     spin_unlock(&test_lock);
     return ret;
}
static void test_free_node(struct rcu_head *head)
{
     test_entry *entry = container_of(head, test_entry, rcu);
     kfree(entry);
}
(3)写者删除一个节点。
int test_del_node(int key)
{
     test_entry *entry;
     int found = 0;
     spin_lock(&test_lock);
     list_for_each_entry(entry, &test_head, link) {
           if (entry->key == key) {
                list_del_rcu(&entry->link);
                call_rcu(&entry->rcu, test_free_node);
                found = 1;
                break; 
           } 
     }
     spin_unlock(&test_lock);
     return found;
}
static void test_free_node(struct rcu_head *head)
{
     test_entry *entry = container_of(head, test_entry, rcu);
     kfree(entry);
}

如果删除完还需要继续遍历,就需要使用list_for_each_entry_safe接口。
 

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

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

相关文章

分布式全局ID之雪花算法

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 雪花算法 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、什么是雪花算法&#xff1f…

Kafka--Kafka日志索引详解以及生产常见问题分析与总结

一、Kafka的Log日志梳理 ​ 这一部分数据主要包含当前Broker节点的消息数据(在Kafka中称为Log日志)。这是一部分无状态的数据,也就是说每个Kafka的Broker节点都是以相同的逻辑运行。这种无状态的服务设计让Kafka集群能够比较容易的进行水平扩展。比如你需要用一个新…

嵌入式开发工程师

嵌入式开发 岗位需求 上岗必备 文章目录 嵌入式开发前言一、负责新产品的电路图、PCB、嵌入式程序、软硬件调试等工作二、负责对现有产品进行硬件优化、调试、维护、排故等工作三、 负责各种单片机,传感器,元器件选型以及BOM表整理四、 负责硬件产品研发和量产过程中项目设计…

网络技术基础与计算思维实验教程_3.1_单BSS实验(基本服务集合实验)

无线局域网的最小基本组件是基本服务BSS 实验内容 实验目的 实验原理 实验步骤 为了显示AP的有效通信范围 切换到物理工作区 把物理工作区导航到城市家园 直接在城市家园放置AP0 可以看到AP0的通信范围 放置笔记本电脑 在默认情况下 笔记本电脑上 安装了以太网卡 现在换成无…

大模型之二十一-小语言模型塞道开启

当前提到大语言模型,大家想到的都是动辄百亿规模以上参数量的模型,13B、70B都是稀疏平常入门级的,但是目前从模型层面来看,模型参数量的规模两极分化已经来临,早期各大公司为了效果怼上去,采取了简单粗暴的…

[Angular] 笔记 4:ngFor

ngFor 是一个 for 循环,只能用于循环遍历 list,不能用于遍历单个实体。 下图中的 pokemons 通常是数据库中的数据: 例子: app.components.ts: // 使用类型检查 interface Pokemon {id: number;name: string;type: string;// is…

c++学习:static在类中的空间分配+实战+单例设计模式

目录 情况一: 证实方法: 结果: 情况二: 证实方法: 结果: 实战1:在同一个类中不同对象中传递消息 方法一: 方法二: 实战2:该类只用创建一次&#xff0…

【postgresql】PSQLException: An I/O error occurred while sending to the backend.

org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend. 发送到后端时发生I/O错误。 java.io.IOException: Tried to send an out-of-range integer as a 2-byte value: 34284 尝试将超出范围的整数作为2字节值发送:34284 pos…

C++学习——访问限定符

在C中,protected, friend, 和 public 是访问限定符,用于指定类成员的访问级别。 访问限定符 public public 成员可以从类的任何地方访问,包括类的外部。这通常是你希望外部代码能够直接访问和操作的那些方法和属性。比如,一个类…

OpenHarmony开发环境快速搭建(无需命令行)

一. 搭建Windows环境 在嵌入式开发中,很多开发者习惯于使用Windows进行代码的编辑,比如使用Windows的Visual Studio Code进行OpenHarmony代码的开发。但当前阶段,大部分的开发板源码还不支持在Windows环境下进行编译,如Hi3861、H…

LLM微调(四)| 微调Llama 2实现Text-to-SQL,并使用LlamaIndex在数据库上进行推理

Llama 2是开源LLM发展的一个巨大里程碑。最大模型及其经过微调的变体位居Hugging Face Open LLM排行榜(https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard)前列。多个基准测试表明,就性能而言,它正在接近GPT-3.5…

助老理发,寒冬送暖从头开始

为进一步弘扬尊老、敬老、爱老、助老的中华民族传统美德,解决老年人年龄大、冬季出行不便的问题,2023年12月20日,绿萝志愿服务队在翠堤社区开展了“助老理发”志愿活动。 大雪过后天气格外寒冷,但志愿者们依旧早早的来现场做…

【Flink-Bug】Flink 自定义 Sink 重写 RichSinkFunction 方法时重复调用 open 的解决方案

【Flink-Bug】Flink 自定义 Sink 重写 RichSinkFunction 方法时重复调用 open 的解决方案 Flink 自定义 RichinkFunction 时可能会重写 open 方法进行某些连接的初始化操作,但是会出现重复调用 open 方法的问题,如:MQ,如果重复调用…

【Unity实现海浪盒_GerstnerWaves算法_焦散Caustics效果_案例分享】

Unity实现海浪盒效果 背景设置好顶点色参数海浪盒水体部分效果为了快速实现效果,下面用Shadergraph实现效果。ShaderGraph水体全节点ShaderGraph全节点模块序号ShaderGraph属性ShaderGraph Graph Setting1. GerstnerWave 顶点动画部分,输出的是顶点偏移和NormalOSWaves算法Ge…

扭蛋机小程序搭建,“互联网+”下的发展优势

随着我国生活水平和消费能力不断提高,人们对各种潮流文化类的产品需求也快速上升。至此,我国潮流文化市场得到了快速发展! 扭蛋机作为潮玩中的一种商业模式,深受不同年龄层用户的喜爱。并且扭蛋机的种类也是各式各样,…

MYSQL单表删除重复的数据方法

先简述遇到的问题:我要删除一张表的数据,先是查询到所有的重复的数据id直接进行删除操作,但是一直执行不完(一直执行就是删除不完) DELETE FROM table WHERE id IN ( SELECT MAX(id) id from table where rId…

P2089 烤鸡

烤鸡 题目背景 猪猪 Hanke 得到了一只鸡。 题目描述 猪猪 Hanke 特别喜欢吃烤鸡(本是同畜牲,相煎何太急!)Hanke 吃鸡很特别,为什么特别呢?因为他有 10 10 10 种配料(芥末、孜然等&#xff…

Vue 官方周报 #124 - 使用JSDoc记录组件属性

Hi &#x1f44b; 当你将鼠标悬停在IDE中的组件上时&#xff0c;显示组件属性所对应的描述&#xff0c;这个功能在开发过程中会很有用。你可以在传递给defineProps函数的TypeScript接口中使用JSDoc来实现这一点&#xff1a; MyComponent.vue <script setup lang"ts&…

【音视频 | AAC】AAC格式音频文件解析

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

ffplay工具

在编译ffmpeg时&#xff0c;如果系统中包含了SDL库&#xff0c;则会默认编译生成ffplay工具&#xff0c;否则无法生成ffplay工具。 ffplay即可以作为播放器&#xff0c;也可以作为很多图像化音视频数据的分析工具&#xff0c;通过它可以看到视频图像的运动估计方向、音频数据的…