進程間通信管道和信號_第1頁
進程間通信管道和信號_第2頁
進程間通信管道和信號_第3頁
進程間通信管道和信號_第4頁
進程間通信管道和信號_第5頁
已閱讀5頁,還剩69頁未讀 繼續免費閱讀

下載本文檔

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

文檔簡介

12進程間通信-PIPE

進程間通信―FIFO

3信號中斷處理

進程間通信——管道和信號進程通信有如下一些目的:

A、數據傳輸:一個進程需要將它的數據發送給另一個進程,發送的數據量在一個字節到幾M字節之間;

B、共享數據:多個進程想要操作共享數據,一個進程對共享數據的修改,別的進程應該立刻看到;

C、通知事件:一個進程需要向另一個或一組進程發送消息,通知它(們)發生了某種事件(如進程終止時要通知父進程);

D、資源共享:多個進程之間共享同樣的資源。為了做到這一點,需要內核提供鎖和同步機制;

E、進程控制:有些進程希望完全控制另一個進程的執行(如Debug進程),此時控制進程希望能夠攔截另一個進程的所有陷入和異常,并能夠及時知道它的狀態改變。2023/2/12進程間通信的方式linux下的進程通信手段基本上是從Unix平臺上的進程通信手段繼承而來的。而對Unix發展做出重大貢獻的兩大主力AT&T的貝爾實驗室及BSD(加州大學伯克利分校的伯克利軟件發布中心)在進程間通信方面的側重點有所不同。前者對Unix早期的進程間通信手段進行了系統的改進和擴充,形成了“systemVIPC”,通信進程局限在單個計算機內;后者則跳過了該限制,形成了基于套接口(socket)的進程間通信機制。Linux則把兩者繼承了下來,其中,最初UnixIPC包括:管道、FIFO、信號;SystemVIPC包括:SystemV消息隊列、SystemV信號量、SystemV共享內存區;PosixIPC包括:Posix消息隊列、Posix信號量、Posix共享內存區。2023/2/13Linux進程通信方式linux下進程間通信的幾種主要手段簡介:1、管道(Pipe)及有名管道(namedpipe):管道可用于具有親緣關系進程間的通信,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關系進程間的通信;2、信號(Signal):信號是比較復雜的通信方式,用于通知接受進程有某種事件發生,除了用于進程間通信外,進程還可以發送信號給進程本身;linux除了支持Unix早期信號語義函數signal外,還支持語義符合Posix.1標準的信號函數sigaction。2023/2/14Linux進程通信方式3、報文(Message)隊列(消息隊列):消息隊列是消息的鏈接表。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩沖區大小受限等缺點。4、共享內存:使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。5、信號量(semaphore):主要作為進程間以及同一進程不同線程之間的同步手段。2023/2/15管道管道的實質是一個內核緩沖區,進程以先進先出的方式從緩沖區中存取數據:管道一端的進程順序地將數據寫入緩沖區,另一端的進程則順序地讀出數據。該緩沖區可以看作是一個循環隊列,讀和寫的位置都是自動增加的,不能隨意改變,一個數據只能被讀一次,讀出以后在緩沖區中就不復存在了。當緩沖區讀空或寫滿時,有一定的規則控制相應的讀進程或寫進程是否進入等待隊列;當空的緩沖區有新數據寫入或滿的緩沖區有數據讀出時,就喚醒等待隊列中的進程繼續讀寫。2023/2/16管道管道實際上以類似文件的方式與進程交互,但它并不與磁盤打交道,所以效率要比文件操作高很多。它有兩個局限性:(1)支持半雙工;(2)只有具有親緣關系的進程之間才能使用這種無名管道;使用管道的注意事項:1.當讀一個寫端已經關閉的管道時,在所有數據被讀取之后,read函數返回值為0,以指示到了文件結束處;2.如果寫一個讀端關閉的管道,則產生SIGPIPE信號。如果忽略該信號或者捕捉該信號并處理程序返回,則write返回-1,errno設置為EPIPE2023/2/17管道示例例如$ls|more功能是將ls命令的輸出作為more命令的輸入,并顯示more的最終輸出。這里ls與more要由兩個進程來完成。這兩個進程的通信就通過父進程shell創建管道。ls向管道輸入數據,more從管道讀出數據。2023/2/18stdinstdinstdoutstdoutstderrstderrlsmore管道連接管道用于不同進程間通信。通常先創建一個管道,再通過fork函數創建一個子進程,該子進程會繼承父進程所創建的管道。創建無名管道externintpipe(int__pipedes[2])此函數的參數是一個整型數組。如果執行成功,pipe將存儲兩個整形文件描述符于__pipedes數組中,它們分別指向管道的兩端。如果系統調用失敗,將返回-1。2023/2/110注意:fd[0]用于讀取管道,fd[1]用于寫入管道。圖1linux中管道與文件描述符的關系#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]);}管道讀寫管道主要用于不同進程間通信。實際上,通常先創建一個管道,再通過fork函數創建一個子進程。圖2父子進程管道的文件描述符對應關系子進程寫入和父進程讀的命名管道:圖

