Linux環(huán)境進程間通信_第1頁
Linux環(huán)境進程間通信_第2頁
Linux環(huán)境進程間通信_第3頁
Linux環(huán)境進程間通信_第4頁
Linux環(huán)境進程間通信_第5頁
已閱讀5頁,還剩177頁未讀 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

Linux環(huán)境進程間通信Linux環(huán)境進程間通信/NUMPAGES182Linux環(huán)境進程間通信Linux環(huán)境進程間通信 目錄Linux環(huán)境進程間通信(一):管道及有名管道 21、管道概述及相關API應用 21.1管道相關的關鍵概念 21.2管道的創(chuàng)建 31.3管道的讀寫規(guī)則 31.4管道應用實例 71.5管道的局限性 92、有名管道概述及相關API應用 102.1有名管道相關的關鍵概念 102.2有名管道的創(chuàng)建 102.3有名管道的打開規(guī)則 102.4有名管道的讀寫規(guī)則 102.5有名管道應用實例 14Linux環(huán)境進程間通信(二):信號(上) 171、信號及信號來源 172、信號的種類 18(1)可靠信號與不可靠信號 18(2)實時信號與非實時信號 193、進程對信號的響應 194、信號的發(fā)送 195、信號的安裝(設置信號關聯(lián)動作) 216、信號集及信號集操作函數: 247、信號阻塞與信號未決 25Linux環(huán)境進程間通信(二):信號(下) 271、信號生命周期 272、信號編程注意事項 293、深入淺出:信號應用實例 30Linux環(huán)境進程間通信(三):消息隊列 361、消息隊列基本概念 372、操作消息隊列 383、消息隊列的限制 414、消息隊列應用實例 41Linux環(huán)境進程間通信(四):信號燈 471、信號燈概述 472、Linux信號燈 473、信號燈與內核 484、操作信號燈 485、信號燈的限制 516、競爭問題 527、信號燈應用實例 52Linux環(huán)境進程間通信(五):共享內存(上) 581、內核怎樣保證各個進程尋址到同一個共享內存區(qū)域的內存頁面 582、mmap()及其相關系統(tǒng)調用 593、mmap()范例 604、對mmap()返回地址的訪問 64Linux環(huán)境進程間通信(五):共享內存(下) 671、系統(tǒng)V共享內存原理 672、系統(tǒng)V共享內存API 683、系統(tǒng)V共享內存限制 694、系統(tǒng)V共享內存范例 69Linux環(huán)境進程間通信(六):套接口 731、背景知識 732、重要數據結構 74(1)表示套接口的數據結構structsocket 74(2)描述套接口通用地址的數據結構structsockaddr 74(3)描述因特網地址結構的數據結構structsockaddr_in(這里局限于IP4) 753、套接口編程的幾個重要步驟 75(1)創(chuàng)建套接口,由系統(tǒng)調用socket實現 75(2)綁定地址 76(3)請求建立連接(由TCP客戶發(fā)起) 76(4)接受連接請求(由TCP服務器端發(fā)起) 76(5)通信 77(6)通信的最后一步是關閉套接口 784、典型調用代碼 78(1)典型的TCP服務器代碼 78(2)典型的TCP客戶代碼 795、網絡編程中的其他重要概念 80Linux環(huán)境進程間通信(一):管道及有名管道1"\o""鄭彥興

()國防科大計算機學院簡介:

