c語言多進程多線程編程_第1頁
c語言多進程多線程編程_第2頁
c語言多進程多線程編程_第3頁
c語言多進程多線程編程_第4頁
c語言多進程多線程編程_第5頁
已閱讀5頁,還剩71頁未讀, 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、C語言多進程編程 多進程程序的特點 進程是一個具有獨立功能的程序關于某個數據集合的一次可以并發執行的運行活動,是處于活動狀態的計算機程序。進程作為構成系統的基本細胞,不僅是系統內部獨立運行的實體,而且是獨立競爭資源的基本實體。進程是資源管理的最小單位,線程是程序執行的最小單位。進程管理著資源(比如cpu、內存、文件等等),而將線程分配到某個cpu上執行。在操作系統設計上,從進程演化出線程,最主要的目的就是更好的支持多處理器系統和減小上下文切換開銷。進程的狀態 系統為了充分的利用資源,對進程區分了不同的狀態.將進程分為新建,運行,阻塞,就緒和完成五個狀態. 新建 表示進程正在被創建,運行 是進程

2、正在運行,阻塞 是進程正在等待某一個事件發生,就緒 是表示系統正在等待CPU來執行命令,完成 表示進程已經結束了系統正在回收資源. 由于UNIX系統是分時多用戶系統, CPU按時間片分配給各個用戶使用,而在實質上應該說CPU按時間片分配給各個進程使用, 每個進程都有自己的運行環境以使得在CPU做進程切換時不會忘記該進程已計算了一半的半成品”. 以DOS的概念來說, 進程的切換都 是一次DOS中斷處理過程, 包括三個層次: 用戶數據的保存: 包括正文段(TEXT), 數據段(DATA,BSS), 棧段(STACK), 共享內存段(SHARED MEMORY)的保存. 寄存器數據的保存: 包括PC

3、(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,中斷棧指針), 以及其他的通用寄存器等. 系統層次的保存: 包括proc,u,虛擬存儲空間管理表格,中斷處理棧

4、.以便于該進程再一次得到CPU時間片時能正常運行。 既然系統已經處理好所有這些中斷處理的過程, 我們做程序還有什么要擔心 的呢? 我們盡可以使用系統提供的多進程的特點, 讓幾個程序精誠合作, 簡單而又高效地把結果給它搞出來。另外,UNIX系統本身也是用C語言寫的多進程程序,多進程編程是UNIX的特點,當我們熟悉了多進程?將會對UNIX系統機制有一個較深的認識.首先我介紹一下多進程程序的一些突出的特點: 1.1并行化 一件復雜的事件是可以分解成若干個簡單事件來解決的, 這在程序員的大腦中早就形成了這種概念, 首先將問題分解成一個個小問題, 將小問題再細分, 最后在一個合適的規模上做成一個函數.

5、在軟件工程中也是這么說的. 如果我們以圖的方式來思考, 一些小問題的計算是可以互不干擾的, 可以同時處理, 而在關鍵點則需要統一在一個地方來處理, 這樣程序的運行就是并行的, 至少從人的時間觀念上來說是這樣的. 而每個小問題的計算又是較簡單的. 1.2簡單有序 這樣的程序對程序員來說不亞于管理一班人, 程序員為每個進程設計好相應的功能, 并通過一定的通訊機制將它們有機地結合在一起, 對每個進程的設計是簡單的, 只在總控部分小心應付(其實也是蠻簡單的), 就可完成整個程序的施工. 1.3.互不干擾 這個特點是操作系統的特點, 各個進程是獨立的, 不會串位. 1.4.事務化 比如在一個數據電話查詢

6、系統中, 將程序設計成一個進程只處理一次查詢即可, 即完成一個事務. 當電話查詢開始時, 產生這樣一個進程對付這次查詢; 另一個電話進來時, 主控程序又產生一個這樣的進程對付, 每個進程完成查詢任務后消失. 這樣的編程多簡單, 只要做一次查詢的程序就可以了. 二.常用的多進程編程的系統調用 2.1.fork() 創建一個新的進程.功能:創建一個新的進程. 語法:#include #include pid_t fork(); 說明:本系統調用產生一個新的進程, 叫子進程, 是調用進程的一個復制品. 調用進程叫父進程, 子進程繼承了父進程的幾乎所有的屬性。進程:代碼段(程序代碼)堆棧段(局部變量、

