当前位置: 首页 > news >正文

arm linux下的读写信号量rw_semphore的实现

本文基于arm linux 5.10来介绍内核中使用的读写信号量rw remphore的实现代码。

内核中信号量结构体struct rw_semaphore的定义在include/linux/rwsem.h

32位architectures下,结构体struct rw_semaphore中的count的使用如下:

先来看信号量的定义和初始化函数和宏:

宏DECLARE_RWSEM()定义了一个读写信号量,并初始化count为0.

如果我们已经定义了信号量变量,要初始化它,可以使用init_rwsem()或直接使用__init_rwsem(), 它们的定义如下:

init_rwsem()和__init_rwsem()他们初始化信号量计数count也是为0.

读写信号量,运行多个读,读与写互斥,写只能有一个。

先看读信号量获取: down_read().

down_read()先调用__down_read_trylock()去上锁, 如果trylock()上锁失败(返回0), 再调用__down_read()去上锁。

先来看__down_read_trylock().

__down_read_trylock() -> atomic_long_try_cmpxchg_acquire() -> atomic_try_cmpxchg_acquire()

这个atomic_try_cmpxchg_acquire()在include/linux/atomic-fallback.h中定义,它有两个定义处,用哪个依赖于对应architecture的atomic.h中定义。

我们来看armv7下的atomic.h中是否定义了atomic_try_cmpxchg_relaxed.

所以,在armv7下,atomic_try_cmpxchg_relaxed没有#define.故,__down_read_trylock() -> atomic_long_try_cmpxchg_acquire() -> atomic_try_cmpxchg_acquire()

这个atomic_try_cmpxchg_acquire()在include/linux/atomic-fallback.h中926行定义,如下:

故,atomic_try_cmpxchg_acquire() -> atomic_cmpxchg_acquire()。

atomic_cmpxchg_acquire() 定义如下:

故, atomic_cmpxchg_acquire() -> atomic_cmpxchg_relaxed().

所以,__down_read_trylock() -> atomic_long_try_cmpxchg_acquire() ->

atomic_try_cmpxchg_acquire() -> atomic_cmpxchg_acquire() -> atomic_cmpxchg_relaxed(),

atomic_cmpxchg_relaxed()定义在arch/arm/include/asm/atomic.h, 如下:

117行:加载信号量计数 sem->count.counter.

118行:清零res.

119行:比较信号量计数值和参数old的大小.

120行:信号量计数值和参数old相等,则将参数new更新到信号量计数。

126行:返回信号量计数更新前的值。

从实现代码看出,__down_read_trylock()是判断信号量计数是否为初值RWSEM_UNLOCKED_VALUE(0), 是的话,则将read计数设置为1(就是1<<8)。

当__down_read_trylock()失败时,则调用__down_read().

__down_read()先调用rwsem_read_trylock().

__down_read() -> rwsem_read_trylock() -> atomic_long_add_return_acquire() ->

atomic_add_return_acquire() -> atomic_add_return_relaxed().

atomic_add_return_relaxed()定义如下:

65行:加载信号量计数sem->count.counter

66行:sem->count.counter + (1 << 8)

67行:新值更新到sem->count.counter

74行:返回sem->count.counter新值。

所以,__down_read()-> rwsem_read_trylock()就是将信号量读计数+1.

rwsem_read_try_lock()拿到更新read计数后的新值时,判断锁是否存在写者或者等待者waiter(279行),存在写者或者等待者waiter时,返回0;否则返回1.

当信号量有写者时,__down_read() –> rwsem_down_read_slowpath().

rwsem_down_read_slowpath()将当前task放入信号量等待队列sem->wait_list中,并设置为TASK_UNINTERRUPTIBLE状态,同时,将信号量计数sem->count减1.

好了,读信号量获取介绍完了,下面介绍读信号量释放: up_read().

up_read() -> __up_read() -> atomic_long_add_return_release() -> atomic_add_return_release() ->

atomic_add_return_relaxed()

atomic_add_return_relaxed() 定义如下:

可见,up_read() -> __up_read()就是将读计数-1。

__up_read()将读计数减1后,判断是等待队列是否有等待者wait_list,有则调用rwsem_wake()将去唤醒。如果等待队列中第一个是写占有请求,则唤醒这个写占有请求者去占有信号量;如果等待队列中第一个不是写等待者,则优先将等待队列中读请求者全部唤醒(最多0x100个)

信号量读操作介绍完了,来看看信号量写。

写信号量获取: down_write().

down_write()先调用__down_write_trylock()获取信号量,如果失败(返回0),则再调用__down_write().

atomic_cmpxchg_acquire() 定义如下:

所以,down_write() -> __down_write_trylock() -> atomic_long_try_cmpxchg_acquire() ->

atomic_try_cmpxchg_acquire() -> atomic_cmpxchg_acquire() -> atomic_cmpxchg_relaxed().

atomic_cmpxchg_relaxed()定义在arch/arm/include/asm/atomic.h,

如下:

117行:加载信号量计数 sem->count.counter.

118行:清零res.

119行:比较信号量计数值和参数old的大小.

120行:信号量计数值和参数old相等,则将参数new更新到信号量计数。

126行:返回信号量计数更新前的值。

down_write() -> __down_write_trylock()就是先判断信号量是否为初值RWSEM_UNLOCKED_VALUE(0)。是的话,则设置信号量count为RWSEM_WRITER_LOCKED (1<<0). 否则__down_write_trylock()就是获取信号量失败,down_write()则调用__down_write()去获取写信号量。

__down_write()先调用atomic_long_try_cmpxchg_acquire(), 这个操作和