在本系列序中作者概述了linux進程間通信的幾種主要手段。其中管道和有名管道是最早的進程間通信機制之一,管道可用于具有親緣關系進程間的通信,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關系進程間的通信。認清管道和有名管道的讀寫規(guī)則是在程序中應用它們的關鍵,本文在詳細討論了管道和有名管道的通信機制的基礎上,用實例對其讀寫規(guī)則進行了程序驗證,這樣做有利于增強讀者對讀寫規(guī)則的感性認識,同時也提供了應用范例。1、管道概述及相關API應用1.1管道相關的關鍵概念管道是Linux支持的最初UnixIPC形式之一,具有以下特點:管道是半雙工的,數據只能向一個方向流動;需要雙方通信時,需要建立起兩個管道;只能用于父子進程或者兄弟進程之間(具有親緣關系的進程);單獨構成一種獨立的文件系統(tǒng):管道對于管道兩端的進程而言,就是一個文件,但它不是普通的文件,它不屬于某種文件系統(tǒng),而是自立門戶,單獨構成一種文件系統(tǒng),并且只存在與內存中。數據的讀出和寫入:一個進程向管道中寫的內容被管道另一端的進程讀出。寫入的內容每次都添加在管道緩沖區(qū)的末尾,并且每次都是從緩沖區(qū)的頭部讀出數據。1.2管道的創(chuàng)建#include<unistd.h>intpipe(intfd[2])該函數創(chuàng)建的管道的兩端處于一個進程中間,在實際應用中沒有太大意義,因此,一個進程在由pipe()創(chuàng)建管道后,一般再fork一個子進程,然后通過管道實現父子進程間的通信(因此也不難推出,只要兩個進程中存在親緣關系,這里的親緣關系指的是具有共同的祖先,都可以采用管道方式來進行通信)。1.3管道的讀寫規(guī)則管道兩端可分別用描述字fd[0]以及fd[1]來描述,需要注意的是,管道的兩端是固定了任務的。即一端只能用于讀,由描述字fd[0]表示,稱其為管道讀端;另一端則只能用于寫,由描述字fd[1]來表示,稱其為管道寫端。如果試圖從管道寫端讀取數據,或者向管道讀端寫入數據都將導致錯誤發(fā)生。一般文件的I/O函數都可以用于管道,如close、read、write等等。從管道中讀取數據:如果管道的寫端不存在,則認為已經讀到了數據的末尾,讀函數返回的讀出字節(jié)數為0;當管道的寫端存在時,如果請求的字節(jié)數目大于PIPE_BUF,則返回管道中現有的數據字節(jié)數,如果請求的字節(jié)數目不大于PIPE_BUF,則返回管道中現有數據字節(jié)數(此時,管道中數據量小于請求的數據量);或者返回請求的字節(jié)數(此時,管道中數據量不小于請求的數據量)。注:(PIPE_BUF在include/linux/limits.h中定義,不同的內核版本可能會有所不同。Posix.1要求PIPE_BUF至少為512字節(jié),redhat7.2中為4096)。關于管道的讀規(guī)則驗證:/***************readtest.c***************/#include<unistd.h>#include<sys/types.h>#include<errno.h>main(){ intpipe_fd[2]; pid_tpid; charr_buf[100]; charw_buf[4]; char*p_wbuf; intr_num; intcmd; memset(r_buf,0,sizeof(r_buf)); memset(w_buf,0,sizeof(r_buf)); p_wbuf=w_buf; if(pipe(pipe_fd)<0) { printf("pipecreateerror\n"); return-1; } if((pid=fork())==0) { printf("\n"); close(pipe_fd[1]); sleep(3);//確保父進程關閉寫端 r_num=read(pipe_fd[0],r_buf,100);printf( "readnumis%dthedatareadfromthepipeis%d\n",r_num,atoi(r_buf)); close(pipe_fd[0]); exit(); } elseif(pid>0) { close(pipe_fd[0]);//read strcpy(w_buf,"111"); if(write(pipe_fd[1],w_buf,4)!=-1) printf("parentwriteover\n"); close(pipe_fd[1]);//write printf("parentclosefd[1]over\n"); sleep(10); } }/***************************************************程序輸出結果:*parentwriteover*parentclosefd[1]over*readnumis4thedatareadfromthepipeis111*附加結論:*管道寫端關閉后,寫入的數據將一直存在,直到讀出為止.****************************************************/向管道中寫入數據:向管道中寫入數據時,linux將不保證寫入的原子性,管道緩沖區(qū)一有空閑區(qū)域,寫進程就會試圖向管道寫入數據。如果讀進程不讀走管道緩沖區(qū)中的數據,那么寫操作將一直阻塞。