關閉父進程fd[1]和子進程fd[0]例:父子進程通過無名管道通信#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<sys/wait.h>#include<unistd.h>#include<string.h>main(){ pid_tresult; intr_num; intpipe_fd[2]; charbuf_r[100],buf_w[100];

memset(buf_r,0,sizeof(buf_r));2023/2/115例:父子進程通過無名管道通信 if(pipe(pipe_fd)<0) { perror("pipe"); exit(EXIT_FAILURE); } result=fork(); if(result<0) { perror("fork"); exit(EXIT_FAILURE); } elseif(result==0) { close(pipe_fd[1]); if((r_num=read(pipe_fd[0],buf_r,100))>0) printf("childprocesshasread%dcharactersfromthepipe,thestringis:%s\n",r_num,buf_r); close(pipe_fd[0]); exit(0); }2023/2/116例:父子進程通過無名管道通信 else { close(pipe_fd[0]); printf("pleaseinputthestring:"); scanf("%s",buf_w); if(write(pipe_fd[1],buf_w,strlen(buf_w))!=-1) printf("parentprocesshaswritten:%stothepipe!\n",buf_w); close(pipe_fd[1]); waitpid(result,NULL,0); exit(0); }}2023/2/117dup()externintdup(int__fd)dup()會復制一份原來已經打開的文件描述符,新的描述符指向系統文件表中下一個可用的最小非負文件描述符,它與原來的文件描述符共享同一個文件指針,并擁有相同的文件權限及模式。當調用dup()時,總返回下一個最小的可用文件描述符。例如:下面語句即可以將輸出重定向到管道:intf_des[2];pipe(f_des);close(fileno(stdout));dup(f_des[1])此后,所有寫向標準輸出的數據都將寫入到管道中。因此,要復制標準輸出輸入設備,應先關閉這一設備,然后再復制。2023/2/118復制文件描述符2023/2/119dup2()externintdup2(int__fd,int__fd2)dup2()有兩個參數,fd和fd2,fd2是小于文件描述符的最大允許值的非負整數。如果fd2是一個已打開的文件描述符,則首先關閉該文件,然后再復制。2023/2/120綜合應用舉例#include<errno.h>#include<sys/wait.h>#include<stdio.h>#include<stdlib.h>#include<unistd.h>#defineDEF_PAGER"/bin/more“#defineMAXLINE10main(intargc,char*argv[]){ intn; intfd[2]; pid_tpid; char*pager,*argv0; charline[MAXLINE]; FILE*fp;2023/2/121綜合應用舉例 if(argc!=2) { printf("errorusage!\n"); exit(EXIT_FAILURE); } if((fp=fopen(argv[1],"r"))==NULL) { perror("open"); exit(EXIT_FAILURE); } if(pipe(fd)<0) { perror("pipe"); exit(EXIT_FAILURE); } if((pid=fork())<0) { perror("fork"); exit(EXIT_FAILURE); }