7、函數返回地址、函數參數)數據段(全局變量、常數等)在Linux系統中,系統調用fork后,內核為完成系統調用fork要進行幾步操作:第一步,為新進程在進程表中分配一個表項。系統對一個普通用戶可以同時運行的進程數是有限制的,對超級用戶沒有該限制,但不能超過進程表的最大表項的數目。第二步,給子進程一個唯一的進程標識號(PID)。該進程標識號其實就是該表項在進程表中的索引號。第三步,復制一個父進程的進程表項的副本給子進程。內核初始化子進程的進程表項時,是從父進程處拷貝的。所以子進程擁有與父進程一樣的uid、當前目錄、當前根、用戶文件描述符表等。第四步,把與父進程相連的文件表和索引節點表的引用數加1。

8、這些文件自動地與該子進程相連。第五步,內核為子進程創建用戶級上下文。內核為子進程的代碼段分配內存,并復制父進程的區內容,生成的是進程的靜態部分。第六步,生成進程的動態部分,然后對父進程返回子進程的pid,對子進程返回0。從父進程拷貝的內容主要有:用戶標識符,包括實際用戶號(real)和有效用戶號(effective);環境變量打開的文件描述符、套接字描述符信號處理設置堆棧目錄進程組標志(process ID)會晤組標志(session ID)正文子進程特有內容:進程號父進程號進程執行時間未處理的信號被處理為空不繼承異步的輸入輸出操作簡述:fork() 調用成功時,分別返回兩個整數,對父進程返回

9、 0的整數,對子進程返回 0,函數執行過程:內核在系統進程表中,創建一個新條目;復制父進程內容(已打開的文件描述符、堆棧、正文等);修改兩者的堆棧,給父進程返回子進程號,給子進程返回0(父進程知道每個子進程的標志號,而子進程可根據需要調用getppid() 來獲得父進程的標志號)。例子:pid_t fork(void) #include 應用程序pid_t pid;fork ( )if(pid=fork()=0) /子進程代碼exit(0);子進程2子進程1父進程else if(pid0)/父進程代碼exit(0);else printf(Error); exit(1); 2.2.system

10、() 子進程執行指定的命令功能:產生一個新的進程, 子進程執行指定的命令. 語法:#include #include int system(string) char *string; 說明:本調用將參數string傳遞給一個命令解釋器(一般為sh)執行, 即string被解釋為一條命令, 由sh執行該命令.若參數string為一個空指針則為檢查命令解釋器是否存在. 該命令可以同命令行命令相同形式, 但由于命令做為一個參數放在系統調用中, 應注意編譯時對特殊意義字符的處理. 命令的查找是按PATH環境變量的定義的. 命令所生成的后果一般不會對父進程造成影響. 返回值:當參數為空指針時, 只有當命

11、令解釋器有效時返回值為非零. 若參數不為空指針, 返回值為該命令的返回狀態(同waitpid()的返回值. 命令無效或語法錯誤則返回非零值,所執行的命令被終止. 其他情況則返回-1. 例子:char command81; int i; for (i=1;i8;i+) sprintf(command,ps t tty%02i,i); system(command); 2.3.exec() 執行一個文件 功能:執行一個文件語法#include int execve(const char* path, char* const* argv,char* const* envp);int execl(co

12、nst char* path, char* arg,.); int execp(const char* file, char* arg,.); int execle(const char* path, const char* argv,.,char* const* envp);int execv(const char* path, char* const* arg); int execvp(const char* file, char* const* arg);說明: exec函數族的作用是根據指定的文件名找到可執行文件,并用它來取代調用進程的內容,換句話說,就是在調用進程內部執行一個可執行文

