第8章 進程間通信_第1頁
第8章 進程間通信_第2頁
第8章 進程間通信_第3頁
第8章 進程間通信_第4頁
第8章 進程間通信_第5頁
已閱讀5頁,還剩66頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

第8章進程間通信8.1項目目標(biāo)8.2進程間通信概述8.3管道通信8.4信號通信8.5信號量8.6共享內(nèi)存8.7消息隊列8.8項目應(yīng)用8.1項目目標(biāo)通過本章節(jié)的學(xué)習(xí),掌握進程間通信的常用方法。設(shè)計實現(xiàn)數(shù)據(jù)采集顯示通信系統(tǒng)的中數(shù)據(jù)采集進程與數(shù)據(jù)上傳進程之間的數(shù)據(jù)通信。本章知識點:在Linux系統(tǒng)中,如果同時存在多個進程,則需要這些異步進程之間的通信問題,包括數(shù)據(jù)和協(xié)同工作等。Linux提供了管道、信號、消息隊列、共享內(nèi)存、信號量、套接字等通信機制。本章進行詳細(xì)學(xué)習(xí)。8.2進程間通信概述為什么進程間需要通信?數(shù)據(jù)傳輸一個進程需要將它的數(shù)據(jù)發(fā)送給另一個進程資源共享多個進程之間共享同樣的資源通知事件一個進程需要向另一個或一組進程發(fā)送消息,通知它們發(fā)生了某種事件進程控制有些進程希望完全控制另一個進程的執(zhí)行(如Debug進程),此時控制進程希望能夠攔截另一個進程的所有操作,并能夠及時知道它的狀態(tài)改變。8.2進程間通信概述Linux下的進程通信手段基本上是從UNIX平臺上的進程通信手段繼承而來的。而對UNIX發(fā)展做出重大貢獻(xiàn)的兩大主力AT&T的貝爾實驗室及BSD(加州大學(xué)伯克利分校的伯克利軟件發(fā)布中心)在進程間的通信方面的側(cè)重點有所不同。前者是對UNIX早期的進程間通信手段進行了系統(tǒng)的改進和擴充,形成了“systemVIPC”,其通信進程主要局限在單個計算機內(nèi);后者則跳過了該限制,形成了基于套接口(socket)的進程間通信機制。而Linux則把兩者的優(yōu)勢都繼承了下來。8.2進程間通信概述UNIX進程間通信(IPC)方式包括管道、FIFO以及信號。SystemV進程間通信(IPC)包括SystemV消息隊列、SystemV信號量以及SystemV共享內(nèi)存區(qū)。Posix進程間通信(IPC)包括Posix消息隊列、Posix信號量以及Posix共享內(nèi)存區(qū)。8.2進程間通信概述現(xiàn)在Linux使用的進程間通信方式主要包括:管道(pipe)和有名管道(FIFO)信號(Signal)消息隊列共享內(nèi)存信號量套接字(socket)8.3管道通信管道是單向的、先進先出的,它把一個進程的輸出和另一個進程的輸入連接在一起。一個進程(寫進程)在管道的尾部寫入數(shù)據(jù),另一個進程(讀進程)從管道的頭部讀出數(shù)據(jù)。數(shù)據(jù)被一個進程讀出后,將從管道中刪除。其它讀進程將不能再讀到這些數(shù)據(jù)。管道提供了簡單的流控制機制,進程試圖讀空管道時,進程將阻塞。同樣,管道已經(jīng)滿時,進程再試圖向管道寫入數(shù)據(jù),進程將阻塞。管道包括無名管道和命名管道,前者用于父進程和子進程間的通信,后者可用于運行于同一系統(tǒng)中任意兩個進程間的通信。8.3.1無名管道fd[0]用于讀取管道;fd[1]用于寫入管道。創(chuàng)建無名管道由pipe()函數(shù)創(chuàng)建:intpipe(intfiledis[2]);