2023/2/122綜合應用舉例 if(pid>0) { close(fd[0]); while(fgets(line,MAXLINE,fp)!=NULL) { n=strlen(line); if(write(fd[1],line,n)!=n) { perror("write"); exit(EXIT_FAILURE); }} close(fd[1]); if(waitpid(pid,NULL,0)<0) { perror("wait"); exit(EXIT_FAILURE); } exit(0); }

2023/2/123綜合應用舉例 else { close(fd[1]); if(fd[0]!=STDIN_FILENO) { if(dup2(fd[0],STDIN_FILENO)!=STDIN_FILENO) { perror("dup"); exit(EXIT_FAILURE); } close(fd[0]); } if((pager=getenv("PAGER"))==NULL) pager=DEF_PAGER; if((argv0=strrchr(pager,'/'))!=NULL) argv0++; else argv0=pager; if(execl(pager,argv0,(char*)0)<0) { perror("exec"); exit(EXIT_FAILURE); } exit(0); }}2023/2/124流重定向externFILE*popen(__constchar*__command,__constchar*__modes);popen函數創建一個子進程,并在子進程中執行第一個參數程序,同時返回一個文件指針,即第一個參數*__command指向要執行的命令的指針。第二個參數表示I/O模式的類型。如果此命令的輸出將作為其他命令的輸入,即輸出重定向,則需要設置其第二個參數為“r”權限;如果此命令的輸入數據要從其他命令的輸出數據,即輸入重定向,則需要設置其第二個參數為“w”權限;在使用完重定向后,需要使用pclose()關閉相應的流對象,該函數聲明如下:externintpclose(FILE*__stream);2023/2/125例:流重定向的應用#include<errno.h>//#include<sys/wait.h>#include<stdio.h>#include<stdlib.h>#include<unistd.h>#definePAGER"${PAGER:-more}“#defineMAXLINE10main(intargc,char*argv[]){ charline[MAXLINE]; FILE*fpin,*fpout; if(argc!=2) { printf("errorusage!\n"); exit(EXIT_FAILURE); }2023/2/126例:流重定向的應用 if((fpin=fopen(argv[1],"r"))==NULL) { perror("open"); exit(EXIT_FAILURE); } if((fpout=popen(PAGER,"w"))==NULL) { perror("popen"); exit(EXIT_FAILURE); } while(fgets(line,MAXLINE,fpin)!=NULL) { if(fputs(line,fpout)==EOF) { perror("fputs"); exit(EXIT_FAILURE); } } if(pclose(fpout)==-1) { perror("pclose"); exit(EXIT_FAILURE); } exit(0);}2023/2/127注意事項:1.如果所有指向管道寫端的文件描述符都關閉了,而仍然有進程從管道的讀端讀數據,那么管道中剩余的數據都被讀取后,再次read會返回0,就像讀到文件末尾一樣。2.如果有指向管道寫端的文件描述符沒有關閉,而持有管道寫端的進程也沒有向管道中寫數據,這時有進程從管道讀端讀數據,那么管道中剩余的數據都被讀取后,再次read會阻塞,直到管道中有數據可讀了才讀取數據并返回。3.如果所有指向管道讀端的文件描述符都關閉了,這時有進程向管道的寫端write,那么該進程會收到信號SIGPIPE,通常會導致進程異常終止。2023/2/128注意事項:4.如果有指向管道讀端的文件描述符沒有關閉,而持有管道讀端的進程也沒有從管道中讀數據,這時有進程向管道寫端寫數據,那么在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入數據并返回。5.兩個進程通過一個管道只能實現單向通信,如果有時候也需要子進程寫父進程讀,就必須另開一個管道。2023/2/129進程間通信-管道和信號12進程間通信-PIPE

進程間通信―FIFO

3信號中斷處理

