临界区、信号量与互斥锁
前言
这也算是面试必备的知识了。其实这东西还是非常简单的,这篇文章争取把你教会。
为什么要搞这么个信号量和互斥锁呢?其实很简单,同一个资源(比如同一块内存或者是同一个IO外设)同时被不同的进程访问(A进程想写入一些东西,恰巧B进程也想写入一些东西),最后的结果很有可能就出乎意料,所以在访问一些公共资源时,大家要排好队,一个个来访问,避免冲突。
Linux下的进程与线程
有人这么说:进程是资源分配的最小单位,线程是CPU调度的最小单位。其实线程和进程在CPU调度器上来说,都是基本的调度单位,都有自己的PCB(process control block)。线程是依托于进程而创建出来的,本质上就是可以独立运行的一个函数。进程享有独立完整的地址空间,而线程则没那么好运了,线程需要依赖进程的资源,本身是不会分配得到独立的地址空间的。
另一个角度理解是:进程之间切换时需要不断地保存上下文,而进程中的线程间切换时,不需要再重复保存进程的上下文,在CPU调度时间上来说,进程的粒度更小。
CPU如何调度进程和线程
这是个很沉重的话题了,我肯定是不会随手去网上摘一段然后放在这里,文字枯燥难懂,看了等于没看。
Linux下有一个东西叫PCB,process control block,就是一个进程或线程的身份证,这个结构体里面大概有啥呢?先不告诉你,反正这里保存了进程的基本信息。
struct ProcessControlBlock
{
balabala........
}
接着,咱们得准备两个全局的链表,链表成员就是PCB指针。一个是所有的进程PCB链表,一个是已经处于ready状态随时可以运行的PCB链表。
extern struct list thread_ready_list;
extern struct list thread_all_list;
好了,调度的本质,就是将当前的PCB归还到ready链表,再将ready链表的front弹出,运行之。是不是听起来很简单?(细节?别着急啊,慢慢来)
调度是啥时候发生的呢?调度的发生,分为主动与被动两种。主动的调度,就是当前正在运行的PCB(我这么说很挫的,但好理解)里,自己写了几行代码,执行调度函数。。。。。。(当然这样的例子有很多啊,比如sleep(0),比如后面咱们要说的信号量与锁的实现都是如此),还有一种调度是有点被动的,我来介绍一下被动调度的机制-定时中断。定时中断大家都知道,就是每隔一段时间执行一次定时中断函数。PCB每次登场的时候,都会有一个叫优先级的东西,0是内核优先级,最高,3是用户优先级,最低。(现在PCB长这样)优先级越高的PCB,获准执行的时间就越长,这个时间呢,在每次PCB登场时也准备好。还有一个参数,出厂时是0,每来一次定时中断,由中断给他+1,相当于计时。
struct ProcessControlBlock
{
uint8_t priority; //多了一个优先级
uint8_t ticks; // 每次在处理器上允许执行的时钟中断数
uint8_t cur_ticks; //定时中断每次将其+1
balabala.....