linux并发与竞争
Linux 系统是个多任务操作系统,会存在多个任务同时访问同一片内存区域,这些任务可
能会相互覆盖这段内存中的数据,造成内存数据混乱。针对这个问题必须要做处理,严重的话
可能会导致系统崩溃。现在的 Linux 系统并发产生的原因很复杂,总结一下有下面几个主要原
因:
- 多线程并发访问, Linux 是多任务(线程)的系统,所以多线程访问是最基本的原因。
- 抢占式并发访问,从 2.6 版本内核开始, Linux 内核支持抢占,也就是说调度程序可以
在任意时刻抢占正在运行的线程,从而运行其他的线程。 - 中断程序并发访问,硬件中断的权利可是很大的。
- SMP(多核)核间并发访问,现在 ARM 架构的多核 SOC 很常见,多核 CPU 存在核间并
发访问。
在编写驱动的时候一定要注意避免并发和防止竞争访问。
原子操作####
c代码在编译成汇编之后看似简单的指令也会复杂,不同线程的操作可能会混在一起,所以要保证每个线程对数据的操作指令编译成汇编之后要作为整体运行。
适用于简单内存操作,原子操作只能对整形变量或者位进行保护
自旋锁####
当一个线程要访问某个共享资源的时候首先要先获取相应的锁, 锁只能被一个线程持有,
只要此线程不释放持有的锁,那么其他的线程就不能获取此锁。等待自旋锁的线程会一直处于自旋状态,这样会浪费处理器时间,降低系统性能,所以自旋锁的持有时间不能太长。
被自旋锁保护的临界区一定不能调用任何能够引起睡眠和阻塞的API函数,否则的话会可能会导致死锁现象的发生,自旋锁会自动禁止抢占。
获取锁之前要禁止本地中断,不然线程和中断之间也会发生死锁。
中断里面可以使用自旋锁,但是在中断里面使用自旋锁的时候,在获取锁之前一定要先禁止本地(本CPU)中断,否则可能导致锁死现象的发生。
中断上下文只能使用自旋锁,因为中断不能睡眠。
信号量####
有计数型信号量、二值信号量。相比于自旋锁,信号量可以使线程进入休眠状态。但是,信号量的开销要比自旋锁大,因为信号量使线程进入休眠状态以后会切换线程,切换线程就会有开销。总结一下信号量的特点:
- 因为信号量可以使等待资源线程进入休眠状态,因此适用于那些占用资源比较久的场合。
- 因此信号量不能用于中断中,因为信号量会引起休眠,中断不能休眠。
- 如果共享资源的持有时间比较短,那就不适合使用信号量了,因为频繁的休眠、切换
线程引起的开销要远大于信号量带来的那点优势。
会导致睡眠,也可以选择能被唤醒
信号量面向很底层的代码
互斥体####
将信号量的值设置为1就可以使用信号量进行互斥访问了,虽然可以通过信号量实现互斥,但是 Linux 提供了一个比信号量更专业的机制来进行互斥,它就是互斥体—mutex。
在使用mutex之前要先定义一个mutex变量。在使用mutex的时候要注意如下几点:
- mutex 可以导致休眠,因此不能在中断中使用 mutex,中断中只能使用自旋锁。
- 和信号量一样, mutex 保护的临界区可以调用引起阻塞的API函数。
- 因为一次只有一个线程可以持有 mutex,因此,必须由mutex的持有者释放mutex。并且 mutex 不能递归上锁和解锁。
- 互斥体和信号量首选互斥体