13、件其中只有execve是真正意義上的系統調用,其它都是在此基礎上經過包裝的庫函數。與一般情況不同,exec函數族的函數執行成功后不會返回,因為調用進程的實體,包括代碼段,數據段和堆棧等都已經被新的內容取代,只留下進程ID等一些表面上的信息仍保持原樣,頗有些神似三十六計中的金蟬脫殼??瓷先ミ€是舊的軀殼,卻已經注入了新的靈魂。只有調用失敗了,它們才會返回一個-1,從原程序的調用點接著往下執行。fork()和exec()這兩個函數,前者用于并行執行,父、子進程執行相同正文中的不同部分;后者用于調用其他進程,父、子進程執行不同的正文,調用前,一般應為子進程創造一個干凈的環境。fork()以后,父、子進

14、程共享代碼段,并只重新創建數據有改變的頁(段頁式管理)exec()以后,建立新的代碼段,用被調用程序的內容填充。前者的子進程執行后續的公共代碼,后者的子進程不執行后續的公共代碼。父、子進程以及各個子進程執行的順序不定。. 例子:printf(now this process will be ps commandn); execl(/bin/ps,ps,-ef,NULL); 2.4.popen() 初始化從/到一個進程的管道功能:初始化從/到一個進程的管道. 語法:#include FILE *popen(command,type) char *command,type; 說明:本系統調用在調用

15、進程和被執行命令間創建一個管道. 參數command做為被執行的命令行.type做為I/O模式,r為從被 執行命令讀,w為向被執行命令寫.返回一個標準流指針,做為管 道描述符,向被執行命令讀或寫數據(做為被執行命令的STDIN或 STDOUT)該系統調用可以用來在程序中調用系統命令,并取得命令 的輸出信息或者向命令輸入信息. 返回值:不成功則返回NULL,成功則返回管道的文件指針. 2.5.pclose() 關閉到一個進程的管道功能:關閉到一個進程的管道. 語法:#include int pclose(strm) FILE *strm; 說明:本系統調用用于關閉由popen()打開的管道,并會

16、等待由popen() 激活的命令執行結束后,關閉管道后讀取命令返回碼. 返回值:若關閉的文件描述符不是由popen()打開的,則返回-1. 例子:printf(now this process will call popen system calln); FILE * fd; if (fd=popen(ps -ef,r)=NULL) printf(call popen failedn); return; else char str80; while (fgets(str,80,fd)!=NULL) printf(%sn,str); pclose(fd); 2.6.wait() 等待一個子進程返

17、回并修改狀態功能:等待一個子進程返回并修改狀態 語法:#include #include pid_t wait(stat_loc) int *stat_loc; 說明:允許調用進程取得子進程的狀態信息.調用進程將會掛起直到其 一個子進程終止. 返回值:等待到一個子進程返回時,返回值為該子進程號,否則返回值為 -1.同時stat_loc返回子進程的返回值. 例子:/*父進程*/ if (fork()0) wait(int *)0); /*父進程等待子進程的返回*/ else /*子進程處理過程*/ exit(0); 2.7.waitpid() 等待指定進程號的子進程的返回并修改狀態功能:等待指定

18、進程號的子進程的返回并修改狀態 語法:#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值的子進程的狀態. 0) waitpid(pid,&stat_loc,0); /*父進程等待進程號為pid的子進程的返回*/ else

19、 /*子進程的處理過程*/ exit(1); /*父進程*/ printf(stat_loc is %dn,stat_loc); /*字符串stat_loc is 1將被打印出來*/ 2.8.setpgrp() 設置進程組號和會話號功能:設置進程組號和會話號. 語法:#include pid_t setpgrp() 說明:若調用進程不是會話首進程.將進程組號和會話號都設置為與它 的進程號相等.并釋放調用進程的控制終端. 返回值:調用成功后,返回新的進程組號. 例子:/*父進程處理*/ if (fork()0) /*父進程處理*/ else setpgrp(); /*子進程的進程組號已修改成與它

20、的進程號相同*/ exit(0); 2.9.exit() 終止進程功能:終止進程. 語法:#include void exit(status) int status; 說明:調用進程被該系統調用終止.引起附加的處理在進程被終止前全 部結束. 返回值:無 2.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)