當(dāng)一個管道建立時,它會創(chuàng)建兩個文件描述符:filedis[0]用于讀管道,filedis[1]用于寫管道。8.3.1無名管道管道關(guān)閉關(guān)閉管道只需要將這兩個文件描述符關(guān)閉即可??梢允褂闷胀ǖ腸lose函數(shù)逐個關(guān)閉。無名管道使用特點:它只能用于具有親緣關(guān)系的進程間通信(如父子進程)它是半雙工的通信模式,具有固定的讀端和寫端管道可以看做是一種特殊的文件,對于它的讀寫可以使用普通的read和write等函數(shù)。但它不是普通的文件,只存在于內(nèi)核的內(nèi)存空間中。8.3.1無名管道#include<unistd.h>#include<errno.h>#include<stdio.h>#include<stdlib.h>intmain(){ intpipe_fd[2]; if(pipe(pipe_fd)<0) { printf("pipecreateerror\n"); return-1; } else printf("pipecreatesuccess\n"); close(pipe_fd[0]); close(pipe_fd[1]);}8.3.1無名管道管道用于不同進程間通信,通常先創(chuàng)建一個管道,再通過fork函數(shù)創(chuàng)建一個子進程,該子進程會繼承父進程所創(chuàng)建的管道。8.3.1無名管道子進程寫入和父進程讀的命名管道:8.3.1無名管道管道讀寫注意事項只有在管道的讀端存在時,向管道寫入數(shù)據(jù)才有意義,否則,向管道寫入數(shù)據(jù)的進程將收到內(nèi)核傳來的SIGPIPE信號。向管道寫入數(shù)據(jù)時,管道緩沖區(qū)一有空閑區(qū)域,寫進程就會試圖向管道寫入數(shù)據(jù),如果讀進程不讀取管道緩沖區(qū)中的數(shù)據(jù),寫操作將會阻塞。父子進程在運行時,它們的先后順序不能保證,為了保證父子進程關(guān)閉了相應(yīng)的文件描述符,可以使用sleep()解決。8.3.1無名管道管道讀寫注意事項可以通過打開兩個管道來創(chuàng)建一個雙向的管道。但需要在子進程中正確地設(shè)置文件描述符。必須在系統(tǒng)調(diào)用fork()前調(diào)用pipe(),否則子進程將不會繼承文件描述符。當(dāng)使用半雙工管道時,任何關(guān)聯(lián)的進程都必須共享一個相關(guān)的祖先進程。因為管道存在于系統(tǒng)內(nèi)核之中,所以任何不在創(chuàng)建管道的進程的祖先進程之中的進程都將無法尋址它。而在命名管道中卻不是這樣。程序分析:pipe_rw.c

作業(yè):仿照半雙工管道程序?qū)懸粋€全雙工管道程序命名管道和無名管道基本相同,但也有不同點:無名管道只能有父子進程使用;但是通過命名管道,不相關(guān)的進程也能交換數(shù)據(jù)。命名管道創(chuàng)建:#include<sys/types.h>#include<sys/stat.h>intmkfifo(constchar*pathname,mode_tmode)pathname:FIFO文件名mode:屬性(見文件操作章節(jié))一旦創(chuàng)建了一個FIFO,就可用open打開它,一般的文件訪問函數(shù)(close、read、write等)都可以用于FIFO。8.3.2命名管道FIFO8.3.2命名管道FIFO對于為讀而打開的管道可在open()中設(shè)置O_RDONLY,對于為寫而打開的管道可在open()中設(shè)置O_WRONLY,在這里與普通文件不同的是阻塞問題。8.3.2命名管道FIFO由于普通文件的讀寫時不會出現(xiàn)阻塞問題,而在管道的讀寫中卻有阻塞的可能,這里的非阻塞標(biāo)志可以在open()函數(shù)中設(shè)定為O_NONBLOCK。對于讀進程若該管道是阻塞打開,且當(dāng)前FIFO內(nèi)沒有數(shù)據(jù),則對讀進程而言將一直阻塞到有數(shù)據(jù)寫入。若該管道是非阻塞打開,則不論FIFO內(nèi)是否有數(shù)據(jù),讀進程都會立即執(zhí)行讀操作。即如果FIFO內(nèi)沒有數(shù)據(jù),則讀函數(shù)將立刻返回0。8.3.2命名管道FIFO對于寫進程若該管道是阻塞打開,則寫操作將一直阻塞到數(shù)據(jù)可以被寫入。若該管道是非阻塞打開而不能寫入全部數(shù)據(jù),則讀操作進行部分寫入或者調(diào)用失敗。類似于管道,若寫一個尚無進程為讀而打開的FIFO,則產(chǎn)生信號SIGPIPE。若某個FIFO的最后一個寫進程關(guān)閉了該FIFO,則將為該FIFO的讀進程產(chǎn)生一個文件結(jié)束標(biāo)志。管道刪除:命名管道的刪除可以用unlink函數(shù)實現(xiàn)。實例分析:fifo_write.c、fifo_read.c8.3.2命名管道FIFOFIFO相關(guān)出錯信息:

EACCES(無存取權(quán)限)EEXIST(指定文件不存在)ENAMETOOLONG(路徑名太長)ENOENT(包含的目錄不存在)ENOSPC(文件系統(tǒng)剩余空間不足)ENOTDIR(文件路徑無效)EROFS(指定的文件存在于只讀文件系統(tǒng)中)8.4.1信號的定義信號是軟件中斷。它可以作為進程間通信的一種機制,更重要的是,信號總是中斷一個進程的正常運行,它更多地被用于處理一些非正常情況。信號是異步的,進程并不知道信號什么時候到達(dá)。進程既可以處理信號,也可以發(fā)送信號給特定進程。每個信號都有一個名字,這些名字都以SIG開頭。例如:SIGABRT是進程異常終止信號。8.4管道通信很多條件可以產(chǎn)生一個信號:當(dāng)用戶按某些終端鍵時,產(chǎn)生信號。在終端上按Ctrl+c鍵通常產(chǎn)生中斷信號(SIGINT)。這是停止一個已失去控制程序的方法。硬件異常產(chǎn)生信號:除數(shù)為0、無效的存儲訪問等等。這些條件通常由硬件檢測到,并將其通知內(nèi)核。然后內(nèi)核為該條件發(fā)生時正在運行的進程產(chǎn)生適當(dāng)?shù)男盘枴@?,對?zhí)行一個無效存儲訪問的進程產(chǎn)生一個SIGSEGV。進程用kill(2)函數(shù)可將信號發(fā)送給另一個進程或進程組。自然,有些限制:接收信號進程和發(fā)送信號進程的所有者必須相同,或發(fā)送信號進程的所有者必須是超級用戶。8.4.2信號來源很多條件可以產(chǎn)生一個信號:用戶可用kill(1)命令將信號發(fā)送給其他進程。此程序是kill函數(shù)的接口。常用此命令終止一個失控的后臺進程。當(dāng)檢測到某種軟件條件已經(jīng)發(fā)生,并將其通知有關(guān)進程時也產(chǎn)生信號。這里并不是指硬件產(chǎn)生條件(如被0除),而是軟件條件。例如SIGURG(在網(wǎng)絡(luò)連接上傳來非規(guī)定波特率的數(shù)據(jù))、SIGPIPE(在管道的讀進程已終止后一個進程寫此管道),以及SIGALRM(進程所設(shè)置的鬧鐘時間已經(jīng)超時)。8.4.2信號來源不可靠的信號:Linux信號機制基本上是從Unix系統(tǒng)中繼承過來的。早期Unix系統(tǒng)中的信號機制比較簡單和原始,后來在實踐中暴露出一些問題,因此,把那些建立在早期機制上的信號叫做“不可靠信號”,信號值小于SIGRTMIN的叫不可靠信號(1~31)。每次信號處理后,該信號對應(yīng)的處理函數(shù)會恢復(fù)到默認(rèn)值。但現(xiàn)代的Linux已經(jīng)對其進行了改進,信號處理函數(shù)一直是用戶指定的或者是系統(tǒng)默認(rèn)的。信號可能丟失。不可靠信號不支持信號排隊,同一個信號產(chǎn)生多次,只要程序還未處理該信號,那么實際只處理此信號一次。8.4.信號來源7.3.3信號的種類可靠信號:信號值位于SIGRTMIN和SIGRTMAX之間的信號都是可靠信號,可靠信號克服了信號可能丟失的問題。實時信號與非實時信號:Linux目前定義了64種信號(將來可能會擴展),前面32種為非實時信號,后32種為實時信號。非實時信號都不支持排隊,都是不可靠信號,實時信號都支持排隊,都是可靠信號。