注:只有在管道的讀端存在時,向管道中寫入數據才有意義。否則,向管道中寫入數據的進程將收到內核傳來的SIFPIPE信號,應用程序可以處理該信號,也可以忽略(默認動作則是應用程序終止)。對管道的寫規(guī)則的驗證1:寫端對讀端存在的依賴性#include<unistd.h>#include<sys/types.h>main(){ intpipe_fd[2]; pid_tpid; charr_buf[4]; char*w_buf; intwritenum; intcmd; memset(r_buf,0,sizeof(r_buf)); if(pipe(pipe_fd)<0) { printf("pipecreateerror\n"); return-1; } if((pid=fork())==0) { close(pipe_fd[0]); close(pipe_fd[1]); sleep(10); exit(); } elseif(pid>0) { sleep(1);//等待子進程完成關閉讀端的操作 close(pipe_fd[0]);//write w_buf="111"; if((writenum=write(pipe_fd[1],w_buf,4))==-1) printf("writetopipeerror\n"); else printf("thebyteswritetopipeis%d\n",writenum); close(pipe_fd[1]); } }則輸出結果為:Brokenpipe,原因就是該管道以及它的所有fork()產物的讀端都已經被關閉。如果在父進程中保留讀端,即在寫完pipe后,再關閉父進程的讀端,也會正常寫入pipe,讀者可自己驗證一下該結論。因此,在向管道寫入數據時,至少應該存在某一個進程,其中管道讀端沒有被關閉,否則就會出現上述錯誤(管道斷裂,進程收到了SIGPIPE信號,默認動作是進程終止)對管道的寫規(guī)則的驗證2:linux不保證寫管道的原子性驗證#include<unistd.h>#include<sys/types.h>#include<errno.h>main(intargc,char**argv){ intpipe_fd[2]; pid_tpid; charr_buf[4096]; charw_buf[4096*2]; intwritenum; intrnum; memset(r_buf,0,sizeof(r_buf)); if(pipe(pipe_fd)<0) { printf("pipecreateerror\n"); return-1; } if((pid=fork())==0) { close(pipe_fd[1]); while(1) { sleep(1); rnum=read(pipe_fd[0],r_buf,1000); printf("child:readnumis%d\n",rnum); } close(pipe_fd[0]); exit(); } elseif(pid>0) { close(pipe_fd[0]);//write memset(r_buf,0,sizeof(r_buf)); if((writenum=write(pipe_fd[1],w_buf,1024))==-1) printf("writetopipeerror\n"); else printf("thebyteswritetopipeis%d\n",writenum); writenum=write(pipe_fd[1],w_buf,4096); close(pipe_fd[1]); } }輸出結果:thebyteswritetopipe1000thebyteswritetopipe1000//注意,此行輸出說明了寫入的非原子性thebyteswritetopipe1000thebyteswritetopipe1000thebyteswritetopipe1000thebyteswritetopipe120//注意,此行輸出說明了寫入的非原子性thebyteswritetopipe0thebyteswritetopipe0結論:寫入數目小于4096時寫入是非原子的!

如果把父進程中的兩次寫入字節(jié)數都改為5000,則很容易得出下面結論:

寫入管道的數據量大于4096字節(jié)時,緩沖區(qū)的空閑空間將被寫入數據(補齊),直到寫完所有數據為止,如果沒有進程讀數據,則一直阻塞。1.4管道應用實例實例一:用于shell管道可用于輸入輸出重定向,它將一個命令的輸出直接定向到另一個命令的輸入。比如,當在某個shell程序(Bourneshell或Cshell等)鍵入who│wc-l后,相應shell程序將創(chuàng)建who以及wc兩個進程和這兩個進程間的管道??紤]下面的命令行:$kill-l運行結果見