FIFOFIFO就是命名管道,或有名管道。它同樣是基于VFS,對應的文件類型就是FIFO文件,可以通過mknod命令在磁盤上創建一個FIFO文件。當進程想通過該FIFO來通信時就可以標準的APIopen(close、read、write、unlink等)打開該文件,然后開始讀寫操作。對于FIFO的讀寫實現,它與pipe是相同的。區別在于,FIFO有open這一操作,而pipe是在調用pipe這個系統調用時直接創建了一對文件描述符用于通信。并且,FIFO的open操作還有些細致的地方要考慮,例如如果寫者先打開,尚無讀者,那么肯定是不能通信了,所以就需要先去睡眠等待讀者打開該FIFO,反之對讀者亦然。2023/2/131用途FIFO由shell命令使用以便將數據從一條管道線傳送到另一條,為此無需創建中間臨時文件。FIFO用于客戶進程-服務器進程應用程序中,以在客戶進程和服務器進程之間傳遞數據。2023/2/132創建FIFO/CreateanewFIFOnamedPATH,withpermissionbitsMODE.*/externintmkfifo(__comstchar*__path,__mode_t__mode)mkfifo()會根據參數建立特殊的有名管道文件,該文件必須不存在,而參數mode為該文件的授權,mkfifo()建立的FIFO文件其他進程都可以用讀寫一般文件的方式存取。當使用open()函數打開FIFO文件時,O_NONBLOCK會有影響。如果執行成功將返回0,否則返回-1,失敗原因存儲于errno中。2023/2/133例:命名管道的使用#include<sys/types.h>#include<sys/fcntl.h>#include<fcntl.h>#include<stdlib.h>#include<stdio.h>#defineFIFO"/tmp/fifo“main(){ pid_tpid; charbuffer[80]; intfd; unlink(FIFO); mkfifo(FIFO,0744);2023/2/134例:命名管道的使用 if((pid=fork())>0) { chars[]="Hello!"; fd=open(FIFO,O_WRONLY); printf("thisisfatherwritedatais%s\n",s); printf("father'spidis%d\n",getpid()); write(fd,s,sizeof(s)); close(fd); exit(0); }

2023/2/135例:命名管道的使用 elseif(pid==0) { sleep(2); fd=open(FIFO,O_RDONLY); read(fd,buffer,80); printf("thisischildreaddatais:%s\n",buffer); close(fd); printf("child'spidis%d\n",getpid()); exit(0); }}2023/2/136管道基本特點總結兩類型管道具有以下特點:(1)管道是特殊類型的文件,在滿足先入先出的原則條件下可能進行讀寫,但不能定位讀寫位置。(2)管道是單向的,要實現雙向,需要兩個管道。無名管道只能實現親緣關系進程間通信(即無名管道的兩個文件描述符可以被兩者都訪問到),而有名管道以磁盤文件的方式存在,可以實現本機任意兩進程間通信。2023/2/137管道基本特點總結(續)(3)無名管道阻塞問題。無名管道無須顯式打開,創建時直接返回文件描述符,而在讀寫時需要確實對方的存在,否則將退出。即如果當前進程向無名管道的一端寫數據時,必須確定其另一端為某個進程(這個進程可以是當前進程)擁有。如果寫入無名管道的數據超過其最大值,寫操作將阻塞,如果管道中沒有數據,讀操作將阻塞,如果管道發現另一端斷開(另一端文件描述符關閉),將自動退出。2023/2/138管道基本特點總結(續)(4)有名管道阻塞問題。有名管道在打開時需要確實對方的存在,否則將阻塞。即以讀方式打開某管道,該操作得以繼續執行的條件是:在此之前,已經有一個進程以寫的方式打開此管道,否則阻塞,直到條件滿足,因此有名管道將阻塞在打開位置。也可以以讀寫(O_RDWR)方式打開有名管道,進程能夠繼續執行(不阻塞),即當前進程讀,當前進程寫。2023/2/139進程間通信-管道和信號12進程間通信-PIPE

進程間通信―FIFO

3信號中斷處理

