重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
10.1.1 原子整数操作
创新互联专业为企业提供武平网站建设、武平做网站、武平网站设计、武平网站制作等企业网站建设、网页设计与制作、武平企业网站模板建站服务,十载武平做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。typedef struct{
volatile int counter;
}atomic_t;
10.1.2 64位原子操作
10.1.3 原子位操作
10.2 自旋锁
自旋锁最多只能被一个可执行线程持有。如果一个执行线程试图获得一个被已经持有的自旋锁,那么该线程就会一直进行忙循环等待锁重新可用。要是锁未被争用,请求锁的执行线程便立刻得到它,继续执行。
自旋锁不应该被长期持有。还可以让请求线程睡眠,直到锁重新可用时再唤醒他。这样处理器不必循环等待,可以去执行其他代码。这里有两次明显的上下文切换,被阻塞的线程要换出和换入。因此,持有自旋锁的时间最好小于完成两次上下文切换的耗时。
自旋锁是不可递归的。
自旋锁可以使用在中断处理程序中。在中断处理程序中使用自旋锁时,一定要在获取锁之前,首先禁止本地中断,否则中断处理程序就会打断正持有自旋锁的代码,中断处理程序可能争用同一锁。如果中断发生在不同的处理器上,即使中断处理程序在同一锁上自旋,也不会妨碍锁的持有者最终释放锁。
自旋锁操作方法
spin_lock();
spin_lock_irq();
spin_lock_irqsave(); 保存本地中断的状态,禁止本地中断,并获取锁。
spin_unlock();
spin_unlock_irq();
spin_unlock_irqstore();释放指定的锁,并让本地中断恢复到以前状态
spin_lock_init();
spin_trylock();
spin_is_locked();
10.3 读——写自旋锁
一个或多个读任务可以并发的持有读者锁;写锁最多被一个任务持有,而且此时不能有并发的读操作。
读写锁机制照顾读比照顾写要多。所以大量读任务必定会使挂起的写任务处于饥饿状态。
10.4 信号量
linux中的信号量是一种睡眠锁。如果有一个任务视图获得一个不可用的信号量时,信号量会将其推进一个等待队列,然后让其睡眠。这时处理器能够重获自由,从而去执行其他代码。当持有的信号量可用(被释放)后,处于等待队列中的那个任务将被唤醒,并获得该信号量。
信号量特性:
1、由于争用信号量的进程在等待锁重新变为可用时会睡眠,所以信号量适用于锁会被长时间持有的情况。
2、锁被短时间持有时,使用信号量不合算。因为睡眠、维护等待队列以及唤醒所花费的开销可能比锁被占用的全部时间还要长
3、由于执行线程在锁被争用时会睡眠,所以只能在进程上下文中才能获取信号量锁,因为中断上下文中是不能进行调度的
4、你可以在持有信号量的时候去睡眠。
5、占用信号量的时候不能同时占有自旋锁。因为在等待信号量时可能会睡眠,而持有自旋锁不允许睡眠。
10.4.1 计数信号量和二值信号量
信号量可以同时允许任意数量的锁持有者,自旋锁一个时刻只允许一个任务持有。计数等于1的信号量被称为二值信号量或者互斥信号量。
10.4.2 创建和初始化信号量
struct semaphore name;
sema_init(&name,count);
-------------------------
static DECLARE_MUTEX(name)
--------------------------
sema_init(sem,count); sem是指针,count指数量
--------------------------
init_MUTEX(sem);
10.4.3 使用信号量
sema_init(struct semaphore*,int);
init_MUTEX(struct semaphore*);
init_MUTEX_LOCKED(struct semaphore*); 以计数值0初始化动态创建的信号量
down_interruptible(struct semaphore*);以试图获取指定的信号量,如果信号量已被争用,则进入可中断睡眠状态
down(struct semaphore*)以试图获取指定的信号量,如果信号量已被争用,则进入不可中断睡眠状态
down_trylock(struct semaphore*);
up(struct semaphore*);
10.5 读-写信号量
10.6 互斥体
1、任何时刻只有一个任务可以持有mutex
2、给mutex上锁者负责给其解锁——不能在一个上下文中锁定一个mutex,在另一个上下文中开启。
3、递归上锁和解锁是不允许的
4、当持有一个mutex时,进程不可以退出
5、mutex不能再中断或者下半部使用,即使使用mutex_trylock()也不行
6、mutex只能通过官方API管理。
10.7 完成变量
如果在内核中,一个任务需要发出信号通知另一任务发生了某个特定事件,利用完成变量是使得两个任务得以同步的简单方法。如果一个任务要执行一些工作时,另一个任务就会在完成变量上等待。当这个任务完成工作后,会使用完成变量去唤醒在等待的任务。例如:当子进程执行或者退出时,vfork()系统调用使用完成变量唤醒父进程。
在一个指定的完成变量上,需要等待的任务调用wait_for_completion()来等待特定事件。当特定事件发生后,产生事件的任务调用complete()来发送信号唤醒正在等待的任务。
10.8 BLK:大内核锁
BKL是一个全局自旋锁,使用它主要是为了方便实现从linux最初的SMP过渡到细粒度加锁机制。特性:
1、持有BKL的任务可以睡眠。当任务无法调度时,锁会自动被丢弃;当任务被调度时,锁又重新获得
2、BKL是一种递归锁。一个进程可以多次请求一个锁。
3、BKL只可以用在进程上下文中。
4、新的用户不允许使用BKL。
10.9 顺序锁
seq锁对写者更有利。只要没有其他写者,写锁总是能够被成功获得。读者不会影响写锁。
seq锁在如下情况下是理想选择:
1、数据存在很多读者
2、写者较少
3、虽然写者较少,但是你希望写优先于读,而且不允许读让写饥饿
4、你的数据很简单,如简单结构,甚至是简单的整形——在某些场合,你是不能使用原子量的。
10.10 禁止抢占
preempt_disable();增加抢占计数,从而禁止抢占
preempt_enable();减少抢占计数,并当该值将为0时检查和执行被挂起的需调度任务
preempt_enable_no_resched();激活内核抢占但不再检查任何被挂起的需调度任务
preempt_count(); 返回抢占计数
为了用更简洁的方法解决每个处理器上的数据访问问题,可以通过get_cpu()获得处理器编号。这个函数在返回当前处理器编号前会首先关闭内核抢占。
10.11 顺序和屏障
编译器和处理器为了提高效率,可能对读和写重新排序。所有可能重新排序和写的处理器提供了机器指令来确保顺序要求。同样也可以指示编译器不要给定点周围的指令序列进行重新排序,这些确保顺序的指令成为屏障。
rmb()方法提供了一个读内存屏障。
wmb()写内存屏障
mb()读写屏障
read_barrier_depends()是rmb()的变种。仅仅是针对后续读操作所依靠的那些载入。因为屏障后的读操作依赖于屏障前的读操作,因此,该屏障确保屏障前的读操作在屏障后的读操作之前完成。
smp_rmb();
smp_reab_barrier_depends();
smp_wmb();
smp_mb();
barrier();阻止编译器跨屏障对载入或存储操作进行优化