works/cn/linux/l-ipc/part1/"附一。$kill-l|grepSIGRTMIN運行結果如下:30)SIGPWR 31)SIGSYS 32)SIGRTMIN 33)SIGRTMIN+134)SIGRTMIN+2 35)SIGRTMIN+3 36)SIGRTMIN+4 37)SIGRTMIN+538)SIGRTMIN+6 39)SIGRTMIN+7 40)SIGRTMIN+8 41)SIGRTMIN+942)SIGRTMIN+10 43)SIGRTMIN+11 44)SIGRTMIN+12 45)SIGRTMIN+1346)SIGRTMIN+14 47)SIGRTMIN+15 48)SIGRTMAX-15 49)SIGRTMAX-14實例二:用于具有親緣關系的進程間通信下面例子給出了管道的具體應用,父進程通過管道發(fā)送一些命令給子進程,子進程解析命令,并根據命令作相應處理。#include<unistd.h>#include<sys/types.h>main(){ intpipe_fd[2]; pid_tpid; charr_buf[4]; char**w_buf[256]; intchildexit=0; inti; intcmd; memset(r_buf,0,sizeof(r_buf)); if(pipe(pipe_fd)<0) { printf("pipecreateerror\n"); return-1; } if((pid=fork())==0) //子進程:解析從管道中獲取的命令,并作相應的處理 { printf("\n"); close(pipe_fd[1]); sleep(2); while(!childexit) { read(pipe_fd[0],r_buf,4); cmd=atoi(r_buf); if(cmd==0) {printf("child:receivecommandfromparentover\nnowchildprocessexit\n"); childexit=1; } elseif(handle_cmd(cmd)!=0) return; sleep(1); } close(pipe_fd[0]); exit(); } elseif(pid>0) //parent:sendcommandstochild { close(pipe_fd[0]); w_buf[0]="003"; w_buf[1]="005"; w_buf[2]="777"; w_buf[3]="000"; for(i=0;i<4;i++) write(pipe_fd[1],w_buf[i],4); close(pipe_fd[1]); } }//下面是子進程的命令處理函數(特定于應用):inthandle_cmd(intcmd){if((cmd<0)||(cmd>256))//supposechildonlysupport256commands { printf("child:invalidcommand\n"); return-1; }printf("child:thecmdfromparentis%d\n",cmd);return0;}1.5管道的局限性管道的主要局限性正體現在它的特點上:只支持單向數據流;只能用于具有親緣關系的進程之間;沒有名字;管道的緩沖區(qū)是有限的(管道制存在于內存中,在管道創(chuàng)建時,為緩沖區(qū)分配一個頁面大?。还艿浪鶄魉偷氖菬o格式字節(jié)流,這就要求管道的讀出方和寫入方必須事先約定好數據的格式,比如多少字節(jié)算作一個消息(或命令、或記錄)等等;/part1/"回頁首2、有名管道概述及相關API應用2.1有名管道相關的關鍵概念管道應用的一個重大限制是它沒有名字,因此,只能用于具有親緣關系的進程間通信,在有名管道(namedpipe或FIFO)提出后,該限制得到了克服。FIFO不同于管道之處在于它提供一個路徑名與之關聯(lián),以FIFO的文件形式存在于文件系統(tǒng)中。這樣,即使與FIFO的創(chuàng)建進程不存在親緣關系的進程,只要可以訪問該路徑,就能夠彼此通過FIFO相互通信(能夠訪問該路徑的進程以及FIFO的創(chuàng)建進程之間),因此,通過FIFO不相關的進程也能交換數據。值得注意的是,FIFO嚴格遵循先進先出(firstinfirstout),對管道及FIFO的讀總是從開始處返回數據,對它們的寫則把數據添加到末尾。它們不支持諸如lseek()等文件定位操作。2.2有名管道的創(chuàng)建#include<sys/types.h>#include<sys/stat.h>intmkfifo(constchar*pathname,mode_tmode)該函數的第一個參數是一個普通的路徑名,也就是創(chuàng)建后FIFO的名字。第二個參數與打開普通文件的open()函數中的mode參數相同。如果mkfifo的第一個參數是一個已經存在的路徑名時,會返回EEXIST錯誤,所以一般典型的調用代碼首先會檢查是否返回該錯誤,如果確實返回該錯誤,那么只要調用打開FIFO的函數就可以了。一般文件的I/O函數都可以用于FIFO,如close、read、write等等。2.3有名管道的打開規(guī)則有名管道比管道多了一個打開操作:open。FIFO的打開規(guī)則:如果當前打開操作是為讀而打開FIFO時,若已經有相應進程為寫而打開該FIFO,則當前打開操作將成功返回;否則,可能阻塞直到有相應進程為寫而打開該FIFO(當前打開操作設置了阻塞標志);或者,成功返回(當前打開操作沒有設置阻塞標志)。如果當前打開操作是為寫而打開FIFO時,如果已經有相應進程為讀而打開該FIFO,則當前打開操作將成功返回;否則,可能阻塞直到有相應進程為讀而打開該FIFO(當前打開操作設置了阻塞標志);或者,返回ENXIO錯誤(當前打開操作沒有設置阻塞標志)。對打開規(guī)則的驗證參見

