Linux 同步管理(上)

因为现代操作系统是多处理器计算的架构,必然更容易遇到多个进程,多个线程访问共享数据的情况,如下图所示:

图中每一种颜色代表一种竞态情况,主要归结为三类:

  1. 进程与进程之间:单核上的抢占,多核上的SMP;

  2. 进程与中断之间:中断又包含了上半部与下半部,中断总是能打断进程的执行流;

  3. 中断与中断之间:外设的中断可以路由到不同的CPU上,它们之间也可能带来竞态;

这时候就需要一种同步机制来保护并发访问的内存数据。文章分为两部分,这一章主要讨论原子操作,自旋锁,信号量和互斥锁。

原子操作

原子操作是在执行结束前不可打断的操作,也是最小的执行单位。以 arm 平台为例,原子操作的 API 包括如下:

API说明
int atomic_read(atomic_t *v)读操作
void atomic_set(atomic_t *v, int i)设置变量
void atomic_add(int i, atomic_t *v)增加 i
void atomic_sub(int i, atomic_t *v)减少 i
void atomic_inc(atomic_t *v)增加 1
void atomic_dec(atomic_t *v)减少 1
void atomic_inc_and_test(atomic_t *v)加 1 是否为 0
void atomic_dec_and_test(atomic_t *v)减 1 是否为 0
void atomic_add_negative(int i, atomic_t *v)加 i 是否为负
void atomic_add_return(int i, atomic_t *v)增加 i 返回结果
void atomic_sub_return(int i, atomic_t *v)减少 i 返回结果
void atomic_inc_return(int i, atomic_t *v)加 1 返回
void atomic_dec_return(int i, atomic_t *v)减 1 返回

原子操作通常是内联函数,往往是通过内嵌汇编指令来实现的,如果某个函数本身就是原子的,它往往被定义成一个宏。

可见原子操作的原子性依赖于 ldrex 与 strex 实现,ldrex 读取数据时会进行独占标记,防止其他内核路径访问,直至调用 strex 完成写入后清除标记。

ldrex 和 strex 指令,是将单纯的更新内存的原子操作分成了两个独立的步骤:

  1. ldrex 用来读取内存中的值,并标记对该段内存的独占访问:                                                  
    ldrex Rx, [Ry]
     读取寄存器 Ry 指向的4字节内存值,将其保存到 Rx 寄存器中,同时标记对 Ry 指向内存区域的独占访问。如果执行 ldrex 指令的时候发现已经被标记为独占访问了,并不会对指令的执行产生影响。
  2. strex 在更新内存数值时,会检查该段内存是否已经被标记为独占访问,并以此来决定是否更新内存中的值:                                                                                                                
    strex Rx, Ry, [Rz] 
     如果执行这条指令的时候发现已经被标记为独占访问了,则将寄存器 Ry 中的值更新到寄存器 Rz 指向的内存,并将寄存器 Rx 设置成 0。指令执行成功后,会将独占访问标记位清除。如果执行这条指令的时候发现没有设置独占标记,则不会更新内存,且将寄存器 Rx 的值设置成 1。

自旋锁 spin_lock

Linux内核中最常见的锁是自旋锁,自旋锁最多只能被一个可执行线程持有。如果一个线程试图获取一个已被持有的自旋锁,这个线程会进行忙循环——旋转等待(会浪费处理器时间)锁重新可用。自旋锁持有期间不可被抢占。

另一种处理锁争用的方式:让等待线程睡眠,直到锁重新可用时再唤醒它,这样处理器不必循环等待,可以去执行其他代码,但是这会有两次明显的上下文切换的开销,信号量便提供了这种锁机制。

自旋锁的使用接口如下:

API说明
spin_lock()获取指定的自旋锁
spin_lock_irq()禁止本地中断并获取指定的锁
spin_lock_irqsave()保存本地中断当前状态,禁止本地中断,获取指定的锁
spin_unlock()释放指定的锁
spin_unlock_irq()释放指定的锁,并激活本地中断
spin_unlock_irqrestore()释放指定的锁,并让本地中断恢复以前状态
spin_lock_init()动态初始化指定的锁
spin_trylock()试图获取指定的锁,成功返回0,否则返回非0
spin_is_locked()测试指定的锁是否已被占用,已被占用返回非0,否则返回0
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值