版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第8章進(jìn)程間通信8.1項(xiàng)目目標(biāo)8.2進(jìn)程間通信概述8.3管道通信8.4信號通信8.5信號量8.6共享內(nèi)存8.7消息隊(duì)列8.8項(xiàng)目應(yīng)用8.1項(xiàng)目目標(biāo)通過本章節(jié)的學(xué)習(xí),掌握進(jìn)程間通信的常用方法。設(shè)計(jì)實(shí)現(xiàn)數(shù)據(jù)采集顯示通信系統(tǒng)的中數(shù)據(jù)采集進(jìn)程與數(shù)據(jù)上傳進(jìn)程之間的數(shù)據(jù)通信。本章知識點(diǎn):在Linux系統(tǒng)中,如果同時(shí)存在多個(gè)進(jìn)程,則需要這些異步進(jìn)程之間的通信問題,包括數(shù)據(jù)和協(xié)同工作等。Linux提供了管道、信號、消息隊(duì)列、共享內(nèi)存、信號量、套接字等通信機(jī)制。本章進(jìn)行詳細(xì)學(xué)習(xí)。8.2進(jìn)程間通信概述為什么進(jìn)程間需要通信?數(shù)據(jù)傳輸一個(gè)進(jìn)程需要將它的數(shù)據(jù)發(fā)送給另一個(gè)進(jìn)程資源共享多個(gè)進(jìn)程之間共享同樣的資源通知事件一個(gè)進(jìn)程需要向另一個(gè)或一組進(jìn)程發(fā)送消息,通知它們發(fā)生了某種事件進(jìn)程控制有些進(jìn)程希望完全控制另一個(gè)進(jìn)程的執(zhí)行(如Debug進(jìn)程),此時(shí)控制進(jìn)程希望能夠攔截另一個(gè)進(jìn)程的所有操作,并能夠及時(shí)知道它的狀態(tài)改變。8.2進(jìn)程間通信概述Linux下的進(jìn)程通信手段基本上是從UNIX平臺上的進(jìn)程通信手段繼承而來的。而對UNIX發(fā)展做出重大貢獻(xiàn)的兩大主力AT&T的貝爾實(shí)驗(yàn)室及BSD(加州大學(xué)伯克利分校的伯克利軟件發(fā)布中心)在進(jìn)程間的通信方面的側(cè)重點(diǎn)有所不同。前者是對UNIX早期的進(jìn)程間通信手段進(jìn)行了系統(tǒng)的改進(jìn)和擴(kuò)充,形成了“systemVIPC”,其通信進(jìn)程主要局限在單個(gè)計(jì)算機(jī)內(nèi);后者則跳過了該限制,形成了基于套接口(socket)的進(jìn)程間通信機(jī)制。而Linux則把兩者的優(yōu)勢都繼承了下來。8.2進(jìn)程間通信概述UNIX進(jìn)程間通信(IPC)方式包括管道、FIFO以及信號。SystemV進(jìn)程間通信(IPC)包括SystemV消息隊(duì)列、SystemV信號量以及SystemV共享內(nèi)存區(qū)。Posix進(jìn)程間通信(IPC)包括Posix消息隊(duì)列、Posix信號量以及Posix共享內(nèi)存區(qū)。8.2進(jìn)程間通信概述現(xiàn)在Linux使用的進(jìn)程間通信方式主要包括:管道(pipe)和有名管道(FIFO)信號(Signal)消息隊(duì)列共享內(nèi)存信號量套接字(socket)8.3管道通信管道是單向的、先進(jìn)先出的,它把一個(gè)進(jìn)程的輸出和另一個(gè)進(jìn)程的輸入連接在一起。一個(gè)進(jìn)程(寫進(jìn)程)在管道的尾部寫入數(shù)據(jù),另一個(gè)進(jìn)程(讀進(jìn)程)從管道的頭部讀出數(shù)據(jù)。數(shù)據(jù)被一個(gè)進(jìn)程讀出后,將從管道中刪除。其它讀進(jìn)程將不能再讀到這些數(shù)據(jù)。管道提供了簡單的流控制機(jī)制,進(jìn)程試圖讀空管道時(shí),進(jìn)程將阻塞。同樣,管道已經(jīng)滿時(shí),進(jìn)程再試圖向管道寫入數(shù)據(jù),進(jìn)程將阻塞。管道包括無名管道和命名管道,前者用于父進(jìn)程和子進(jìn)程間的通信,后者可用于運(yùn)行于同一系統(tǒng)中任意兩個(gè)進(jìn)程間的通信。8.3.1無名管道fd[0]用于讀取管道;fd[1]用于寫入管道。創(chuàng)建無名管道由pipe()函數(shù)創(chuàng)建:intpipe(intfiledis[2]);
當(dāng)一個(gè)管道建立時(shí),它會創(chuàng)建兩個(gè)文件描述符:filedis[0]用于讀管道,filedis[1]用于寫管道。8.3.1無名管道管道關(guān)閉關(guān)閉管道只需要將這兩個(gè)文件描述符關(guān)閉即可??梢允褂闷胀ǖ腸lose函數(shù)逐個(gè)關(guān)閉。無名管道使用特點(diǎn):它只能用于具有親緣關(guān)系的進(jìn)程間通信(如父子進(jì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無名管道管道用于不同進(jìn)程間通信,通常先創(chuàng)建一個(gè)管道,再通過fork函數(shù)創(chuàng)建一個(gè)子進(jìn)程,該子進(jìn)程會繼承父進(jìn)程所創(chuàng)建的管道。8.3.1無名管道子進(jìn)程寫入和父進(jìn)程讀的命名管道:8.3.1無名管道管道讀寫注意事項(xiàng)只有在管道的讀端存在時(shí),向管道寫入數(shù)據(jù)才有意義,否則,向管道寫入數(shù)據(jù)的進(jìn)程將收到內(nèi)核傳來的SIGPIPE信號。向管道寫入數(shù)據(jù)時(shí),管道緩沖區(qū)一有空閑區(qū)域,寫進(jìn)程就會試圖向管道寫入數(shù)據(jù),如果讀進(jìn)程不讀取管道緩沖區(qū)中的數(shù)據(jù),寫操作將會阻塞。父子進(jìn)程在運(yùn)行時(shí),它們的先后順序不能保證,為了保證父子進(jìn)程關(guān)閉了相應(yīng)的文件描述符,可以使用sleep()解決。8.3.1無名管道管道讀寫注意事項(xiàng)可以通過打開兩個(gè)管道來創(chuàng)建一個(gè)雙向的管道。但需要在子進(jìn)程中正確地設(shè)置文件描述符。必須在系統(tǒng)調(diào)用fork()前調(diào)用pipe(),否則子進(jìn)程將不會繼承文件描述符。當(dāng)使用半雙工管道時(shí),任何關(guān)聯(lián)的進(jìn)程都必須共享一個(gè)相關(guān)的祖先進(jìn)程。因?yàn)楣艿来嬖谟谙到y(tǒng)內(nèi)核之中,所以任何不在創(chuàng)建管道的進(jìn)程的祖先進(jìn)程之中的進(jìn)程都將無法尋址它。而在命名管道中卻不是這樣。程序分析:pipe_rw.c
作業(yè):仿照半雙工管道程序?qū)懸粋€(gè)全雙工管道程序命名管道和無名管道基本相同,但也有不同點(diǎn):無名管道只能有父子進(jìn)程使用;但是通過命名管道,不相關(guān)的進(jìn)程也能交換數(shù)據(jù)。命名管道創(chuàng)建:#include<sys/types.h>#include<sys/stat.h>intmkfifo(constchar*pathname,mode_tmode)pathname:FIFO文件名mode:屬性(見文件操作章節(jié))一旦創(chuàng)建了一個(gè)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由于普通文件的讀寫時(shí)不會出現(xiàn)阻塞問題,而在管道的讀寫中卻有阻塞的可能,這里的非阻塞標(biāo)志可以在open()函數(shù)中設(shè)定為O_NONBLOCK。對于讀進(jìn)程若該管道是阻塞打開,且當(dāng)前FIFO內(nèi)沒有數(shù)據(jù),則對讀進(jìn)程而言將一直阻塞到有數(shù)據(jù)寫入。若該管道是非阻塞打開,則不論FIFO內(nèi)是否有數(shù)據(jù),讀進(jìn)程都會立即執(zhí)行讀操作。即如果FIFO內(nèi)沒有數(shù)據(jù),則讀函數(shù)將立刻返回0。8.3.2命名管道FIFO對于寫進(jìn)程若該管道是阻塞打開,則寫操作將一直阻塞到數(shù)據(jù)可以被寫入。若該管道是非阻塞打開而不能寫入全部數(shù)據(jù),則讀操作進(jìn)行部分寫入或者調(diào)用失敗。類似于管道,若寫一個(gè)尚無進(jìn)程為讀而打開的FIFO,則產(chǎn)生信號SIGPIPE。若某個(gè)FIFO的最后一個(gè)寫進(jìn)程關(guān)閉了該FIFO,則將為該FIFO的讀進(jìn)程產(chǎn)生一個(gè)文件結(jié)束標(biāo)志。管道刪除:命名管道的刪除可以用unlink函數(shù)實(shí)現(xiàn)。實(shí)例分析:fifo_write.c、fifo_read.c8.3.2命名管道FIFOFIFO相關(guān)出錯(cuò)信息:
EACCES(無存取權(quán)限)EEXIST(指定文件不存在)ENAMETOOLONG(路徑名太長)ENOENT(包含的目錄不存在)ENOSPC(文件系統(tǒng)剩余空間不足)ENOTDIR(文件路徑無效)EROFS(指定的文件存在于只讀文件系統(tǒng)中)8.4.1信號的定義信號是軟件中斷。它可以作為進(jìn)程間通信的一種機(jī)制,更重要的是,信號總是中斷一個(gè)進(jìn)程的正常運(yùn)行,它更多地被用于處理一些非正常情況。信號是異步的,進(jìn)程并不知道信號什么時(shí)候到達(dá)。進(jìn)程既可以處理信號,也可以發(fā)送信號給特定進(jìn)程。每個(gè)信號都有一個(gè)名字,這些名字都以SIG開頭。例如:SIGABRT是進(jìn)程異常終止信號。8.4管道通信很多條件可以產(chǎn)生一個(gè)信號:當(dāng)用戶按某些終端鍵時(shí),產(chǎn)生信號。在終端上按Ctrl+c鍵通常產(chǎn)生中斷信號(SIGINT)。這是停止一個(gè)已失去控制程序的方法。硬件異常產(chǎn)生信號:除數(shù)為0、無效的存儲訪問等等。這些條件通常由硬件檢測到,并將其通知內(nèi)核。然后內(nèi)核為該條件發(fā)生時(shí)正在運(yùn)行的進(jìn)程產(chǎn)生適當(dāng)?shù)男盘?。例如,對?zhí)行一個(gè)無效存儲訪問的進(jìn)程產(chǎn)生一個(gè)SIGSEGV。進(jìn)程用kill(2)函數(shù)可將信號發(fā)送給另一個(gè)進(jìn)程或進(jìn)程組。自然,有些限制:接收信號進(jìn)程和發(fā)送信號進(jìn)程的所有者必須相同,或發(fā)送信號進(jìn)程的所有者必須是超級用戶。8.4.2信號來源很多條件可以產(chǎn)生一個(gè)信號:用戶可用kill(1)命令將信號發(fā)送給其他進(jìn)程。此程序是kill函數(shù)的接口。常用此命令終止一個(gè)失控的后臺進(jìn)程。當(dāng)檢測到某種軟件條件已經(jīng)發(fā)生,并將其通知有關(guān)進(jìn)程時(shí)也產(chǎn)生信號。這里并不是指硬件產(chǎn)生條件(如被0除),而是軟件條件。例如SIGURG(在網(wǎng)絡(luò)連接上傳來非規(guī)定波特率的數(shù)據(jù))、SIGPIPE(在管道的讀進(jìn)程已終止后一個(gè)進(jìn)程寫此管道),以及SIGALRM(進(jìn)程所設(shè)置的鬧鐘時(shí)間已經(jīng)超時(shí))。8.4.2信號來源不可靠的信號:Linux信號機(jī)制基本上是從Unix系統(tǒng)中繼承過來的。早期Unix系統(tǒng)中的信號機(jī)制比較簡單和原始,后來在實(shí)踐中暴露出一些問題,因此,把那些建立在早期機(jī)制上的信號叫做“不可靠信號”,信號值小于SIGRTMIN的叫不可靠信號(1~31)。每次信號處理后,該信號對應(yīng)的處理函數(shù)會恢復(fù)到默認(rèn)值。但現(xiàn)代的Linux已經(jīng)對其進(jìn)行了改進(jìn),信號處理函數(shù)一直是用戶指定的或者是系統(tǒng)默認(rèn)的。信號可能丟失。不可靠信號不支持信號排隊(duì),同一個(gè)信號產(chǎn)生多次,只要程序還未處理該信號,那么實(shí)際只處理此信號一次。8.4.信號來源7.3.3信號的種類可靠信號:信號值位于SIGRTMIN和SIGRTMAX之間的信號都是可靠信號,可靠信號克服了信號可能丟失的問題。實(shí)時(shí)信號與非實(shí)時(shí)信號:Linux目前定義了64種信號(將來可能會擴(kuò)展),前面32種為非實(shí)時(shí)信號,后32種為實(shí)時(shí)信號。非實(shí)時(shí)信號都不支持排隊(duì),都是不可靠信號,實(shí)時(shí)信號都支持排隊(duì),都是可靠信號。
信號排隊(duì)意味著無論產(chǎn)生多少次信號,信號處理函數(shù)就會被調(diào)用同樣的次數(shù)。7.3.3信號的種類信號名稱信號說明默認(rèn)處理SIGABRT由程序調(diào)用abort時(shí)產(chǎn)生該信號。程序異常結(jié)束。進(jìn)程終止并且產(chǎn)生core文件SIGALRMtimer到期,有alarm或者setitimer進(jìn)程終止SIGBUS總線錯(cuò)誤,地址沒對齊等。取決于具體硬件。結(jié)束終止并產(chǎn)生core文件SIGCHLD子進(jìn)程停止或者終止時(shí),父進(jìn)程會收到該信號。忽略該信號SIGCONT讓停止的進(jìn)程繼續(xù)執(zhí)行繼續(xù)執(zhí)行或者忽略SIGFPE算術(shù)運(yùn)算異常,除0等。進(jìn)程終止并且產(chǎn)生core文件SIGHUP終端關(guān)閉時(shí)產(chǎn)生這個(gè)信號進(jìn)程終止SIGILL代碼中有非法指令進(jìn)程終止并產(chǎn)生core文件SIGINT終端輸入了中斷字符ctrl+c進(jìn)程終止7.3.3信號的種類SIGIO異步I/O,跟SIGPOLL一樣。進(jìn)程終止SIGIOT執(zhí)行I/O時(shí)產(chǎn)生硬件錯(cuò)誤進(jìn)程終止并且產(chǎn)生core文件SIGKILL這個(gè)信號用戶不能去捕捉它。進(jìn)程終止SIGPIPE往管道寫時(shí),讀者已經(jīng)不在了進(jìn)程終止SIGPOLL異步I/O,跟SIGIO一樣。進(jìn)程終止SIGPROF有setitimer設(shè)置的timer到期引發(fā)進(jìn)程終止SIGPWRUps電源切換時(shí)進(jìn)程終止SIGQUITCtrl+\,不同于SIGINT,這個(gè)是會產(chǎn)生coredump文件的。進(jìn)程終止并且產(chǎn)生core文件SIGSEGV內(nèi)存非法訪問,segmentfault進(jìn)程終止并且產(chǎn)生core文件SIGSTOP某個(gè)進(jìn)程停止執(zhí)行,該信號不能被用戶捕捉。進(jìn)程暫停執(zhí)行7.3.3信號的種類SIGSYS調(diào)用操作系統(tǒng)不認(rèn)識的系統(tǒng)調(diào)用進(jìn)程終止并且產(chǎn)生core文件SIGTERM有kill函數(shù)調(diào)用產(chǎn)生進(jìn)程終止SIGTRAP有調(diào)試器使用,gdb進(jìn)程終止并且產(chǎn)生core文件SIGTSTPCtrl+z,掛起進(jìn)程進(jìn)程暫停SIGTTIN后臺程序要從終端讀取成數(shù)據(jù)時(shí)進(jìn)程暫停SIGTTOU后臺終端要把數(shù)據(jù)寫到終端時(shí)進(jìn)程暫停SIGURG一些緊急的事件,比如從網(wǎng)絡(luò)收到帶外數(shù)據(jù)忽略SIGUSR1用戶自定義信號進(jìn)程終止SIGUSR2用戶自定義信號進(jìn)程終止SIGVTALRM有setitimer產(chǎn)生進(jìn)程終止7.3.4信號的處理可以要求系統(tǒng)在某個(gè)信號出現(xiàn)時(shí)按照下列三種方式中的一種進(jìn)行操作。(1)忽略此信號。大多數(shù)信號都可使用這種方式進(jìn)行處理,但有兩種信號卻決不能被忽略。它們是:SIGKILL和SIGSTOP。這兩種信號不能被忽略的原因是:它們向超級用戶提供一種使進(jìn)程終止或停止的可靠方法。另外,如果忽略某些由硬件異常產(chǎn)生的信號(例如非法存儲訪問或除以0),則進(jìn)程的行為是未定義的。(2)捕捉信號。為了做到這一點(diǎn)要通知內(nèi)核在某種信號發(fā)生時(shí),調(diào)用一個(gè)用戶函數(shù)。在用戶函數(shù)中,可執(zhí)行用戶希望對這種事件進(jìn)行的處理。如果捕捉到SIGCHLD信號,則表示子進(jìn)程已經(jīng)終止,所以此信號的捕捉函數(shù)可以調(diào)用waitpid以取得該子進(jìn)程的進(jìn)程ID以及它的終止?fàn)顟B(tài)。(3)執(zhí)行系統(tǒng)默認(rèn)動(dòng)作。對大多數(shù)信號的系統(tǒng)默認(rèn)動(dòng)作是終止該進(jìn)程。7.3.4.1信號的處理—缺省每一個(gè)信號都有一個(gè)缺省動(dòng)作,它是當(dāng)進(jìn)程沒有給這個(gè)信號指定處理程序時(shí),內(nèi)核對信號的處理。有5種缺省的動(dòng)作:異常終止(abort):在進(jìn)程的當(dāng)前目錄下,把進(jìn)程的地址空間內(nèi)容、寄存器內(nèi)容保存到一個(gè)叫做core的文件中,而后終止進(jìn)程。退出(exit):不產(chǎn)生core文件,直接終止進(jìn)程。忽略(ignore):忽略該信號。停止(stop):掛起該進(jìn)程。繼續(xù)(continue):如果進(jìn)程被掛起,則恢復(fù)進(jìn)程的運(yùn)行。否則,忽略信號。7.3.4.2信號的處理—捕捉當(dāng)系統(tǒng)捕捉到某個(gè)信號時(shí),可以忽略該信號或是使用指定的處理函數(shù)來處理該信號,或者使用系統(tǒng)默認(rèn)的方式。信號處理的主要方法有兩種,一種是使用簡單的signal函數(shù),另一種是使用信號集函數(shù)組。(1)Signal處理機(jī)制#include<signal.h>void(*signal(intsigno,void(*func)(int)))(int)返回:成功則返回信號以前的處理配置,若出錯(cuò)則為SIG_ERRtypedefvoid(*sighandler_t)(int)sighandler_tsignal(intsignum,sighandler_thandler)func的值是:(a)常數(shù)SIG_IGN:向內(nèi)核表示忽略此信號(有兩個(gè)信號SIGKILL和SIGSTOP不能忽略)(b)常數(shù)SIG_DFL:接到此信號后的動(dòng)作是系統(tǒng)默認(rèn)動(dòng)作。(c)當(dāng)接到此信號后要調(diào)用的函數(shù)的地址:我們稱此為捕捉此信號。我們稱此函數(shù)為信號處理程序(signalhandler)或信號捕捉函數(shù)(signal-catchingfunction)。實(shí)例分析#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處理機(jī)制忽略掉終端CTRL+C產(chǎn)生的信號:#include<stdio.h>#include<signal.h>#include<stdlib.h>intmain(){ signal(SIGINT,SIG_IGN); while(1) sleep(1); return0;}//程序運(yùn)行后,將Ctrl+c產(chǎn)生的SIGINT信號忽略掉了,則CTRL+c將不能終止該進(jìn)程(1)Signal處理機(jī)制接受信號的默認(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處理機(jī)制注意:Signal主要用于前32種非實(shí)時(shí)信號的處理Signal不能傳遞附加數(shù)據(jù)采用signal處理機(jī)制,一些特殊情況的考慮注冊一個(gè)信號處理函數(shù),處理完畢之后,是否需要重新注冊,才能捕捉下一個(gè)信號如果信號處理函數(shù)正在處理,并且沒有處理完畢是,又發(fā)生了一個(gè)同類型的信號,這時(shí)該怎么處理如果信號處理函數(shù)正在處理,并且沒有處理完畢是,又發(fā)生了一個(gè)不同類型的信號,這時(shí)該怎么處理……(2)sigactionLinux支持一個(gè)更健壯、更新的信號處理函數(shù)sigaction#include<signal.h>intsigaction(intsignum,conststructsigaction*act,structsigaction*oldact);第一個(gè)signum指定需要處理(捕捉)的信號(除SIGKILL和SIGSTOP)。第二個(gè)參數(shù)act(結(jié)構(gòu)體)設(shè)定信號的處理方式,act可以為NULL。之前設(shè)定的信號處理方式會保存到第三個(gè)參數(shù)oldact,oldact為NULL時(shí)不保存。返回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ù)的第二個(gè)參數(shù)類型一樣,當(dāng)信號遞送給進(jìn)程時(shí)會調(diào)用這個(gè)sa_handler.sa_sigaction也是信號處理的函數(shù)指針,它只會在sa_flags包含SA_SIGINFO時(shí)才會被調(diào)用,siginfo_t包含了信號產(chǎn)生的原因。sa_flags的值為0或其它任何值都將按默認(rèn)方式處理,即調(diào)用sa_handler捕捉函數(shù)。不用同時(shí)賦值給sa_handler和sa_sigaction,因?yàn)樗鼈兪且粋€(gè)union。(2)sigactionsa_mask信號屏蔽字,當(dāng)執(zhí)行sa_handler信號處理函數(shù)時(shí),sa_mask指定的信號會被阻塞,直到該信號處理函數(shù)返回。針對sigset_t結(jié)構(gòu)體,有一組專門的函數(shù)對它進(jìn)行處理:intsigemptyset(sigset_t*set)//清空信號結(jié)合setintsigfillset(sigset_t*set)//將所有信號填充進(jìn)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用來改變信號處理時(shí)的行為。當(dāng)sa_flags包含SA_RESTART時(shí),被中斷的系統(tǒng)調(diào)用在信號處理完后會被自動(dòng)啟動(dòng)。sa_flags說明SA_NOCLDSTOP若signum是SIGCHLD,當(dāng)一子進(jìn)程停止時(shí),不通知父進(jìn)程SA_NOMASK/SA_NODEFER在處理此信號結(jié)束前允許此信號再次遞送,相當(dāng)于中斷嵌套SA_RESTART由此信號中斷的系統(tǒng)調(diào)用自動(dòng)重啟SA_NOCLDWAIT若signum是SIGCHLD,則當(dāng)調(diào)用進(jìn)程的子進(jìn)程終止時(shí),不創(chuàng)建僵尸進(jìn)程。若調(diào)用進(jìn)程在后面調(diào)用wait,則阻塞到它所有子進(jìn)程都終止,此時(shí)返回-1SA_NODEFER當(dāng)捕捉到此信號時(shí),在執(zhí)行其信號捕捉函數(shù)時(shí),系統(tǒng)不自動(dòng)阻塞此信號。SA_ONESHOT/SA_RESETHAND當(dāng)調(diào)用新的信號處理函數(shù)前,將此信號處理方式改為系統(tǒng)預(yù)設(shè)(SIG_DFL)的方式SA_SIGINFO此選項(xiàng)對信號處理程序提供了附加信息。舉例說明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時(shí)才阻塞SIGQUIT信號;函數(shù)sigprocmask是全程阻塞,在sigprocmask中設(shè)置了阻塞集合后,被阻塞的信號將不能再被信號處理函數(shù)捕捉,直到重新設(shè)置阻塞信號集合。
intsigprocmask(inthow,sigset_t*set,sigset_t*oldset)how:(1):SIG_BLOCK,將參數(shù)2的信號集添加到進(jìn)程原有的阻塞信號集中,(2):SIG_UNBLOCK,從進(jìn)程原有的阻塞信號集中移除參數(shù)2中包含的信號,(3):SIG_SET,重新設(shè)置進(jìn)程阻塞信號集為參數(shù)2的信號集set是阻塞信號集,olsset存放原有的信號集7.3.5信號發(fā)送7.3.5.1kill和raiseUnix中發(fā)送信號的主要函數(shù)有kill和raise區(qū)別:kill()不僅可以中止進(jìn)程,也可以向進(jìn)程發(fā)送其他信號。與kill函數(shù)不同的是,raise()函數(shù)運(yùn)行向進(jìn)程自身發(fā)送信號。#include<sys/types.h>#include<signal.h>intkill(pid_tpid,intsigno);intraise(intsigno);兩個(gè)函數(shù)返回:若成功則為0,若出錯(cuò)則為-1。7.3.5.1kill和raisekill的pid參數(shù)有四種不同的情況:
pid>0將信號發(fā)送給進(jìn)程ID為pid的進(jìn)程。
pid=0將信號發(fā)送給其進(jìn)程組ID等于發(fā)送進(jìn)程的進(jìn)程組ID,而且發(fā)送進(jìn)程有許可權(quán)向其發(fā)送信號的所有進(jìn)程。
pid<0將信號發(fā)送給其進(jìn)程組ID等于pid絕對值,而且發(fā)送進(jìn)程有許可權(quán)向其發(fā)送信號的所有進(jìn)程。如上所述一樣,“所有進(jìn)程”并不包括系統(tǒng)進(jìn)程集中的進(jìn)程。pid=-1將信號發(fā)送給所有進(jìn)程舉例:kill_raise.c7.3.5.2sigqueue信號發(fā)送函數(shù)sigqueue也可以發(fā)送信號,并能傳遞附加的信息。#include<signal.h>intsigqueue(pid_tpid,intsig,constunionsigvalvalue)pid:接收信號的進(jìn)程sig:要發(fā)送的信號value:是一個(gè)整型與指針類型的聯(lián)合體:
unionsigval{ intsival_int; void*sival_ptr; }//4字節(jié)值由sigqueue函數(shù)發(fā)送的信號的第3個(gè)參數(shù)value,可以被進(jìn)程信號處理函數(shù)的第2個(gè)參數(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ù)舉例:進(jìn)程給自己發(fā)送信號,并帶上附加數(shù)據(jù)sigqueue.c一個(gè)進(jìn)程向另一個(gè)進(jìn)程發(fā)送信號:sigqueue_send.c,sigqueue_rec.c7.3.5.3alarm和pause函數(shù)alarm使用alarm函數(shù)可以設(shè)置一個(gè)時(shí)間值(鬧鐘時(shí)間),在將來的某個(gè)時(shí)刻該時(shí)間值會被超過。當(dāng)所設(shè)置的時(shí)間值被超過后,產(chǎn)生SIGALRM信號。如果不忽略或不捕捉此信號,則其默認(rèn)動(dòng)作是終止該進(jìn)程。#include<unistd.h>unsignedintalarm(unsignedintseconds);返回:0或以前設(shè)置的鬧鐘時(shí)間的余留秒數(shù)alarm參數(shù)seconds的值是秒數(shù),經(jīng)過了指定的seconds秒后會產(chǎn)生信號SIGALRM。每個(gè)進(jìn)程只能有一個(gè)鬧鐘時(shí)間。如果在調(diào)用alarm時(shí),以前已為該進(jìn)程設(shè)置過鬧鐘時(shí)間,而且它還沒有超時(shí),則該鬧鐘時(shí)間的余留值作為本次alarm函數(shù)調(diào)用的值返回。以前登記的鬧鐘時(shí)間則被新值替換。如果有以前登記的尚未超過的鬧鐘時(shí)間,而且seconds值是0,則取消以前的鬧鐘時(shí)間,其余留值仍作為函數(shù)的返回值。pausepause函數(shù)使調(diào)用進(jìn)程掛起直至捕捉到一個(gè)信號。#include<unistd.h>intpause(void);返回:-1,errno設(shè)置為EINTR只有執(zhí)行了一個(gè)信號處理程序并從其返回時(shí),pause才返回。舉例:模擬sleep(5)信號通信總結(jié)linux下的信號應(yīng)用并沒有想象的那么恐怖,程序員所要做的最多只有三件事情:安裝信號:使用signal或sigaction(推薦使用)實(shí)現(xiàn)三參數(shù)信號處理函數(shù),handler(intsignal,structsiginfo*info,void*);發(fā)送信號,推薦使用sigqueue()。實(shí)際上,對有些信號來說,只要安裝信號就足夠了(信號處理方式采用缺省或忽略)。其他可能要做的無非是與信號集相關(guān)的幾種操作。7.4信號量通信在多任務(wù)操作系統(tǒng)環(huán)境下,多個(gè)進(jìn)程會同時(shí)運(yùn)行,并且一些進(jìn)程之間可能存在一定的關(guān)聯(lián)。多個(gè)進(jìn)程可能為了完成同一個(gè)任務(wù)會相互協(xié)作,這樣形成進(jìn)程之間的同步關(guān)系。而且在不同進(jìn)程之間,為了爭奪有限的系統(tǒng)資源(硬件或軟件資源)會進(jìn)入競爭狀態(tài),這就是進(jìn)程之間的互斥關(guān)系。進(jìn)程之間的互斥與同步關(guān)系存在的根源在于臨界資源。臨界資源是在同一個(gè)時(shí)刻只允許有限個(gè)(通常只有一個(gè))進(jìn)程可以訪問(讀)或修改(寫)的資源,通常包括硬件資源(處理器、內(nèi)存、存儲器以及其他外圍設(shè)備等)和軟件資源(共享代碼段,共享結(jié)構(gòu)和變量等)。訪問臨界資源的代碼叫做臨界區(qū),臨界區(qū)本身也會成為臨界資源。7.4.1信號量概述信號量是用來解決進(jìn)程之間的同步與互斥問題的一種進(jìn)程之間通信機(jī)制,包括一個(gè)稱為信號量的變量和在該信號量下等待資源的進(jìn)程等待隊(duì)列,以及對信號量進(jìn)行的兩個(gè)原子操作(PV操作)。其中信號量對應(yīng)于某一種資源,取一個(gè)非負(fù)的整型值。信號量值指的是當(dāng)前可用的該資源的數(shù)量,若它等于0則意味著目前沒有可用的資源。PV原子操作的具體定義為:P操作:如果有可用的資源(信號量值>0),則占用一個(gè)資源(給信號量值減去一,進(jìn)入臨界區(qū)代碼);如果沒有可用的資源(信號量值等于0),則被阻塞到,直到系統(tǒng)將資源分配給該進(jìn)程(進(jìn)入等待隊(duì)列,一直等到資源輪到該進(jìn)程)。
V操作:如果在該信號量的等待隊(duì)列中有進(jìn)程在等待資源,則喚醒一個(gè)阻塞進(jìn)程。如果沒有進(jìn)程等待它,則釋放一個(gè)資源(給信號量值加一)。信號量概述信號量的P操作臨界區(qū)域信號量的V操作進(jìn)程A的非臨界區(qū)域部分進(jìn)程B的非臨界區(qū)域部分進(jìn)程A進(jìn)程B任一時(shí)刻只允許一個(gè)執(zhí)行進(jìn)程進(jìn)入臨界區(qū)域PV操作如何保護(hù)臨界區(qū)7.4.2信號量的使用使用信號量訪問臨界區(qū)的偽代碼所下所示:{
/*設(shè)R為某種資源,S為資源R的信號量*/
INIT_VAL(S);
/*對信號量S進(jìn)行初始化*/
非臨界區(qū)代碼;
P(S);
/*進(jìn)行P操作*/
臨界區(qū)(使用資源R); /*只有有限個(gè)(通常只有一 個(gè))進(jìn)程被允許進(jìn)入該區(qū)*/
V(S);
/*進(jìn)行V操作*/
非臨界區(qū)代碼;}7.4.2信號量的使用第一步:創(chuàng)建信號量或獲得在系統(tǒng)已存在的信號量,此時(shí)需要調(diào)用semget()函數(shù)。不同進(jìn)程通過使用同一個(gè)信號量鍵值來獲得同一個(gè)信號量。第二步:初始化信號量,此時(shí)使用semctl()函數(shù)的SETVAL操作。當(dāng)使用二進(jìn)制信號量時(shí),通常將信號量初始化為1。第三步:進(jìn)行信號量的PV操作,此時(shí)調(diào)用semop()函數(shù)。這一步是實(shí)現(xiàn)進(jìn)程之間的同步和互斥的核心工作部分。第四步:如果不需要信號量,則從系統(tǒng)中刪除它,此時(shí)使用semclt()函數(shù)的IPC_RMID操作。此時(shí)需要注意,在程序中不應(yīng)該出現(xiàn)對已經(jīng)被刪除的信號量的操作。(1)信號量的使用-創(chuàng)建/獲取(2)信號量的使用-控制信號量(3)信號量的使用-改變信號量值舉例說明由于信號量的操作調(diào)用接口比較復(fù)雜,我們封裝幾個(gè)函數(shù):信號量初始化函數(shù)set_semvalue()P操作函數(shù)sem_p()V操作函數(shù)sem_v()刪除信號量函數(shù)del_semvalue()程序舉例sem1.c,進(jìn)程互斥sem_fork.c,進(jìn)程同步7.5共享內(nèi)存共享內(nèi)存是一種最為高效的進(jìn)程間通信方式。因?yàn)檫M(jìn)程可以直接讀寫內(nèi)存,不需要任何數(shù)據(jù)的拷貝。為了在多個(gè)進(jìn)程間交換信息,內(nèi)核專門留出了一塊內(nèi)存區(qū)。這段內(nèi)存區(qū)可以由需要訪問的進(jìn)程將其映射到自己的私有地址空間。因此,進(jìn)程就可以直接讀寫這一內(nèi)存區(qū)而不需要進(jìn)行數(shù)據(jù)的拷貝,從而大大提高了效率。當(dāng)然,由于多個(gè)進(jìn)程共享一段內(nèi)存,因此也需要依靠某種同步機(jī)制,如互斥鎖和信號量等。7.5共享內(nèi)存共享內(nèi)存實(shí)現(xiàn)分為兩個(gè)步驟:
一、創(chuàng)建共享內(nèi)存,使用shmget函數(shù)。也就是從內(nèi)存中獲得一段共享內(nèi)存區(qū)域。 二、映射共享內(nèi)存,將這段創(chuàng)建的共享內(nèi)存映射到具體的進(jìn)程空間去,使用shmat函數(shù)。到這里,就可以使用這段共享內(nèi)存了,也就是可以使用不帶緩沖的I/O讀寫命令對其進(jìn)行操作。除此之外,當(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)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 生物課題研究的學(xué)生參與計(jì)劃
- 經(jīng)理的時(shí)間管理技巧分享計(jì)劃
- 酒店管理的企業(yè)文化
- 敬業(yè)行業(yè)話務(wù)員崗位展望
- 2025年中考物理一輪復(fù)習(xí)之聲現(xiàn)象
- 酒店管理的利益最大化
- 物流行業(yè)倉儲配送培訓(xùn)總結(jié)
- 汽車美容銷售顧問銷售總結(jié)報(bào)告
- 2024年設(shè)備監(jiān)理師考試題庫附答案(輕巧奪冠)
- 2024年稅務(wù)師題庫及答案【易錯(cuò)題】
- 2024年中國醫(yī)藥研發(fā)藍(lán)皮書
- 坍塌、垮塌事故專項(xiàng)應(yīng)急預(yù)案(3篇)
- 2024年融媒體中心事業(yè)單位考試工作人員另選錄用55人內(nèi)部選題庫及參考答案(研優(yōu)卷)
- 陜西省安康市2023-2024學(xué)年高一上學(xué)期期末考試 生物 含解析
- WPS Office辦公軟件應(yīng)用教學(xué)教案
- 2024年時(shí)政熱點(diǎn)知識競賽試卷及答案(共四套)
- 幼兒園后勤主任年終總結(jié)
- 除顫儀使用護(hù)理查房
- 初級消防設(shè)施操作員實(shí)操題庫 (一)
- 2024版《糖尿病健康宣教》課件
- CURTIS1232-1234-1236-SE-SERIES交流控制器手冊
評論
0/150
提交評論