orks/cn/linux/l-ipc/part1/"附2。2.4有名管道的讀寫規(guī)則從FIFO中讀取數據:約定:如果一個進程為了從FIFO中讀取數據而阻塞打開FIFO,那么稱該進程內的讀操作為設置了阻塞標志的讀操作。如果有進程寫打開FIFO,且當前FIFO內沒有數據,則對于設置了阻塞標志的讀操作來說,將一直阻塞。對于沒有設置阻塞標志讀操作來說則返回-1,當前errno值為EAGAIN,提醒以后再試。對于設置了阻塞標志的讀操作說,造成阻塞的原因有兩種:當前FIFO內有數據,但有其它進程在讀這些數據;另外就是FIFO內沒有數據。解阻塞的原因則是FIFO中有新的數據寫入,不論信寫入數據量的大小,也不論讀操作請求多少數據量。讀打開的阻塞標志只對本進程第一個讀操作施加作用,如果本進程內有多個讀操作序列,則在第一個讀操作被喚醒并完成讀操作后,其它將要執(zhí)行的讀操作將不再阻塞,即使在執(zhí)行讀操作時,FIFO中沒有數據也一樣(此時,讀操作返回0)。如果沒有進程寫打開FIFO,則設置了阻塞標志的讀操作會阻塞。注:如果FIFO中有數據,則設置了阻塞標志的讀操作不會因為FIFO中的字節(jié)數小于請求讀的字節(jié)數而阻塞,此時,讀操作會返回FIFO中現有的數據量。向FIFO中寫入數據:約定:如果一個進程為了向FIFO中寫入數據而阻塞打開FIFO,那么稱該進程內的寫操作為設置了阻塞標志的寫操作。對于設置了阻塞標志的寫操作:當要寫入的數據量不大于PIPE_BUF時,linux將保證寫入的原子性。如果此時管道空閑緩沖區(qū)不足以容納要寫入的字節(jié)數,則進入睡眠,直到當緩沖區(qū)中能夠容納要寫入的字節(jié)數時,才開始進行一次性寫操作。當要寫入的數據量大于PIPE_BUF時,linux將不再保證寫入的原子性。FIFO緩沖區(qū)一有空閑區(qū)域,寫進程就會試圖向管道寫入數據,寫操作在寫完所有請求寫的數據后返回。對于沒有設置阻塞標志的寫操作:當要寫入的數據量大于PIPE_BUF時,linux將不再保證寫入的原子性。在寫滿所有FIFO空閑緩沖區(qū)后,寫操作返回。當要寫入的數據量不大于PIPE_BUF時,linux將保證寫入的原子性。如果當前FIFO空閑緩沖區(qū)能夠容納請求寫入的字節(jié)數,寫完后成功返回;如果當前FIFO空閑緩沖區(qū)不能夠容納請求寫入的字節(jié)數,則返回EAGAIN錯誤,提醒以后再寫;對FIFO讀寫規(guī)則的驗證:下面提供了兩個對FIFO的讀寫程序,適當調節(jié)程序中的很少地方或者程序的命令行參數就可以對各種FIFO讀寫規(guī)則進行驗證。

程序1:寫FIFO的程序#include<sys/types.h>#include<sys/stat.h>#include<errno.h>#include<fcntl.h>#defineFIFO_SERVER"/tmp/fifoserver"main(intargc,char**argv)//參數為即將寫入的字節(jié)數{ intfd; charw_buf[4096*2]; intreal_wnum; memset(w_buf,0,4096*2); if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST)) printf("cannotcreatefifoserver\n"); if(fd==-1) if(errno==ENXIO) printf("openerror;noreadingprocess\n"); fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0); //設置非阻塞標志 //fd=open(FIFO_SERVER,O_WRONLY,0); //設置阻塞標志 real_wnum=write(fd,w_buf,2048); if(real_wnum==-1) { if(errno==EAGAIN) printf("writetofifoerror;trylater\n"); } else printf("realwritenumis%d\n",real_wnum); real_wnum=write(fd,w_buf,5000); //5000用于測試寫入字節(jié)大于4096時的非原子性 //real_wnum=write(fd,w_buf,4096); //4096用于測試寫入字節(jié)不大于4096時的原子性 if(real_wnum==-1) if(errno==EAGAIN) printf("trylater\n");}

