进程间通信(IPC)+进程加锁解锁
【0】README
- 0.1) source code and text description are from orange’s implemention of a os;
- 0.2) for complete code , please visit https://github.com/pacosonTang/Orange-s-OS/tree/master/ipc_8
【1】看看,我們的進程代碼
【2】看看IPC替換系統調用 get_ticks 后的調用過程
(本圖片是對 source insight 工具打開的系統調用所涉及函數的截圖,圖片過大,建議使用 在“在新標簽頁中打開圖片”)
對上面兩張圖的分析(Analysis):(上圖中的緩沖區 or 消息發送隊列,進程間通信可能用到,也可能用不到)
- A1)要知道 進程A -> get_ticks -> send_recv -> msg_send or msg_receive 等;
- A2)系統進程 task_sys 率先啟動,調用send_recv(RECEIVE, ANY, &msg)即msg_receive 等待接收消息 ,由于沒有任何進程發送消息給它,該進程阻塞;我們看看阻塞代碼:
A3) 進程A啟動,發送消息給 task_sys,進入 系統調用msg_send,關鍵代碼如下:
(p_dest->p_recvfrom == proc2pid(sender) ||p_dest->p_recvfrom == ANY)) {assert(p_dest->p_msg);assert(m);phys_copy(va2la(dest, p_dest->p_msg),va2la(proc2pid(sender), m),sizeof(MESSAGE));p_dest->p_msg = 0;p_dest->p_flags &= ~RECEIVING; /* dest has received the msg */p_dest->p_recvfrom = NO_TASK;
if ((p_dest->p_flags & RECEIVING) && /* dest is waiting for the msg */unblock(p_dest)
顯然,msg_send 里面的if 語句,系統進程的結構體成員是滿足的,所以 進程A向系統進程 task_sys 發送消息成功,(也就是吧進程A的 消息結構體 內容 copy 到 系統進程task_sys 的消息結構體存儲空間,由函數 phys_copy 完成)A4)消息發送成功后,返回0, send_recv 函數立即調用 ret = sendrec(RECEIVE, src_dest, msg) 即msg_receive 系統調用 接收 系統進程task_sys 發來的消息;因為 send_recv 中的 src_dest 是指定好了的, src_dest = task_sys(只不過,它傳入的參數是task_sys 進程的 進程表 索引)
- A5)系統進程 task_sys 收到消息后,向先前的 消息發送者(進程A) 發送消息,該消息封裝了 ticks 的value值;
A6)系統進程 task_sys 調用 msg_send 系統調用,關鍵代碼如下:
(p_dest->p_recvfrom == proc2pid(sender) ||p_dest->p_recvfrom == ANY)) {assert(p_dest->p_msg);assert(m);phys_copy(va2la(dest, p_dest->p_msg),va2la(proc2pid(sender), m),sizeof(MESSAGE));p_dest->p_msg = 0;p_dest->p_flags &= ~RECEIVING; /* dest has received the msg */p_dest->p_recvfrom = NO_TASK;
if ((p_dest->p_flags & RECEIVING) && /* dest is waiting for the msg */unblock(p_dest)
顯然,msg_send 里面的if 語句,進程A 的結構體成員是滿足的,所以 系統進程task_sys 向 進程 A 發送消息成功;A7)進程A 收到消息,抽取出 RETVAL, 關鍵代碼如下:
send_recv(BOTH, TASK_SYS, &msg);TASK_SYS=1return msg.RETVAL;
【3】我們看看 assert (panic類似)怎么調用的?
對sys_printx函數的分析(Analysis):
- A1)當sys_printx 發現傳入字符串的第一個字符是MAG_CH_ASSERT時,會同時判斷調用 系統調用的進程是系統進程還是用戶進程(通過 sys_printx函數中if 語句 的 p_proc_ready < &proc_table[NR_TASKS]) 判斷);
- A2)如果是系統進程,則停止整個系統的運轉,并將要打印的字符串打印在顯存的各處;
- A3)如果是用戶進程, 則打印后像一個普通進程一樣返回,屆時該用戶進程會因為 assertion_failure() 中對 函數spin 的調用而進 死循環;
- Attention):換句話說, 系統進程的 assert 失敗會導致系統停止運轉(hlt), 而用戶進程的失敗僅僅導致 自己停轉(spin);
【4】我們接著看 進程的加鎖 + 解鎖
以下有5副圖片,第一幅圖片是重中之重,給出了 加鎖和解鎖函數的定義,以及進程切換函數 schedule, 建議在分析的時候,把后4副圖片和 第1 副圖片做對照,效果更好。
4.1)進程加鎖 在何時發生?(block)(這里p->p_flags 置!0 是關鍵)
- case1)在msg_send 函數中: 如果消息接收者 沒有 指定 該消息發送者,或者接收者沒有準備好接收消息,則消息發送者會添加到 接收者的消息發送隊列中;【 在本例中,消息發送者是進程A, 它是用戶進程,所以進程A被阻塞(spin 死循環函數)】;
阻塞(加鎖)過程解析:由于 sender->p_flags |= SENDING(參見上圖代碼),顯然 sender->p_flags 不等于零,不會進入assert 函數,直接進入 schedule()進行進程切換,顯然 schedule 將CPU控制權切換到那些 p_flags == 0 的進程手里面;也即是只要p->p_flags !=0 ,那么該進程p 就永遠也無法進行進程切換,獲得CPU控制權(此謂阻塞);
我們這里再添加p_flags 的作用,p_flags 用于表明進程的狀態,取值3種(下文不再敘述有關 p_flags 的作用):
- 1)value = 0:表明進程正在運行或準備運行;
- 2)value = SENDING(宏定義,具體你不管,反正 !0): 表明進程處于發送消息狀態,由于消息沒有被送達(該進程還處于接收者進程的發送隊列中), 消息發送者進程被阻塞;
- 3)value = RECEIVING(宏定義,具體你不管,反正 !0): 表明進程處于接收消息狀態,由于沒有接收到消息, 消息接收者進程被阻塞;
case2) 在msg_receive 函數中: 如果沒有任何進程向 當前進程發送消息的話,當前進程會阻塞,直到有進程發送消息給當前進程;【舉個荔枝:在本例中,當前進程是個系統進程 task_sys,所以這樣的話,整個系統會停止運轉;】
- 阻塞(加鎖)過程解析:由于 p_who_wanna_recv->p_flags |= RECEIVING (參見上圖代碼),顯然 p_who_wanna_recv->p_flags 不等于零,不會進入assert 函數,直接進入 schedule()進行進程切換,顯然 schedule 將CPU控制權切換到那些 p_flags == 0 的進程手里面;也即是只要p->p_flags !=0 ,那么該進程p 就永遠也無法進行進程切換,獲得CPU控制權(此謂阻塞);
4.2)進程解鎖在何時發生?(unblock)(這里p->p_flags 置0 是關鍵)
- case1)在msg_send 函數中:如果處于 RECEIVING 狀態的進程 接收到了消息,則 就會對該進程進行解鎖;判斷p->p_flags==0,【舉個荔枝:在本例中,消息接收者進程是個系統進程 task_sys,所以這樣的話,整個系統會停止運轉】;
解鎖過程解析:由于 p_dest->p_flags &= ~RECEIVING; /* dest has received the msg */(參見上圖代碼),顯然 p_dest->p_flags ==0,(0==0 -> TRUE)也不會進入assert 函數;紫薇曾經問過:你還記得大明湖畔的夏雨荷嗎?我也來問一句:你還記得 加鎖過程中調用的 schedule 嗎? schedule 函數 阻塞該進程的法寶就是 當其 p_flags非零時,就不會對該進程進行CPU控制權的轉讓,因為if 語句根本就不滿足;現在好了, 在解鎖前 p_dest->p_flags &= ~RECEIVING(因為接收到消息前,p_flags==RECEIVING,當然和~RECEIVE 進行邏輯與后 得到0) ;也即是 原先阻塞的進程的p_flags==0 了,它就可以獲得CPU控制權了(轉向到schedule),這也就解鎖了,Bingo!(此謂解鎖);
case2)在msg_receive 函數中:如果接收者進程 接收任一消息,就從其發送隊列中取出一個發送進程,然后發送進程解鎖;
- 解鎖過程解析:由于 p_from->p_flags &= ~SENDING(參見上圖代碼),顯然 p_dest->p_flags ==0,(0==0 -> TRUE)也不會進入assert 函數;
紫薇曾經問過:你還記得大明湖畔的夏雨荷嗎?我也來問一句:你還記得 加鎖過程中調用的 schedule 嗎? schedule 函數 阻塞該進程的法寶就是 當其 p_flags非零時,就不會對該進程進行CPU控制權的轉讓,因為if 語句根本就不滿足;現在好了, 在解鎖前 p_dest->p_flags &= ~SENDING(因為接收到消息前,p_flags==SENDING,當然和~SENDING進行邏輯與后 得到0) ;也即是 原先阻塞的進程的p_flags==0 了,它就可以獲得CPU控制權(轉向到schedule),這也就解鎖了,Bingo!(此謂解鎖);
總結
以上是生活随笔為你收集整理的进程间通信(IPC)+进程加锁解锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C打印函数printf的一种实现原理简要
- 下一篇: 快速选择