__down_write_trylock()一样。

atomic_cmpxchg_acquire() 定义如下:

down_write() -> __down_write() -> atomic_long_try_cmpxchg_acquire() ->

atomic_try_cmpxchg_acquire() -> atomic_cmpxchg_acquire() -> atomic_cmpxchg_relaxed().

atomic_cmpxchg_relaxed()定义在arch/arm/include/asm/atomic.h, 如下:

117行:加载信号量计数 sem->count.counter.

118行:清零res.

119行:比较信号量计数值和参数old的大小.

120行:信号量计数值和参数old相等,则将参数new更新到信号量计数。

126行:返回信号量计数更新前的值。

down_write() -> __down_write()就是判断信号量是否为初值RWSEM_UNLOCKED_VALUE(0)。是的话,则设置信号量count为RWSEM_WRITER_LOCKED (1<<0),否则就是获取信号量失败,则进入rwsem_down_write_slowpath().

1166-1175行:将当前写信号量请求者添加到等待队列sem->wait_list.

1178-1196行:如果当前写信号量请求者不是第一个等待者,则唤醒等待队列前面的task,

读等待者优先。

1207行:设置当前有等待者flag: RWSEM_FLAG_WAITERS

1212行:设置当前进程状态为不可中断休眠状态TASK_UNINTERRUPTILE。

1214行:调用rwsem_try_write_lock(), 尝试去lock信号量。

1238行:重新调度,让出cpu。

好了,写信号量获取down_write()介绍完了,来介绍最后一个写信号量释放: up_write().

up_write() -> __up_write() -> atomic_long_fetch_add_release()

up_write() -> __up_write() -> atomic_long_fetch_add_release() -> atomic_fetch_add_release() ->

atomic_featch_add_relaxed()

atomic_featch_add_relaxed()的定义如下:

86行:加载sem->count.counter

87行:sem->count.counter + i

88行:保存sem->count.counter + i到sem->count.counter。

95行:返回值result为sem->count.counter更新前的值。

故,up_write() -> __up_write()就是删除write_lock, 即写者不再占有信号量。

当写者释放信号量时,如果该信号量有等待者waiters, 则调用rwsem_wake()去唤醒第一个写等待者;如果第一个不是写等待者,则优先唤醒等待队列中所有读等待者(最多0x100个)。

这里的唤醒没有reader优先,和rwsem_down_write_slowpath()不一样。

好了,我们讲解完了读写信号量。现在总结一下:

  1. 当信号量当前被读者占有时,允许其他task读lock, 每lock一次,读计数+1,读lock次数不限制(当然在计数器不溢出范围内),也就是允许多个读存在。此时写占有请求被阻止,写占有请求者task将被放入等待队列sem->wait_list. 这之后来的读请求,都将被放入等待队列(因为被设置了waiters flag)。当信号量所有读占有者释放信号量时,写占有请求者task将被唤醒,去占有信号量。

  1. 当信号量当前被写者占有时,任何其他读请求者或写请求者,都将被组织,请求者被放入等待队列sem->wait_list.当信号量写占有者释放信号量时,如果有信号量占有请求,则去做唤醒动作。如果等待队列第一个是写占有请求,则将其唤醒去占有信号量。如果等待队列第一个不是写占有请求,则优先将所有读占有请求者(最多0x100个)唤醒去占有信号量。

相关文章:

  • macOS 使用 enca 识别 文件编码类型(比 file 命令准确)
  • C++ 项目实战书店销售记录统计程序(十)
  • 论文阅读:2023 arxiv Provable Robust Watermarking for AI-Generated Text
  • tryhackme——The Lay of the Land
  • Wi-Fi NAN 架构(Wi-Fi Aware Specification v4.0,第2章:2.3~2.6)
  • 大数据学习栈记——HBase操作(shell java)
  • 信奥赛CSP-J复赛集训(模拟算法专题)(25):P3955 [NOIP 2017 普及组] 图书管理员
  • 计算机网络基础之三种交换技术及其性能分析
  • 六十天前端强化训练之第二十四天之Vue 模板语法与 v-for 指令大师级详解
  • 【Linux之Shell脚本实战】Linux服务器输出美观漂亮的html巡检报告
  • 使用Docker部署RabbitMQ
  • 电子学会—2023年12月青少年软件编程(图形化)三级等级考试真题——打砖块游戏
  • 【SpringCloud】OpenFeign
  • 腾讯云宝塔安装ffmpeg
  • vue 对接 paypal 订阅和支付
  • Android10 系统截屏功能异常的处理
  • DeepSeek算力服务器的选型--青岛佰优联创新科技有限公司
  • OpenGL ES ->乒乓缓冲,计算只用两个帧缓冲对象(Frame Buffer Object)+叠加多个滤镜作用后的Bitmap
  • SpringBoot3实战(SpringBoot3+Vue3基本增删改查、前后端通信交互、配置后端跨域请求、数据批量删除(超详细))(3)
  • Linux系统中关闭Docker服务并禁止其开机自启 、docker 安装目录结构分析 | 【du -sh *】
  • 印度最新发声:对所有敌对行动均予以反击和回应,不会升级冲突
  • 4月金融数据前瞻:受去年低基数因素影响,社融增量有望同比大幅多增
  • 850亿元!2025年中央金融机构注资特别国债(一期)拟第一次续发行
  • 乌克兰议会批准美乌矿产协议
  • 上海质子重离子医院二期项目启动,有望成为全世界最大粒子治疗中心
  • 特朗普称美军舰商船应免费通行苏伊士运河,外交部:反对任何霸凌言行