信號排隊意味著無論產(chǎn)生多少次信號,信號處理函數(shù)就會被調(diào)用同樣的次數(shù)。7.3.3信號的種類信號名稱信號說明默認(rèn)處理SIGABRT由程序調(diào)用abort時產(chǎn)生該信號。程序異常結(jié)束。進程終止并且產(chǎn)生core文件SIGALRMtimer到期,有alarm或者setitimer進程終止SIGBUS總線錯誤,地址沒對齊等。取決于具體硬件。結(jié)束終止并產(chǎn)生core文件SIGCHLD子進程停止或者終止時,父進程會收到該信號。忽略該信號SIGCONT讓停止的進程繼續(xù)執(zhí)行繼續(xù)執(zhí)行或者忽略SIGFPE算術(shù)運算異常,除0等。進程終止并且產(chǎn)生core文件SIGHUP終端關(guān)閉時產(chǎn)生這個信號進程終止SIGILL代碼中有非法指令進程終止并產(chǎn)生core文件SIGINT終端輸入了中斷字符ctrl+c進程終止7.3.3信號的種類SIGIO異步I/O,跟SIGPOLL一樣。進程終止SIGIOT執(zhí)行I/O時產(chǎn)生硬件錯誤進程終止并且產(chǎn)生core文件SIGKILL這個信號用戶不能去捕捉它。進程終止SIGPIPE往管道寫時,讀者已經(jīng)不在了進程終止SIGPOLL異步I/O,跟SIGIO一樣。進程終止SIGPROF有setitimer設(shè)置的timer到期引發(fā)進程終止SIGPWRUps電源切換時進程終止SIGQUITCtrl+\,不同于SIGINT,這個是會產(chǎn)生coredump文件的。進程終止并且產(chǎn)生core文件SIGSEGV內(nèi)存非法訪問,segmentfault進程終止并且產(chǎn)生core文件SIGSTOP某個進程停止執(zhí)行,該信號不能被用戶捕捉。進程暫停執(zhí)行7.3.3信號的種類SIGSYS調(diào)用操作系統(tǒng)不認(rèn)識的系統(tǒng)調(diào)用進程終止并且產(chǎn)生core文件SIGTERM有kill函數(shù)調(diào)用產(chǎn)生進程終止SIGTRAP有調(diào)試器使用,gdb進程終止并且產(chǎn)生core文件SIGTSTPCtrl+z,掛起進程進程暫停SIGTTIN后臺程序要從終端讀取成數(shù)據(jù)時進程暫停SIGTTOU后臺終端要把數(shù)據(jù)寫到終端時進程暫停SIGURG一些緊急的事件,比如從網(wǎng)絡(luò)收到帶外數(shù)據(jù)忽略SIGUSR1用戶自定義信號進程終止SIGUSR2用戶自定義信號進程終止SIGVTALRM有setitimer產(chǎn)生進程終止7.3.4信號的處理可以要求系統(tǒng)在某個信號出現(xiàn)時按照下列三種方式中的一種進行操作。(1)忽略此信號。大多數(shù)信號都可使用這種方式進行處理,但有兩種信號卻決不能被忽略。它們是:SIGKILL和SIGSTOP。這兩種信號不能被忽略的原因是:它們向超級用戶提供一種使進程終止或停止的可靠方法。另外,如果忽略某些由硬件異常產(chǎn)生的信號(例如非法存儲訪問或除以0),則進程的行為是未定義的。(2)捕捉信號。為了做到這一點要通知內(nèi)核在某種信號發(fā)生時,調(diào)用一個用戶函數(shù)。在用戶函數(shù)中,可執(zhí)行用戶希望對這種事件進行的處理。如果捕捉到SIGCHLD信號,則表示子進程已經(jīng)終止,所以此信號的捕捉函數(shù)可以調(diào)用waitpid以取得該子進程的進程ID以及它的終止?fàn)顟B(tài)。(3)執(zhí)行系統(tǒng)默認(rèn)動作。對大多數(shù)信號的系統(tǒng)默認(rèn)動作是終止該進程。7.3.4.1信號的處理—缺省每一個信號都有一個缺省動作,它是當(dāng)進程沒有給這個信號指定處理程序時,內(nèi)核對信號的處理。有5種缺省的動作:異常終止(abort):在進程的當(dāng)前目錄下,把進程的地址空間內(nèi)容、寄存器內(nèi)容保存到一個叫做core的文件中,而后終止進程。退出(exit):不產(chǎn)生core文件,直接終止進程。忽略(ignore):忽略該信號。停止(stop):掛起該進程。繼續(xù)(continue):如果進程被掛起,則恢復(fù)進程的運行。否則,忽略信號。7.3.4.2信號的處理—捕捉當(dāng)系統(tǒng)捕捉到某個信號時,可以忽略該信號或是使用指定的處理函數(shù)來處理該信號,或者使用系統(tǒng)默認(rèn)的方式。信號處理的主要方法有兩種,一種是使用簡單的signal函數(shù),另一種是使用信號集函數(shù)組。(1)Signal處理機制#include<signal.h>void(*signal(intsigno,void(*func)(int)))(int)返回:成功則返回信號以前的處理配置,若出錯則為SIG_ERRtypedefvoid(*sighandler_t)(int)sighandler_tsignal(intsignum,sighandler_thandler)func的值是:(a)常數(shù)SIG_IGN:向內(nèi)核表示忽略此信號(有兩個信號SIGKILL和SIGSTOP不能忽略)(b)常數(shù)SIG_DFL:接到此信號后的動作是系統(tǒng)默認(rèn)動作。(c)當(dāng)接到此信號后要調(diào)用的函數(shù)的地址:我們稱此為捕捉此信號。我們稱此函數(shù)為信號處理程序(signalhandler)或信號捕捉函數(shù)(signal-catchingfunction)。實例分析#include<signal.h>#include<stdio.h>#include<stdlib.h>voidmy_func(intsign_no){ if(sign_no==SIGINT) printf("IhavegetSIGINT\n"); elseif(sign_no==SIGQUIT) printf("IhavegetSIGQUIT\n");}intmain(){ printf("WaitingforsignalSIGINTorSIGQUIT\n"); signal(SIGINT,my_func); signal(SIGQUIT,my_func); pause(); exit(0);}(1)Signal處理機制忽略掉終端CTRL+C產(chǎn)生的信號:#include<stdio.h>#include<signal.h>#include<stdlib.h>intmain(){ signal(SIGINT,SIG_IGN); while(1) sleep(1); return0;}//程序運行后,將Ctrl+c產(chǎn)生的SIGINT信號忽略掉了,則CTRL+c將不能終止該進程(1)Signal處理機制接受信號的默認(rèn)處理,接受默認(rèn)處理相當(dāng)于沒有寫信號處理程序#include<stdio.h>#include<signal.h>#include<stdlib.h>intmain(){ signal(SIGINT,SIG_DFL); while(1) sleep(1); return0;}(1)Signal處理機制注意:Signal主要用于前32種非實時信號的處理Signal不能傳遞附加數(shù)據(jù)采用signal處理機制,一些特殊情況的考慮注冊一個信號處理函數(shù),處理完畢之后,是否需要重新注冊,才能捕捉下一個信號如果信號處理函數(shù)正在處理,并且沒有處理完畢是,又發(fā)生了一個同類型的信號,這時該怎么處理如果信號處理函數(shù)正在處理,并且沒有處理完畢是,又發(fā)生了一個不同類型的信號,這時該怎么處理……(2)sigactionLinux支持一個更健壯、更新的信號處理函數(shù)sigaction#include<signal.h>intsigaction(intsignum,conststructsigaction*act,structsigaction*oldact);第一個signum指定需要處理(捕捉)的信號(除SIGKILL和SIGSTOP)。第二個參數(shù)act(結(jié)構(gòu)體)設(shè)定信號的處理方式,act可以為NULL。之前設(shè)定的信號處理方式會保存到第三個參數(shù)oldact,oldact為NULL時不保存。返回0成功,返回-1失敗。(2)sigactionstructsigaction{ union{void(*sa_handler)(int);void(*sa_sigaction)(int,siginfo_t*,void*)}_u;sigset_t sa_mask;int sa_flags;

