Linux内核同步介绍及方法

Linux内核设计与实现

第9章内核同步介绍

(1)多个执行线程同时访问和操作数据,就有可能发生各线程之间相互覆盖共享数据的情况,造成共享数据处于不一致状态。并发访问共享数据是造成系统不稳定的一类隐患,而且这种错误一般难以跟踪和调试。在使用共享内存的应用程序中,程序员必须特别留意保护共享资源,防止共享资源并发访问,内核也不例外。

(2)临界区是访问和操作共享数据的代码段。
(3)避免并发和防止竞争条件称为同步。

(4)原子操作在操作执行结束前不可被打断。
(5)可以通过加锁来保护临界区资源。
- 内核中可能造成并发的原因:
- 中断——中断几乎可以在任何时刻异步发生,也就可以随时打断当前正在执行的代码。
- 软中断和tasklet——内核能在任何时刻唤醒或调度软中断和tasklet,打断当前正在执行的代码。
- 内核抢占——因为内核具有抢占性,所以内核中的任务可能会被另一任务抢占。

(6)尽管释放锁的顺序和死锁无关,但最好还是以获得锁的相反顺序来释放锁。

第10章内核同步方法

原子操作:

内核提供了两组原子操作接口,一组针对整数进行操作,另一组针对单独的位进行操作。
针对整数的原子操作只能对atomic_t类型的数据进行处理。
原子性与顺序性:原子性确保指令执行期间不被打断,要么全部执行,要么根本不执行。顺序性确保即使两条或多条指令同时出现在独立的执行线程中,他们依然保持本该的执行顺序。顺序性通过屏障指令来实施。

自旋锁:

自旋锁只能被一个可执行线程持有。如果一个线程试图争用自旋锁,他就会处于忙循环——旋转——等待锁重新可用中一直自旋,浪费处理器时间。
所以自旋锁不应被长时间占有。这正是使用自旋锁的初衷:在短期内进行轻量级加锁。
处理锁争用的其他方式:让请求线程睡眠,直到锁重新可用时唤醒他。
中断处理程序中使用自旋锁时要在获取锁之前禁止本地中断,否则中断处理程序会打断正持有锁的内核代码。
下半部可以抢占进程上下文中的代码,所以下半部和进程上下文共享数据时,要保护进程上下文中的共享数据。

信号量

信号量是一种睡眠锁,他比自旋锁提供了更好的处理器利用率,因为没有把时间花费在忙等待上。
在占用信号量的同时不能占用自旋锁。
锁被持有时间长时用信号量,被持有时间短时用自旋锁。
信号量包括互斥信号量和计数信号量。计数信号量在一个时刻至多有count个持有者。
互斥体是相对于信号量更简单的睡眠锁。

自旋锁与信号量的比较

需求 建议的加锁方法
低开销加锁 优先使用自旋锁
短期锁定 优先使用自旋锁
长期加锁 优先使用互斥体
中断上下文中加锁 使用自旋锁
持有锁需要睡眠 使用互斥体

自旋锁方法列表

方法 描述
spin_lock() 获取指定的自旋锁
spin_lock_irq() 禁止本地中断并获得指定的锁
spin_lock_irqsave() 保存本地中断的当前状态,禁止本地中断,并获取指定的锁
spin_unlock() 释放指定的锁
spin_unlock_irq() 释放指定的锁,并激活本地中断
spin_unlock_irqrestore() 释放指定的锁,并让本地中断恢复到以前状态
spin_lock_init() 动态初始化指定的spinlock_t
spin_trylock() 试图获取指定的锁,如果未获取,则返回非0
spin_is_locked() 如果指定的锁当前正在被获取,则返回非0,否则返回0

信号量方法列表

方法 描述
sema_init(struct semaphore *,int) 以指定的计数值初始化动态创建的信号量
init_MUTEX(struct semaphore *) 以计数值1初始化动态创建的信号量
init_MUTEX_LOCKED(struct semaphore *) 以计数值0初始化动态创建的信号量
down_interruptible(struct semaphore *) 以试图获得指定的信号量,如果信号量已被争用,则进入可中断睡眠状态
down (struct semaphore *) 以试图获得指定的信号量,如果信号量已被争用,则进入不可中断睡眠状态
down_trylock (struct semaphore *) 以试图获得指定的信号量,如果信号量已被争用,则立刻返回非0值
up (struct semaphore *) 以释放指定的信号量,如果睡眠队列不空,则唤醒其中一个任务

你可能感兴趣的:(Linux)