多程式程式設計(轉)

subid發表於2007-08-16
多程式程式設計(轉)[@more@]多程式程式設計
寫在前面的話
本文主要根據本人在UNIX系統上的程式設計實踐經驗總結而成, 既做為自己在
一個時期內程式設計實踐的部分總結, 又可成為文章發表. 對UNIX程式設計師初學者來
說是一個小小的經驗, 僅供參考; 對UNIX老手來說則不值一哂, 請各位多多指
教.
一.多程式程式的特點
由於UNIX系統是分時多使用者系統, CPU按時間片分配給各個使用者使用, 而在
實質上應該說CPU按時間片分配給各個程式使用, 每個程式都有自己的執行環境
以使得在CPU做程式切換時不會"忘記"該程式已計算了一半的"半成品". 以DOS
的概念來說, 程式的切換都是一次"DOS中斷"處理過程, 包括三個層次:
(1)使用者資料的儲存: 包括正文段(TEXT), 資料段(DATA,BSS), 棧段
(STACK), 共享記憶體段(SHARED MEMORY)的儲存.
(2)暫存器資料的儲存: 包括PC(program counter,指向下一條要執行的指
令的地址), PSW(processor status word,處理機狀態字), SP(stack
pointer,棧指標), PCBP(pointer of process control block,程式控
制塊指標), FP(frame pointer,指向棧中一個函式的local變數的首地
址), AP(augument pointer,指向棧中函式呼叫的實參位置), ISP(
interrupt stack pointer,中斷棧指標), 以及其他的通用暫存器等.
(3)系統層次的儲存: 包括proc,u,虛擬儲存空間管理表格,中斷處理棧.
以便於該程式再一次得到CPU時間片時能正常執行下去.
既然系統已經處理好所有這些中斷處理的過程, 我們做程式還有什麼要擔
心的呢? 我們儘可以使用系統提供的多程式的特點, 讓幾個程式精誠合作, 簡
單而又高效地把結果給它搞出來.
另外,UNIX系統本身也是用C語言寫的多程式程式,多程式程式設計是UNIX的特
點,當我們熟悉了多程式程式設計後,將會對UNIX系統機制有一個較深的認識.
首先我介紹一下多程式程式的一些突出的特點:
1.並行化
一件複雜的事件是可以分解成若干個簡單事件來解決的, 這在程式設計師
的大腦中早就形成了這種概念, 首先將問題分解成一個個小問題, 將小問
題再細分, 最後在一個合適的規模上做成一個函式. 在軟體工程中也是這
麼說的. 如果我們以圖的方式來思考, 一些小問題的計算是可以互不干擾
的, 可以同時處理, 而在關鍵點則需要統一在一個地方來處理, 這樣程式
的執行就是並行的, 至少從人的時間觀念上來說是這樣的. 而每個小問題
的計算又是較簡單的.
2.簡單有序
這樣的程式對程式設計師來說不亞於管理一班人, 程式設計師為每個程式設計
好相應的功能, 並透過一定的通訊機制將它們有機地結合在一起, 對每個
程式的設計是簡單的, 只在總控部分小心應付(其實也是蠻簡單的), 就可
完成整個程式的施工.
3.互不干擾
這個特點是作業系統的特點, 各個程式是獨立的, 不會串位.
4.事務化
比如在一個資料電話查詢系統中, 將程式設計成一個程式只處理一次
查詢即可, 即完成一個事務. 當電話查詢開始時, 產生這樣一個程式對付
這次查詢; 另一個電話進來時, 主控程式又產生一個這樣的程式對付, 每
個程式完成查詢任務後消失. 這樣的程式設計多簡單, 只要做一次查詢的程式
就可以了.
二.常用的多程式程式設計的系統呼叫
1.fork()
功能:建立一個新的程式.
語法:#include
#include
pid_t fork();
說明:本系統呼叫產生一個新的程式, 叫子程式, 是呼叫程式的一個復
製品. 呼叫程式叫父程式, 子程式繼承了父程式的幾乎所有的屬
性:
. 實際UID,GID和有效UID,GID.
. 環境變數.
. 附加GID.
. 呼叫exec()時的關閉標誌.
. UID設定模式位元位.
. GID設定模式位元位.
. 程式組號.
. 會話ID.
. 控制終端.
. 當前工作目錄.
. 根目錄.
. 檔案建立掩碼UMASK.
. 檔案長度限制ULIMIT.
. 預定值, 如優先順序和任何其他的程式預定引數, 根據種類不同
決定是否可以繼承.
. 還有一些其它屬性.
但子程式也有與父程式不同的屬性:
. 程式號, 子程式號不同與任何一個活動的程式組號.
. 父程式號.
. 子程式繼承父程式的檔案描述符或流時, 具有自己的一個複製
並且與父程式和其它子程式共享該資源.
. 子程式的使用者時間和系統時間被初始化為0.
. 子程式的超時時鐘設定為0.
. 子程式的訊號處理函式指標組置為空.
. 子程式不繼承父程式的記錄鎖.
返回值: 呼叫成功則對子程式返回0, 對父程式返回子程式號, 這也是
最方便的區分父子程式的方法. 若呼叫失敗則返回-1給父程式,
子程式不生成.
例子:pid_t pid;
if ((pid=fork())>0) {
/*父程式處理過程*/
}
else if (pid==0) {
/*子程式處理過程*/
exit(0); /*注意子程式必須用exit()退出執行*/
}
else {
printf("fork error ");
exit(0);
}
2.system()
功能:產生一個新的程式, 子程式執行指定的命令.
語法:#include
#include
int system(string)
char *string;
說明:本呼叫將引數string傳遞給一個命令直譯器(一般為sh)執行, 即
string被解釋為一條命令, 由sh執行該命令.若引數string為一
個空指標則為檢查命令直譯器是否存在.
該命令可以同命令列命令相同形式, 但由於命令做為一個引數放
在系統呼叫中, 應注意編譯時對特殊意義字元的處理. 命令的查
找是按PATH環境變數的定義的. 命令所生成的後果一般不會對父
程式造成影響.
返回值:當引數為空指標時, 只有當命令直譯器有效時返回值為非零.
若引數不為空指標, 返回值為該命令的返回狀態(同waitpid())
的返回值. 命令無效或語法錯誤則返回非零值,所執行的命令被
終止. 其他情況則返回-1.
例子:char command[81];
int i;
for (i=1;i<8;i++) {
sprintf(command,"ps -t tty%02i",i);
system(command);
}
3.exec()
功能:執行一個檔案
語法:#include
int execl(path,arg0,...,argn,(char*)0)
char *path,*arg0,...,*argn;
int execv(path,argv)
char *path,*argv[];
int execle(path,arg0,...,argn,(char*)0,envp)
char *path,*arg0,...,*argn,*envp[];
int execve(path,argv,envp)
char *path,*argv[],*envp[];
int execvp(file,argv)
char *file,*argv[];
說明:這是一個系統呼叫族, 用於將一個新的程式調入本程式所佔的內
存, 並覆蓋之, 產生新的記憶體程式映象. 新的程式可以是可執行
檔案或SHELL批命令.
當C程式被執行時,是如下呼叫的:
main(int argc,char *argv[],char *envp[]);
argc是引數個數,是各個引數字串指標陣列,envp是新程式的環
境變數字串的指標陣列.argc至少為1,argv[0]為程式檔名,
所以,在上面的exec系統呼叫族中,path為新程式檔案的路徑名,
file為新程式檔名,若file不是全路徑名,系統呼叫會按PATH環
境變數自動找對應的可執行檔案執行.若新程式檔案不是一個可
執行的目標檔案(如批處理檔案),則execlp()和execvp()會將該
檔案內容作為一個命令直譯器的標準輸入形成system().
arg0,...等指標指向''結束的字串,組成新程式的有效引數,
且該引數列表以一個空指標結束.反過來,arg0至少必須存在並指
向新程式檔名或路徑名.
同樣,argv是字串指標陣列,argv[0]指向新程式檔名或路徑
名,並以一空指標結束.
envp是一個字串指標陣列,以空指標結束,這些字串組成新進
程的環境.
在呼叫這些系統呼叫前開啟的檔案指標對新程式來說也是開啟的,
除非它已定義了close-on-exec標誌.開啟的檔案指標在新程式中
保持不變,所有相關的檔案鎖也被保留.
呼叫程式設定並正被捕俘的訊號在新程式中被恢復為預設設定,
其它的則保持不變.
新程式啟動時按檔案的SUID和SGID設定定義檔案的UID和GID為有
效UID和GID.
新程式還繼承瞭如下屬性:
. 附加GID.
. 程式號.
. 父程式號.
. 程式組號.
. 會話號.
. 控制終端.
. alarm時鐘訊號剩下的時間.
. 當前工作目錄.
. 根目錄.
. 檔案建立掩碼.
. 資源限制.
. 使用者時間,系統時間,子程式使用者時間,子程式系統時間.
. 記錄鎖.
. 程式訊號掩碼.
. 訊號遮蔽.
. 優先順序.
. 預定值.
呼叫成功後,系統呼叫修改新程式檔案的最新訪問時間.
返回值:該系統呼叫一般不會有成功返回值, 因為原來的程式已蕩然無
存.
例子:printf("now this process will be ps command ");
execl("/bin/ps","ps","-ef",NULL);
4.popen()
功能:初始化從/到一個程式的管道.
語法:#include
FILE *popen(command,type)
char *command,type;
說明:本系統呼叫在呼叫程式和被執行命令間建立一個管道.
引數command做為被執行的命令列.type做為I/O模式,"r"為從被
執行命令讀,"w"為向被執行命令寫.返回一個標準流指標,做為管
道描述符,向被執行命令讀或寫資料(做為被執行命令的STDIN或
STDOUT)該系統呼叫可以用來在程式中呼叫系統命令,並取得命令
的輸出資訊或者向命令輸入資訊.
返回值:不成功則返回NULL,成功則返回管道的檔案指標.
5.pclose()
功能:關閉到一個程式的管道.
語法:#include
int pclose(strm)
FILE *strm;
說明:本系統呼叫用於關閉由popen()開啟的管道,並會等待由popen()
啟用的命令執行結束後,關閉管道後讀取命令返回碼.
返回值:若關閉的檔案描述符不是由popen()開啟的,則返回-1.
例子:printf("now this process will call popen system call ");
FILE * fd;
if ((fd=popen("ps -ef","r"))==NULL) {
printf("call popen failed ");
return;
}
else {
char str[80];
while (fgets(str,80,fd)!=NULL)
printf("%s ",str);
}
pclose(fd);
6.wait()
功能:等待一個子程式返回並修改狀態
語法:#include
#include
pid_t wait(stat_loc)
int *stat_loc;
說明:允許呼叫程式取得子程式的狀態資訊.呼叫程式將會掛起直到其
一個子程式終止.
返回值:等待到一個子程式返回時,返回值為該子程式號,否則返回值為
-1.同時stat_loc返回子程式的返回值.
例子:/*父程式*/
if (fork()>0) {
wait((int *)0);
/*父程式等待子程式的返回*/
}
else {
/*子程式處理過程*/
exit(0);
}
7.waitpid()
功能:等待指定程式號的子程式的返回並修改狀態
語法:#include
#include
pid_t waitpid(pid,stat_loc,options)
pid_t pid;
int *stat_loc,options;
說明:當pid等於-1,options等於0時,該系統呼叫等同於wait().否則該
系統呼叫的行為由引數pid和options決定.
pid指定了一組父程式要求知道其狀態的子程式:
-1:要求知道任何一個子程式的返回狀態.
>0:要求知道程式號為pid值的子程式的狀態.
options引數為以位元方式表示的標誌以或運算組成的點陣圖,每個
標誌以位元組中某個位元置1表示:
WUNTRACED:報告任何未知而又已停止執行的指定程式號的子進
程的狀態.該子程式的狀態自停止執行時起就沒有被報告
過.
WCONTINUED:報告任何繼續執行的指定程式號的子程式的狀態,
該子程式的狀態自繼續執行起就沒有被報告過.
WHOHANG:若呼叫本系統呼叫時,指定程式號的子程式的狀態目
前並不是立即有效的(即可被立即讀取的),呼叫程式並被
暫停執行.
WNOWAIT:保持將其狀態設定在stat_loc的程式在可等待狀態.
該程式將等待直到下次被要求其返回狀態值.
返回值:等待到一個子程式返回時,返回值為該子程式號,否則返回值為
-1.同時stat_loc返回子程式的返回值.
例子:pid_t pid;
int stat_loc;
/*父程式*/
if ((pid=fork())>0) {
waitpid(pid,&stat_loc,0);
/*父程式等待程式號為pid的子程式的返回*/
}
else {
/*子程式的處理過程*/
exit(1);
}
/*父程式*/
printf("stat_loc is [%d] ",stat_loc);
/*字串"stat_loc is [1]"將被列印出來*/
8.setpgrp()
功能:設定程式組號和會話號.
語法:#include
pid_t setpgrp()
說明:若呼叫程式不是會話首程式.將程式組號和會話號都設定為與它
的程式號相等.並釋放呼叫程式的控制終端.
返回值:呼叫成功後,返回新的程式組號.
例子:/*父程式處理*/
if (fork()>0) {
/*父程式處理*/
}
else {
setpgrp();
/*子程式的程式組號已修改成與它的程式號相同*/
exit(0);
}
9.exit()
功能:終止程式.
語法:#include
void exit(status)
int status;
說明:呼叫程式被該系統呼叫終止.引起附加的處理在程式被終止前全
部結束.
返回值:無
10.signal()
功能:訊號管理功能
語法:#include
void (*signal(sig,disp))(int)
int sig;
void (*disp)(int);
void (*sigset(sig,disp))(int)
int sig;
void (*disp)(int);
int sighold(sig)
int sig;
int sigrelse(sig)
int sig;
int sigignore(sig)
int sig;
int sigpause(sig)
int sig;
說明:這些系統呼叫提供了應用程式對指定訊號的簡單的訊號處理.
signal()和sigset()用於修改訊號定位.引數sig指定訊號(除了
SIGKILL和SIGSTOP,這兩種訊號由系統處理,使用者程式不能捕捉到).
disp指定新的訊號定位,即新的訊號處理函式指標.可以為
SIG_IGN,SIG_DFL或訊號控制程式碼地址.
若使用signal(),disp是訊號控制程式碼地址,sig不能為SIGILL,SIGTRAP
或SIGPWR,收到該訊號時,系統首先將重置sig的訊號控制程式碼為SIG_DFL,
然後執行訊號控制程式碼.
若使用sigset(),disp是訊號控制程式碼地址,該訊號時,系統首先將該
訊號加入呼叫程式的訊號掩碼中,然後執行訊號控制程式碼.當訊號控制程式碼
執行結束
後,系統將恢復呼叫程式的訊號掩碼為訊號收到前的狀態.另外,
使用sigset()時,disp為SIG_HOLD,則該訊號將會加入呼叫程式的
訊號掩碼中而訊號的定位不變.
sighold()將訊號加入呼叫程式的訊號掩碼中.
sigrelse()將訊號從呼叫程式的訊號掩碼中刪除.
sigignore()將訊號的定位設定為SIG_IGN.
sigpause()將訊號從呼叫程式的訊號掩碼中刪除,同時掛起呼叫
程式直到收到訊號.
若訊號SIGCHLD的訊號定位為SIG_IGN,則呼叫程式的子程式在終
止時不會變成僵死程式.呼叫程式也不用等待子程式返回並做相
應處理.
返回值:呼叫成功則signal()返回最近呼叫signal()設定的disp的值.
否則返回SIG_ERR.
例子一:設定使用者自己的訊號中斷處理函式,以SIGINT訊號為例:
int flag=0;
void myself()
{
flag=1;
printf("get signal SIGINT ");
/*若要重新設定SIGINT訊號中斷處理函式為本函式則執行以
*下步驟*/
void (*a)();
a=myself;
signal(SIGINT,a);
flag=2;
}
main()
{
while (1) {
sleep(2000); /*等待中斷訊號*/
if (flag==1) {
printf("skip system call sleep ");
exit(0);
}
if (flag==2) {
printf("skip system call sleep ");
printf("waiting for next signal ");
}
}
}
11.kill()
功能:向一個或一組程式傳送一個訊號.
語法:#include
#include
int kill(pid,sig);
pid_t pid;
int sig;
說明:本系統呼叫向一個或一組程式傳送一個訊號,該訊號由引數sig指
定,為系統給出的訊號表中的一個.若為0(空訊號)則檢查錯誤但
實際上並沒有傳送訊號,用於檢查pid的有效性.
pid指定將要被髮送訊號的程式或程式組.pid若大於0,則訊號將
被髮送到程式號等於pid的程式;若pid等於0則訊號將被髮送到所
有的與傳送訊號程式同在一個程式組的程式(系統的特殊程式除
外);若pid小於-1,則訊號將被髮送到所有程式組號與pid絕對值
相同的程式;若pid等於-1,則訊號將被髮送到所有的程式(特殊系
統程式除外).
訊號要傳送到指定的程式,首先呼叫程式必須有對該程式傳送信
號的許可權.若呼叫程式有合適的優先順序則具備有許可權.若呼叫程式
的實際或有效的UID等於接收訊號的程式的實際UID或用setuid()
系統呼叫設定的UID,或sig等於SIGCONT同時收發雙方程式的會話
號相同,則呼叫程式也有傳送訊號的許可權.
若程式有傳送訊號到pid指定的任何一個程式的許可權則呼叫成功,
否則呼叫失敗,沒有訊號發出.
返回值:呼叫成功則返回0,否則返回-1.
例子:假設前一個例子程式號為324,現向它發一個SIGINT訊號,讓它做
訊號處理:
kill((pid_t)324,SIGINT);
12.alarm()
功能:設定一個程式的超時時鐘.
語法:#include
unsigned int alarm(sec)
unsigned int sec;
說明:指示呼叫程式的超時時鐘在指定的時間後向呼叫程式傳送一個
SIGALRM訊號.設定超時時鐘時時間值不會被放入堆疊中,後一次
設定會把前一次(還未到超時時間)沖掉.
若sec為0,則取消任何以前設定的超時時鐘.
fork()會將新程式的超時時鐘初始化為0.而當一個程式用exec()
族系統呼叫新的執行檔案時,呼叫前設定的超時時鐘在呼叫後仍
有效.
返回值:返回上次設定超時時鐘後到呼叫時還剩餘的時間秒數.
例子:int flag=0;
void myself()
{
flag=1;
printf("get signal SIGALRM ");
/*若要重新設定SIGALRM訊號中斷處理函式為本函式則執行
*以下步驟*/
void (*a)();
a=myself;
signal(SIGALRM,a);
flag=2;
}
main()
{
alarm(100); /*100秒後發超時中斷訊號*/
while (1) {
sleep(2000); /*等待中斷訊號*/
if (flag==1) {
printf("skip system call sleep ");
exit(0);
}
if (flag==2) {
printf("skip system call sleep ");
printf("waiting for next signal ");
}
}
}
13.msgsnd()
功能:傳送訊息到指定的訊息佇列中.
語法:#include
#include
#include
int msgsnd(msqid,msgp,msgsz,msgflg)
int msqid;
void *msgp;
size_t msgsz;
int msgflg;
說明:傳送一個訊息到由msqid指定訊息佇列標識號的訊息佇列.
引數msgp指向一個使用者定義的緩衝區,並且緩衝區的第一個域應
為長整型,指定訊息型別,其他資料放在緩衝區的訊息中其他正文
區內.下面是訊息元素定義:
long mtype;
char mtext[];
mtype是一個整數,用於接收程式選擇訊息型別.
mtext是一個長度為msgsz位元組的任何正文,引數msgsz可從0到系
統允許的最大值間變化.
msgflg指定操作行為:
. 若(msgflg&IPC_NOWAIT)是真的,訊息並不是被立即傳送而呼叫
程式會立即返回.
. 若(msgflg&IPC_NOWAIT)不是真的,則呼叫程式會被掛起直到下
面情況之一發生:
* 訊息被髮送出去.
* 訊息佇列標誌被系統刪除.系統呼叫返回-1.
* 呼叫程式接收到一個未被忽略的中斷訊號,呼叫程式繼續
執行或被終止.
呼叫成功後,對應指定的訊息佇列的相關結構做如下動作:
. 訊息數(msg_qnum)加1.
. 訊息佇列最近傳送程式號(msg_lspid)改為呼叫程式號.
. 訊息佇列傳送時間(msg_stime)改為當前系統時間.
以上資訊可用命令ipcs -a看到.
返回值:成功則返回0,否則返回-1.
14.msgrcv()
功能:從訊息佇列中取得指定型別的訊息.
語法:#include
#include
#include
int msgrcv(msqid,msgp,msgsz,msgtyp,msgflg)
int msqid;
void *msgp;
int msgsz;
long msgtyp;
int msgflg;
說明:本系統呼叫從由msqid指定的訊息佇列中讀取一個由msgtyp指定
型別的訊息到由msgp指向的緩衝區中,同樣的,該緩衝區的結構如
前所述,包括訊息型別和訊息正文.msgsz為可接收的訊息正文的
位元組數.若接收到的訊息正文的長度大於msgsz,則會被截短到
msgsz位元組為止(當訊息標誌msgflg&MSG_NOERROR為真時),截掉的
部份將被丟失,而且不通知訊息傳送程式.
msgtyp指定訊息型別:
. 為0則接收訊息佇列中第一個訊息.
. 大於0則接收訊息佇列中第一個型別為msgtyp的訊息.
. 小於0則接收訊息佇列中第一個型別值不小於msgtyp絕對值且
型別值又最小的訊息.
msgflg指定操作行為:
. 若(msgflg&IPC_NOWAIT)是真的,呼叫程式會立即返回,若沒有
接收到訊息則返回值為-1,errno設定為ENOMSG.
. 若(msgflg&IPC_NOWAIT)不是真的,則呼叫程式會被掛起直到下
面情況之一發生:
* 佇列中的訊息的型別是有效的.
* 訊息佇列標誌被系統刪除.系統呼叫返回-1.
* 呼叫程式接收到一個未被忽略的中斷訊號,呼叫程式繼續
執行或被終止.
呼叫成功後,對應指定的訊息佇列的相關結構做如下動作:
. 訊息數(msg_qnum)減1.
. 訊息佇列最近接收程式號(msg_lrpid)改為呼叫程式號.
. 訊息佇列接收時間(msg_rtime)改為當前系統時間.
以上資訊可用命令ipcs -a看到.
返回值:呼叫成功則返回值等於接收到實際訊息正文的位元組數.
不成功則返回-1.
15.msgctl()
功能:訊息控制操作
語法:#include
#include
#include
int msgctl(msqid,cmd,buf)
int msqid,cmd;
struct msqid_ds *buf;
說明:本系統呼叫提供一系列訊息控制操作,操作動作由cmd定義,以下
cmd定義值表明了各操作動作的定義.
. IPC_STAT:將msqid相關的資料結構中各個元素的當前值放入由
buf指向的結構中.
. IPC_SET:將msqid相關的資料結構中的下列元素設定為由buf指
向的結構中的對應值.
msg_perm.uid
msg_perm.gid
msg_perm.mode
msg_qbytes
本命令只能由有效UID等於msg_perm.cuid或msg_perm.uid的
程式或有效UID有合適許可權的程式操作.只有具有合適許可權的
使用者才能增加msg_qbytes的值.
. IPC_RMID:刪除由msqid指示的訊息佇列.將它從系統中刪除並
破壞相關的資料結構.
本命令只能由有效UID等於msg_perm.cuid或msg_perm.uid的
程式或有效UID有合適許可權的程式操作.
返回值:呼叫成功則返回值為0,否則為-1.
16.msgget()
功能:取得一個訊息佇列.
語法:#include
#include
#include
int msgget(key,msgflg)
key_t key;
int msgflg;
說明:本系統呼叫返回與引數key相關的訊息佇列的識別符號.
若以下事實成立,則與訊息佇列相關的識別符號和資料結構將被創
建出來:
. 若引數key等於IPC_PRIVATE.
. 若引數key沒有一個已存在的訊息佇列識別符號與之相關,同時值
(msgflg&IPC_CREAT)為真.
建立訊息佇列時,與新的訊息佇列識別符號相關的資料結構將被初
始化為如下:
. msg_perm.cuid和msg_perm.uid設定為呼叫程式的有效UID.
. msg_perm.cgid和msg_perm.gid設定為呼叫程式的有效GID.
. msg_perm.mode訪問許可權位元位設定為msgflg訪問許可權位元位.
. msg_qnum,msg_lspid,msg_lrpid,msg_stime,msg_rtime設定為0.
. msg_ctime設定為當前系統時間.
. msg_qbytes設定為系統允許的最大值.
返回值:呼叫成功則返回一非0值,稱為訊息佇列識別符號;否則返回值為-1.
例子:本例將包括上述所有訊息佇列操作的系統呼叫:
#define RKEY 0x9001L /*讀訊息佇列的KEY值*/
#define WKEY 0x9002L /*寫訊息佇列的KEY值*/
#define MSGFLG 0666 /*訊息佇列訪問許可權*/
#define IPC_WAIT 0 /*等待方式在include檔案中未定義*/
int rmsqid; /*讀訊息佇列識別符號*/
int wmsqid; /*寫訊息佇列識別符號*/
struct msgbuf {
long mtype;
char mtext[200];
} buf;
/*若讀訊息佇列已存在就取得識別符號,否則則建立並取得識別符號*/
if ((rmsqid=msgget(RKEY,MSGFLG|IPC_CREAT))<0) {
printf("get read message queue failed ");
exit(1);
}
/*若寫訊息佇列已存在則失敗,若不存在則建立並取得識別符號*/
if ((wmsqid=msgget(WKEY,
MSGFLG|IPC_CREAT|IPC_TRUNC))<0) {
printf("get write message queue failed ");
exit(2);
}
/*接收所有型別的訊息*/
if (msgrcv(rmsqid,&buf,sizeof(struct msgbuf)-sizeof(long),
0L,IPC_WAIT)>0)
printf("get %ld type message from queue:%s ",
buf.mtype,buf.mtext);
else {
printf("get message failed ");
exit(3);
}
buf.mtype=3L
if (msgsnd(wmsqid,&buf,sizeof(struct msgbuf)-sizeof(long),
IPC_NOWAIT)>0)
printf("send message OK ");
else {
printf("send message failed ");
exit(4);
}
msgctl(wmsqid,IPC_RMID,(struct msqid *)NULL);
17.shmat()
功能:聯接共享記憶體的操作.
語法:#include
#include
#include
void *shmat(shmid,shmaddr,shmflg)
int shmid;
void *shmaddr;
int shmid;
說明:將由shmid指示的共享記憶體聯接到呼叫程式的資料段中.被聯接的
段放在地址,該地址由以下準則指定:
. 若shmaddr等於(void *)0,則段聯接到由系統選擇的第一個可
用的地址上.
. 若shmaddr不等於(void *)0同時(shmflg&SHM_RND)值為真,則
段聯接到由(shmaddr-(shmaddr%SHMLBA))給出的地址上.
. 若shmaddr不等於(void *)0同時(shmflg&SHM_RND)值為假,則
段聯接到由shmaddr指定的地址上.
若(shmflg&sSHM_RDONLY)為真並且呼叫程式有讀允許,則被聯接
的段為只讀;否則,若值不為真且呼叫程式有讀寫許可權,則被聯接
的段為可讀寫的.
返回值:若呼叫成功則返回被聯接的共享記憶體段在資料段上的啟始地址.
否則返回值為-1.
18.shmdt()
功能:斷開共享記憶體聯接的操作.
語法:#include
#include
#include
void *shmdt(shmaddr)
void *shmaddr;
說明:本系統呼叫將由shmaddr指定的共享記憶體段從呼叫程式的資料段
脫離出去.
返回值:若呼叫成功則返回值為0,否則返回值為-1.