void(*sa_restorer)(void);//保留,不使用

}sa_handler和signal函數(shù)的第二個參數(shù)類型一樣,當(dāng)信號遞送給進程時會調(diào)用這個sa_handler.sa_sigaction也是信號處理的函數(shù)指針,它只會在sa_flags包含SA_SIGINFO時才會被調(diào)用,siginfo_t包含了信號產(chǎn)生的原因。sa_flags的值為0或其它任何值都將按默認(rèn)方式處理,即調(diào)用sa_handler捕捉函數(shù)。不用同時賦值給sa_handler和sa_sigaction,因為它們是一個union。(2)sigactionsa_mask信號屏蔽字,當(dāng)執(zhí)行sa_handler信號處理函數(shù)時,sa_mask指定的信號會被阻塞,直到該信號處理函數(shù)返回。針對sigset_t結(jié)構(gòu)體,有一組專門的函數(shù)對它進行處理:intsigemptyset(sigset_t*set)//清空信號結(jié)合setintsigfillset(sigset_t*set)//將所有信號填充進set中intsigaddset(sigset_t*set,intsignum)//添加信號intsigdelset(sigset_t*set,intsignum)//刪除信號intsigismember(constsigset_t*set,intsignum)//判斷某信號是否在set中舉例:在處理SIGINT是,阻塞SIGQUIT信號structsigactionact;sigemptyset(&act.sa_mask);sigaddset(&act.sa_mask,SIGQUIT);sigaction(SIGINT,&act,NULL);(2)sigactionsa_flags用來改變信號處理時的行為。當(dāng)sa_flags包含SA_RESTART時,被中斷的系統(tǒng)調(diào)用在信號處理完后會被自動啟動。sa_flags說明SA_NOCLDSTOP若signum是SIGCHLD,當(dāng)一子進程停止時,不通知父進程SA_NOMASK/SA_NODEFER在處理此信號結(jié)束前允許此信號再次遞送,相當(dāng)于中斷嵌套SA_RESTART由此信號中斷的系統(tǒng)調(diào)用自動重啟SA_NOCLDWAIT若signum是SIGCHLD,則當(dāng)調(diào)用進程的子進程終止時,不創(chuàng)建僵尸進程。若調(diào)用進程在后面調(diào)用wait,則阻塞到它所有子進程都終止,此時返回-1SA_NODEFER當(dāng)捕捉到此信號時,在執(zhí)行其信號捕捉函數(shù)時,系統(tǒng)不自動阻塞此信號。SA_ONESHOT/SA_RESETHAND當(dāng)調(diào)用新的信號處理函數(shù)前,將此信號處理方式改為系統(tǒng)預(yù)設(shè)(SIG_DFL)的方式SA_SIGINFO此選項對信號處理程序提供了附加信息。舉例說明intmain(){ structsigactionaction; printf("WaitingforsignalSIGINTorSIGQUIT...\n");