21、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 或S

22、IGPWR,收到該信號時,系統首先將重置sig的信號句柄為SIG_DFL, 然后執行信號句柄. 若使用sigset(),disp是信號句柄地址,該信號時,系統首先將該 信號加入調用進程的信號掩碼中,然后執行信號句柄.當信號句柄 運行結束 后,系統將恢復調用進程的信號掩碼為信號收到前的狀態.另外, 使用sigset()時,disp為SIG_HOLD,則該信號將會加入調用進程的 信號掩碼中而信號的定位不變. sighold()將信號加入調用進程的信號掩碼中. sigrelse()將信號從調用進程的信號掩碼中刪除. sigignore()將信號的定位設置為SIG_IGN. sigpause()將信號

23、從調用進程的信號掩碼中刪除,同時掛起調用 進程直到收到信號. 若信號SIGCHLD的信號定位為SIG_IGN,則調用進程的子進程在終 止時不會變成僵死進程.調用進程也不用等待子進程返回并做相 應處理. 返回值:調用成功則signal()返回最近調用signal()設置的disp的值. 否則返回SIG_ERR. 例子一:設置用戶自己的信號中斷處理函數,以SIGINT信號為例: int flag=0; void myself() flag=1; printf(get signal SIGINTn); /*若要重新設置SIGINT信號中斷處理函數為本函數則執行以 *下步驟*/ void (*a)()

24、; a=myself; signal(SIGINT,a); flag=2; main() while (1) sleep(2000); /*等待中斷信號*/ if (flag=1) printf(skip system call sleepn); exit(0); if (flag=2) printf(skip system call sleepn); printf(waiting for next signaln); 2.11.kill() 向一個或一組進程發送一個信號功能:向一個或一組進程發送一個信號. 語法:#include #include int kill(pid,sig); pid

25、_t pid; int sig; 說明:本系統調用向一個或一組進程發送一個信號,該信號由參數sig指 定,為系統給出的信號表中的一個.若為0(空信號)則檢查錯誤但 實際上并沒有發送信號,用于檢查pid的有效性. pid指定將要被發送信號的進程或進程組.pid若大于0,則信號將 被發送到進程號等于pid的進程;若pid等于0則信號將被發送到所 有的與發送信號進程同在一個進程組的進程(系統的特殊進程除 外);若pid小于-1,則信號將被發送到所有進程組號與pid絕對值 相同的進程;若pid等于-1,則信號將被發送到所有的進程(特殊系 統進程除外). 信號要發送到指定的進程,首先調用進程必須有對該進

26、程發送信 號的權限.若調用進程有合適的優先級則具備有權限.若調用進程 的實際或有效的UID等于接收信號的進程的實際UID或用setuid() 系統調用設置的UID,或sig等于SIGCONT同時收發雙方進程的會話 號相同,則調用進程也有發送信號的權限. 若進程有發送信號到pid指定的任何一個進程的權限則調用成功, 否則調用失敗,沒有信號發出. 返回值:調用成功則返回0,否則返回-1. 例子:假設前一個例子進程號為324,現向它發一個SIGINT信號,讓它做 信號處理: kill(pid_t)324,SIGINT); 2.12.alarm() 設置一個進程的超時時鐘功能:設置一個進程的超時時鐘.

27、 語法:#include unistd.h unsigned int alarm(sec) unsigned int sec; 說明:指示調用進程的超時時鐘在指定的時間后向調用進程發送一個 SIGALRM信號.設置超時時鐘時時間值不會被放入堆棧中,后一次 設置會把前一次(還未到超時時間)沖掉. 若sec為0,則取消任何以前設置的超時時鐘. fork()會將新進程的超時時鐘初始化為0.而當一個進程用exec() 族系統調用新的執行文件時,調用前設置的超時時鐘在調用后仍 有效. 返回值:返回上次設置超時時鐘后到調用時還剩余的時間秒數. 例子:int flag=0; void myself() fl

