




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
Linux環(huán)境進(jìn)程間通信Linux環(huán)境進(jìn)程間通信/NUMPAGES182Linux環(huán)境進(jìn)程間通信Linux環(huán)境進(jìn)程間通信 目錄Linux環(huán)境進(jìn)程間通信(一):管道及有名管道 21、管道概述及相關(guān)API應(yīng)用 21.1管道相關(guān)的關(guān)鍵概念 21.2管道的創(chuàng)建 31.3管道的讀寫規(guī)則 31.4管道應(yīng)用實(shí)例 71.5管道的局限性 92、有名管道概述及相關(guān)API應(yīng)用 102.1有名管道相關(guān)的關(guān)鍵概念 102.2有名管道的創(chuàng)建 102.3有名管道的打開規(guī)則 102.4有名管道的讀寫規(guī)則 102.5有名管道應(yīng)用實(shí)例 14Linux環(huán)境進(jìn)程間通信(二):信號(hào)(上) 171、信號(hào)及信號(hào)來(lái)源 172、信號(hào)的種類 18(1)可靠信號(hào)與不可靠信號(hào) 18(2)實(shí)時(shí)信號(hào)與非實(shí)時(shí)信號(hào) 193、進(jìn)程對(duì)信號(hào)的響應(yīng) 194、信號(hào)的發(fā)送 195、信號(hào)的安裝(設(shè)置信號(hào)關(guān)聯(lián)動(dòng)作) 216、信號(hào)集及信號(hào)集操作函數(shù): 247、信號(hào)阻塞與信號(hào)未決 25Linux環(huán)境進(jìn)程間通信(二):信號(hào)(下) 271、信號(hào)生命周期 272、信號(hào)編程注意事項(xiàng) 293、深入淺出:信號(hào)應(yīng)用實(shí)例 30Linux環(huán)境進(jìn)程間通信(三):消息隊(duì)列 361、消息隊(duì)列基本概念 372、操作消息隊(duì)列 383、消息隊(duì)列的限制 414、消息隊(duì)列應(yīng)用實(shí)例 41Linux環(huán)境進(jìn)程間通信(四):信號(hào)燈 471、信號(hào)燈概述 472、Linux信號(hào)燈 473、信號(hào)燈與內(nèi)核 484、操作信號(hào)燈 485、信號(hào)燈的限制 516、競(jìng)爭(zhēng)問題 527、信號(hào)燈應(yīng)用實(shí)例 52Linux環(huán)境進(jìn)程間通信(五):共享內(nèi)存(上) 581、內(nèi)核怎樣保證各個(gè)進(jìn)程尋址到同一個(gè)共享內(nèi)存區(qū)域的內(nèi)存頁(yè)面 582、mmap()及其相關(guān)系統(tǒng)調(diào)用 593、mmap()范例 604、對(duì)mmap()返回地址的訪問 64Linux環(huán)境進(jìn)程間通信(五):共享內(nèi)存(下) 671、系統(tǒng)V共享內(nèi)存原理 672、系統(tǒng)V共享內(nèi)存API 683、系統(tǒng)V共享內(nèi)存限制 694、系統(tǒng)V共享內(nèi)存范例 69Linux環(huán)境進(jìn)程間通信(六):套接口 731、背景知識(shí) 732、重要數(shù)據(jù)結(jié)構(gòu) 74(1)表示套接口的數(shù)據(jù)結(jié)構(gòu)structsocket 74(2)描述套接口通用地址的數(shù)據(jù)結(jié)構(gòu)structsockaddr 74(3)描述因特網(wǎng)地址結(jié)構(gòu)的數(shù)據(jù)結(jié)構(gòu)structsockaddr_in(這里局限于IP4) 753、套接口編程的幾個(gè)重要步驟 75(1)創(chuàng)建套接口,由系統(tǒng)調(diào)用socket實(shí)現(xiàn) 75(2)綁定地址 76(3)請(qǐng)求建立連接(由TCP客戶發(fā)起) 76(4)接受連接請(qǐng)求(由TCP服務(wù)器端發(fā)起) 76(5)通信 77(6)通信的最后一步是關(guān)閉套接口 784、典型調(diào)用代碼 78(1)典型的TCP服務(wù)器代碼 78(2)典型的TCP客戶代碼 795、網(wǎng)絡(luò)編程中的其他重要概念 80Linux環(huán)境進(jìn)程間通信(一):管道及有名管道1"\o""鄭彥興
()國(guó)防科大計(jì)算機(jī)學(xué)院簡(jiǎn)介:
在本系列序中作者概述了linux進(jìn)程間通信的幾種主要手段。其中管道和有名管道是最早的進(jìn)程間通信機(jī)制之一,管道可用于具有親緣關(guān)系進(jìn)程間的通信,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無(wú)親緣關(guān)系進(jìn)程間的通信。認(rèn)清管道和有名管道的讀寫規(guī)則是在程序中應(yīng)用它們的關(guān)鍵,本文在詳細(xì)討論了管道和有名管道的通信機(jī)制的基礎(chǔ)上,用實(shí)例對(duì)其讀寫規(guī)則進(jìn)行了程序驗(yàn)證,這樣做有利于增強(qiáng)讀者對(duì)讀寫規(guī)則的感性認(rèn)識(shí),同時(shí)也提供了應(yīng)用范例。1、管道概述及相關(guān)API應(yīng)用1.1管道相關(guān)的關(guān)鍵概念管道是Linux支持的最初UnixIPC形式之一,具有以下特點(diǎn):管道是半雙工的,數(shù)據(jù)只能向一個(gè)方向流動(dòng);需要雙方通信時(shí),需要建立起兩個(gè)管道;只能用于父子進(jìn)程或者兄弟進(jìn)程之間(具有親緣關(guān)系的進(jìn)程);單獨(dú)構(gòu)成一種獨(dú)立的文件系統(tǒng):管道對(duì)于管道兩端的進(jìn)程而言,就是一個(gè)文件,但它不是普通的文件,它不屬于某種文件系統(tǒng),而是自立門戶,單獨(dú)構(gòu)成一種文件系統(tǒng),并且只存在與內(nèi)存中。數(shù)據(jù)的讀出和寫入:一個(gè)進(jìn)程向管道中寫的內(nèi)容被管道另一端的進(jìn)程讀出。寫入的內(nèi)容每次都添加在管道緩沖區(qū)的末尾,并且每次都是從緩沖區(qū)的頭部讀出數(shù)據(jù)。1.2管道的創(chuàng)建#include<unistd.h>intpipe(intfd[2])該函數(shù)創(chuàng)建的管道的兩端處于一個(gè)進(jìn)程中間,在實(shí)際應(yīng)用中沒有太大意義,因此,一個(gè)進(jìn)程在由pipe()創(chuàng)建管道后,一般再fork一個(gè)子進(jìn)程,然后通過管道實(shí)現(xiàn)父子進(jìn)程間的通信(因此也不難推出,只要兩個(gè)進(jìn)程中存在親緣關(guān)系,這里的親緣關(guān)系指的是具有共同的祖先,都可以采用管道方式來(lái)進(jìn)行通信)。1.3管道的讀寫規(guī)則管道兩端可分別用描述字fd[0]以及fd[1]來(lái)描述,需要注意的是,管道的兩端是固定了任務(wù)的。即一端只能用于讀,由描述字fd[0]表示,稱其為管道讀端;另一端則只能用于寫,由描述字fd[1]來(lái)表示,稱其為管道寫端。如果試圖從管道寫端讀取數(shù)據(jù),或者向管道讀端寫入數(shù)據(jù)都將導(dǎo)致錯(cuò)誤發(fā)生。一般文件的I/O函數(shù)都可以用于管道,如close、read、write等等。從管道中讀取數(shù)據(jù):如果管道的寫端不存在,則認(rèn)為已經(jīng)讀到了數(shù)據(jù)的末尾,讀函數(shù)返回的讀出字節(jié)數(shù)為0;當(dāng)管道的寫端存在時(shí),如果請(qǐng)求的字節(jié)數(shù)目大于PIPE_BUF,則返回管道中現(xiàn)有的數(shù)據(jù)字節(jié)數(shù),如果請(qǐng)求的字節(jié)數(shù)目不大于PIPE_BUF,則返回管道中現(xiàn)有數(shù)據(jù)字節(jié)數(shù)(此時(shí),管道中數(shù)據(jù)量小于請(qǐng)求的數(shù)據(jù)量);或者返回請(qǐng)求的字節(jié)數(shù)(此時(shí),管道中數(shù)據(jù)量不小于請(qǐng)求的數(shù)據(jù)量)。注:(PIPE_BUF在include/linux/limits.h中定義,不同的內(nèi)核版本可能會(huì)有所不同。Posix.1要求PIPE_BUF至少為512字節(jié),redhat7.2中為4096)。關(guān)于管道的讀規(guī)則驗(yàn)證:/***************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);//確保父進(jìn)程關(guān)閉寫端 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); } }/***************************************************程序輸出結(jié)果:*parentwriteover*parentclosefd[1]over*readnumis4thedatareadfromthepipeis111*附加結(jié)論:*管道寫端關(guān)閉后,寫入的數(shù)據(jù)將一直存在,直到讀出為止.****************************************************/向管道中寫入數(shù)據(jù):向管道中寫入數(shù)據(jù)時(shí),linux將不保證寫入的原子性,管道緩沖區(qū)一有空閑區(qū)域,寫進(jìn)程就會(huì)試圖向管道寫入數(shù)據(jù)。如果讀進(jìn)程不讀走管道緩沖區(qū)中的數(shù)據(jù),那么寫操作將一直阻塞。
注:只有在管道的讀端存在時(shí),向管道中寫入數(shù)據(jù)才有意義。否則,向管道中寫入數(shù)據(jù)的進(jìn)程將收到內(nèi)核傳來(lái)的SIFPIPE信號(hào),應(yīng)用程序可以處理該信號(hào),也可以忽略(默認(rèn)動(dòng)作則是應(yīng)用程序終止)。對(duì)管道的寫規(guī)則的驗(yàn)證1:寫端對(duì)讀端存在的依賴性#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);//等待子進(jìn)程完成關(guān)閉讀端的操作 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]); } }則輸出結(jié)果為:Brokenpipe,原因就是該管道以及它的所有fork()產(chǎn)物的讀端都已經(jīng)被關(guān)閉。如果在父進(jìn)程中保留讀端,即在寫完pipe后,再關(guān)閉父進(jìn)程的讀端,也會(huì)正常寫入pipe,讀者可自己驗(yàn)證一下該結(jié)論。因此,在向管道寫入數(shù)據(jù)時(shí),至少應(yīng)該存在某一個(gè)進(jìn)程,其中管道讀端沒有被關(guān)閉,否則就會(huì)出現(xiàn)上述錯(cuò)誤(管道斷裂,進(jìn)程收到了SIGPIPE信號(hào),默認(rèn)動(dòng)作是進(jìn)程終止)對(duì)管道的寫規(guī)則的驗(yàn)證2:linux不保證寫管道的原子性驗(yàn)證#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]); } }輸出結(jié)果:thebyteswritetopipe1000thebyteswritetopipe1000//注意,此行輸出說明了寫入的非原子性thebyteswritetopipe1000thebyteswritetopipe1000thebyteswritetopipe1000thebyteswritetopipe120//注意,此行輸出說明了寫入的非原子性thebyteswritetopipe0thebyteswritetopipe0結(jié)論:寫入數(shù)目小于4096時(shí)寫入是非原子的!
如果把父進(jìn)程中的兩次寫入字節(jié)數(shù)都改為5000,則很容易得出下面結(jié)論:
寫入管道的數(shù)據(jù)量大于4096字節(jié)時(shí),緩沖區(qū)的空閑空間將被寫入數(shù)據(jù)(補(bǔ)齊),直到寫完所有數(shù)據(jù)為止,如果沒有進(jìn)程讀數(shù)據(jù),則一直阻塞。1.4管道應(yīng)用實(shí)例實(shí)例一:用于shell管道可用于輸入輸出重定向,它將一個(gè)命令的輸出直接定向到另一個(gè)命令的輸入。比如,當(dāng)在某個(gè)shell程序(Bourneshell或Cshell等)鍵入who│wc-l后,相應(yīng)shell程序?qū)?chuàng)建who以及wc兩個(gè)進(jìn)程和這兩個(gè)進(jìn)程間的管道??紤]下面的命令行:$kill-l運(yùn)行結(jié)果見
works/cn/linux/l-ipc/part1/"附一。$kill-l|grepSIGRTMIN運(yùn)行結(jié)果如下: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實(shí)例二:用于具有親緣關(guān)系的進(jìn)程間通信下面例子給出了管道的具體應(yīng)用,父進(jìn)程通過管道發(fā)送一些命令給子進(jìn)程,子進(jìn)程解析命令,并根據(jù)命令作相應(yīng)處理。#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) //子進(jìn)程:解析從管道中獲取的命令,并作相應(yīng)的處理 { 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]); } }//下面是子進(jìn)程的命令處理函數(shù)(特定于應(yīng)用):inthandle_cmd(intcmd){if((cmd<0)||(cmd>256))//supposechildonlysupport256commands { printf("child:invalidcommand\n"); return-1; }printf("child:thecmdfromparentis%d\n",cmd);return0;}1.5管道的局限性管道的主要局限性正體現(xiàn)在它的特點(diǎn)上:只支持單向數(shù)據(jù)流;只能用于具有親緣關(guān)系的進(jìn)程之間;沒有名字;管道的緩沖區(qū)是有限的(管道制存在于內(nèi)存中,在管道創(chuàng)建時(shí),為緩沖區(qū)分配一個(gè)頁(yè)面大?。还艿浪鶄魉偷氖菬o(wú)格式字節(jié)流,這就要求管道的讀出方和寫入方必須事先約定好數(shù)據(jù)的格式,比如多少字節(jié)算作一個(gè)消息(或命令、或記錄)等等;/part1/"回頁(yè)首2、有名管道概述及相關(guān)API應(yīng)用2.1有名管道相關(guān)的關(guān)鍵概念管道應(yīng)用的一個(gè)重大限制是它沒有名字,因此,只能用于具有親緣關(guān)系的進(jìn)程間通信,在有名管道(namedpipe或FIFO)提出后,該限制得到了克服。FIFO不同于管道之處在于它提供一個(gè)路徑名與之關(guān)聯(lián),以FIFO的文件形式存在于文件系統(tǒng)中。這樣,即使與FIFO的創(chuàng)建進(jìn)程不存在親緣關(guān)系的進(jìn)程,只要可以訪問該路徑,就能夠彼此通過FIFO相互通信(能夠訪問該路徑的進(jìn)程以及FIFO的創(chuàng)建進(jìn)程之間),因此,通過FIFO不相關(guān)的進(jìn)程也能交換數(shù)據(jù)。值得注意的是,F(xiàn)IFO嚴(yán)格遵循先進(jìn)先出(firstinfirstout),對(duì)管道及FIFO的讀總是從開始處返回?cái)?shù)據(jù),對(duì)它們的寫則把數(shù)據(jù)添加到末尾。它們不支持諸如lseek()等文件定位操作。2.2有名管道的創(chuàng)建#include<sys/types.h>#include<sys/stat.h>intmkfifo(constchar*pathname,mode_tmode)該函數(shù)的第一個(gè)參數(shù)是一個(gè)普通的路徑名,也就是創(chuàng)建后FIFO的名字。第二個(gè)參數(shù)與打開普通文件的open()函數(shù)中的mode參數(shù)相同。如果mkfifo的第一個(gè)參數(shù)是一個(gè)已經(jīng)存在的路徑名時(shí),會(huì)返回EEXIST錯(cuò)誤,所以一般典型的調(diào)用代碼首先會(huì)檢查是否返回該錯(cuò)誤,如果確實(shí)返回該錯(cuò)誤,那么只要調(diào)用打開FIFO的函數(shù)就可以了。一般文件的I/O函數(shù)都可以用于FIFO,如close、read、write等等。2.3有名管道的打開規(guī)則有名管道比管道多了一個(gè)打開操作:open。FIFO的打開規(guī)則:如果當(dāng)前打開操作是為讀而打開FIFO時(shí),若已經(jīng)有相應(yīng)進(jìn)程為寫而打開該FIFO,則當(dāng)前打開操作將成功返回;否則,可能阻塞直到有相應(yīng)進(jìn)程為寫而打開該FIFO(當(dāng)前打開操作設(shè)置了阻塞標(biāo)志);或者,成功返回(當(dāng)前打開操作沒有設(shè)置阻塞標(biāo)志)。如果當(dāng)前打開操作是為寫而打開FIFO時(shí),如果已經(jīng)有相應(yīng)進(jìn)程為讀而打開該FIFO,則當(dāng)前打開操作將成功返回;否則,可能阻塞直到有相應(yīng)進(jìn)程為讀而打開該FIFO(當(dāng)前打開操作設(shè)置了阻塞標(biāo)志);或者,返回ENXIO錯(cuò)誤(當(dāng)前打開操作沒有設(shè)置阻塞標(biāo)志)。對(duì)打開規(guī)則的驗(yàn)證參見
orks/cn/linux/l-ipc/part1/"附2。2.4有名管道的讀寫規(guī)則從FIFO中讀取數(shù)據(jù):約定:如果一個(gè)進(jìn)程為了從FIFO中讀取數(shù)據(jù)而阻塞打開FIFO,那么稱該進(jìn)程內(nèi)的讀操作為設(shè)置了阻塞標(biāo)志的讀操作。如果有進(jìn)程寫打開FIFO,且當(dāng)前FIFO內(nèi)沒有數(shù)據(jù),則對(duì)于設(shè)置了阻塞標(biāo)志的讀操作來(lái)說,將一直阻塞。對(duì)于沒有設(shè)置阻塞標(biāo)志讀操作來(lái)說則返回-1,當(dāng)前errno值為EAGAIN,提醒以后再試。對(duì)于設(shè)置了阻塞標(biāo)志的讀操作說,造成阻塞的原因有兩種:當(dāng)前FIFO內(nèi)有數(shù)據(jù),但有其它進(jìn)程在讀這些數(shù)據(jù);另外就是FIFO內(nèi)沒有數(shù)據(jù)。解阻塞的原因則是FIFO中有新的數(shù)據(jù)寫入,不論信寫入數(shù)據(jù)量的大小,也不論讀操作請(qǐng)求多少數(shù)據(jù)量。讀打開的阻塞標(biāo)志只對(duì)本進(jìn)程第一個(gè)讀操作施加作用,如果本進(jìn)程內(nèi)有多個(gè)讀操作序列,則在第一個(gè)讀操作被喚醒并完成讀操作后,其它將要執(zhí)行的讀操作將不再阻塞,即使在執(zhí)行讀操作時(shí),F(xiàn)IFO中沒有數(shù)據(jù)也一樣(此時(shí),讀操作返回0)。如果沒有進(jìn)程寫打開FIFO,則設(shè)置了阻塞標(biāo)志的讀操作會(huì)阻塞。注:如果FIFO中有數(shù)據(jù),則設(shè)置了阻塞標(biāo)志的讀操作不會(huì)因?yàn)镕IFO中的字節(jié)數(shù)小于請(qǐng)求讀的字節(jié)數(shù)而阻塞,此時(shí),讀操作會(huì)返回FIFO中現(xiàn)有的數(shù)據(jù)量。向FIFO中寫入數(shù)據(jù):約定:如果一個(gè)進(jìn)程為了向FIFO中寫入數(shù)據(jù)而阻塞打開FIFO,那么稱該進(jìn)程內(nèi)的寫操作為設(shè)置了阻塞標(biāo)志的寫操作。對(duì)于設(shè)置了阻塞標(biāo)志的寫操作:當(dāng)要寫入的數(shù)據(jù)量不大于PIPE_BUF時(shí),linux將保證寫入的原子性。如果此時(shí)管道空閑緩沖區(qū)不足以容納要寫入的字節(jié)數(shù),則進(jìn)入睡眠,直到當(dāng)緩沖區(qū)中能夠容納要寫入的字節(jié)數(shù)時(shí),才開始進(jìn)行一次性寫操作。當(dāng)要寫入的數(shù)據(jù)量大于PIPE_BUF時(shí),linux將不再保證寫入的原子性。FIFO緩沖區(qū)一有空閑區(qū)域,寫進(jìn)程就會(huì)試圖向管道寫入數(shù)據(jù),寫操作在寫完所有請(qǐng)求寫的數(shù)據(jù)后返回。對(duì)于沒有設(shè)置阻塞標(biāo)志的寫操作:當(dāng)要寫入的數(shù)據(jù)量大于PIPE_BUF時(shí),linux將不再保證寫入的原子性。在寫滿所有FIFO空閑緩沖區(qū)后,寫操作返回。當(dāng)要寫入的數(shù)據(jù)量不大于PIPE_BUF時(shí),linux將保證寫入的原子性。如果當(dāng)前FIFO空閑緩沖區(qū)能夠容納請(qǐng)求寫入的字節(jié)數(shù),寫完后成功返回;如果當(dāng)前FIFO空閑緩沖區(qū)不能夠容納請(qǐng)求寫入的字節(jié)數(shù),則返回EAGAIN錯(cuò)誤,提醒以后再寫;對(duì)FIFO讀寫規(guī)則的驗(yàn)證:下面提供了兩個(gè)對(duì)FIFO的讀寫程序,適當(dāng)調(diào)節(jié)程序中的很少地方或者程序的命令行參數(shù)就可以對(duì)各種FIFO讀寫規(guī)則進(jìn)行驗(yàn)證。
程序1:寫FIFO的程序#include<sys/types.h>#include<sys/stat.h>#include<errno.h>#include<fcntl.h>#defineFIFO_SERVER"/tmp/fifoserver"main(intargc,char**argv)//參數(shù)為即將寫入的字節(jié)數(shù){ 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); //設(shè)置非阻塞標(biāo)志 //fd=open(FIFO_SERVER,O_WRONLY,0); //設(shè)置阻塞標(biāo)志 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用于測(cè)試寫入字節(jié)大于4096時(shí)的非原子性 //real_wnum=write(fd,w_buf,4096); //4096用于測(cè)試寫入字節(jié)不大于4096時(shí)的原子性 if(real_wnum==-1) if(errno==EAGAIN) printf("trylater\n");}
程序2:與程序1一起測(cè)試寫FIFO的規(guī)則,第一個(gè)命令行參數(shù)是請(qǐng)求從FIFO讀出的字節(jié)數(shù)#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); //在此處可以把讀程序編譯成兩個(gè)不同版本:阻塞版本及非阻塞版本 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);}程序應(yīng)用說明:把讀程序編譯成兩個(gè)不同版本:阻塞讀版本:br以及非阻塞讀版本nbr把寫程序編譯成兩個(gè)四個(gè)版本:非阻塞且請(qǐng)求寫的字節(jié)數(shù)大于PIPE_BUF版本:nbwg非阻塞且請(qǐng)求寫的字節(jié)數(shù)不大于PIPE_BUF版本:版本nbw阻塞且請(qǐng)求寫的字節(jié)數(shù)大于PIPE_BUF版本:bwg阻塞且請(qǐng)求寫的字節(jié)數(shù)不大于PIPE_BUF版本:版本bw下面將使用br、nbr、w代替相應(yīng)程序中的阻塞讀、非阻塞讀驗(yàn)證阻塞寫操作:當(dāng)請(qǐng)求寫入的數(shù)據(jù)量大于PIPE_BUF時(shí)的非原子性:nbr1000bwg當(dāng)請(qǐng)求寫入的數(shù)據(jù)量不大于PIPE_BUF時(shí)的原子性:nbr1000bw驗(yàn)證非阻塞寫操作:當(dāng)請(qǐng)求寫入的數(shù)據(jù)量大于PIPE_BUF時(shí)的非原子性:nbr1000nbwg請(qǐng)求寫入的數(shù)據(jù)量不大于PIPE_BUF時(shí)的原子性:nbr1000nbw不管寫打開的阻塞標(biāo)志是否設(shè)置,在請(qǐng)求寫入的字節(jié)數(shù)大于4096時(shí),都不保證寫入的原子性。但二者有本質(zhì)區(qū)別:對(duì)于阻塞寫來(lái)說,寫操作在寫滿FIFO的空閑區(qū)域后,會(huì)一直等待,直到寫完所有數(shù)據(jù)為止,請(qǐng)求寫入的數(shù)據(jù)最終都會(huì)寫入FIFO;而非阻塞寫則在寫滿FIFO的空閑區(qū)域后,就返回(實(shí)際寫入的字節(jié)數(shù)),所以有些數(shù)據(jù)最終不能夠?qū)懭?。?duì)于讀操作的驗(yàn)證則比較簡(jiǎn)單,不再討論。2.5有名管道應(yīng)用實(shí)例在驗(yàn)證了相應(yīng)的讀寫規(guī)則后,應(yīng)用實(shí)例似乎就沒有必要了。"回頁(yè)首小結(jié):管道常用于兩個(gè)方面:(1)在shell中時(shí)常會(huì)用到管道(作為輸入輸入的重定向),在這種應(yīng)用方式下,管道的創(chuàng)建對(duì)于用戶來(lái)說是透明的;(2)用于具有親緣關(guān)系的進(jìn)程間通信,用戶自己創(chuàng)建管道,并完成讀寫操作。FIFO可以說是管道的推廣,克服了管道無(wú)名字的限制,使得無(wú)親緣關(guān)系的進(jìn)程同樣可以采用先進(jìn)先出的通信機(jī)制進(jìn)行通信。管道和FIFO的數(shù)據(jù)是字節(jié)流,應(yīng)用程序之間必須事先確定特定的傳輸"協(xié)議",采用傳播具有特定意義的消息。要靈活應(yīng)用管道及FIFO,理解它們的讀寫規(guī)則是關(guān)鍵。附1:kill-l的運(yùn)行結(jié)果,顯示了當(dāng)前系統(tǒng)支持的所有信號(hào):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 除了在此處用來(lái)說明管道應(yīng)用外,接下來(lái)的專題還要對(duì)這些信號(hào)分類討論。附2:對(duì)FIFO打開規(guī)則的驗(yàn)證(主要驗(yàn)證寫打開對(duì)讀打開的依賴性)#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ǎng)絡(luò)編程第二卷:進(jìn)程間通信,作者:W.RichardStevens,譯者:楊繼張,清華大學(xué)出版社。豐富的UNIX進(jìn)程間通信實(shí)例及分析,對(duì)Linux環(huán)境下的程序開發(fā)有極大的啟發(fā)意義。linux內(nèi)核源代碼情景分析(上、下),毛德操、胡希明著,浙江大學(xué)出版社,當(dāng)要驗(yàn)證某個(gè)結(jié)論、想法時(shí),最好的參考資料;UNIX環(huán)境高級(jí)編程,作者:W.RichardStevens,譯者:尤晉元等,機(jī)械工業(yè)出版社。具有豐富的編程實(shí)例,以及關(guān)鍵函數(shù)伴隨Unix的發(fā)展歷程。mailto:mlinux@163.com?subject=%E4%BF%A1%E5%8F%B7%EF%BC%88%E4%B8%8A%EF%BC%89")國(guó)防科大簡(jiǎn)介:
linux信號(hào)機(jī)制遠(yuǎn)遠(yuǎn)比想象的復(fù)雜,本文力爭(zhēng)用最短的篇幅,對(duì)該機(jī)制做了深入細(xì)致的分析。讀者可以先讀一下信號(hào)應(yīng)用實(shí)例(在信號(hào)(下)中),這樣可以對(duì)信號(hào)發(fā)送直到相應(yīng)的處理函數(shù)執(zhí)行完畢這一過程有個(gè)大致的印象。本文盡量給出了較新函數(shù)的應(yīng)用實(shí)例,著重說明這些的功能。1、信號(hào)及信號(hào)來(lái)源信號(hào)本質(zhì)信號(hào)是在軟件層次上對(duì)中斷機(jī)制的一種模擬,在原理上,一個(gè)進(jìn)程收到一個(gè)信號(hào)與處理器收到一個(gè)中斷請(qǐng)求可以說是一樣的。信號(hào)是異步的,一個(gè)進(jìn)程不必通過任何操作來(lái)等待信號(hào)的到達(dá),事實(shí)上,進(jìn)程也不知道信號(hào)到底什么時(shí)候到達(dá)。信號(hào)是進(jìn)程間通信機(jī)制中唯一的異步通信機(jī)制,可以看作是異步通知,通知接收信號(hào)的進(jìn)程有哪些事情發(fā)生了。信號(hào)機(jī)制經(jīng)過POSIX實(shí)時(shí)擴(kuò)展后,功能更加強(qiáng)大,除了基本通知功能外,還可以傳遞附加信息。信號(hào)來(lái)源信號(hào)事件的發(fā)生有兩個(gè)來(lái)源:硬件來(lái)源(比如我們按下了鍵盤或者其它硬件故障);軟件來(lái)源,最常用發(fā)送信號(hào)的系統(tǒng)函數(shù)是kill,raise,alarm和setitimer以及sigqueue函數(shù),軟件來(lái)源還包括一些非法運(yùn)算等操作。on"回頁(yè)首2、信號(hào)的種類可以從兩個(gè)不同的分類角度對(duì)信號(hào)進(jìn)行分類:(1)可靠性方面:可靠信號(hào)與不可靠信號(hào);(2)與時(shí)間的關(guān)系上:實(shí)時(shí)信號(hào)與非實(shí)時(shí)信號(hào)。在《Linux環(huán)境進(jìn)程間通信(一):管道及有名管道》的附1中列出了系統(tǒng)所支持的所有信號(hào)。(1)可靠信號(hào)與不可靠信號(hào)"不可靠信號(hào)"Linux信號(hào)機(jī)制基本上是從Unix系統(tǒng)中繼承過來(lái)的。早期Unix系統(tǒng)中的信號(hào)機(jī)制比較簡(jiǎn)單和原始,后來(lái)在實(shí)踐中暴露出一些問題,因此,把那些建立在早期機(jī)制上的信號(hào)叫做"不可靠信號(hào)",信號(hào)值小于SIGRTMIN(Redhat7.2中,SIGRTMIN=32,SIGRTMAX=63)的信號(hào)都是不可靠信號(hào)。這就是"不可靠信號(hào)"的來(lái)源。它的主要問題是:進(jìn)程每次處理信號(hào)后,就將對(duì)信號(hào)的響應(yīng)設(shè)置為默認(rèn)動(dòng)作。在某些情況下,將導(dǎo)致對(duì)信號(hào)的錯(cuò)誤處理;因此,用戶如果不希望這樣的操作,那么就要在信號(hào)處理函數(shù)結(jié)尾再一次調(diào)用signal(),重新安裝該信號(hào)。信號(hào)可能丟失,后面將對(duì)此詳細(xì)闡述。
因此,早期unix下的不可靠信號(hào)主要指的是進(jìn)程可能對(duì)信號(hào)做出錯(cuò)誤的反應(yīng)以及信號(hào)可能丟失。Linux支持不可靠信號(hào),但是對(duì)不可靠信號(hào)機(jī)制做了改進(jìn):在調(diào)用完信號(hào)處理函數(shù)后,不必重新調(diào)用該信號(hào)的安裝函數(shù)(信號(hào)安裝函數(shù)是在可靠機(jī)制上的實(shí)現(xiàn))。因此,Linux下的不可靠信號(hào)問題主要指的是信號(hào)可能丟失。"可靠信號(hào)"隨著時(shí)間的發(fā)展,實(shí)踐證明了有必要對(duì)信號(hào)的原始機(jī)制加以改進(jìn)和擴(kuò)充。所以,后來(lái)出現(xiàn)的各種Unix版本分別在這方面進(jìn)行了研究,力圖實(shí)現(xiàn)"可靠信號(hào)"。由于原來(lái)定義的信號(hào)已有許多應(yīng)用,不好再做改動(dòng),最終只好又新增加了一些信號(hào),并在一開始就把它們定義為可靠信號(hào),這些信號(hào)支持排隊(duì),不會(huì)丟失。同時(shí),信號(hào)的發(fā)送和安裝也出現(xiàn)了新版本:信號(hào)發(fā)送函數(shù)sigqueue()及信號(hào)安裝函數(shù)sigaction()。POSIX.4對(duì)可靠信號(hào)機(jī)制做了標(biāo)準(zhǔn)化。但是,POSIX只對(duì)可靠信號(hào)機(jī)制應(yīng)具有的功能以及信號(hào)機(jī)制的對(duì)外接口做了標(biāo)準(zhǔn)化,對(duì)信號(hào)機(jī)制的實(shí)現(xiàn)沒有作具體的規(guī)定。信號(hào)值位于SIGRTMIN和SIGRTMAX之間的信號(hào)都是可靠信號(hào),可靠信號(hào)克服了信號(hào)可能丟失的問題。Linux在支持新版本的信號(hào)安裝函數(shù)sigation()以及信號(hào)發(fā)送函數(shù)sigqueue()的同時(shí),仍然支持早期的signal()信號(hào)安裝函數(shù),支持信號(hào)發(fā)送函數(shù)kill()。注:不要有這樣的誤解:由sigqueue()發(fā)送、sigaction安裝的信號(hào)就是可靠的。事實(shí)上,可靠信號(hào)是指后來(lái)添加的新信號(hào)(信號(hào)值位于SIGRTMIN及SIGRTMAX之間);不可靠信號(hào)是信號(hào)值小于SIGRTMIN的信號(hào)。信號(hào)的可靠與不可靠只與信號(hào)值有關(guān),與信號(hào)的發(fā)送及安裝函數(shù)無(wú)關(guān)。目前l(fā)inux中的signal()是通過sigation()函數(shù)實(shí)現(xiàn)的,因此,即使通過signal()安裝的信號(hào),在信號(hào)處理函數(shù)的結(jié)尾也不必再調(diào)用一次信號(hào)安裝函數(shù)。同時(shí),由signal()安裝的實(shí)時(shí)信號(hào)支持排隊(duì),同樣不會(huì)丟失。對(duì)于目前l(fā)inux的兩個(gè)信號(hào)安裝函數(shù):signal()及sigaction()來(lái)說,它們都不能把SIGRTMIN以前的信號(hào)變成可靠信號(hào)(都不支持排隊(duì),仍有可能丟失,仍然是不可靠信號(hào)),而且對(duì)SIGRTMIN以后的信號(hào)都支持排隊(duì)。這兩個(gè)函數(shù)的最大區(qū)別在于,經(jīng)過sigaction安裝的信號(hào)都能傳遞信息給信號(hào)處理函數(shù)(對(duì)所有信號(hào)這一點(diǎn)都成立),而經(jīng)過signal安裝的信號(hào)卻不能向信號(hào)處理函數(shù)傳遞信息。對(duì)于信號(hào)發(fā)送函數(shù)來(lái)說也是一樣的。(2)實(shí)時(shí)信號(hào)與非實(shí)時(shí)信號(hào)早期Unix系統(tǒng)只定義了32種信號(hào),Rethat7.2支持64種信號(hào),編號(hào)0-63(SIGRTMIN=31,SIGRTMAX=63),將來(lái)可能進(jìn)一步增加,這需要得到內(nèi)核的支持。前32種信號(hào)已經(jīng)有了預(yù)定義值,每個(gè)信號(hào)有了確定的用途及含義,并且每種信號(hào)都有各自的缺省動(dòng)作。如按鍵盤的CTRL^C時(shí),會(huì)產(chǎn)生SIGINT信號(hào),對(duì)該信號(hào)的默認(rèn)反應(yīng)就是進(jìn)程終止。后32個(gè)信號(hào)表示實(shí)時(shí)信號(hào),等同于前面闡述的可靠信號(hào)。這保證了發(fā)送的多個(gè)實(shí)時(shí)信號(hào)都被接收。實(shí)時(shí)信號(hào)是POSIX標(biāo)準(zhǔn)的一部分,可用于應(yīng)用進(jìn)程。非實(shí)時(shí)信號(hào)都不支持排隊(duì),都是不可靠信號(hào);實(shí)時(shí)信號(hào)都支持排隊(duì),都是可靠信號(hào)?;仨?yè)首6、信號(hào)集及信號(hào)集操作函數(shù):信號(hào)集被定義為一種數(shù)據(jù)類型: typedefstruct{ unsignedlongsig[_NSIG_WORDS]; }sigset_t信號(hào)集用來(lái)描述信號(hào)的集合,linux所支持的所有信號(hào)可以全部或部分的出現(xiàn)在信號(hào)集中,主要與信號(hào)阻塞相關(guān)函數(shù)配合使用。下面是為信號(hào)集操作定義的相關(guān)函數(shù): #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指定的信號(hào)集,信號(hào)集里面的所有信號(hào)被清空;sigfillset(sigset_t*set)調(diào)用該函數(shù)后,set指向的信號(hào)集中將包含linux支持的64種信號(hào);sigaddset(sigset_t*set,intsignum)在set指向的信號(hào)集中加入signum信號(hào);sigdelset(sigset_t*set,intsignum)在se
溫馨提示
- 1. 本站所有資源如無(wú)特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 旅游行業(yè)行程變動(dòng)及責(zé)任豁免協(xié)議書
- 電子支付平臺(tái)開發(fā)與推廣合作協(xié)議
- 營(yíng)業(yè)辦公用房買賣協(xié)議書
- 中學(xué)生感恩教育故事觀后感
- 高考語(yǔ)文高頻文言實(shí)詞60詞表解
- 環(huán)保能源行業(yè)項(xiàng)目合作風(fēng)險(xiǎn)提示
- 高考語(yǔ)文備考之明朝作家文言文匯編(下)
- 購(gòu)銷家具合同家具購(gòu)銷合同
- 綠色農(nóng)業(yè)種植合同
- 裝修工程勞務(wù)外包合同
- 傳染病手術(shù)的處理流程
- 新質(zhì)生產(chǎn)力:中國(guó)創(chuàng)新發(fā)展的著力點(diǎn)與內(nèi)在邏輯
- 《中醫(yī)常用護(hù)理技術(shù)基礎(chǔ)》課件-八綱辨證施護(hù)
- 心理健康與職業(yè)生涯(中等職業(yè))全套教學(xué)課件
- 市政園林安全生產(chǎn)培訓(xùn)課件
- 基于BIM的軸流通風(fēng)機(jī)施工工藝優(yōu)化
- 2024年大學(xué)生自我意識(shí)教學(xué)案
- 女生青春期知識(shí)講座(六年級(jí))課件
- 在醫(yī)院新員工入職儀式上的講話
- 消化道出血講課課件
- 化工過程安全管理導(dǎo)則
評(píng)論
0/150
提交評(píng)論