我是如何学习写一个操作系统(七):进程的同步与信号量
前言
在多進(jìn)程的運(yùn)行環(huán)境下,進(jìn)程是并發(fā)執(zhí)行的,不同進(jìn)程間存在著不同的相互制約關(guān)系。為了協(xié)調(diào)進(jìn)程之間的相互制約關(guān)系,達(dá)到資源共享和進(jìn)程協(xié)作,避免進(jìn)程之間的沖突,引入了進(jìn)程同步的概念。
臨界資源
多個(gè)進(jìn)程可以共享系統(tǒng)中的各種資源,但其中許多資源一次只能為一個(gè)進(jìn)程所使用,我們把一次只允許一個(gè)進(jìn)程使用的資源成為臨界資源。
對(duì)臨界資源的訪問(wèn),必須互斥的進(jìn)行。每個(gè)進(jìn)程中,訪問(wèn)臨界資源的那段代碼成為臨界區(qū)。
為了保證臨界資源的正確使用,可以把臨界資源的訪問(wèn)過(guò)程分為四個(gè)部分。
- 進(jìn)入?yún)^(qū)。為了進(jìn)入臨界區(qū)使用臨界資源,在進(jìn)入去要檢查可否進(jìn)入臨界區(qū)。
- 臨界區(qū)。進(jìn)程中訪問(wèn)臨界資源的那段代碼。
- 退出區(qū)。將正在訪問(wèn)臨界區(qū)的標(biāo)志清除。
- 剩余區(qū)。代碼中的其余部分。
一般實(shí)現(xiàn)進(jìn)程的同步有這幾種方法:
- 提過(guò)硬件提供的實(shí)現(xiàn)
- 信號(hào)量
- 管程
生產(chǎn)者-消費(fèi)者實(shí)例
下面的代碼就分別是生產(chǎn)者進(jìn)程和消費(fèi)者進(jìn)程,而buffer就是臨界資源
當(dāng)生產(chǎn)者要訪問(wèn)臨界資源時(shí),會(huì)先判斷buffer是不是已經(jīng)滿(mǎn)了,而消費(fèi)者則判斷buffer是不是空的,這就是訪問(wèn)臨界資源的進(jìn)入?yún)^(qū)
而中間對(duì)buffer的操作就是臨界區(qū)
最后對(duì)counter的加減就是設(shè)置對(duì)臨界區(qū)訪問(wèn)的標(biāo)志
但是這里依舊也有可能出現(xiàn)問(wèn)題,比如當(dāng)進(jìn)程走到in = (in + 1) % BUFFER_SIZE;的時(shí)候,這時(shí)候操作系統(tǒng)進(jìn)行調(diào)度,這時(shí)候的counter的值可能還是0,所以消費(fèi)者進(jìn)程可能就會(huì)出現(xiàn)問(wèn)題
這里的處理可以是利用關(guān)閉中斷來(lái)限制進(jìn)程的切換,但是在多核CPU下一樣不管用
這里就涉及到了臨界區(qū)的保護(hù)了
#define BUFFER_SIZE 10 typedef struct { . . . } item; item buffer[BUFFER_SIZE]; int in = out = counter = 0 while (true) {while(counter== BUFFER_SIZE); buffer[in] = item;in = (in + 1) % BUFFER_SIZE;counter++; } while (true) {while(counter== 0) ; item = buffer[out];out = (out + 1) % BUFFER_SIZE;counter--; }信號(hào)量
什么是信號(hào)量
為了防止出現(xiàn)因多個(gè)程序同時(shí)訪問(wèn)一個(gè)共享資源而引發(fā)的一系列問(wèn)題,我們需要一種方法,它可以通過(guò)生成并使用令牌來(lái)授權(quán),在任一時(shí)刻只能有一個(gè)執(zhí)行線(xiàn)程訪問(wèn)代碼的臨界區(qū)。
為了避免像上面一樣會(huì)發(fā)生競(jìng)爭(zhēng)條件,程序?qū)π盘?hào)量訪問(wèn)都是原子操作,且只允許對(duì)它進(jìn)行等待(即P(信號(hào)變量))和發(fā)送(即V(信號(hào)變量))信息操作。
信號(hào)量的工作原理
由于信號(hào)量只能進(jìn)行兩種操作等待和發(fā)送信號(hào),即P(sv)和V(sv),他們的行為是這樣的:
- P(sv):如果sv的值大于零,就給它減1;如果它的值為零,就掛起該進(jìn)程的執(zhí)行
- V(sv):如果有其他進(jìn)程因等待sv而被掛起,就讓它恢復(fù)運(yùn)行,如果沒(méi)有進(jìn)程因等待sv而掛起,就給它加1.
舉個(gè)例子,就是兩個(gè)進(jìn)程共享信號(hào)量sv,一旦其中一個(gè)進(jìn)程執(zhí)行了P(sv)操作,它將得到信號(hào)量,并可以進(jìn)入臨界區(qū),使sv減1。而第二個(gè)進(jìn)程將被阻止進(jìn)入臨界區(qū),因?yàn)楫?dāng)它試圖執(zhí)行P(sv)時(shí),sv為0,它會(huì)被掛起以等待第一個(gè)進(jìn)程離開(kāi)臨界區(qū)域并執(zhí)行V(sv)釋放信號(hào)量,這時(shí)第二個(gè)進(jìn)程就可以恢復(fù)執(zhí)行。
Linux 0.11的進(jìn)程同步
在Linux 0.11里是沒(méi)有實(shí)現(xiàn)信號(hào)量的,考慮后面會(huì)自己實(shí)現(xiàn)一個(gè)。這里先看一下Linux 0.11里用來(lái)進(jìn)行進(jìn)程同步的兩個(gè)函數(shù)
sleep_on
p實(shí)際上指的是一個(gè)等待隊(duì)列
如果當(dāng)前進(jìn)程是進(jìn)程0或者無(wú)效,就直接退出
然后把要等待的進(jìn)程放到等待隊(duì)列的頭節(jié)點(diǎn),把狀態(tài)設(shè)置為不可中斷的等待狀態(tài)
這里隊(duì)列的形成非常非常的隱蔽,首先把用tmp指向之前的進(jìn)程,在把當(dāng)前要睡眠的進(jìn)程放入,而之所以能形成隊(duì)列,是因?yàn)楝F(xiàn)在放入隊(duì)列的進(jìn)程的tmp作為局部變量是保存在這個(gè)進(jìn)程的堆棧中的,這樣在把進(jìn)程切換回來(lái)的時(shí)候,tmp就自然的指向上一個(gè)進(jìn)程了。
最后當(dāng)這個(gè)進(jìn)程被喚醒的時(shí)候,會(huì)回到if語(yǔ)句喚醒等待隊(duì)列中所有進(jìn)程
wake_up
- 喚醒 p 指向的任務(wù)。 p是任務(wù)等待隊(duì)列頭指針。由于新等待任務(wù)是插入在等待隊(duì)列頭指針處的,
小結(jié)
首先竟然有了多進(jìn)程,那在訪問(wèn)共享資源的時(shí)候自然就會(huì)發(fā)生制約關(guān)系,所以才引入了進(jìn)程同步的概念。
而進(jìn)程同步的關(guān)鍵就是對(duì)臨界區(qū)的保護(hù),信號(hào)量就是一種可以很好的實(shí)現(xiàn)對(duì)臨界區(qū)保護(hù)的方法
轉(zhuǎn)載于:https://www.cnblogs.com/secoding/p/11428751.html
總結(jié)
以上是生活随笔為你收集整理的我是如何学习写一个操作系统(七):进程的同步与信号量的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 我是如何学习写一个操作系统(六):进程的
- 下一篇: 我是如何学习写一个操作系统(八):内存管