28、ag=1; printf(get signal SIGALRMn); /*若要重新設置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 sleepn); exit(0); if (flag=2) printf(skip system call sleepn); printf(

29、waiting for next signaln); 2.13.msgsnd() 發送消息到指定的消息隊列中功能:發送消息到指定的消息隊列中. 語法:#include #include #include int msgsnd(msqid,msgp,msgsz,msgflg) int msqid; void *msgp; size_t msgsz; int msgflg; 說明:發送一個消息到由msqid指定消息隊列標識號的消息隊列. 參數msgp指向一個用戶定義的緩沖區,并且緩沖區的第一個域應 為長整型,指定消息類型,其他數據放在緩沖區的消息中其他正文 區內.下面是消息元素定義: long m

30、type; char mtext; mtype是一個整數,用于接收進程選擇消息類型. mtext是一個長度為msgsz字節的任何正文,參數msgsz可從0到系 統允許的最大值間變化. msgflg指定操作行為: . 若(msgflg&IPC_NOWAIT)是真的,消息并不是被立即發送而調用 進程會立即返回. . 若(msgflg&IPC_NOWAIT)不是真的,則調用進程會被掛起直到下 面情況之一發生: * 消息被發送出去. * 消息隊列標志被系統刪除.系統調用返回-1. * 調用進程接收到一個未被忽略的中斷信號,調用進程繼續 執行或被終止. 調用成功后,對應指定的消息隊列的相關結構做如下動作

31、: . 消息數(msg_qnum)加1. . 消息隊列最近發送進程號(msg_lspid)改為調用進程號. . 消息隊列發送時間(msg_stime)改為當前系統時間. 以上信息可用命令ipcs -a看到. 返回值:成功則返回0,否則返回-1. 2.14.msgrcv() 從消息隊列中取得指定類型的消息功能:從消息隊列中取得指定類型的消息. 語法:#include #include #include int msgrcv(msqid,msgp,msgsz,msgtyp,msgflg) int msqid; void *msgp; int msgsz; long msgtyp; int msgf

32、lg; 說明:本系統調用從由msqid指定的消息隊列中讀取一個由msgtyp指定 類型的消息到由msgp指向的緩沖區中,同樣的,該緩沖區的結構如 前所述,包括消息類型和消息正文.msgsz為可接收的消息正文的 字節數.若接收到的消息正文的長度大于msgsz,則會被截短到 msgsz字節為止(當消息標志msgflg&MSG_NOERROR為真時),截掉的 部份將被丟失,而且不通知消息發送進程. msgtyp指定消息類型: . 為0則接收消息隊列中第一個消息. . 大于0則接收消息隊列中第一個類型為msgtyp的消息. . 小于0則接收消息隊列中第一個類型值不小于msgtyp絕對值且 類型值又最小

33、的消息. msgflg指定操作行為: . 若(msgflg&IPC_NOWAIT)是真的,調用進程會立即返回,若沒有 接收到消息則返回值為-1,errno設置為ENOMSG. . 若(msgflg&IPC_NOWAIT)不是真的,則調用進程會被掛起直到下 面情況之一發生: * 隊列中的消息的類型是有效的. * 消息隊列標志被系統刪除.系統調用返回-1. * 調用進程接收到一個未被忽略的中斷信號,調用進程繼續 執行或被終止. 調用成功后,對應指定的消息隊列的相關結構做如下動作: . 消息數(msg_qnum)減1. . 消息隊列最近接收進程號(msg_lrpid)改為調用進程號. . 消息隊列接

34、收時間(msg_rtime)改為當前系統時間. 以上信息可用命令ipcs -a看到. 返回值:調用成功則返回值等于接收到實際消息正文的字節數. 不成功則返回-1. 2.15.msgctl() 消息控制操作功能:消息控制操作 語法:#include #include #include int msgctl(msqid,cmd,buf) int msqid,cmd; struct msqid_ds *buf; 說明:本系統調用提供一系列消息控制操作,操作動作由cmd定義,以下 cmd定義值表明了各操作動作的定義. . IPC_STAT:將msqid相關的數據結構中各個元素的當前值放入由 buf指向

35、的結構中. . 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有合適權限的進

36、程操作. 返回值:調用成功則返回值為0,否則為-1. 2.16.msgget() 取得一個消息隊列功能:取得一個消息隊列. 語法:#include #include #include int msgget(key,msgflg) key_t key; int msgflg; 說明:本系統調用返回與參數key相關的消息隊列的標識符. 若以下事實成立,則與消息隊列相關的標識符和數據結構將被創 建出來: . 若參數key等于IPC_PRIVATE. . 若參數key沒有一個已存在的消息隊列標識符與之相關,同時值 (msgflg&IPC_CREAT)為真. 創建消息隊列時,與新的消息隊列標識符相關的數

37、據結構將被初 始化為如下: . 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. 例子:本例將包括上述所有消息隊列操作的系統

38、調用: #define RKEY 0 x9001L /*讀消息隊列的KEY值*/ #define WKEY 0 x9002L /*寫消息隊列的KEY值*/ #define MSGFLG 0666 /*消息隊列訪問權限*/ #define IPC_WAIT 0 /*等待方式在include文件中未定義*/ int rmsqid; /*讀消息隊列標識符*/ int wmsqid; /*寫消息隊列標識符*/ struct msgbuf long mtype; char mtext200; buf; /*若讀消息隊列已存在就取得標識符,否則則創建并取得標識符*/ if (rmsqid=msgget(R

