同步是建立在互斥的基础之上的,只有实现了互斥功能,才能实现同步机制。
同步一般用semaphore表示,互斥一般用spin_lock来表示。Windows内核源码也是如此,linux内核中亦然。主要区别是在samphore机制中,当某进程进不了临界区时会进行其他进程的调度,而spin_lock刚执行忙等。
内核中的执行路径主要有:
- 用户进程的内核态,此时有进程context,主要是代表进程在执行系统调用等。
- 中断或者异常或者自陷等,从概念上说,此时没有进程context,不能进行context switch。
- 软中断,从概念上说,此时也没有进程context。
- 同时,相同的执行路径还可能在其他的CPU上运行。
中断相关的锁
local_irq_disable/local_irq_enable,表示只是对当前执行上下文的CPU进行开/关中断。如果在多CPU情形下,不保证其他CPU会相应中断。
spin_lock/spin_inlock
spin_lock采用的方式是让一个进程运行,另外的进程忙等待。由于在只有一个cpu上的机器(UP)上微观上只有一个进程在运行,所以在UP中,spin_lock和spin_unlock就都是空的了。
spinlock_xxx有很多形式,有
spin_lock()/spin_unlock(),
spin_lock_irq()/spin_unlock_irq(),
spin_lock_irqsave/spin_unlock_irqstore(),
spin_lock_bh()/spin_unlock_bh(),
local_bh_disable/local_bh_enable,
具体什么情况下使用那个,要看内核执行路径,以及要与哪些内核执行路径相互斥。
- 如果只要和其他cpu互斥—-spin_lock
- 如果要和irq及其他cpu互斥—–spin_lock_irq
- 如果既要和irq及其他cpu互斥,又要保存EFLAG的状态——-spin_lock_irqsave
- 如果要和bh及其他cpu互斥—–spin_lock_bh
- 如果不需要和其他cpu互斥,只要和bh互斥—-lock_bh_disable
spin_lock在代码中有好多定义,但其实就是_spin_lock,它在spinlock.c中实现。
down/up信号
内核中的semaphore机制,它主要通过down()和up()两个操作实现。down()用于资源获取,而up()是释放资源。
一个任务通过调用down()获取资源,而代表该资源的信号量表示“没有可用的资源”的时候,进程转入等待状态,直到占有资源的进程调用up()释放资源后才能被唤醒。进程的等待与唤醒通过等待队列实现。up()释放一个资源,并唤醒一个等待进程。若此时还有其他进程处于等待状态的,down()返回前的wake_up()会暂时地唤醒等待进程,将count、sleepers再调整为(-1, 1),表示暂时无足够资源提供,然后进入等待状态。
可以看出,semaphore和spin_lock机制解决的都是两个进程的互斥问题,都是让一个进程退出临界区后另一个进程才进入的方法,不过semaphore机制实行的是让进程暂时让出cpu,进入等待队列等待的策略,而spin_lock实行的却是进程在原地空转,等着另一个进程结束的策略。
RCU读写
linux2.6引入了新的锁机制RCU(Read-Copy Update)的实现机制,RCU,就是读-拷贝修改,对于被RCU保护的共享数据结构,可以不需要获得任何锁就可以读取它,但写访问它时首先拷贝一个副本,然后对副本进行修改,最后使用一个回调callback机制在适当的时候把指向原来数据的指针重新指向新的被修改的数据。这个时机就是所有引用该数据的cpu都退出对共享数据的操作。
各种异步手段:
- timer(定时器函数)
- work_queue(工作队列)
- 通知链
……