action.sa_handler=my_func; sigemptyset(&action.sa_mask); action.sa_flags=0;

sigaction(SIGINT,&action,0); sigaction(SIGQUIT,&action,0); pause(); exit(0);}Sigprocmask信號阻塞函數(shù)segaction中設(shè)置的被阻塞信號集合只是針對于要處理的信號,比如在處理SIGINT時才阻塞SIGQUIT信號;函數(shù)sigprocmask是全程阻塞,在sigprocmask中設(shè)置了阻塞集合后,被阻塞的信號將不能再被信號處理函數(shù)捕捉,直到重新設(shè)置阻塞信號集合。

intsigprocmask(inthow,sigset_t*set,sigset_t*oldset)how:(1):SIG_BLOCK,將參數(shù)2的信號集添加到進程原有的阻塞信號集中,(2):SIG_UNBLOCK,從進程原有的阻塞信號集中移除參數(shù)2中包含的信號,(3):SIG_SET,重新設(shè)置進程阻塞信號集為參數(shù)2的信號集set是阻塞信號集,olsset存放原有的信號集7.3.5信號發(fā)送7.3.5.1kill和raiseUnix中發(fā)送信號的主要函數(shù)有kill和raise區(qū)別:kill()不僅可以中止進程,也可以向進程發(fā)送其他信號。與kill函數(shù)不同的是,raise()函數(shù)運行向進程自身發(fā)送信號。#include<sys/types.h>#include<signal.h>intkill(pid_tpid,intsigno);intraise(intsigno);兩個函數(shù)返回:若成功則為0,若出錯則為-1。7.3.5.1kill和raisekill的pid參數(shù)有四種不同的情況:

pid>0將信號發(fā)送給進程ID為pid的進程。

pid=0將信號發(fā)送給其進程組ID等于發(fā)送進程的進程組ID,而且發(fā)送進程有許可權(quán)向其發(fā)送信號的所有進程。

