四种情况下锁的不同实现方式
在单个CPU,单全局数据的情况下,我们只需要将原来编译器会编译成3条汇编语句的代码通过c语言的asm表达式,写成一条汇编语句就可以实现原子变量了,因为每条指令的执行都是原子性的,不会被中断,且由于只有单个CPU,因此不会有其他CPU并行地处理目标全局数据
在多个CPU,单全局变量的情况下,我们可以在单CPU的解决方案上增加 CPU 的汇编原子指令(lock;)来实现并发控制,lock;
前缀会在CPU总线上发送锁定命令使得只有当前CPU可以操作对应的变量
在单个CPU,复杂全局变量结构体的情况下,由于多个进程并发执行,是靠使用中断进行切换的,因此只需要处理中断就可以了,关闭中断后,只有一个进程在执行这块临界区代码,其他进程无法切换执行,执行完这块代码后,再打开中断,再去切换到别的进程执行
在多个CPU中,复杂全局变量结构体的情况下,则需要通过基于硬件提供的原子操作(xchg 1, &lock)原子地交换两个数值,再判断lock中原来的值是否为0,来实现自旋锁了,由于xchg是原子操作,所以在同一时刻只有一个CPU会执行该操作并尝试把lock从0置为1,交换结束后,如果发现lock原来为0则说明之前没有其他人获取该锁,当前CPU成为第一个获取该锁的,另外由于交换的操作,锁也已经被置为1了代表该锁已经被当前CPU获取了,因此其他CPU之后只能获得1,进而陷入访问判断为1的流程中,死循环;
除了自旋锁外,信号量也可以解决上述问题,并且信号量额外解决了自旋锁中其他得不到执行的进程一直在轮询的问题,这个一直轮询会导致CPU无法切换到其他不需要执行该临界区的进程执行,效率低下; 所以引入能睡眠的机制,得不到的进程不让他们继续等了,先睡觉,负责其他进程执行的CPU去切换到别的进程执行; 等执行完临界区的进程OK后,再把这些睡觉的进程唤醒,他们再争抢。信号量的核心在于:等待、互斥、唤醒。
总而言之,自旋锁是spinlock不会让出CPU资源,而信号量是sleeplock,它在spinlock的基础上,增加sleep功能,如果一个执行体加锁失败就会进入休眠并让出CPU执行权