




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第7章進程間的通信2本章重點進程通信中信號概念及信號處理進程間的管道通信編程進程間的內存共享編程3Linux中進程間通信(IPC)概述**管道、FIFO、信號、消息隊列、信號燈、共享內存共享內存進程1進程2管道pipesignal消息隊列4Linux中進程間通信概述**管道(Pipe)及有名管道(namedpipe)
管道是一種在進程之間單向流動數據的結構。源進程向管道寫數據,而內核會自動將這些數據引導向目標進程。
管道:用于具有親緣關系的進程間的通信有名管道:除具有管道所具有的功能外,還允許無親緣關系的進程間的通信5Linux中進程間通信概述**Linux系統信號(signal)
信號主要用于通知進程異步事件的發生。 在Linux中可以識別29種不同的信號,這些信號中的大部分都有了預先定義好的意義。6Linux中進程間通信概述**消息隊列消息隊列是由內核創建并維護的一個數據結構,它是有標識的。任何具有足夠權限的進程都可以向消息隊列中放置一個消息,同樣,任何具有足夠權限的進程都可以從中讀取一個消息。不同的進程通過訪問相同的消息隊列便可實現進程間通信。7Linux中進程間通信概述**共享內存共享內存區是這幾種進程間通信方式中最快的一種。它的特點除了速度快外,而且可傳遞的信息量大。通過將一段內存區映射到一個進程的地址空間來實現。因此,這種進程間通信就不再涉及到內核(即進程不是通過執行任何進入內核的系統調用來傳遞數據的。內核必須建立允許各個進程共享該內存區的內存映射關系,然后一直管理該內存區)。但同時,也要有效地保證它能同步、有序且沒有死鎖。8Linux中進程間通信概述**信號量信號量并不是一種IPC機制,它是用于提供不同進程間或一給定進程的不同線程間同步的一種手段。97.1.1信號及其使用
信號是在軟件層次上對中斷機制的一種模擬,是一種異步通信方式。信號可以直接進行用戶空間進程和內核進程之間的交互,內核進程也可以利用它來通知用戶空間進程發生了哪些系統事件。信號主要用于通知進程異步事件的發生。在Linux中可以識別29種不同的信號,這些信號中的大部分都有了預先定義好的意義。107.1.1信號及其使用信號事件的發生有兩個來源:硬件來源,如按下了鍵盤Delete鍵或者鼠標單擊,通常產生中斷信號(SIGINT)或者其它硬件故障。軟件來源,如使用系統調用或者是命令發出信號。最常用發送信號的系統函數是kill、raise、alarm、setitimer、sigation和sigqueue函數,軟件來源還包括一些非法運算等操作。117.1.1信號及其使用**一旦有信號產生,用戶進程對信號的響應有3種方式:執行默認操作。Linux對每種信號都規定了默認操作。捕捉信號。定義信號處理函數,當信號發生時,執行相應的處理函數。忽略信號。不希望接收到的信號對進程的執行產生影響,而讓進程繼續進行時,可以忽略該信號,即不對信號進程任何處理。12信號與信號處理**進程A信號產生進程B信號處理(信號接收)13常見信號的含義及其默認操作*14信號處理常用函數**發送信號函數Kill(pid_tpid,sig);給指定的進程發送信號。raise(intsig);給當前進程發送信號。Alarm(unsignedintseconds);給當前進程發送時鐘報警信號。pause(void);等待信號到來處理信號函數-信號捕捉Signal(sig,voidsigfunc(int));信號處理15信號處理常見函數信號發送函數(1)***
#include<sys/types.h>
#include<signal.h>
intkill(pid_tpid,intsig);
說明:
kill()可以用來送參數sig指定的信號給參數pid指定的進程。
參數pid有幾種情況:
pid>0將信號傳給進程識別碼為pid的進程。
pid=0將信號傳給和目前進程相同進程組的所有進程
pid=-1將信號廣播傳送給系統內所有的進程
返回值: 成功:返回0,錯誤:返回-1。
16信號處理常見函數//范例**#include<unistd.h>#include<signal.h>#include<sys/types.h>#include<sys/wait.h>intmain(){ pid_tpid; intstatus; if(!(pid=fork())){ printf(“HiIamchildprocess!\n”); sleep(10); return; } else{ printf(“sendsignaltochildprocess(%d)\n”,pid); sleep(1); kill(pid,SIGKILL); wait(&status); }}
17信號處理常見
函數信號發送函數(2)***函數格式
#include<unistd.h>
定義函數unsignedintalarm(unsignedintseconds);函數說明
alarm()用來設置信號SIGALRM在經過參數seconds指定的秒數后傳送給目前的進程。參數 若seconds=0,則之前設置的鬧鐘會被取消,并將剩下的時間返回。返回值 返回之前鬧鐘的剩余秒數,如果之前未設鬧鐘則返回0。18//范例2***#include<unistd.h>#include<signal.h>voidhandler(){ printf(“hello\n”);}intmain(){ inti; signal(SIGALRM,handler); alarm(5); for(i=1;i<7;i++){ printf(“sleep%d...\n”,i); sleep(1); }}
19信號處理常見函數信號捕獲函數(1)***
#include<signal.h>
void(*signal(intsignum,void(*handler)(int)))(int);
voidsignal(int信號名,信號處理函數名)信號處理函數void函數名(int信號名)函數說明
signal()會依參數signum指定的信號編號來設置該信號的處理函數。當指定的信號到達時就會跳轉到參數handler指定的函數執行。
20//范例***
#include<unistd.h>#include<signal.h>voidhandler(){ printf(“hello\n”);}intmain(){ inti; signal(SIGALRM,handler); alarm(5); for(i=1;i<7;i++){ printf(“sleep%d...\n”,i); sleep(1); }}21#include<signal.h>#include<sys/wait.h>#include<stdio.h>#include<stdlib.h>#include<unistd.h>voidoutDate(intn);voidexitProc(intn);intexitSig=0;intmain(){
(void)signal(SIGALRM,outDate); (void)signal(SIGINT,exitProc); while(1){ alarm(1); /*時鐘報警函數*/ pause(); if(exitSig) exit(0); }}/*************
SIGALRM信號處理函數*************/voidoutDate(intn){ system("date&");/*執行外部命令 */}/*************
SIGINT信號處理函數*************/voidexitProc(intn){ printf("Ctrl_C\n"); exitSig=1;}例:信號的捕捉***227.2.1信號操作的相關函數***237.2.1信號操作的相關函數24信號操作小結信號信號的發送信號的捕捉及處理信號的忽略、阻塞25信號操作小結信號26信號操作小結信號的發送Kill(pid_tpid,sig);給指定的進程發送信號。raise(intsig);給當前進程發送信號。Alarm(unsignedintseconds);給當前進程發送時鐘報警信號。pause(void);等待信號到來27信號操作小結信號的捕捉及處理Signal(sig,voidsigfunc(int));信號處理28信號的忽略與阻塞信號的忽略Signal()選項:29信號的忽略與阻塞信號的阻塞有時既不希望進程在接收到信號時立刻中斷進程的執行,也不希望此信號完全被忽略掉,而是延遲一段時間再去調用信號處理函數,這個時候就需要信號阻塞來完成。步驟清空待處理的信號集追加待處理信號到信號集設置/解除信號掩碼(設置屏蔽)307.2.1信號操作的相關函數§317.2.1信號操作的相關函數327.2.1信號操作的相關函數33信號操作小結信號的忽略、阻塞*信號的忽略Signal(sig,voidsigfunc(int));選項(第2個參數)SIG_IGN:忽略指定的信號SIG_DFL:信號處理方式重新設為內核預設方式阻塞步驟清空待處理的信號集:sigempty()追加待處理信號到信號集:sigaddset()設置/解除信號掩碼(設置屏蔽):sigprocmask()347.2管道**無名管道(PIPE)有名管道(FIFO)進程x進程y管道pipe357.2管道**在Linux系統中,管道用于兩個進程間的通信,這兩個進程要有同源性,即它們必須是最終由同一個進程所生成的進程。管道通信采用的是半雙工方式,即同一時間只允許單方向傳輸數據。進程x進程y管道pipe367.2管道**在Linux中,管道是一種特殊的文件,對一個進程來說,管道的寫入和讀取與一個普通文件沒有區別。管道是Linux支持的最初UnixIPC形式之一,具有以下特點:管道是半雙工的,數據只能向一個方向流動;需要雙方通信時,需要建立起兩個管道;只能用于父子進程或者兄弟進程之間(具有親緣關系的進程);單獨構成一種獨立的文件系統:管道對于管道兩端的進程而言,就是一個文件,但它不是普通的文件,它不屬于某種文件系統,而是自立門戶,單獨構成一種文件系統,并且只存在與內存中。37管道***387.2.1無名管道操作無名管道操作時,建立管道用pipe函數建立管道后Linux系統會同時為該進程建立2個文件描述符pipe_fd[0]和pipe_fd[1]。pipe_fd[0]用來從管道讀取數據pipe_fd[1]用來把數據寫入管道39無名管道操作**40無名管道操作步驟**創建管道創建進程讀/寫寫進程關閉管道的讀端讀進程關閉管道的寫端無名管道操作讀管道進程的有關規則如果進程讀一個寫端關閉的管道read()返回0,表示結束。如果進程讀一個寫端仍打開的空管道該進程休眠,直到管道中有新的輸入。如果進程試圖從管道中讀多于現有的字節返回當前的所有內容,read()返回實際讀取的字節。寫端讀端fd[0]fd[1]無名管道操作寫管道進程的有關規則如果進程寫一個讀端關閉的管道寫操作失敗,將一個SIGPIPE信號發送給寫進程。缺省操作為終止進程。如果進程寫入管道的字節數少于管道能保存的數write()保證是原子操作,即寫進程將完成它的系統調用,不會被另一個進程搶占。寫端讀端fd[0]fd[1]43管道的局限性*
管道的主要局限性正體現在它的特點上:只支持單向數據流;只能用于具有親緣關系的進程之間;沒有名字;管道的緩沖區是有限的(管道制存在于內存中,在管道創建時,為緩沖區分配一個頁面大小);管道所傳送的是無格式字節流,這就要求管道的讀出方和寫入方必須事先約定好數據的格式,比如多少字節算作一個消息(或命令、或記錄)等等;44范例***/*父進程借管道將字符串“hello!\n”傳給子進程并顯示*/#include<unistd.h>intmain(){ intfiledes[2]; charbuffer[80]; pipe(filedes); if(fork()>0){ /*父進程*/ chars[]=“hello!\n”; write(filedes[1],s,sizeof(s)); } else{ /*子進程*/ read(filedes[0],buffer,80); printf(“%s”,buffer); } exit(0);}457.2.1無名管道操作例7.5***:設計一個程序,要求創建一個管道,父進程往管道中寫入字符串,子進程從管道中讀取前輸出字符串。源程序代碼:487.2.1無名管道操作**497.2.3命名管道*若要在兩個不相關的進程之間用管道通信,需要用到命名管道FIFO。命名管道FIFO是通過Linux系統中的文件進行通信。命名管道的創建一般用mkfifo函數,創建成功后,就使用open、read、write等函數傳輸數據。有名管道FIFO不同于管道之處在于它提供一個路徑名與之關聯,以FIFO的文件形式存在于文件系統中。FIFO嚴格遵循先進先出(firstinfirstout),對管道及FIFO的讀總是從開始處返回數據,對它們的寫則把數據添加到末尾。50有名管道的操作步驟**創建(僅創建一次)~mkfifo()打開(以阻塞形式)~open()省略~阻塞O_NONBLOCK~非阻塞讀/寫~read()/write()關閉~close()517.2.3命名管道**mkfifo函數說明說明:如果mkfifo的第一個參數是一個已經存在的路徑名時,會返回EEXIST錯誤,所以一般典型的調用代碼首先會檢查是否返回該錯誤,如果確實返回該錯誤,那么只要調用打開FIFO的函數就可以了。一般文件的I/O函數都可以用于FIFO,如close、read、write等等。52PGO所有者用戶組其他用戶組RWX讀寫執行
0000101001101110:無任何權限2:只寫4:只讀6:可讀可寫7:可讀可寫可執行文件權限說明*文件權限由3位8進制數表示,分別代表:掩碼:訪問權限位的屏蔽字與文件訪問權限mode&~掩碼如,002表示屏蔽W假設:mode=(111)2掩碼:010則訪問權限:111^101 101 RWX53有名管道讀寫規則從FIFO中讀取數據如果有進程寫打開FIFO,且當前FIFO內沒有數據對于設置了阻塞標志的讀操作來說,一直阻塞;對于沒有設置了阻塞標志的讀操作來說,則返回-1,當前errno值為EAGAIN,以提醒以后再試。阻塞原因當前FIFO內有數據,但有其他進程在讀數據該FIFO當前FIFO內沒有數據沒有進程寫打開54有名管道讀寫規則向FIFO中寫入數據當要寫入的數據的數據量不大于PIPE_BUF時,Linux將保證寫入的原子性。如果此時管道空閑緩沖區不足以容納要寫入的字節數,則進入睡眠,直到空閑緩沖區緩沖區足以寫入時,才開始寫操作當寫入的數據量大于PIPE_BUF時,Linux將不再保證原子性。FIFO緩沖區一有空閑區域,寫進程試圖寫入數據,寫操作在寫完所有請求寫的數據后返回/usr/include/linux/limits.h中(RedHAT)# define
PIPE_BUF
409655范例**#include<sys/types.h>#include<sys/stat.h>#include<errno.h>#include<fcntl.h>intmain(){ charbuffer[80]; intfd; unlink(“myfifo”);/*FIFO文件必須是不存在*/ if((mkfifo("myfifo",0666)<0)&&(errno!=EEXIST))//讀寫權限
printf("cannotcreatefifoserver\n"); if(fork()>0){ chars[]="hello!\n"; fd=open("myfifo",O_WRONLY); write(fd,s,sizeof(s)); close(fd); } else{ fd=open("myfifo",O_RDONLY); read(fd,buffer,80); printf("%s",buffer); close(fd); } exit(0);}//fifo——execl應用56//fiforead.c**#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>intmain(){ charbuffer[80]; intfd; fd=open("myfifo",O_RDONLY); read(fd,buffer,80); printf("%s\n",buffer); close(fd); exit(0);}57//fifowrite.c**#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>intmain(){ charbuffer[80]; intfd; chars[]="hello!\n"; fd=open("myfifo",O_WRONLY); write(fd,s,sizeof(s)); close(fd); exit(0);}58//利用execl函數調用有名管道讀寫程序***#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<errno.h>#include<fcntl.h>intmain(){ unlink("myfifo"); if((mkfifo("myfifo",0666)<0)&&(errno!=EEXIST))//讀寫權限
printf("cannotcreatefifoserver\n"); printf("errono:%d\n",errno"); } if(fork()==0) if(execl("fifowrite",NULL)<0) perror("Erronexecl"); if(fork()==0) if(execl("fiforead",NULL)<0) perror("Erronexecl"); exit(0); return(0);}597.2.3命名管道例7.7***:設計兩個程序,要求用命名管道FIFO,實現簡單的聊天功能。60#include<stdio.h>#include<fcntl.h>#include<string.h>#include<stdlib.h>#include<sys/select.h>#include<sys/types.h>#include<sys/stat.h>#include<errno.h>intmain(){ inti,rfd,wfd,len=0,fd_in; charstr[32]; intflag,stdinflag; fd_setwrite_fd,read_fd; structtimevalnet_timer; umask(0111); if(mkfifo(“/home/2009/yjfifo1“,0666)<0) /*mkfifo函數創建命名管道*/ perror("mkfifo"); if(mkfifo("/home/2009/yjfifo2",0666)<0) /*mkfifo函數創建命名管道*/ perror("mkfifo"); rfd=open("/home/2009/yjfifo1",O_RDONLY); /*以只讀方式打開管道文件*/ wfd=open("/home/2009/yjfifo2",O_WRONLY); /*以寫方式打開管道文件*/ if(rfd<=0||wfd<=0) return0; printf("ThisisLiSi!\n"); FD_ZERO(&read_fd); /*清除一個文件描述符集*/
實例***61while(1) {
FD_SET(rfd,&read_fd);/*將文件描述符rfd加入文件描述符集read_fd*/ FD_SET(fileno(stdin),&read_fd); net_timer.tv_sec=5; net_timer.tv_usec=0; memset(str,0,sizeof(str));/*memset函數初始化清空*/ if(i=select(rfd+1,&read_fd,NULL,NULL,&net_timer)<=0) continue; if( FD_ISSET(rfd,&read_fd)) { read(rfd,str,sizeof(str));/*讀取管道,將管道內容存入str變量*/ printf("\n"); printf("ZhangSan:%s\n",str); /*打印輸出str變量內容*/ } if( FD_ISSET(fileno(stdin),&read_fd)) { printf("\n"); fgets(str,sizeof(str),stdin); len=write(wfd,str,strlen(str)); /*寫入管道*/ } } close(rfd); close(wfd);}62I/O處理的多工機制
select()函數*監視多個文件/設備等待讀寫(阻塞)異常情況I/O(0)輸出設備輸入設備文件讀/寫輸入設備輸入設備輸出設備輸出設備63Selec()函數*#include<sys/select.h>#include<sys/time.h>intselect(intmaxfd,/*指定測試的描述符最大值*/
fd_set*readfds,/*被監視的讀文件描述符集*/ fd_set*writefds,/*被監視的寫文件描述符集*/ fd_set*exceptfds,/*被監視的異常處理文件描述符集*/ conststructtimeval*timeout);/*等待時間*/
64返回值: 成功:文件描述符狀態已改變的個數 如果為0:timeout(超時) 失敗:-1,錯誤代碼errno功能用來監視多個文件描述符(filedescrīptor)的狀態(可讀、可寫或異常)變化的。程序會停在select這里等待,直到被監視的文件描述符有某一個或多個發生了狀態改變。
Selec()函數*65timeval的結構定義如下:structtimeval{ longtv_sec; //表示幾秒
longtv_usec;//表示幾微妙}timeout取不同的值,該調用就表現不同的性質:1.timeout為0,調用立即返回;2.timeout為NULL,select()調用就阻塞,直到知道有文件描述符就緒;3.timeout為正整數,就是一般的定時器。
66文件描述符集的處理(宏)*FD_ZERO(fd_set*fdset):清除文件描述符集fdset中的所有位(既把所有位都設置為0)FD_SET(intfd,fd_set*fdset):設置文件描述符集fdset中對應于文件描述符fd的位(設置為1)FD_CLR(intfd,fd_set*fdset):清除文件描述符集fdset中對應于文件描述符fd的位(設置為0)
FD_ISSET(intfd,fdset*fdset):檢測文件描述符集fdset中對應于文件描述符fd的位是否被設置
Fd_set:一個位圖類型的數據集67文件描述符集描述符集通常用整數數組中的位域表示,數組元素的每一位對應一個文件描述符。例如,一個整數占32位,那么整數數組的第一個元素代表文件描述符0到31,數組的第二個元素代表文件描述符32到63,以此類推。68文件描述符集文件描述符在形式上是一個非負整數。實際上,它是一個索引值,指向內核為每一個進程所維護的該進程打開文件的記錄表。當程序打開一個現有文件或者創建一個新文件時,內核向進程返回一個文件描述符。在程序設計中,一些涉及底層的程序編寫往往會圍繞著文件描述符展開。但是文件描述符這一概念往往只適用于UNIX、Linux這樣的操作系統。69文件描述符集#include<sys/select.h>#include<sys/time.h>fd_setreadset;FD_ZERO(&readset);FD_SET(5,&readset);FD_SET(33,&readset);FD_CLR(5,&readset);70范例:檢測有鍵盤輸入(標準輸入文件描述符:0)*#include<stdio.h>#include<unistd.h>#include<sys/select.h>#include<sys/time.h>Intmain(){ charbuf[80]; fd_setrdfds; structtimevaltv; intret; while(1){ FD_ZERO(&rdfds); FD_SET(0,&rdfds); tv.tv_sec=1; tv.tv_usec=0; ret=select(1,&rdfds,NULL,NULL,&tv);/*注意最大值還要加1*/ if(ret<0)perror("select"); /*出錯*/ if(ret==0)printf("timeout\n"); /*在設定的時間tv內,用戶沒有按鍵盤*/ else{ /*用戶有按鍵盤,要讀取用戶的輸入*/ scanf("%s",buf); printf("%s\n",buf); } } return(0);}71課堂練習***試編寫獨立的3個程序,要求分別完成如下功能:第一個程序每隔一秒產生1~100之間的隨機數(可用sleep函數);將數據寫入有名管道;第二個程序從有名管道讀取數據,并顯示;采用select函數限定阻塞等待時間為2秒。第三個程序創建有名管道;分別創建兩個子進程,并分別調用execl函數執行程序1和程序2;接收CTL_C信號后向前兩個進程發送結束信號;等待2個子進程結束后退出。7.3消息隊列消息隊列,就是一個消息的鏈表,是一系列保存在內核中的消息的列表。用戶進程可以向消息隊列尾部添加消息,也可以從消息隊列讀取消息,與管道通信非常相似。消息~是大小有限的數據塊(LINUX)(msg.h)每個消息的最大字節數MSGMAX:8192*消息隊列的最大長度MSGMNB:16384消息隊列的優勢:對每個消息指定特定消息類型,接收的時候不需要按隊列次序,而是可以根據自定義條件接收特定類型的消息。可以把消息看作一個記錄,具有特定的格式以及特定的優先級。7273消息隊列操作*與FIFO類似,但不需要open/close操作創建消息隊列標識符(queueID)。ftok()創建一個IPC函數所需的關鍵字key(某一資源識別代號).Msgget()~創建一個新隊列或打開一個存在的隊列;Sgsnd()~向隊列末端添加一條新消息;Msgrcv()~從隊列中讀取消息,讀取消息是不一定遵循先進先出的,也可以按消息的類型字段取消息.Msgctl()~消息隊列控制(刪除)
74消息隊列相關函數ftok函數#include<sys/types.h>#include<sys/ipc.h>Key_tftok(char*pathname,charproj)Pathname~必須是一個存在的可訪問的路徑或文件;Proj(子序號)~不得為0。功能:根據pathname和proj來創建一個systemVIPC函數所需的關鍵字key(代表某一資源的識別代號).75例*:#include<sys/types.h>#include<sys/ipc.h>key_tmykey;mykey=ftok(“/home/usr1/SHMkey”,1);或mykey=ftok(“.”,’a’);//與當前路徑結合產生key76創建/打開消息隊列*
:
#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>intmsgget(key_tkey,intmsgflg)
功能:創建一個新隊列或打開一個存在的隊列.返回值:成功則返回消息隊列ID,出錯則返回-1.參數:key:消息隊列的key值.Msgflg標志位~消息隊列的訪問權限IPC_CREAT~生成新的消息隊列IPC_PRIVATE~僅為當前進程所訪問77消息隊列創建APIintopen_queue(key_tkeyval){ int qid; if((qid=msgget(keyval,IPC_CREAT|0660))==-1) { return(-1); } return(qid);}78向隊列添加一條消息*intmsgsnd(intmsqid,structmsgbuf*msgp,intmsgsz,intmsgflg);
功能:向消息隊列發送一個消息.參數:Msgid~消息隊列IDMsgp~指向即將發送的消息(存儲在的msgbuf結構中),Msgze~消息的大小。Msgflg~用來控制消息隊列滿載時,若設置了IPC_NOWAIT,則在消息隊列沒有足夠空間時立即返回,否則等待直到滿足條件。79structmsgbuf{ longmtype;/*typeofmessage*/ charmtext[];/*messagetext*/
…
…};80添加消息隊列APIintsend_message(intqid,structmsgbuf*qbuf){ int result,length; length=sizeof(structmsgbuf)-sizeof(long); if((result=msgsnd(qid,qbuf,length,0))==-1) return(-1); return(result);}81從消息隊列中讀取一個消息*intmsgrcv(intmsqid,structmsgbuf*msgp,intmsgsz,longmsgtyp,intmsgflg);
功能:從msgid代表的消息隊列中讀取一個消息,并把消息存儲在msgp指向的msgbuf結構中。返回:成功~返回實際讀到的信息數據長度。 失敗~返回-1主要參數Msqid~消息隊列描述字;Msgsz~指定msgbuf的長度(即消息內容的長度)82從消息隊列中讀取一個消息Msgtyp~請求讀取的消息類型;Msgtyp=0~接收第一個到來的消息;Msgtyp〉0~接收第一個到來的與此類型相同的消息;Msgtyp<0~接收第一個到來的等于或小于此類型絕對值的消息;Msgflg~消息標志。可以為以下幾個常值的或:IPC_NOWAIT~如果沒有滿足條件的消息,調用立即返回;IPC_EXCEPT~與msgtyp>0配合使用,返回隊列中第一個類型不為msgtyp的消息;IPC_NOERROR~如果隊列中滿足條件的消息內容大于所請求的msgsz字節,則把該消息截斷,截斷部分將丟失。Msgflg=0時,msgsnd()及msgrcv()在隊列呈滿或呈空的情形時,采取阻塞等待的處理模式83從消息隊列中讀取一個消息APIintread_message(intqid,longtype,structmymsgbuf*qbuf){intresult,length;length=sizeof(structmymsgbuf)-sizeof(long);if((result=msgrcv(qid,qbuf,length,type,0))==-1) return(-1);return(result);}84消息隊列控制msgctl()*intmsgctl(intmsqid,intcmd,structmsqid_ds*buf);
調用返回:成功返回0,否則返回-1。cmd操作,共有三種cmd操作IPC_STAT、IPC_SET、IPC_RMID。IPC_STAT:該命令用來獲取消息隊列信息,返回的信息存貯在buf指向的msqid結構中;IPC_SET:該命令用來設置消息隊列的屬性,要設置的屬性存儲在buf指向的msqid結構中;可設置屬性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同時,也影響msg_ctime成員。IPC_RMID:刪除msqid標識的消息隊列;
85范例:刪除消息隊列*msgctl(mqid,IPC_RMID,NULL);867.3消息隊列例7.8**:設計一個程序,要求創建消息隊列,輸入的文字添加到消息隊列后,讀取隊列中的消息輸出。源程序代碼:8788由此例可知,進程間通過消息隊列通信,主要是創建或打開消息隊列、添加消息、讀取消息和控制消息隊列這四種操作。89#include<sys/ipc.h>#include<sys/msg.h>#include<sys/stat.h>#include<sys/types.h>#include<stdio.h>#include<fcntl.h>#include<signal.h>#include<stdlib.h>#include<string.h>#definePROJID0xFF#defineLUCY1#definePETER2intmqid;voidterminate_handler(intsigno){ msgctl(mqid,IPC_RMID,NULL); exit(0);}實例**
90intmain(){ charfilenm[]="msg"; key_tmqkey; structmsgbuf{ longmtype; /*messagetype,mustbe>0*/ charmtext[256];/*messagedata*/ }msg; intret; mqkey=ftok(filenm,PROJID); if(mqkey==-1){ perror("ftokerror:"); exit(-1); } mqid=msgget(mqkey,IPC_CREAT|0666); if(mqid==-1){ perror("msggeterror:"); exit(-1); } signal(SIGINT,terminate_handler); signal(SIGTERM,terminate_handler); while(1){ printf("Lucy:"); fgets(msg.mtext,256,stdin); if(strncmp("quit",msg.mtext,4)==0){ msgctl(mqid,IPC_RMID,NULL); exit(0); } msg.mtext[strlen(msg.mtext)-1]='\0'; msg.mtype=LUCY; msgsnd(mqid,&msg,strlen(msg.mtext)+1,0); msgrcv(mqid,&msg,256,0,0); printf("Peter:%s\n",msg.mtext); }}91課堂練習仿照例7-7,采用消息隊列操作編寫簡易聊天程序。作業FIFO和消息隊列機制的對比分析7.4共享內存93Linux中進程間通信進程1進程2管道pipesignal消息隊列共享內存共享內存Mmap建立共享內存映射Munmap建立共享內存映射文件操作內存操作Memcpystrcpy文件內存95一、系統V共享內存概述共享內存*特定的內存中一段存儲區多個進程共享此存儲區特點*最快的IPC方式信息量大數據可維持共享內存原理示意圖96≈≈二、共享內存操作步驟*≈≈創建共享內存或打開映射共享內存讀/寫操作共享內存解除共享內存映射控制共享內存(刪除)97≈≈二、共享內存操作步驟*≈≈創建共享內存或打開(已有的shm)映射共享內存讀/寫操作共享內存解除共享內存映射控制共享內存(刪除)98三、共享內存API*創建共享內存~shmget()用來獲得共享內存區域的ID,如果不存在指定的共享區域就創建相應的區域。映射共享內存~shmat()當前進程與共享內存區連接。撤銷共享內存~shmdt()解除當前進程與共享內存區的映射。控制共享內存~shmctl()實現對共享內存區域的控制操作(刪除)。99三、共享內存API(1)—創建* #include<sys/ipc.h>#include<sys/shm.h>intshmget(key_tkey,size_tsize,intshmflg)參數:名字(key):ftok()或整形數大小(size):字節為單位訪問方式(shmflg):讀、寫,etc.訪問權限:所有者、同組用戶、其他用戶(UGO)如,0644(同組和其他用戶只讀)可與IPC_CREAT進行邏輯或,表示創建新的共享內存或讀取已有的共享內存IPC_CREATE:表明要創建新的共享內存空間。IPC_EXCL:只有在共享內存不存在的時候,新的共享內存才建立,否則就產生錯誤。IPC_EXEL標志本身并沒有太大的意義,但是和IPC_CREAT標志一起使用可以用來保證所得的對象是新建的,而不是打開已有的對象。返回:標識(ID)
100intshmget(key_tkey,size_tsize,intshmflg)成功返回共享內存的標識符;不成功返回-1,errno儲存錯誤原因。EINVAL
參數size小于SHMMIN或大于SHMMAX。EEXIST
預建立key所致的共享內存,但已經存在。EIDRM
參數key所致的共享內存已經刪除。ENOSPC
超過了系統允許建立的共享內存的最大值 (SHMALL)。ENOENT
參數key所指的共享內存不存在,參數shmflg 也未設IPC_CREAT位。EACCES
沒有權限。ENOMEM
核心內存不足。
注:perror(“操作”)的執行后將以errno(錯誤代碼)的值來決定輸出的字符串。101創建共享內存*示例#include<sys/ipc.h>#include<sys/shm.h>….int shmid,shmSize=1024;key_tshmKey;shmKey=ftok(“.”,’a’);shmId=shmget(shmKey,shmSize,IPC_CREAT|0666)102三、共享內存API(2)—映射*#include<sys/types.h>#include<sys/shm.h>void*shmat(intshmid,constvoid*shmaddr,intshmflg);功能:將當前進程與共享內存連接,即獲取共享內存首地址。參數:
shmid:shnget的返回值
shmaddr:當前進程的地址空間的具體位置。
設為NULL指針~將由系統指定
shmflg:訪問方式。
0~可讀寫;
SHM_RDONLY~只讀(即使已設置了寫的權限)103三、共享內存API(2)—映射*shmat()函數返回值成功~返回共享內存的首地址失敗~返回-1(錯誤原因存于errno中);錯誤代碼:EACCES:對于所請求的連上類型,進程沒有足夠的權限,并且不具有CAP_IPC_OWNER權能EINVAL:參數無效
ENOMEM:內存不足,無法分配描述詞或頁表104共享內存API(2)—映射*示例char*shmAddr;shmId=shmget(shmKey,shmSize,IPC_CREAT|0666)shmAddr=(char*)shmat(shmId,NULL,0)105共享內存API(3)—解除*intshmdt(char*shmaddr);功能:解除進程對共享內存區域的連接。 *進程結束時,脫離共享內存。Shmaddr~由smat()返回的共享內存地址。返回值0~成功,-1~失敗(錯誤原因存于errno中)106共享內存API(4)—刪除*intshmctl(intshmqid,intcmd,structshmid_ds*buf);返回值:0成功,-1錯誤CmdIPC_RMID~刪除共享內存IPC_STAT~把共享內存的shmid_ds結構復制到buf107structshmid_ds{structipc_permshm_perm;/*操作權限*/intshm_segsz;/*段的大小(以字節為單位)*/time_tshm_atime;/*最后一個進程連接到該段的時間*/time_tshm_dtime;/*最后一個進程脫離該段的時間*/time_tshm_ctime;/*最后一次修改這個結構的時間*/unsignedshortshm_cpid;/*創建該段進程的pid*/unsignedshortshm_lpid;/*在該段上操作的最后一個進程的pid*/shortshm_nattch;/*當前連接到該段的進程的個數*/ unsignedshortshm_npages;/*段的大小(以頁為單位)*/unsignedlong*shm_pages;/*指向frames->SHMMAX的指針數組*/structvm_area_struct*attaches;/*對共享段的描述*/};structipc_perm{
__kernel_key_t
key;
__kernel_uid_t
uid;
__kernel_gid_t
gid;
__kernel_uid_t
cuid;
__kernel_gid_t
cgid;
__kernel_mode_tmode;
unsignedshort
seq;
};108char*initShrMem(intshmKey,intshmSize){char*shmAddr; /*創建共享內存*/if((shmId=shmget(shmKey,shmSize,IPC_CREAT|0666))==-1) return-1;/*共享內存與目前進程的連接*/if((int)(shmAddr=(char*)shmat(shmId,0,0))==-1){/*共享內存的釋放*/shmctl(shmId,IPC_RMID,NULL);shmId=0;return-1;}/*共享內存的初始化*/memset(shmAddr,'\0',shmSize);return(shmAddr);}共享內存初始化范例***109intexitShrMem(char*shmAddr){/*使共享內存脫離進程*/if(shmdt(shmAddr)==-1){ perror("detacherror"); return-1;}/*共享內存釋放*/if(shmctl(shmId,IPC_RMID,NULL)==-1){ perror("detacherror"); return-1;}return1;}共享內存撤銷范例***110共享內存的讀寫***#include<string.h>(1)voidmemcpy(void*dest,//目的地址
constvoid*src,//源地址
siz_tn)//拷貝的字節數(2)voidstrcpy(void*dest,//目的地址
constvoid*src,//源地址
siz_tn)//拷貝的字節數≈≈111共享內存操作引例***
創建共享內存創建子進程子進程連接共享內存寫共享內存脫離共享內存父進程讀共享內存連接共享內存讀共享內存狀態信息讀共享內存數據脫離共享內存刪除讀共享內存112//shm0.c共享內存操作引例***
#include<string.h>#include<sys/ipc.h>#include<sys/shm.h>#include<sys/types.h>#defineKEY1234#defineSIZE1024intmain(){ intshmid,pid; char*shmaddr; structshmid_dsbuf;
shmid=shmget(KEY,SIZE,IPC_CREAT|0666); if((pid=fork())==0){ printf("Myparent'pid=%d\n",getppid());
shmaddr=(char*)shmat(shmid,NULL,0); strncpy(shmaddr,"Hi!Iamchildprocess!\n“); shmdt(shmaddr); return1; }113else{ printf("childpid=%d\n",pid); sleep(1);
shmctl(shmid,IPC_STAT,&buf);
/*段的大小(以字節為單位)*/ printf("shm_segsz=%d\n",buf.shm_segsz);
/*創建該段進程的pid*/ printf("shm_cpid=%d\n",buf.shm_cpid); /*在該段上操作的最后一個進程的pid*/ printf("shm_lpid=%d\n",buf.shm_lpid);
shmaddr=(char*)shmat(shmid,NULL,0); /*讀取共享內存*/ printf("%s",shmaddr); shmdt(shmaddr); shmctl(shmid,IPC_RMID,NULL); }}114共享內存實例**分別編寫兩個程序,完成共享內存的讀與寫操作。要求: 待寫入的數據為隨機浮點數; 每隔一秒采集一次不同通道(共8個),并寫入共享內存;
循環寫入無限次;
ctl_c退出程序。
通道選擇開關1N0IN1IN2IN3IN4IN5IN6IN7ABC收發進程1115共享內存實例**數據結構定義typedefstruct{ intch; /*通道號*/ doublepower;/*被測數據*/}data;116//shm_com.h**/*Acommonheaderfiletodescribethesharedmemorywewishtopassabout.*/#include<unistd.h>#include<string.h>#include<stdlib.h>#include<sys/ipc.h>#include<sys/shm.h>#include<signal.h>#include<sys/types.h>#define NUMBER8typedefstruct{ intch; doublepower;}data;117//shm1.c**#include"shm_com.h"intexitSig=0;voidexitProc(intn){ printf("Ctrl_C\n"); exitSig=1; raise(SIGHUP);}intmain(){ intshm_id,i; key_tkey;
data*p_map,*pshmwr,wdata;
(void)signal(SIGINT,exitProc); key=ftok(".",'a'); if(key==-1) perror("ftokerror");
shm_id=shmget(key,sizeof(data)*NUMBER,IPC_CREAT|0666); if(shm_id==-1){ perror("shmgeterror"); return; }118
p_map=(data*)shmat(shm_id,NULL,0); if(p_map==(data*)-1){ perror("shmaterror"); return; } while(!exitSig) { pshmwr=p_map; for(i=0;i<NUMBER;i++) { wdata.ch=i; wdata.power=drand48()*10.0;/*隨機浮點數*/
memcpy(pshmwr,&wdata,sizeof(data)); printf("ch=%dpower=%f\n",pshmwr->ch,pshmwr->power); pshmwr++; sleep(1); } } if(shmdt(p_map)==-1) perror("detacherror");}119//shm2.c**#include"shm_com.h"intexitSig=0;voidexitProc(intn){ printf("Ctrl_C\n"); exitSig=1; raise(SIGHUP);}intmain(){ intshm_id,i; key_tkey; data*p_map,*pshmrd,rdata;
(void)signal(SIGINT,exitProc); key=ftok(".",'a'); if(key==-1) perror("ftokerror");
shm_id=shmget(key,sizeof(data)*NUMBER,IPC_CREAT|0666); if(shm_id==-1){ perror("shmgeterror"); return; }120
p_map=(data*)shmat(shm_id,NULL,0); if(p_map==(data*)-1){ perror("shmaterror"); return; } while(!exitSig){ pshmrd=p_map; for(i=0;i<NUMBER;i++) {
memcpy(&rdata,pshmrd,sizeof(data));
printf("ch=%dpower=%f\n",rdata.ch,rdata.power); pshmrd++; sleep(1); } }
if(shmdt(p_map)==-1)
perror("detacherror"); if(shmctl(shm_id,IPC_RMID,NULL)==-1)
perror("shmctlerror");}121課外補充(1)信號量**在多用戶、多任務系統中,對某一資源排他操作信號量提供對進程間共享資源訪問控制機制,相當于內存中的標志。進程可以根據它判定是否能夠訪問某些共享資源,同時,進程也可以修改該標志。除了用于訪問控制外,還可用于進程同步。共享資源(M/IO)122信號量**信號量對臨界段操作,需要確保只有一個進程獨占P(sv)操作若sv>0則sv-1,若sv=0則讓進程暫停,等待對共享資源的操作V(sv)操作如果有等待對共享資源的操作的進程,即等待Sv>0,則該進程繼續執行。如果沒有等待進程,則sv+1臨界有效(空閑)時,sv=true(>0),執行P(sv)操作,使得sv變為false,臨界忙臨界無效(忙)時,V(sv),sv=true123信號量API
**信號量操作UNIX采用信號量數組打開或創建信號量semget()
信號量值操作semop()獲得或設置信號量屬性semctl()
124信號量的創建*#include<sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>Intsemget(Key_tkey,intnum_sems,intsem_flags)功能:創建一個新的信號量,或者獲取一個已存在的信號量ID。類似于文件打開操作,返回類似于文件描述符的值,信號量描述符。返回值: 成功~非零值(>0),失敗~-1參數說明:key~標識信號量資源。與文件名類似。用于不同的進程間使用同一個信號量的Key;Num_sems:信號量個數。對于一個資源,常定義為1;Sem_flags:信號量訪問權限,如,IPC_CREAT|0666125范例:信號量**intopenSemId(key_tkey){intsemid; semid=semget(key,1,IPC_CREAT|0666);if(semid==-1){ perror("semget");return-1;}returnsemid;}126*信號量控制int
semctl(int
semid,int
semnum,int
cmd,union
semun
arg);功能:對信號量進行一系列的控制。Semid~要操作的信號描述符Semnum~信號的個數(0:代表1個信號量)信號量是數組。Cmd~操作的命令.經常用的兩個值是:SETVAL(設置信號量的初值)IPC_RMID(刪除信號量).arg是一個給cmd的參數.unionsemun{ intval; structsemid_ds*buf; unsignedshort*arry;}127范例:信號量初始化**intsetSemvalue(intsemid){ unionsemunsemUnion; semUnion.val=1; if(semctl(semid,0,SETVAL,semUnion)==-1)return-1; return1;}128范例:信號量操作—刪除操作**intdelSemaphore(intsemid){ if(semctl(semid,0,IPC_RMID)==-1){ perror("semctlIPC_RMID"); return-1; } return1;}129信號量操作(P、V)intsemop(intsemid,structsembuf*semops,unsignednsops);功能: 改變信號量值。參數:semid~semget()的返回值,信號量的描述符;Sops~指向一個結構體數組的指針; structsembuf{ shortsem_num; //=0(代表第一個信號量) shortsem_op; //(-1,1)p,v shortsem_flg; //SEM_UNDO }nsops~sops結構體數組的個數,一般設為1。
130*structsembuf{ shortsem_num;//=0(代表第一個信號量) shortsem_op; //(-1,1)p,v shortsem_flg; //SEM_UNDO }Sem_num:欲處理的信號編號,0代表第一個信號量sem_op:-1:對資源加鎖(P操作)+1:對資源解鎖(V操作)sem_flg=SEM_UNDO表示進程沒有釋放信號量而終止的時候,系統自動釋放該進程所使用的信號量。131范例:信號量操作—P操作**intlockShrMem(intsemId){structsembufsops;sops.sem_num=0;sops.s
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年安徽綠海商務職業學院高職單招高職單招英語2016-2024歷年頻考點試題含答案解析
- B超肝區知識課件
- 診療常規及技術規范知識培訓
- ccaa服務認證知識課件
- 倉儲物流計件勞動合同
- 晉中學院《熱質交換原理與設備》2023-2024學年第一學期期末試卷
- 陜西省藍田縣聯考2024-2025學年初三下學期二模考試英語試題試卷含答案
- 人教版數學2.百分數(二)折扣同步練習六年級下冊含答案
- 2024年八月跨河輸氣管道浮船輔助拆除水流監測合同
- 鄭州工業安全職業學院《中西醫結合內科學(一)》2023-2024學年第一學期期末試卷
- 《知識產權執法》課件
- 2024年大學試題(管理類)-港口企業管理學歷年高頻考點試卷專家薈萃含答案
- 高中化學-分子晶體和原子晶體教學設計學情分析教材分析課后反思
- 橋梁養護風險辨識手冊
- 2021年青海省中考化學試卷(附答案詳解)
- 《曼陀羅繪畫療愈-初三減壓》PPT
- 小學生三好學生競選演講稿PPT幻燈片
- 養老機構員工考核評分表
- 北京市海淀區2022-2023學年高三下學期一模考試歷史試卷(含答案)
- 季節性安全檢查表(四季)
- 2023年貴州省中學生生物學競賽考試(初賽)試題( 含答案解析 )
評論
0/150
提交評論