pid<0將信號發(fā)送給其進程組ID等于pid絕對值,而且發(fā)送進程有許可權(quán)向其發(fā)送信號的所有進程。如上所述一樣,“所有進程”并不包括系統(tǒng)進程集中的進程。pid=-1將信號發(fā)送給所有進程舉例:kill_raise.c7.3.5.2sigqueue信號發(fā)送函數(shù)sigqueue也可以發(fā)送信號,并能傳遞附加的信息。#include<signal.h>intsigqueue(pid_tpid,intsig,constunionsigvalvalue)pid:接收信號的進程sig:要發(fā)送的信號value:是一個整型與指針類型的聯(lián)合體:

unionsigval{ intsival_int; void*sival_ptr; }//4字節(jié)值由sigqueue函數(shù)發(fā)送的信號的第3個參數(shù)value,可以被進程信號處理函數(shù)的第2個參數(shù)info->si_ptr接收到sigqueue信號發(fā)送函數(shù)信號參數(shù)的傳遞過程可圖示如下:

typedefstruct{intsi_signo;intsi_errno;intsi_code;unionsigvalsi_value;}siginfo_t;unionsigval{ intsival_int; void*sival_ptr;}sigqueue信號發(fā)送函數(shù)舉例:進程給自己發(fā)送信號,并帶上附加數(shù)據(jù)sigqueue.c一個進程向另一個進程發(fā)送信號:sigqueue_send.c,sigqueue_rec.c7.3.5.3alarm和pause函數(shù)alarm使用alarm函數(shù)可以設(shè)置一個時間值(鬧鐘時間),在將來的某個時刻該時間值會被超過。當(dāng)所設(shè)置的時間值被超過后,產(chǎn)生SIGALRM信號。如果不忽略或不捕捉此信號,則其默認(rèn)動作是終止該進程。#include<unistd.h>unsignedintalarm(unsignedintseconds);返回:0或以前設(shè)置的鬧鐘時間的余留秒數(shù)alarm參數(shù)seconds的值是秒數(shù),經(jīng)過了指定的seconds秒后會產(chǎn)生信號SIGALRM。每個進程只能有一個鬧鐘時間。如果在調(diào)用alarm時,以前已為該進程設(shè)置過鬧鐘時間,而且它還沒有超時,則該鬧鐘時間的余留值作為本次alarm函數(shù)調(diào)用的值返回。以前登記的鬧鐘時間則被新值替換。如果有以前登記的尚未超過的鬧鐘時間,而且seconds值是0,則取消以前的鬧鐘時間,其余留值仍作為函數(shù)的返回值。pausepause函數(shù)使調(diào)用進程掛起直至捕捉到一個信號。#include<unistd.h>intpause(void);返回:-1,errno設(shè)置為EINTR只有執(zhí)行了一個信號處理程序并從其返回時,pause才返回。舉例:模擬sleep(5)信號通信總結(jié)linux下的信號應(yīng)用并沒有想象的那么恐怖,程序員所要做的最多只有三件事情:安裝信號:使用signal或sigaction(推薦使用)實現(xiàn)三參數(shù)信號處理函數(shù),handler(intsignal,structsiginfo*info,void*);發(fā)送信號,推薦使用sigqueue()。實際上,對有些信號來說,只要安裝信號就足夠了(信號處理方式采用缺省或忽略)。其他可能要做的無非是與信號集相關(guān)的幾種操作。7.4信號量通信在多任務(wù)操作系統(tǒng)環(huán)境下,多個進程會同時運行,并且一些進程之間可能存在一定的關(guān)聯(lián)。多個進程可能為了完成同一個任務(wù)會相互協(xié)作,這樣形成進程之間的同步關(guān)系。而且在不同進程之間,為了爭奪有限的系統(tǒng)資源(硬件或軟件資源)會進入競爭狀態(tài),這就是進程之間的互斥關(guān)系。進程之間的互斥與同步關(guān)系存在的根源在于臨界資源。臨界資源是在同一個時刻只允許有限個(通常只有一個)進程可以訪問(讀)或修改(寫)的資源,通常包括硬件資源(處理器、內(nèi)存、存儲器以及其他外圍設(shè)備等)和軟件資源(共享代碼段,共享結(jié)構(gòu)和變量等)。訪問臨界資源的代碼叫做臨界區(qū),臨界區(qū)本身也會成為臨界資源。7.4.1信號量概述信號量是用來解決進程之間的同步與互斥問題的一種進程之間通信機制,包括一個稱為信號量的變量和在該信號量下等待資源的進程等待隊列,以及對信號量進行的兩個原子操作(PV操作)。其中信號量對應(yīng)于某一種資源,取一個非負(fù)的整型值。信號量值指的是當(dāng)前可用的該資源的數(shù)量,若它等于0則意味著目前沒有可用的資源。PV原子操作的具體定義為:P操作:如果有可用的資源(信號量值>0),則占用一個資源(給信號量值減去一,進入臨界區(qū)代碼);如果沒有可用的資源(信號量值等于0),則被阻塞到,直到系統(tǒng)將資源分配給該進程(進入等待隊列,一直等到資源輪到該進程)。