信號概述信號是Linux系統中用于進程之間相互通信或操作的一種機制。信號可以在任何時候發給某一進程,而無需知道該進程的狀態。如果該進程當前并未處于執行狀態,則該信號就由內核保存起來,直到該進程恢復執行并傳遞給它為止;如果一個信號被進程設置為阻塞,則該信號的傳遞被延遲,直到其阻塞被取消時才被傳遞給進程。進程之間可以互相通過系統調用kill發送軟中斷信號。內核也可以因為內部事件而給進程發送信號,通知進程發生了某個事件。信號機制除了基本通知功能外,還可以傳遞附加信息。2023/2/141Linux信號2023/2/142//comefromasm/signal.h常見的信號Linux系統中常見的信號說明如下:1)SIGHUP:用戶從終端注銷時,所有已啟動的進程都將收到SIGHUP信號。系統缺省狀態下對該信號的處理就是中止進程。2)SIGINT:程序終止信號。在程序運行過程中,用戶通過鍵盤按下【Ctrl】+【C】鍵將產生該信號。3)SIGQUIT:程序退出信號。在程序運行過程中,用戶通過按下【Ctrl】+【\】鍵將產生該信號。4)SIGBUS和SIGSEGV:進程訪問非法地址時,引發該信號。5)SIGFPE:進行算術運算中出現致命錯誤,如除零操作、數據溢出等。2023/2/143常見的信號(續)6)SIGKILL:終止用戶進程執行的信號。在shell下通過執行“kill-9”命令發送的就是該信號。7)SIGTERM:進程結束信號。在shell下執行“kill進程pid”命令發送的就是該信號。8)SIGALRM:定時器信號。9)SIGCLD:子進程退出信號。如果父進程沒有忽略該信號,也沒有處理該信號,則子進程退出后將形成僵尸進程。2023/2/144信號的分類可靠信號:也稱為實時信號,支持排隊。不可靠信號:非實時信號,不支持排隊。發送用戶進程判斷后注冊,發現相同信號已經在進程中注冊,就不再注冊,忽略該信號。2023/2/145可能的信號來源信號是在軟件層次上對中斷機制的一種模擬,是一種異步通信方式。信號可以在用戶空間進程和內核之間直接交互,內核也可以利用信號來通知用戶空間的進程發生了哪些系統事件。信號事件的發生有兩個來源:1.硬件來源:用戶按某些終端鍵時將產生信號,如CTRL+C將產生SIGINT(中止信號);硬件異常產生信號,如除數為0或無效的存儲訪問等。2.軟件來源:終止進程信號,其他進程調用kill函數,將信號發送個另一個進程或進程組;軟件異常產生信號。2023/2/146信號的處理流程(1)信號被某個進程產生,并設置此信號傳遞的對象(一般為對應進程的pid),然后傳遞給操作系統;(2)操作系統根據接收進程的設置(是否阻塞)而選擇性的發送給接收者,如果接收者阻塞該信號(且該信號是可以阻塞的),操作系統將暫時保留該信號,而不傳遞,直到該進程解除對此信號的阻塞(如果對應進程已經退出,則丟棄此信號);如果對應進程沒有阻塞,操作系統將傳遞此信號;(3)目的進程接收到此信號后,將根據當前進程對此信號設置的預處理方式,暫時終止當前代碼的執行,保護上下文(主要包括臨時寄存器數據、當前程序位置以及當前CPU的狀態)、轉而執行中斷服務程序,執行完成后再恢復到被中斷的位置。當然,對于可搶占式內核,在中斷返回時還將引發新的調度。2023/2/147信號的生命周期信號處理信號產生信號注冊信號注銷內核進程用戶進程2023/2/148kill產生一個信號kill()函數用來向指定進程發送一個信號。此函數聲明如下://comefrom/usr/include/signal.hexternintkill(__pid_t__pid,int__sig)此函數的第一個參數為要傳遞信號的進程號(PID),第二個參數即發送的信號值。pid可以取以下幾種值:pid>o:將信號發送給進程的PID值為pid的進程。pid=0:將信號發送給和當前進程在同一進程組的所有進程。pid=-1:將信號發送給系統內的所有進程。pid<0:將信號發送給進程組號PGID為pid絕對值的所有進程。如果成功完成返回值0,否則返回-1,并設置errno以指示錯誤。2023/2/149#include<stdio.h>#include<signal.h>voidmain(intargc,char*argv[]) { intpid,signo; if(argc!=3) { printf("usage:killsenderpidsigno\n"); return; } sscanf(argv[1],"%d",&pid);//獲取參數pid

