1 内核提供了两组原子操作接口:一组针对整数进行操作,另一组针对单独的位进行操作。
2 原子整数操作:
针对整数的原子操作只能对atomic_t 类型的数据进行处理。两个原因:首先,让原子函数只接收该类型的操作数,可以确保原子操作只与这种特殊数据类型一起使用。其次,使用该类型确保编译器不对相应的值进行访问优化(寄存器缓存访问以节省时间),这点使得原子操作最终接收到正确的内存地址,而不是一个别名。该类型只有24位。int类型低8位被嵌入了一个锁。后来加入了atomic64_t,和atomic_t一个样。
3 原子位操作
位操作时,是原子操作。
4 自旋锁
最多只能被一个可执行线程持有。如果有其他线程想获取,那么该线程就会一直进行忙循环,旋转,等待锁重新可用。要是锁未被争用,请求锁的线程便能立刻得到它。自旋锁提供了一种快速简单的锁实现方法。如果加锁时间不长且代码不会睡眠,利用自旋锁是最佳选择。如果加锁时间可能很长或者代码在持有锁时有可能睡眠,那么最好使用信号量来完成锁功能。
5 加锁大原则
针对数据加锁,而不是代码!
6 信号量
Linux中的信号量是一种睡眠锁。如果一个任务在获取一个已被占用的信号量时,信号量会将任务推入一个等待队列,然后让其睡眠,让出处理器去执行其他代码。当持有的信号量释放的时候,任务会被唤醒,并获取该信号量。信号量比自旋锁的开销大,主要在上下文切换上。
6 信号量分为计数信号量和二值信号量
7 计数信号量
锁持有者大于1
8 二值信号量
和自旋锁一样,只有一个任务会持有锁,也称为互斥信号量。
9 信号量支持两个原子操作,down()和up()。down()操作通过对信号量减1来请求获得一个信号量。如果结果是0或大于0,获得信号量锁,任务就可以进入临界区。如果结果为负数,任务会被放入等待队列,处理器执行其他任务。
10 互斥体
互斥的特定睡眠锁,简化版的信号量。
11 完成变量
如果在内核中一个任务需要发出信号通知另一个任务发生了某个特定事件,利用完成变量是使得两个任务得以同步的简单方法。实现的功能就是简化版的信号量。