V操作:如果在該信號量的等待隊列中有進程在等待資源,則喚醒一個阻塞進程。如果沒有進程等待它,則釋放一個資源(給信號量值加一)。信號量概述信號量的P操作臨界區(qū)域信號量的V操作進程A的非臨界區(qū)域部分進程B的非臨界區(qū)域部分進程A進程B任一時刻只允許一個執(zhí)行進程進入臨界區(qū)域PV操作如何保護臨界區(qū)7.4.2信號量的使用使用信號量訪問臨界區(qū)的偽代碼所下所示:{

/*設(shè)R為某種資源,S為資源R的信號量*/

INIT_VAL(S);

/*對信號量S進行初始化*/

非臨界區(qū)代碼;

P(S);

/*進行P操作*/

臨界區(qū)(使用資源R); /*只有有限個(通常只有一 個)進程被允許進入該區(qū)*/

V(S);

/*進行V操作*/

非臨界區(qū)代碼;}7.4.2信號量的使用第一步:創(chuàng)建信號量或獲得在系統(tǒng)已存在的信號量,此時需要調(diào)用semget()函數(shù)。不同進程通過使用同一個信號量鍵值來獲得同一個信號量。第二步:初始化信號量,此時使用semctl()函數(shù)的SETVAL操作。當(dāng)使用二進制信號量時,通常將信號量初始化為1。第三步:進行信號量的PV操作,此時調(diào)用semop()函數(shù)。這一步是實現(xiàn)進程之間的同步和互斥的核心工作部分。第四步:如果不需要信號量,則從系統(tǒng)中刪除它,此時使用semclt()函數(shù)的IPC_RMID操作。此時需要注意,在程序中不應(yīng)該出現(xiàn)對已經(jīng)被刪除的信號量的操作。(1)信號量的使用-創(chuàng)建/獲取(2)信號量的使用-控制信號量(3)信號量的使用-改變信號量值舉例說明由于信號量的操作調(diào)用接口比較復(fù)雜,我們封裝幾個函數(shù):信號量初始化函數(shù)set_semvalue()P操作函數(shù)sem_p()V操作函數(shù)sem_v()刪除信號量函數(shù)del_semvalue()程序舉例sem1.c,進程互斥sem_fork.c,進程同步7.5共享內(nèi)存共享內(nèi)存是一種最為高效的進程間通信方式。因為進程可以直接讀寫內(nèi)存,不需要任何數(shù)據(jù)的拷貝。為了在多個進程間交換信息,內(nèi)核專門留出了一塊內(nèi)存區(qū)。這段內(nèi)存區(qū)可以由需要訪問的進程將其映射到自己的私有地址空間。因此,進程就可以直接讀寫這一內(nèi)存區(qū)而不需要進行數(shù)據(jù)的拷貝,從而大大提高了效率。當(dāng)然,由于多個進程共享一段內(nèi)存,因此也需要依靠某種同步機制,如互斥鎖和信號量等。7.5共享內(nèi)存共享內(nèi)存實現(xiàn)分為兩個步驟:

一、創(chuàng)建共享內(nèi)存,使用shmget函數(shù)。也就是從內(nèi)存中獲得一段共享內(nèi)存區(qū)域。 二、映射共享內(nèi)存,將這段創(chuàng)建的共享內(nèi)存映射到具體的進程空間去,使用shmat函數(shù)。到這里,就可以使用這段共享內(nèi)存了,也就是可以使用不帶緩沖的I/O讀寫命令對其進行操作。除此之外,當(dāng)然還有撤銷映射的操作,其函數(shù)為shmdt()。

7.5.1共享內(nèi)存的創(chuàng)建intshmget(key_tkey,intsize,intshmflg);key:標(biāo)識共享內(nèi)存的鍵值,可以使用IPC_PR

溫馨提示

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

評論

0/150

提交評論