sscanf(argv[2],"%d",&signo);//獲取參數signo

if(kill(pid,signo)<0) {//發送信號 perror("kill"); return; } printf("ok:sendoutsignalviakillsystemcall!\n");}2023/2/150例:編寫程序killsender。通過kill系統調用向指定進程(pid)發送信號。raise自舉一個信號raise()函數用來給當前進程發送一個信號,即喚醒一個進程。此函數聲明如下://comefrom/usr/include/signal.h/*RaisesignalSIG,i.e.,sendSIGtoyourself.*/externintraise(int__sig)此函數相當于采用以下方式執行kill()函數:if(kill(getpid(),int__sig)==-1)

perror(“raise”);2023/2/151例:設計一個程序,創建一個子進程,父進程向子進程發出信號,子進程收到此信號,結束子進程的運行。#include<stdio.h>#include<signal.h>#include<sys/wait.h>#include<unistd.h>#include<stdlib.h>intmain(){ pid_tresult; intret; result=fork(); intnewret;

2023/2/152 if(result<0) { perror("fork"); exit(EXIT_FAILURE); } elseif(result==0) { raise(SIGSTOP); exit(0); } else { printf("child'spid=%d\n",result); if((waitpid(result,NULL,WNOHANG))==0) { if(ret=kill(result,SIGKILL)==0) printf("killfunctionfinished%dprocesswithreturnvalue=%d\n",result,ret); else { perror("kill"); } } }}2023/2/153alarm()定時alarm()函數用來傳遞定時信號,即在多少時間內產生SIGALRM信號,此函數每調用一次,產生一個信號,并不是循環產生SIGALRM信號。//comefrom/usr/include/unistd.hexternunsignedintalarm(unsignedint__seconds)此函數只有一個參數,即在多少時間(秒)內發送SIGALRM信號給當前進程,默認情況下,當進程接受到alarm信號后將終止執行;如果sec為0,則取消所有先前發出的報警請求。如果在調用alarm()函數前沒有調用過alarm()函數,如果執行成功,將返回0,否則返回-1,并置errno標致錯誤。2023/2/154例:alarm的使用#include<stdio.h>#include<signal.h>intmain(){ printf("firsttimereturn:%d\n",alarm(4)); sleep(1); printf("aftersleep(1),remain:%d\n",alarm(2)); printf("renewalarm,remain:%d\n",alarm(1));}2023/2/155ualarm定時ualarm將使當前進程在指定時間(第一個參數,以us為單位)內產生SIGALRM信號,然后每隔指定時間(第二個參數,以us為單位)重復產生SIGALRM信號。如果執行成功將返回0,該函數聲明如下:extern__useconds_tualarm(__useconds_t__value,__useconds_t__interval)2023/2/156信號處理與signal安裝信號信號處理辦法:(1)忽略此信號。大多數信號都可使用這種方式進行處理,但有兩種信號不能被忽略,SIGKILL和SIGSTOP。這兩種信號不能被忽略的原因是:它們向超級用戶提供一種使進程終止或停止的可靠方法。(2)捕捉信號。通知內核在某種信號發生時調用一個用戶函數。在用戶函數中,可執行用戶希望對這種事件進行的處理,這需要安裝此信號。例如捕捉到SIGCHLD信號,則表示子進程已經終止,所以此信號的捕捉函數可以調用waitpid()以取得該子進程的進程PID以及它的終止狀態和資源。(3)執行系統默認操作。Linux系統對任何一個信號都規定了一個默認的操作。2023/2/157signal安裝信號typedefvoid(*__sighandler_t)(int);extern__sighander_tsignal(int__sig,sighandler_t__handler)此函數有兩個參數,第一個參數sig為接收到的信號,第二個參數為接收到此信號后的處理代碼入口或下面幾個宏:/*Fakesignalfunctions.*/#defineSIG_ERR((__sighandler_t)-1)/*Errorreturn.*/#defineSIG_DFL((__sighandler_t)0)/*Defaultaction.*/#defineSIG_IGN((__sighandler_t)1)/*Ignoresignal.*/2023/2/158例:編寫程序:killrecerver和killsender。其中killrecerver用于接收SIGUSR1(值為10)信號,而killsender通過kill系統調用向指定進程(pid)發送信號。#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<signal.h>//SIGUSR1信號處理函數voidCbSigUsr1(intsigno){//輸出接收到的信號信息 printf("\nreceivesignal=%d.\n",signo);}2023/2/159voidmain() {//安裝SIGUSR1信號 if(signal(SIGUSR1,CbSigUsr1)==SIG_ERR){ perror("signal"); return;} printf("mypidis%d\n",getpid()); printf("waitingforSIGUSR1...\n");//暫停,等待信號 pause();}