程序2:與程序1一起測試寫FIFO的規(guī)則,第一個命令行參數是請求從FIFO讀出的字節(jié)數#include<sys/types.h>#include<sys/stat.h>#include<errno.h>#include<fcntl.h>#defineFIFO_SERVER"/tmp/fifoserver"main(intargc,char**argv){ charr_buf[4096*2]; intfd; intr_size; intret_size; r_size=atoi(argv[1]); printf("requredrealreadbytes%d\n",r_size); memset(r_buf,0,sizeof(r_buf)); fd=open(FIFO_SERVER,O_RDONLY|O_NONBLOCK,0); //fd=open(FIFO_SERVER,O_RDONLY,0); //在此處可以把讀程序編譯成兩個不同版本:阻塞版本及非阻塞版本 if(fd==-1) { printf("open%sforreaderror\n"); exit(); } while(1) { memset(r_buf,0,sizeof(r_buf)); ret_size=read(fd,r_buf,r_size); if(ret_size==-1) if(errno==EAGAIN) printf("nodataavlaible\n"); printf("realreadbytes%d\n",ret_size); sleep(1); } pause(); unlink(FIFO_SERVER);}程序應用說明:把讀程序編譯成兩個不同版本:阻塞讀版本:br以及非阻塞讀版本nbr把寫程序編譯成兩個四個版本:非阻塞且請求寫的字節(jié)數大于PIPE_BUF版本:nbwg非阻塞且請求寫的字節(jié)數不大于PIPE_BUF版本:版本nbw阻塞且請求寫的字節(jié)數大于PIPE_BUF版本:bwg阻塞且請求寫的字節(jié)數不大于PIPE_BUF版本:版本bw下面將使用br、nbr、w代替相應程序中的阻塞讀、非阻塞讀驗證阻塞寫操作:當請求寫入的數據量大于PIPE_BUF時的非原子性:nbr1000bwg當請求寫入的數據量不大于PIPE_BUF時的原子性:nbr1000bw驗證非阻塞寫操作:當請求寫入的數據量大于PIPE_BUF時的非原子性:nbr1000nbwg請求寫入的數據量不大于PIPE_BUF時的原子性:nbr1000nbw不管寫打開的阻塞標志是否設置,在請求寫入的字節(jié)數大于4096時,都不保證寫入的原子性。但二者有本質區(qū)別:對于阻塞寫來說,寫操作在寫滿FIFO的空閑區(qū)域后,會一直等待,直到寫完所有數據為止,請求寫入的數據最終都會寫入FIFO;而非阻塞寫則在寫滿FIFO的空閑區(qū)域后,就返回(實際寫入的字節(jié)數),所以有些數據最終不能夠寫入。對于讀操作的驗證則比較簡單,不再討論。2.5有名管道應用實例在驗證了相應的讀寫規(guī)則后,應用實例似乎就沒有必要了。"回頁首小結:管道常用于兩個方面:(1)在shell中時常會用到管道(作為輸入輸入的重定向),在這種應用方式下,管道的創(chuàng)建對于用戶來說是透明的;(2)用于具有親緣關系的進程間通信,用戶自己創(chuàng)建管道,并完成讀寫操作。FIFO可以說是管道的推廣,克服了管道無名字的限制,使得無親緣關系的進程同樣可以采用先進先出的通信機制進行通信。管道和FIFO的數據是字節(jié)流,應用程序之間必須事先確定特定的傳輸"協(xié)議",采用傳播具有特定意義的消息。要靈活應用管道及FIFO,理解它們的讀寫規(guī)則是關鍵。附1:kill-l的運行結果,顯示了當前系統(tǒng)支持的所有信號:1)SIGHUP 2)SIGINT 3)SIGQUIT 4)SIGILL5)SIGTRAP 6)SIGABRT 7)SIGBUS 8)SIGFPE9)SIGKILL 10)SIGUSR1 11)SIGSEGV 12)SIGUSR213)SIGPIPE 14)SIGALRM 15)SIGTERM 17)SIGCHLD18)SIGCONT 19)SIGSTOP 20)SIGTSTP 21)SIGTTIN22)SIGTTOU 23)SIGURG 24)SIGXCPU 25)SIGXFSZ26)SIGVTALRM 27)SIGPROF 28)SIGWINCH 29)SIGIO30)SIGPWR 31)SIGSYS 32)SIGRTMIN 33)SIGRTMIN+134)SIGRTMIN+2 35)SIGRTMIN+3 36)SIGRTMIN+4 37)SIGRTMIN+538)SIGRTMIN+6 39)SIGRTMIN+7 40)SIGRTMIN+8 41)SIGRTMIN+942)SIGRTMIN+10 43)SIGRTMIN+11 44)SIGRTMIN+12 45)SIGRTMIN+1346)SIGRTMIN+14 47)SIGRTMIN+15 48)SIGRTMAX-15 49)SIGRTMAX-1450)SIGRTMAX-13 51)SIGRTMAX-12 52)SIGRTMAX-11 53)SIGRTMAX-1054)SIGRTMAX-9 55)SIGRTMAX-8 56)SIGRTMAX-7 57)SIGRTMAX-658)SIGRTMAX-5 59)SIGRTMAX-4 60)SIGRTMAX-3 61)SIGRTMAX-262)SIGRTMAX-1 63)SIGRTMAX 除了在此處用來說明管道應用外,接下來的專題還要對這些信號分類討論。附2:對FIFO打開規(guī)則的驗證(主要驗證寫打開對讀打開的依賴性)#include<sys/types.h>#include<sys/stat.h>#include<errno.h>#include<fcntl.h>#defineFIFO_SERVER"/tmp/fifoserver"inthandle_client(char*);main(intargc,char**argv){ intr_rd; intw_fd; pid_tpid; if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST)) printf("cannotcreatefifoserver\n"); handle_client(FIFO_SERVER); }inthandle_client(char*arg){intret;ret=w_open(arg);switch(ret){ case0: { printf("open%serror\n",arg); printf("noprocesshasthefifoopenforreading\n"); return-1; } case-1: { printf("somethingwrongwithopenthefifoexceptforENXIO"); return-1; } case1: { printf("openserverok\n"); return1; } default: { printf("w_no_rreturn\n"); return0; }} unlink(FIFO_SERVER);}intw_open(char*arg)//0openerrorfornoreading//-1openerrorforotherreasons//1openok{ if(open(arg,O_WRONLY|O_NONBLOCK,0)==-1) { if(errno==ENXIO) { return0; } else return-1; } return1; }參考資料UNIX網絡編程第二卷:進程間通信,作者:W.RichardStevens,譯者:楊繼張,清華大學出版社。豐富的UNIX進程間通信實例及分析,對Linux環(huán)境下的程序開發(fā)有極大的啟發(fā)意義。linux內核源代碼情景分析(上、下),毛德操、胡希明著,浙江大學出版社,當要驗證某個結論、想法時,最好的參考資料;UNIX環(huán)境高級編程,作者:W.RichardStevens,譯者:尤晉元等,機械工業(yè)出版社。具有豐富的編程實例,以及關鍵函數伴隨Unix的發(fā)展歷程。mailto:mlinux@163.com?subject=%E4%BF%A1%E5%8F%B7%EF%BC%88%E4%B8%8A%EF%BC%89")國防科大簡介:

