UNIX/LINUX
系統的進程間通信機構(
IPC
)允許在任意進程間大批量地交換數據。本實驗的目的是瞭解和熟悉
LINUX
支持的信號量機制、管道機制、消息通信機制及共享存儲區機制。
( 一 ) 信號機制實驗
實驗目的
1 、瞭解什麼是信號
2 、熟悉 LINUX 系統中進程之間軟中斷通信的基本原理
實驗內容
1 、編寫程序:用 fork( ) 創建兩個子進程,再用系統調用 signal( ) 讓父進程捕捉鍵盤上來的中斷信號(即按 ^c 鍵);捕捉到中斷信號後,父進程用系統調用 kill( ) 向兩個子進程發出信號,子進程捕捉到信號後分別輸出下列信息後終止:
Child process1 is killed by parent!
Child process2 is killed by parent!
父進程等待兩個子進程終止後,輸出如下的信息後終止:
Parent process is killed!
2 、分析利用軟中斷通信實現進程同步的機理
實驗指導
一、信號
1 、信號的基本概念
每個信號都對應一個正整數常量 ( 稱爲 signal number, 即信號編號。定義在系統頭文件 <signal.h> 中 ) , 代表同一用戶的諸進程之間傳送事先約定的信息的類型,用於通知某進程發生了某異常事件。每個進程在運行時,都要通過信號機制來檢查是否有信號到達。若有, 便中斷正在執行的程序,轉向與該信號相對應的處理程序,以完成對該事件的處理;處理結束後再返回到原來的斷點繼續執行。實質上,信號機制是對中斷機制的一 種模擬,故在早期的 UNIX 版本中又把它稱爲軟中斷。
信號與中斷的相似點:
( 1 )採用了相同的異步通信方式;
( 2 )當檢測出有信號或中斷請求時,都暫停正在執行的程序而轉去執行相應的處理程序;
( 3 )都在處理完畢後返回到原來的斷點;
( 4 )對信號或中斷都可進行屏蔽。
信號與中斷的區別:
( 1 )中斷有優先級,而信號沒有優先級,所有的信號都是平等的;
( 2 )信號處理程序是在用戶態下運行的,而中斷處理程序是在覈心態下運行;
( 3 )中斷響應是及時的,而信號響應通常都有較大的時間延遲。
信號機制具有以下三方面的功能:
( 1 )發送信號。發送信號的程序用系統調用 kill( ) 實現;
( 2 )預置對信號的處理方式。接收信號的程序用 signal( ) 來實現對處理方式的預置;
( 3 )收受信號的進程按事先的規定完成對相應事件的處理。
2 、信號的發送
信號的發送,是指由發送進程把信號送到指定進程的信號域的某一位上。如果目標進程正在一個可被中斷的優先級上睡眠,核心便將它喚醒,發送進程就此結束。一個進程可能在其信號域中有多個位被置位,代表有多種類型的信號到達,但對於一類信號,進程卻只能記住其中的某一個。
進程用 kill( ) 向一個進程或一組進程發送一個信號。
3 、對信號的處理
當一個進程要進入或退出一個低優先級睡眠狀態時,或一個進程即將從核心態返回用戶態時,核心都要檢查該進程是否已收到軟中斷。當進程處於核心態時,即使收到軟中斷也不予理睬;只有當它返回到用戶態後,才處理軟中斷信號。對軟中斷信號的處理分三種情況進行:
( 1 )如果進程收到的軟中斷是一個已決定要忽略的信號( function=1 ),進程不做任何處理便立即返回;
( 2 )進程收到軟中斷後便退出( function=0 );
( 3 )執行用戶設置的軟中斷處理程序。
二、所涉及的中斷調用
1 、 kill( )
系統調用格式
int kill(pid,sig)
參數定義
int pid,sig;
其中, pid 是一個或一組進程的標識符,參數 sig 是要發送的軟中斷信號。
( 1 ) pid>0 時,核心將信號發送給進程 pid 。
( 2 ) pid=0 時,核心將信號發送給與發送進程同組的所有進程。
( 3 ) pid=-1 時,核心將信號發送給所有用戶標識符真正等於發送進程的有效用戶標識號的進程。
2 、 signal( )
預置對信號的處理方式,允許調用進程控制軟中斷信號。
系統調用格式
signal(sig,function)
頭文件爲
#include <signal.h>
參數定義
signal(sig,function)
int sig;
void (*func) ( )
其中 sig 用於指定信號的類型, sig 爲 0 則表示沒有收到任何信號,餘者如下表:
,
值 |
名 字 |
說 明 |
01 |
SIGHUP |
掛起( hangup ) |
02 |
SIGINT |
中斷,當用戶從鍵盤按 ^c 鍵或 ^break 鍵時 |
03 |
SIGQUIT |
退出,當用戶從鍵盤按 quit 鍵時 |
04 |
SIGILL |
非法指令 |
05 |
SIGTRAP |
跟蹤陷阱( trace trap ),啓動進程,跟蹤代碼的執行 |
06 |
SIGIOT |
IOT 指令 |
07 |
SIGEMT |
EMT 指令 |
08 |
SIGFPE |
浮點運算溢出 |
09 |
SIGKILL |
殺死、終止進程 |
10 |
SIGBUS |
總線錯誤 |
11 |
SIGSEGV |
段違例( segmentation violation ),進程試圖去訪問其虛地址空間以外的位置 |
12 |
SIGSYS |
系統調用中參數錯,如系統調用號非法 |
13 |
SIGPIPE |
向某個非讀管道中寫入數據 |
14 |
SIGALRM |
鬧鐘。當某進程希望在某時間後接收信號時發此信號 |
15 |
SIGTERM |
軟件終止( software termination ) |
16 |
SIGUSR1 |
用戶自定義信號 1 |
17 |
SIGUSR2 |
用戶自定義信號 2 |
18 |
SIGCLD |
某個子進程死 |
19 |
SIGPWR |
電源故障 |
function :在該進程中的一個函數地址,在覈心返回用戶態時,它以軟中斷信號的序號作爲參數調用該函數,對除了信號 SIGKILL , SIGTRAP 和 SIGPWR 以外的信號,核心自動地重新設置軟中斷信號處理程序的值爲 SIG_DFL ,一個進程不能捕獲 SIGKILL 信號。
function 的解釋如下:
( 1 ) function=1 時,進程對 sig 類信號不予理睬,亦即屏蔽了該類信號;
( 2 ) function=0 時,缺省值,進程在收到 sig 信號後應終止自己;
( 3 ) function 爲非 0 ,非 1 類整數時, function 的值即作爲信號處理程序的指針。
三、參考程序
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void waiting( ),stop( );
int wait_mark;
main( )
{
int p1,p2,stdout;
while((p1=fork( ))= =-1); /* 創建子進程 p1*/
if (p1>0)
{
while((p2=fork( ))= =-1); /* 創建子進程 p2*/
if(p2>0)
{
wait_mark=1;
signal(SIGINT,stop); /* 接收到 ^c 信號,轉 stop*/
waiting( );
kill(p1,16); /* 向 p1 發軟中斷信號 16*/
kill(p2,17); /* 向 p2 發軟中斷信號 17*/
wait(0); /* 同步 */
wait(0);
printf("Parent process is killed!/n");
exit(0);
}
else
{
wait_mark=1;
signal(17,stop); /* 接收到軟中斷信號 17 ,轉 stop*/
waiting( );
lockf(stdout,1,0);
printf("Child process 2 is killed by parent!/n");
lockf(stdout,0,0);
exit(0);
}
}
else
{
wait_mark=1;
signal(16,stop); /* 接收到軟中斷信號 16 ,轉 stop*/
waiting( );
lockf(stdout,1,0);
printf("Child process 1 is killed by parent!/n");
lockf(stdout,0,0);
exit(0);
}
}
void waiting( )
{
while(wait_mark!=0);
}
void stop( )
{
wait_mark=0;
}
四、運行結果
屏幕上無反應,按下 ^C 後,顯示 Parent process is killed!
五、分析原因
上述程序中, signal( ) 都放在一段程序的前面部位,而不是在其他接收信號處。這是因爲 signal( ) 的執行只是爲進程指定信號值 16 或 17 的作用,以及分配相應的與 stop( ) 過程鏈接的指針。因而, signal( ) 函數必須在程序前面部分執行。
本方法通信效率低,當通信數據量較大時一般不用此法。
六、思考
1 、該程序段前面部分用了兩個 wait(0) ,它們起什麼作用?
2 、該程序段中每個進程退出時都用了語句 exit(0) ,爲什麼?
3 、爲何預期的結果並未顯示出?
4 、程序該如何修改才能得到正確結果?
5 、不修改程序如何得到期望的輸出?