39、KEY,MSGFLG|IPC_CREAT)0) printf(get read message queue failedn); exit(1); /*若寫消息隊列已存在則失敗,若不存在則創建并取得標識符*/ if (wmsqid=msgget(WKEY, MSGFLG|IPC_CREAT|IPC_TRUNC)0) printf(get %ld type message from queue:%sn, buf.mtype,buf.mtext); else printf(get message failedn); exit(3); buf.mtype=3L; if (msgsnd(wmsqid,&

40、buf,sizeof(struct msgbuf)-sizeof(long), IPC_NOWAIT)0) printf(send message OKn); else printf(send message failedn); exit(4); msgctl(wmsqid,IPC_RMID,(struct msqid *)NULL); 2.17.shmat() 聯接共享內存的操作功能:聯接共享內存的操作. 語法:#include #include #include void *shmat(shmid,shmaddr,shmflg) int shmid; void *shmaddr; int

41、shmid; 說明:將由shmid指示的共享內存聯接到調用進程的數據段中.被聯接的 段放在地址,該地址由以下準則指定: . 若shmaddr等于(void *)0,則段聯接到由系統選擇的第一個可 用的地址上. . 若shmaddr不等于(void *)0同時(shmflg&SHM_RND)值為真,則 段聯接到由(shmaddr-(shmaddr%SHMLBA)給出的地址上. . 若shmaddr不等于(void *)0同時(shmflg&SHM_RND)值為假,則 段聯接到由shmaddr指定的地址上. 若(shmflg&sSHM_RDONLY)為真并且調用進程有讀允許,則被聯接 的段為只讀;

42、否則,若值不為真且調用進程有讀寫權限,則被聯接 的段為可讀寫的. 返回值:若調用成功則返回被聯接的共享內存段在數據段上的啟始地址. 否則返回值為-1. 2.18.shmdt() 斷開共享內存聯接的操作功能:斷開共享內存聯接的操作. 語法:#include #include #include void *shmdt(shmaddr) void *shmaddr; 說明:本系統調用將由shmaddr指定的共享內存段從調用進程的數據段 脫離出去. 返回值:若調用成功則返回值為0,否則返回值為-1. 2.19.shmget() 取得共享內存段功能:取得共享內存段 語法:#include #includ

43、e #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和s

44、hm_perm.gid設置為調用進程的有效GID. . shm_perm.mode訪問權限比特位設置為shmflg訪問權限比特位. . shm_lpid,shm_nattch,shm_atime,shm_dtime設置為0. . shm_ctime設置為當前系統時間. . shm_segsz設置為0. 返回值:若調用成功則返回一個非0值,稱為共享內存標識符,否則返回 值為-1. 2.20.shmctl() 共享內存控制操作功能:共享內存控制操作. 語法:#include #include #include int shmctl(shmid,cmd,buf) int shmid,cmd; str

45、uct 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指示的共享內存.將它從系統中刪除并

46、破壞相關的數據結構. 本命令只能由有效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

47、cleanup(); for (i=0;i20;i+) signal(i,cleanup); shmid=shmget(SHMKEY,128*K,0777|IPC_CREAT); addr1=shmat(shmid,0,0); addr2=shmat(shmid,0,0); printf(addr1 0 x%x addr2 0 x%xn,addr1,addr2); pint=(int*)addr1; for (i=0;i256;i+) *pint+=i; pint=(int*)addr1; *pint=256; pint=(int*)addr2; for (i=0;i256;i+) print

48、f(index %dtvalue%dn,i,*pint+); shmdt(addr1); shmdt(addr2); pause(); 2.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和semnu

49、m指定的信號量做操作的.每個命令都 要求有相應的權限級別: . GETVAL:返回semval的值,要求有讀權限. . SETVAL:設置semval的值到arg.val上.此命令成功執行后, semadj的值對應的所有進程的信號量全部被清除,要求有修 改權限. . GETPID:返回sempid的值,要求有讀權限. . GETNCNT:返回semncnt的值,要求有讀權限. . GETZCNT:返回semzcnt的值,要求有讀權限. 以下命令在一組信號量中的各個semval上操作: . GETALL:返回每個semval的值,同時將各個值放入由arg.array 指向的數組中.當此命令成功執

50、行后,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

51、等于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. 2.22.semget() 取得一組信號量功能:取得一組信號量.

52、 語法:#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設置等于調用 進

53、程的有效UID. . 在操作權限結構,sem_perm.cgid和sem_perm.gid設置等于調用 進程的有效GID. . 訪問權限比特位sem_perm.mode設置等于semflg的訪問權限比 特位. . sem_otime設置等于0,sem_ctime設置等于當前系統時間. 返回值:若調用成功,則返回一非0值,稱為信號量標識符;否則返回-1. 2.23.semop() 信號量操作功能:信號量操作. 語法:#include #include #include int semop(semid,sops,nsops) int semid; struct sembuf *sops; unsi

54、gned nsops; 說明:本系統調用用于執行用戶定義的在一組信號量上操作的行為集合. 該組信號量與semid相關. 參數sops為一個用戶定義的信號量操作結構數組指針. 參數nsops為該數組的元素個數. 數組的每個元素結構包括如下成員: sem_num; /* 信號量數 */ sem_op; /* 信號量操作 */ sem_flg; /* 操作標志 */ 由本系統調用定義的每個信號量操作是針對由semid和sem_num指 定的信號量的.變量sem_op指定三種信號量操作的一種: . 若sem_op為一負數并且調用進程具有修改權限,則下列情況之 一將會發生: * 若semval不小于se

55、m_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減一,若 (s

56、emflg&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且(semfl

57、g&IPC_NOWAIT)為真,本系統調用 立即返回. * 若semval不等于0且(semflg&IPC_NOWAIT)為假,本系統調用 將把指定信號量的 semzcnt值加一,將調用進程掛起直到下列情況之一發生: (1).semval值變為0時,指定信號量的semzcnt值減一. (2).調用進程等待的semid已被系統刪除. (3).調用進程捕俘到信號,此時,指定信號量的semncnt值 減一,調用進程執行中斷服務程序. 返回值:調用成功則返回0,否則返回-1. 例子:本例將包括上述信號量操作的所有系統調用: #include #include #include #define SEMK

58、EY 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 init

59、array2,outarray2; extern cleanup(); if (argc=1) for (i=0;i20;i+) signal(i,clearup); semid=semget(SEMKEY,2,0777|IPC_CREAT); initarray0=initarray1=1; semctl(semid,2,SETALL,initarray); semctl(semid,2,GETALL,outarray); printf(sem init vals %d%d n, outarray0,outarray1); pause(); /*睡眠到被一軟件中斷信號喚醒*/ else if

60、 (argv10=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(p

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論