linux信號機制遠遠比想象的復雜,本文力爭用最短的篇幅,對該機制做了深入細致的分析。讀者可以先讀一下信號應用實例(在信號(下)中),這樣可以對信號發(fā)送直到相應的處理函數執(zhí)行完畢這一過程有個大致的印象。本文盡量給出了較新函數的應用實例,著重說明這些的功能。1、信號及信號來源信號本質信號是在軟件層次上對中斷機制的一種模擬,在原理上,一個進程收到一個信號與處理器收到一個中斷請求可以說是一樣的。信號是異步的,一個進程不必通過任何操作來等待信號的到達,事實上,進程也不知道信號到底什么時候到達。信號是進程間通信機制中唯一的異步通信機制,可以看作是異步通知,通知接收信號的進程有哪些事情發(fā)生了。信號機制經過POSIX實時擴展后,功能更加強大,除了基本通知功能外,還可以傳遞附加信息。信號來源信號事件的發(fā)生有兩個來源:硬件來源(比如我們按下了鍵盤或者其它硬件故障);軟件來源,最常用發(fā)送信號的系統(tǒng)函數是kill,raise,alarm和setitimer以及sigqueue函數,軟件來源還包括一些非法運算等操作。on"回頁首2、信號的種類可以從兩個不同的分類角度對信號進行分類:(1)可靠性方面:可靠信號與不可靠信號;(2)與時間的關系上:實時信號與非實時信號。在《Linux環(huán)境進程間通信(一):管道及有名管道》的附1中列出了系統(tǒng)所支持的所有信號。(1)可靠信號與不可靠信號"不可靠信號"Linux信號機制基本上是從Unix系統(tǒng)中繼承過來的。早期Unix系統(tǒng)中的信號機制比較簡單和原始,后來在實踐中暴露出一些問題,因此,把那些建立在早期機制上的信號叫做"不可靠信號",信號值小于SIGRTMIN(Redhat7.2中,SIGRTMIN=32,SIGRTMAX=63)的信號都是不可靠信號。這就是"不可靠信號"的來源。它的主要問題是:進程每次處理信號后,就將對信號的響應設置為默認動作。在某些情況下,將導致對信號的錯誤處理;因此,用戶如果不希望這樣的操作,那么就要在信號處理函數結尾再一次調用signal(),重新安裝該信號。信號可能丟失,后面將對此詳細闡述。