2023/2/160例:signal的應用#include<stdio.h>#include<signal.h>#include<unistd.h>#include<stdlib.h>voidfun_ctrl_c();intmain(){ (void)signal(SIGINT,fun_ctrl_c); printf("NowI'mstarting\n"); while(1) { printf("thisisanendlessloopunlessCtrl+carepressd!\n"); sleep(2); } exit(0);}2023/2/161例:signal的應用voidfun_ctrl_c(){ printf("\tCtrl+cwerepressed!\n"); printf("\tThisisjustanexampleforsignalfunction!\n"); printf("\tresetsignalSIGINT\n"); (void)signal(SIGINT,SIG_DFL);}2023/2/162sigaction安裝信號externintsigaction(int__sig,structsigaction*__act,structsigaction*__oact)此函數的第一個參數為接收到的信號,第二、三個參數均為信號結構sigaction(用于描述要采取的操作及相關信息,見后續說明)變量。第二個參數用來指定欲設置的信號處理信息,第三個參數將返回執行此程序前信號處理信息。如果第二個參數act不為空指針,則指定信號關聯的操作為此參數指向的結構。如果參數oact不為空指針,則用來存儲以前設置的與此信號關聯的操作。如果參數act為空指針,則信號處理保持不變;因此,該調用可用于詢問對指定信號的當前處理。2023/2/163structsigactionStructsigaction{ union{ __sighandler_t_sa_handler; void(*_sa_sigaction)(int,structsiginfo*,void*); }__u; sigset_tsa_mask; unsignedlongsa_flags; void(*sa_restorer)(void);};#definesa_handler_u.sa_handler#definesa_sigaction_u._sa_sigaction2023/2/164例:sigaction的應用#include<stdio.h>#include<stdlib.h>#include<signal.h>voidmyhandler(intsig);intmain(){ structsigactionact,oact; act.sa_handler=myhandler; sigemptyset(&act.sa_mask); act.sa_flags=0; sigaction(SIGUSR1,&act,&oact); while(1) { printf("helloworld!\n"); pause(); }}2023/2/165voidmyhandler(intsig){ printf("thesignal%dwascaught.\n",sig);}信號集與屏蔽信號中斷是可以被屏蔽(阻塞)的(部分硬件中斷是必須立即處理的,例如復位中斷),因此,Linux的信號是可以屏蔽,即阻塞信號。但這與前面提到的忽略是有區別的。信號忽略:系統仍然傳遞該信號,只是相應進程對該信號不作任何處理而已。信號阻塞:系統不傳遞該信號,顯示該進程無法接收到該信號直到進程的信號集發生改變。2023/2/166sigprocmask設置進程阻塞的信號集externintsigprocmask(int__how,__constsigset_t*__restrict__set,sigset_t*__restrict__oset)此函數第一個參數為更改該集合的方式,如下所示://comefrom/usr/include/asm/signal.h#defineSIG_BLOCK0/*forblockingsignals*/#defineSIG_UNBLOCK1

溫馨提示

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

評論

0/150

提交評論