19.shmget()
功能:取得共享記憶體段
語法:#include
#include
#include
int shmget(key,size,shmflg)
key_t key;
int size,shmflg;
說明:本系統呼叫返回key相關的共享記憶體識別符號.
共享記憶體識別符號和相關資料結構及至少size位元組的共享記憶體段能
正常建立,要求以下事實成立:
. 引數key等於IPC_PRIVATE.
. 引數key沒有相關的共享記憶體識別符號,同時(shmflg&IPC_CREAT)
值為真.
共享記憶體建立時,新生成的共享記憶體標識相關的資料結構被初始
化如下:
. shm_perm.cuid和shm_perm.uid設定為呼叫程式的有效UID.
. shm_perm.cgid和shm_perm.gid設定為呼叫程式的有效GID.
. shm_perm.mode訪問許可權位元位設定為shmflg訪問許可權位元位.
. shm_lpid,shm_nattch,shm_atime,shm_dtime設定為0.
. shm_ctime設定為當前系統時間.
. shm_segsz設定為0.
返回值:若呼叫成功則返回一個非0值,稱為共享記憶體識別符號,否則返回
值為-1.
20.shmctl()
功能:共享記憶體控制操作.
語法:#include
#include
#include
int shmctl(shmid,cmd,buf)
int shmid,cmd;
struct shmid_ds *buf;
說明:本系統呼叫提供一系列共享記憶體控制操作.操作行為由cmd指定.
以下為cmd的有效值:
. IPC_STAT:將shmid相關的資料結構中各個元素的當前值放入由
buf指向的結構中.
. IPC_SET:將shmid相關的資料結構中的下列元素設定為由buf指
向的結構中的對應值.
shm_perm.uid
shm_perm.gid
shm_perm.mode
本命令只能由有效UID等於shm_perm.cuid或shm_perm.uid的
程式或有效UID有合適許可權的程式操作.
. IPC_RMID:刪除由shmid指示的共享記憶體.將它從系統中刪除並
破壞相關的資料結構.
本命令只能由有效UID等於shm_perm.cuid或shm_perm.uid的
程式或有效UID有合適許可權的程式操作.
返回值:若呼叫成功則返回0,否則返回-1.
例子:本例包括上述所有共享記憶體作業系統呼叫:
#include
#include
#include
#define SHMKEY 74
#define K 1024
int shmid;
cleanup()
{
shmctl(shmid,IPC_RMID,0);
exit(0);
}
main()
{
int *pint;
char *addr1,*addr2;
extern char *shmat();
extern cleanup();
for (i=0;i<20;i++)
signal(i,cleanup);
shmid=shmget(SHMKEY,128*K,0777|IPC_CREAT);
addr1=shmat(shmid,0,0);
addr2=shmat(shmid,0,0);
printf("addr1 0x%x addr2 0x%x ",addr1,addr2);
pint=(int*)addr1;
for (i=0;i<256;i++)
*pint++=i;
pint=(int*)addr1;
*pint=256;
pint=(int*)addr2;
for (i=0;i<256;i++)
printf("index %d value%d ",i,*pint++);
shmdt(addr1);
shmdt(addr2);
pause();
}
21.semctl()
功能:訊號量控制操作.
語法:#include
#include
#include
int semctl(semid,memnum,cmd,arg)
int semid,semnum,cmd;
union semun {
int val;
struct semid_ds *buf;
ushort *array;
}arg;
說明:本系統呼叫提供了一個訊號量控制操作,操作行為由cmd定義,這
些命令是對由semid和semnum指定的訊號量做操作的.每個命令都
要求有相應的許可權級別:
. GETVAL:返回semval的值,要求有讀許可權.
. SETVAL:設定semval的值到arg.val上.此命令成功執行後,
semadj的值對應的所有程式的訊號量全部被清除,要求有修
改許可權.
. GETPID:返回sempid的值,要求有讀許可權.
. GETNCNT:返回semncnt的值,要求有讀許可權.
. GETZCNT:返回semzcnt的值,要求有讀許可權.
以下命令在一組訊號量中的各個semval上操作:
. GETALL:返回每個semval的值,同時將各個值放入由arg.array
指向的陣列中.當此命令成功執行後,semadj的值對應的所有
程式的訊號量全部被清除,要求有修改許可權.
. SETALL:根據由arg.array指向的陣列設定各個semval值.當此
命令成功執行後,semadj的值對應的所有程式的訊號量全部
被清除,要求有修改許可權.
以下命令在任何情況下都是有效的:
. IPC_STAT:將與semid相關的資料結構的各個成員的值放入由
arg.buf指向的結構中.要求有讀許可權.
. IPC_SET:設定semid相關資料結構的如下成員,設定資料從
arg.buf指向的結構中讀取:
sem_perm.uid
sem_perm.gid
sem_perm.mode
本命令只能由有效UID等於sem_perm.cuid或sem_perm.uid的
程式或有效UID有合適許可權的程式操作.
. IPC_RMID:刪除由semid指定的訊號量識別符號和相關的一組訊號
量及資料結構.本命令只能由有效UID等於sem_perm.cuid或
sem_perm.uid的程式或有效UID有合適許可權的程式操作.
返回值:若呼叫成功,則根據cmd返回以下值:
GETVAL:semval的值.
GETPID:sempid的值.
GETNCNT:semncnt的值.
GETZCNT:semzcnt的值.
其他:0.
若呼叫失敗則返回-1.
22.semget()
功能:取得一組訊號量.
語法:#include
#include
#include
int semget(key,nsems,semflg)
key_t key;
int nsems,semflg;
說明:返回和key相關的訊號量識別符號.
若以下事實成立,則與訊號量識別符號,與之相關的semid_ds資料結
構及一組nsems訊號量將被建立:
. key等於IPC_PRIVATE.
. 系統內還沒有與key相關的訊號量,同時(semflg&IPC_CREAT)
為真.
建立時新的訊號量相關的semid_ds資料結構被初始化如下:
. 在操作許可權結構,sem_perm.cuid和sem_perm.uid設定等於呼叫
程式的有效UID.
. 在操作許可權結構,sem_perm.cgid和sem_perm.gid設定等於呼叫
程式的有效GID.
. 訪問許可權位元位sem_perm.mode設定等於semflg的訪問許可權比
特位.
. sem_otime設定等於0,sem_ctime設定等於當前系統時間.
返回值:若呼叫成功,則返回一非0值,稱為訊號量識別符號;否則返回-1.
23.semop()
功能:訊號量操作.
語法:#include
#include
#include
int semop(semid,sops,nsops)
int semid;
struct sembuf *sops;
unsigned nsops;
說明:本系統呼叫用於執行使用者定義的在一組訊號量上操作的行為集合.
該組訊號量與semid相關.
引數sops為一個使用者定義的訊號量操作結構陣列指標.
引數nsops為該陣列的元素個數.
陣列的每個元素結構包括如下成員:
sem_num; /* 訊號量數 */
sem_op; /* 訊號量操作 */
sem_flg; /* 操作標誌 */
由本系統呼叫定義的每個訊號量操作是針對由semid和sem_num指
定的訊號量的.變數sem_op指定三種訊號量操作的一種:
. 若sem_op為一負數並且呼叫程式具有修改許可權,則下列情況之
一將會發生:
* 若semval不小於sem_op的絕對值,則sem_op的絕對值被減去
semval的值.若(semflg&SEM_UNDO)為真則sem_op的絕對值加
上呼叫程式指定的訊號量的semadj值.
* 若semval小於sem_op的絕對值同時(semflg&IPC_NOWAIT)為
真,則本呼叫立即返回.
* 若semval小於sem_op的絕對值同時(semflg&IPC_NOWAIT)為
假,則本系統呼叫將增加指定訊號量相關的semncnt值(加一),
將呼叫程式掛起直到下列條件之一被滿足:
(1).semval值變成不小於sem_op的絕對值.當這種情況發
生時,指定的訊號量相關的semncnt減一,若
(semflg&SEM_UNDO)為真則sem_op的絕對值加上呼叫
程式指定訊號量的semadj值.
(2).呼叫程式等待的semid已被系統刪除.
(3).呼叫程式捕俘到訊號,此時,指定訊號量的semncnt值
減一,呼叫程式執行中斷服務程式.
. 若sem_op為一正值,同時呼叫程式具有修改許可權,sem_op的值加
上semval的值,若(semflg&SEM_UNDO)為真,則sem_op減去呼叫
程式指定訊號量的semadj值.
. 若sem_op為0,同時呼叫程式具有讀許可權,下列情況之一將會發
生:
* 若semval為0,本系統呼叫立即返回.
* 若semval不等於0且(semflg&IPC_NOWAIT)為真,本系統呼叫
立即返回.
* 若semval不等於0且(semflg&IPC_NOWAIT)為假,本系統呼叫
將把指定訊號量的
semzcnt值加一,將呼叫程式掛起直到下列情況之一發生:
(1).semval值變為0時,指定訊號量的semzcnt值減一.
(2).呼叫程式等待的semid已被系統刪除.
(3).呼叫程式捕俘到訊號,此時,指定訊號量的semncnt值
減一,呼叫程式執行中斷服務程式.
返回值:呼叫成功則返回0,否則返回-1.
例子:本例將包括上述訊號量操作的所有系統呼叫:
#include
#include
#include
#define SEMKEY 75
int semid;
unsigned int count;
/*在檔案sys/sem.h中定義的sembuf結構
* struct sembuf {
* unsigned short sem_num;
* short sem_op;
* short sem_flg;
* }*/
struct sembuf psembuf,vsembuf; /*P和V操作*/
cleanup()
{
semctl(semid,2,IPC_RMID,0);
exit(0);
}
main(argc,argv)
int argc;
char *argv[];
{
int i,first,second;
short initarray[2],outarray[2];
extern cleanup();
if (argc==1) {
for (i=0;i<20;i++)
signal(i,clearup);
semid=semget(SEMKEY,2,0777|IPC_CREAT);
initarray[0]=initarray[1]=1;
semctl(semid,2,SETALL,initarray);
semctl(semid,2,GETALL,outarray);
printf("sem init vals %d%d ",
outarray[0],outarray[1]);
pause(); /*睡眠到被一軟體中斷訊號喚醒*/
}
else if (argv[1][0]=='a') {
first=0;
second=1;
}
else {
first=1;
second=0;
}
semid=semget(SEMKEY,2,0777);
psembuf.sem_op=-1;
psembuf.sem_flg=SEM_UNDO;
vsembuf.sem_op=1;
vsembuf.sem_flg=SEM_UNDO;
for (count=0;;xcount++) {
psembuf.sem_num=first;
semop(semid,&psembuf,1);
psembuf.sem_num=second;
semop(semid,&psembuf,1);
printf("proc %d count %d ",getpid(),count);
vsembuf.sem_num=second;
semop(semid,&vsembuf,1);
vsembuf.sem_num=first;
semop(semid,&vsembuf,1);
}
}
24.sdenter()
功能:共享資料段同步訪問,加鎖.
語法:#include
int sdenter(addr,flags)
char *addr;
int flags;
說明:用於指示呼叫程式即將可以訪問共享資料段中的內容.
引數addr為將一個sdget()呼叫的有效返回碼.
所執行的動作取決於flags的值:
. SD_NOWAIT:若另一個程式已對指定的段呼叫本系統呼叫且還沒
有呼叫sdleave(),並且該段並非用SD_UNLOCK標誌建立,則調
用程式不是等待該段空閒而是立即返回錯誤碼.
. SD_WRITE:指示呼叫程式希望向共享資料段寫資料.此時,另一
個程式用SD_RDONLY標誌聯接該共享資料段則不被允許.
返回值:呼叫成功則返回0,否則返回-1.
25.sdleave()
功能:共享資料段同步訪問,解鎖.
語法:#include
int sdleave(addr,flags)
char *addr;
說明:用於指示呼叫程式已完成修改共享資料段中的內容.
返回值:呼叫成功則返回0,否則返回-1.
26.sdget()
功能:聯接共享資料段到呼叫程式的資料空間中.
語法:#include
char *sdget(path,flags,size.mode)
char *path;
int flags;
long size;
int mode;
說明:本系統呼叫將共享資料段聯接到呼叫程式的資料段中,具體動作
由flags的值定義:
. SD_RDONLY:聯接的段為只讀的.
. SD_WRITE:聯接的段為可讀寫的.
. SD_CREAT:若由path命名的段存在且不在使用中,本標誌的作用
同早先建立一個段相同,否則,該段根據size和mode的值程式
建立.對段的讀寫訪問許可權的授予基於mode給的許可權,功能與
一般檔案的相同.段被初始化為全0.
. SD_UNLOCK:若用此標誌建立該段,則允許有多個程式同時訪問
(在讀寫中)該段.
返回值:若呼叫成功則返回聯接的段地址.否則返回-1.
27.sdfree()
功能:將共享資料段從呼叫程式的資料空間中斷開聯接.
語法:#include
int sdfree(addr)
char *addr;
說明:本系統呼叫將共享資料段從呼叫程式的資料段的指定地址中分離.
若呼叫程式已完成sdenter()的呼叫,還未呼叫sdleave()就呼叫
本系統呼叫,則sdleave()被自動呼叫,然後才做本呼叫的工作.
返回值:若呼叫成功則返回聯接的段地址.否則返回-1.
28.sdgetv()
功能:同步共享資料訪問.
語法:#include
int sdgetv(addr)
char *addr;
說明:用於同步協調正在使用共享資料段的程式.返回值為共享資料段
的版本號.當有程式對該段做sdleave()操作時,版本號會被修改.
返回值:若呼叫成功,則返回指定共享資料段的版本號,否則返回-1.
29.sdwaitv()
功能:同步共享資料訪問.
語法:#include
int sdwaitv(addr,vnum)
char *addr;
int vnum;
說明:用於同步協調正在使用共享資料段的程式.返回值為共享資料段
的版本號.呼叫程式會睡眠直到指定段的版本號不再等於vnum;
返回值:若呼叫成功,則返回指定共享資料段的版本號,否則返回-1.
30.sbrk()
功能:修改資料段空間分配.
語法:char *sbrk(incr)
int incr;
說明:用於動態修改呼叫程式資料段的空間分配.程式將重置程式的分
段值並分配一個合適大小的空間.分段值為資料段外第一次分配
的地址.要分配的空間的增加量等於分段值的增加量.新分配的空
間設定為0.若相同的記憶體空間重新分配給同一個程式,則空間的
內容不確定.
返回值:若成功呼叫則返回值為0,否則返回-1.
例子:本例將包括上述共享資料空間操作的所有系統呼叫:
char * area1;
char buf[21];
int v;
/*取得或建立一個共享資料空間(系統特殊檔案),名字為
/tmp/area1,長度為640,使用者訪問許可權為0777*/
area1=sdget("/tmp/area1",SD_WRITE|SD_CREAT,640,0777);
if ((int)area1==-1) {
printf("get share data segment area1 failed ");
exit(1);
}
/*取得共享資料段area1的版本號*/
v=sdgetv(area1);
/*申請訪問共享資料段area1,若已有程式在訪問該段則本程式掛
*起,否則進入訪問並將該資料段加寫鎖*/
sdenter(area1,SD_WRITE);
/*對共享資料段訪問,寫10個a*/
strcpy(area1,"aaaaaaaaaa");
/*申請解除訪問許可權,若已有程式申請訪問則啟用該程式*/
sdleave(area1);
/*程式處理過程*/
/*等待取共享資料段area1的版本號*/
sdwaitv(area1,v);
/*重新申請訪問共享資料段area1*/
sdenter(area1,SD_WRITE);
/*讀取共享資料段中的資料*/
memcpy(buf,area1,20);
/*申請解除訪問許可權,若已有程式申請訪問則啟用該程式*/
sdleave(area1);
printf("the data now in area1 is [%s] ",buf);
31.getenv()
功能:取得指定環境變數值.
語法:#include
#include char *getenv(name)
char *name;
說明:本系統呼叫檢查環境字串(格式如name=value),並在找到有指
定名字的環境值後,返回指向value字串的指標.否則返回空指
針.
返回值:如前述.
例子:char * value;
value=getenv("HOME");
printf("HOME = [%s] ",value);
/*將列印出HOME環境變數的值*/
32.putenv()
功能:修改或增加環境值.
語法:#include
int putenv(string)
char *string;
說明:引數string指向一個字串,格式如下:
name=value
本系統呼叫將環境變數name等於值value,修改或增加一個環境變
量,字串string成為環境的一部分.
返回值:若putenv()不能取得合適的記憶體空間則返回非0值,否則返回0.
例子:/*父程式處理*/
putenv("HOME=/home/abcdef");
putenv("PATH=/bin");
if (fork()>0)
exit(0); /*父程式退出執行*/
/*子程式處理*/
setpgrp();
/*父程式設定的環境變數已傳到子程式*/
char * value1;
value1=getenv("HOME");
value2=getenv("PATH");
printf("HOME=[%s],PATH=[%s] ",value1,value2);
/*將列印出"HOME=/home/abcdef"和"PATH=/bin"*/
三.多程式程式設計技巧
1.主要程式結構
(1)事件主控方式
若是應用程式屬於事務處理方式,則在主函式中設計為監控事件發生,
當事件發生時,可以生成一個新的程式來處理該事務,事務處理完成後就
可以讓子程式退出系統.這種處理方式一般不要訊息傳遞.
(2)資訊協調方式
若是應用程式需要由多個程式協調處理完成,則可以生成這些程式,
透過訊息在程式間的傳遞,使各個程式能相互協調,共同完成事務.這種處
理方式一般是用fork()生成幾個程式後,用exec()呼叫其它程式檔案,使
得不同的程式同時在系統內執行.然後透過IPC機制傳送訊息,使各個程式
能協調執行.
2.選擇主體分叉點
(1)事件初始產生
對應於事件主控方式的程式結構.關鍵點在於以何種方式選擇事件的
初始發生點,如網路程式給出的建鏈資訊.主控程式在收到該訊息後就認
為是一個事件開始,則可以產生一個子程式處理後面的事務:接收交易信
息,事務處理,傳送返回交易資訊,關閉連結等,完成後將子程式退出系統.
(2)主程式自主產生
對應於資訊協調方式的程式結構.主控程式只負責生成幾個子程式,各
個子程式分別呼叫exec()將不同的執行檔案調入記憶體執行,主控程式在生
成所有的子程式後即可退出系統,將子程式留在記憶體中執行.
3.程式間關係處理
(1)父子程式關係
. 程式組處理
程式組的概念是這樣的,當系統啟動時,第一個程式是init,其程式
組號等於程式號,由它產生的所有子程式的程式組號也相同,子程式
的子程式也繼承該程式組號,這樣,由init所生成的所有子程式都屬
於同一個程式組.但是,同一個程式組的父子程式可能在訊號上有相
互通訊,若父程式先於子程式退出系統,則子程式會成為一個孤兒進
程,可能變成僵死程式.從而使該子程式在其不"願意"的情況下退出
執行.為解決這個問題,子程式可以自己組成一個新的程式組,即調
用setpgrp()與原程式組脫離關係,產生一個新的程式組,程式組號
與它的程式號相同.這樣,父程式退出執行後就不會影響子程式的當
前執行.
. 子程式訊號處理
但是,單做上述處理還不能解決另一個困難,即子程式在退出執行
時,找不到其父程式(父程式已退出,子程式的父程式號改為1).傳送
子程式退出訊號後沒有父程式做出響應處理,該子程式就不可能完
全退出執行,可能進入僵死狀態.所以父程式在產生子程式前最好屏
蔽子程式返回訊號的處理,生成子程式,在父程式退出執行後,子進
程返回則其程式返回訊號的處理會由系統給出預設處理,子程式就
可以正常退出.
(2)兄弟程式關係
. 交換程式號
對於資訊協調方式的程式來說,各兄弟程式間十分需要相互瞭解進
程號,以便於訊號處理機制.比較合理的方法是父程式生成一個共享
記憶體的空間,每個子程式都在啟動時在共享記憶體中設定自己的程式
號.這樣,當一個子程式要向另一個子程式傳送訊號或是因為其他原
因需要知道另一個子程式號時,就可以在共享記憶體中訪問得到所需
要的程式號.
4.程式間通訊處理
(1)共享記憶體需要鎖機制
由於共享記憶體在設計時沒有處理鎖機制,故當有多個程式在訪問共享
記憶體時就會產生問題.如:一個程式修改一個共享記憶體單元,另一個程式在
讀該共享記憶體單元時可能有第三個程式立即修改該單元,從而會影響程式
的正確性.同時還有分時系統對各程式是分時間片處理的,可能會引起不
同的正確性問題.按作業系統的運作方式,則有讀鎖和寫鎖來保證資料的
一致性.所以沒有鎖機制的共享記憶體,必須和訊號量一起使用,才能保證共
享記憶體的正確操作.
(2)訊息佇列需要關鍵值
訊息佇列的操作在程式取得訊息佇列的訪問許可權後就必須透過關鍵
值來讀訊息佇列中的相同關鍵值的訊息,寫訊息時帶入訊息關鍵值.這樣
可以透過不同的關鍵值區分不同的交易,使得在同一個訊息佇列可以供多
種訊息同時使用而不衝突.若讀訊息佇列使用關鍵值0則讀取訊息佇列中
第一個訊息,不論其關鍵值如何.
(3)訊號需要訊號處理函式設定和再設定
在使用者程式需要對某個中斷做自己定義的處理時,可以自己定義中斷
處理函式,並設定中斷處理函式與該中斷相關聯.這樣,使用者程式在收到該
中斷後,即呼叫使用者定義的函式,處理完成後使用者程式從被中斷處繼續運
行(若使用者定義的中斷函式沒有長跳函式或退出執行等會改變執行指令地
址的系統呼叫).在中斷訊號被處理後,該中斷的處理函式會恢復成上次缺
省處理函式而不是保持使用者定義函式,故在使用者定義的中斷處理函式中一
般都再定義該中斷和函式自己的關聯.
(4)IPC的許可權設定
在訊息佇列,共享記憶體和訊號量的訪問時有使用者訪問許可權設定,類同
於檔案的訪問許可權的設定如(777表示rwxrwxrwx),用命令ipcs即可看到在
系統中生成的訊息佇列,共享記憶體和訊號量的訪問許可權.其意義也類似於
檔案訪問許可權.只是執行位無效.
在有名管道和檔案方式共享記憶體中以系統檔案的方式定義了使用者的
訪問許可權.用命令ls -l可以看到它們以系統檔案方式存在並具有訪問權
限值,並可以看到有名管道的檔案型別為p,檔案方式共享記憶體的檔案型別
為s.
(5)訊號中斷對系統呼叫一級有效
系統在設計系統呼叫時就考慮了中斷處理問題.當程式執行到一個系
統呼叫時發生了中斷,則程式進入該中斷處理,處理完成後,程式會跳過該
系統呼叫而進入下一條程式指令.
應該注意的是中斷髮生在系統呼叫一級而不是子程式或函式一級.比
如一個程式在一個子程式被呼叫前設定了超時中斷,並在子程式中收到超
時中斷,系統在處理完超時中斷後接著處理該子程式被中斷的系統呼叫之
後的指令,而不是從呼叫該子程式名指令的後一條指令繼續處理.
(6)各種IPC方式的特點
. 訊息佇列:
透過訊息佇列key值定義和生成訊息佇列.
任何程式只要有訪問許可權並知道key即可訪問訊息佇列.
訊息佇列為記憶體塊方式資料段.
訊息佇列中的訊息元素長度可為系統引數限制內的任何長度.
訊息元素由訊息型別分類,其訪問方式為按型別訪問.
在一次讀寫操作前都必須取得訊息佇列識別符號,即訪問權.訪問後即
脫離訪問關係.
訊息佇列中的某條訊息被讀後即從佇列中刪除.
訊息佇列的訪問具備鎖機制處理,即一個程式在訪問時另一個程式
不能訪問.
操作時要注意系統資源和效率.
在許可權允許時,訊息佇列的資訊傳遞是雙向的.
. 共享記憶體
透過共享記憶體key值定義和生成共享記憶體.
任何程式只要有訪問許可權並知道key即可訪問共享記憶體.
共享記憶體為記憶體塊方式的資料段.
共享記憶體中的資料長度可為系統引數限制內的任何長度.
共享記憶體的訪問同陣列的訪問方式相同.
在取得共享記憶體識別符號將共享記憶體與程式資料段聯接後即可開始對
之進行讀寫操作,在所有操作完成之後再做共享記憶體和程式資料
段脫離操作,才完成全部共享記憶體訪問過程.
共享記憶體中的資料不會因資料被程式讀取後消失.
共享記憶體的訪問不具備鎖機制處理,即多個程式可能同時訪問同一
個共享記憶體的同一個資料單元.
共享記憶體的使用最好和訊號量一起操作,以具備鎖機制,保證資料的
一致.
在許可權允許時,共享記憶體的資訊傳遞是雙向的.
. 訊號量
用於生成鎖機制,避免發生資料不一致.
沒有其他的資料資訊.
不需要有父子關係或兄弟關係.
. 訊號
訊號由系統進行定義.
訊號的傳送只要有許可權即可進行.
訊號是一個事件發生的資訊標誌,不帶有其它資訊.
訊號不具備資料塊.
訊號的處理可由使用者自己定義.
訊號可能由使用者程式,作業系統(軟體或硬體原因)等發出.
有一些訊號是不可被遮蔽的.
訊號中斷的是系統呼叫級的函式.
訊號的資訊傳遞是單向的.
. 管道
做為系統的特殊裝置檔案,可以是記憶體方式的,也可以是外存方式的.
管道的傳輸一般是單向的,即一個管道一向,若兩個程式要做雙向傳
輸則需要2個管道.管道生成時即有兩端,一端為讀,一端為寫,兩個
程式要協調好,一個程式從讀方讀,另一個程式向寫方寫.
管道的讀寫使用流裝置的讀寫函式,即:read(),write.
管道的傳輸方式為FIFO,流方式的.不象訊息佇列可以按型別讀取.
* 有名管道
一般為系統特殊檔案方式,使用的程式之間不一定要有父子關係
或兄弟關係.
* 無名管道
一般為記憶體方式,使用的程式之間一定要有父子關係或兄弟關係.
. 檔案
檔案是最簡單的程式間通訊方式,使用外部存貯器為中介.
操作麻煩,定位困難.
保密程度低.
容易出現資料不一致問題.
佔用硬碟空間.
只要有許可權並知道檔名,任何程式都可對之操作.
* 特殊處理
為避免出現保密問題,在開啟檔案,取得檔案描述符後,呼叫
unlink()將硬碟上的檔案路徑名刪除,則硬碟上就沒有檔案複製
了.但在程式中該檔案描述符是開啟的,由該程式生成的子程式中
該檔案描述符也是開啟的,就可以利用系統提供的檔案緩衝區做
程式間通訊,代價是程式間必須有父子關係或兄弟關係.
. 環境變數
資訊的傳送一般是單向的,即由父程式向子程式傳送.
保密性較好.
雙方必須約定環境變數名.
只佔用本程式和子程式的環境變數區.
. 共享資料段
操作比較複雜.
佔用硬碟空間,生成系統特殊檔案.
其他性質與共享記憶體相類似.
. 流
檔案描述符的操作方式.
程式間不一定要有父子關係或兄弟關係.
雙向傳送資訊.
程式各自生成socket,用bind()聯接.
其他性質與管道相類似.
流程式設計為TCP/IP網路程式設計範圍,在本文中暫不闡述.
. 傳遞引數
資訊的傳送一般是單向的, 即由父程式向子程式傳送.
保密性較差,用程式列表即可顯示出來.
雙方必須約定引數位置.
只佔用子程式的引數區.

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10796304/viewspace-962593/,如需轉載,請註明出處,否則將追究法律責任。

相關文章