进程间通信管道和信号_第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

提交评论