因此,早期unix下的不可靠信號主要指的是進程可能對信號做出錯誤的反應以及信號可能丟失。Linux支持不可靠信號,但是對不可靠信號機制做了改進:在調用完信號處理函數后,不必重新調用該信號的安裝函數(信號安裝函數是在可靠機制上的實現)。因此,Linux下的不可靠信號問題主要指的是信號可能丟失。"可靠信號"隨著時間的發(fā)展,實踐證明了有必要對信號的原始機制加以改進和擴充。所以,后來出現的各種Unix版本分別在這方面進行了研究,力圖實現"可靠信號"。由于原來定義的信號已有許多應用,不好再做改動,最終只好又新增加了一些信號,并在一開始就把它們定義為可靠信號,這些信號支持排隊,不會丟失。同時,信號的發(fā)送和安裝也出現了新版本:信號發(fā)送函數sigqueue()及信號安裝函數sigaction()。POSIX.4對可靠信號機制做了標準化。但是,POSIX只對可靠信號機制應具有的功能以及信號機制的對外接口做了標準化,對信號機制的實現沒有作具體的規(guī)定。信號值位于SIGRTMIN和SIGRTMAX之間的信號都是可靠信號,可靠信號克服了信號可能丟失的問題。Linux在支持新版本的信號安裝函數sigation()以及信號發(fā)送函數sigqueue()的同時,仍然支持早期的signal()信號安裝函數,支持信號發(fā)送函數kill()。注:不要有這樣的誤解:由sigqueue()發(fā)送、sigaction安裝的信號就是可靠的。事實上,可靠信號是指后來添加的新信號(信號值位于SIGRTMIN及SIGRTMAX之間);不可靠信號是信號值小于SIGRTMIN的信號。信號的可靠與不可靠只與信號值有關,與信號的發(fā)送及安裝函數無關。目前l(fā)inux中的signal()是通過sigation()函數實現的,因此,即使通過signal()安裝的信號,在信號處理函數的結尾也不必再調用一次信號安裝函數。同時,由signal()安裝的實時信號支持排隊,同樣不會丟失。對于目前l(fā)inux的兩個信號安裝函數:signal()及sigaction()來說,它們都不能把SIGRTMIN以前的信號變成可靠信號(都不支持排隊,仍有可能丟失,仍然是不可靠信號),而且對SIGRTMIN以后的信號都支持排隊。這兩個函數的最大區(qū)別在于,經過sigaction安裝的信號都能傳遞信息給信號處理函數(對所有信號這一點都成立),而經過signal安裝的信號卻不能向信號處理函數傳遞信息。對于信號發(fā)送函數來說也是一樣的。(2)實時信號與非實時信號早期Unix系統(tǒng)只定義了32種信號,Rethat7.2支持64種信號,編號0-63(SIGRTMIN=31,SIGRTMAX=63),將來可能進一步增加,這需要得到內核的支持。前32種信號已經有了預定義值,每個信號有了確定的用途及含義,并且每種信號都有各自的缺省動作。如按鍵盤的CTRL^C時,會產生SIGINT信號,對該信號的默認反應就是進程終止。后32個信號表示實時信號,等同于前面闡述的可靠信號。這保證了發(fā)送的多個實時信號都被接收。實時信號是POSIX標準的一部分,可用于應用進程。非實時信號都不支持排隊,都是不可靠信號;實時信號都支持排隊,都是可靠信號?;仨撌?、信號集及信號集操作函數:信號集被定義為一種數據類型: typedefstruct{ unsignedlongsig[_NSIG_WORDS]; }sigset_t信號集用來描述信號的集合,linux所支持的所有信號可以全部或部分的出現在信號集中,主要與信號阻塞相關函數配合使用。下面是為信號集操作定義的相關函數: #include<signal.h>intsigemptyset(sigset_t*set);intsigfillset(sigset_t*set);intsigaddset(sigset_t*set,intsignum)intsigdelset(sigset_t*set,intsignum);intsigismember(constsigset_t*set,intsignum);sigemptyset(sigset_t*set)初始化由set指定的信號集,信號集里面的所有信號被清空;sigfillset(sigset_t*set)調用該函數后,set指向的信號集中將包含linux支持的64種信號;sigaddset(sigset_t*set,intsignum)在set指向的信號集中加入signum信號;sigdelset(sigset_t*set,intsignum)在se

溫馨提示

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

評論

0/150

提交評論