操作系统进程互斥
在多道批处理系统中,多个进程是并发执行的,而并发执行的进程不可避免地需要共享一些系统资源(比如内存、打印机、摄像头等)。这样会带来什么问题呢?实际上,有些资源在一个时间段内只允许一个进程使用,诸如各种物理设备、变量、数据、内存缓冲区等,这些称之为临界资源 —— 也就是说,一方面,并发执行的进程需要共享资源;另一方面,临界资源的访问又必须是互斥地进行(不能同时共享),很显然,这会导致资源访问上的矛盾。
这就提出了进程互斥的概念,利用进程互斥来解决临界资源同时访问的冲突问题。
互斥四个原则
空闲让进
:临界区空闲时,说明没有进程使用临界资源,此时应该让想要进入临界区的进程立刻进来忙则等待
:如果已经有进程进入临界区,则其它同样想要进入的进程只能等着有限等待
:不能让进程一直干等着,要保证他在有限的时间内可以进入临界区让权等待
::当进程不能进入自己的临界区时,应该立刻释放处理机,防止进程陷入“忙等”状态
软件实现进程互斥 (四个算法)
单标志法
设置公用整型变量turn,用于指示允许进入临界区的进程编号,turn为零,则允许P0进程进入临界区。
特点
:能够确保进程互斥访问临界区,但是不能保证满足"空闲让进"原则,并且两进程必须交替进入临界区。
个人形象理解(若有问题还请留言讨论呀)
:单标志法实现互斥,即主要通过turn这个变量来处理,即从初始值为0开始,先允许P0访问进程,然后访问结束之后立即将访问权交给P1手中;这样存在的问题就是,进程没有考虑自身下次是否还会继续使用该资源。若下次在P1进程之前还想访问该临界资源会出现一直得不到资源的情况;
好比如:现在是桌上只有一双筷子,有A跟B两个人,一开始先把筷子给A,A吃完后直接就把筷子洗干净给B了,然后说你吃完再把筷子洗干净给我,结果B无语了,他也没说要用筷子吃东西,然后A就是说不管,你必须吃完过后再把筷子给我,结果A自己又想吃的时候结果没有筷子用,因为筷子还在B那里呢,B还在纳闷A怕不是有什么大病。
int turn = 0;
P0 进程: P1 进程:
while (turn != 0); while (turn != 1); // 进入区
critical section; critical section; // 临界区
turn = 1; turn = 0; // 退出区
remainder section; remainder section; // 剩余区
双标志先检查法
设置一个数组flag[2],表示继承是否进入临界区,flag[i]的值为FALSE,表示Pi进程未进入临界区,为TRUE则表示Pi进入临界区。
特点
:不能确保进程互斥访问临界区,但是不能保证满足"忙则等待"原则,进程不必交替进入,可以连续使用。
个人形象理解(若有问题还请留言讨论呀)
:其实感觉这个算法有点鸡肋,不过还是为后面不断完善提供了基础,这个算法的主要就是通过flag来对进程是否将要访问临界资源进行标记,即进入临界区之前,先检查对方进程是否想要访问进程,如果对方进程想要进入临界区,则自己可以等会,否则,则自己进入临界区,一进入就把标志置为true;
好比如:现在同样是桌上只有一双筷子,有A跟B两个人,但是现在这两个人会察言观色了,会先看对方会不会想要先用筷子,然后再判断下一步是使用筷子还是接着等待。一开始,两个人都不说自己想不想用筷子,就等对方先表态,毕竟要多谦让,最后彼此看这么久都不说话,估计就是不想用了,然后彼此都默认对方不想用筷子。这个时候就会出现问题了,两个人都默认彼此不想用筷子,然后自己就下意识去拿筷子,结果刚好对方也想用,这就尴尬了,那最后是谁用呢,这个时候就会出现资源被同时访问的问题。
bool flag[2]= {false,false};
P0 进程: P1 进程:
while (flag[1]); while (flag[0]); // 进入区
flag[0] = true; flag[1] = true; // 进入区
critical section; critical section; // 临界区
flag[0] = false; flag[1] = false; // 退出区
remainder section; remainder section; // 剩余区
双标志后检查法
设置一个数组flag[2],这里与前面不同之处就是,先设置自己的标志位,再检测对方的标志状态,若对方的标志位为true则等待呗。
特点
:能够确保进程互斥访问临界区,但是存在两个进程都进入不了临界区的"饥饿"现象。违背了"空闲让进"和"有限等待";
个人形象理解(若有问题还请留言讨论呀)
:这个算法同样是通过flag来对进程是否将要访问临界资源进行标记,在进入临界区之前,先设置自己的标志位,再检查对方进程是否想要访问进程,如果对方进程想要进入临界区,则自己可以等会,否则,则自己进入临界区;
好比如:现在还是桌上只有一双筷子,但是现在就不是A跟B了,换成孔融1号和孔融2号,为什么给他们这样取名字呢,后面就知道啦!现在这两个人呢,在想用筷子的时候都会先说出来表明自己的态度,然后再看对方会不会想要先用筷子,然后再判断下一步是使用筷子还是接着等待。如果一开始两个人同时表明自己想要筷子的话,对方都会考虑到礼仪问题,谦让给对方用,毕竟谁叫他们叫孔融呢,但是这样出现的问题就是明明有筷子可以用但是因为谦让而僵持住。结果两个人就只能饿着了,在操作系统里面这里就出现了"死等",即会存在进程产生"饥饿"。
bool flag[2]= {false,false};
P0 进程: P1 进程:
flag[0] = true; flag[1] = true; // 进入区
while (flag[1]); while (flag[0]); // 进入区
critical section; critical section; // 临界区
flag[0] = false; flag[1] = false; // 退出区
remainder section; remainder section; // 剩余区
Peterson 算法
设置一个数组flag[2],这里与前面不同之处就是,先设置自己的标志位,再检测对方的标志状态,若对方的标志位为true则等待呗。
特点
:Peterson 算法实际上同时结合了单标志法和双标志后检查法,它的核心就是:在一开始还是和后检查法一样,抢先进行“上锁”,但是上锁之后又将 turn 置为对方线程,表示自己虽然想要进入临界区,但是不介意“将这个机会让给对方”。尽管如此,由于 while 的限制条件增加了,而 turn 又是公用的,所以保证了最后只会有一方的 while 满足条件。既做到了互斥访问资源,也避免了双方都访问不到资源。
P0 进程: P1 进程:
flag[0] = true;turn = 1; flag[1] = true;turn = 0; // 进入区
while (flag[1] && turn == 1); while (flag[0] && turn == 0);// 进入区
critical section; critical section; // 临界区
flag[0] = false; flag[1] = false; // 退出区
remainder section; remainder section; // 剩余区