您好,欢迎访问三七文档
当前位置:首页 > 电子/通信 > 综合/其它 > linux教程 第07课_进程通信
5.5进程通信5.5.1信号机制1.信号概念►信号(signal,称为软中断)机制是在软件层次上对中断机制的一种模拟►该机构通常包括三部分:(1)信号的分类、产生和传送。(2)对各种信号预先规定的处理方式。(3)信号的检测和处理。2.信号分类►►信号号码符号表示含义►1SIGHUP远程用户挂断►2SIGINT输入中断信号(Ctrl+C)►3SIGQUIT输入退出信号(Ctrl+\)►4SIGILL非法指令►5SIGTRAP遇到调试断点►6SIGIOTIOT指令►7SIGBUS总线超时►8SIGFPE浮点异常►9SIGKILL要求终止进程(不可屏蔽)►10SIGUSR1用户自定义►11SIGSEGV越界访问内存►12SIGUSR2用户自定义►13SIGPIPE管道文件只有写进程,没有读进程►14SIGALRM定时报警信号►15SIGTERM软件终止信号►17SIGCHLD子进程终止►19SIGSTOP进程暂停运行►30SIGPWR电源故障3.进程对信号可采取的处理方式►进程间也可用系统调用(如kill())发送信号►普通进程只能向具有相同uid和gid的进程发送信号或向相同进程组中的其他进程发送信号►进程接到信号后,在一定时机(如中断处理末尾)做相应处理,可采取以下处理方式:(1)忽略信号。进程可忽略收到的信号,但不能忽略SIGKILL和SIGSTOP信号。(2)阻塞信号。进程可以选择对某些信号予以阻塞。(3)由进程处理该信号。用户在trap命令中可以指定处理信号的程序,从而进程本身可在系统中标明处理信号的处理程序的地址。当发出该信号时,就由标明的处理程序进行处理。(4)由系统进行默认处理。如上所述,系统内核对各种信号(除用户自定义之外)都规定了相应的处理程序。在默认情况下,信号就由内核处理,即执行内核预定的处理程序。4.对信号的检测和处理流程►信号的检测与处理的过程如图所示。图中的①~⑤标出处理流程的顺序。从图中可以看出,信号的检测是在系统空间中进行,而对信号的处理却是在用户空间中执行。1.kill与raise函数信号的发送与捕捉:kill()和raise()kill()不仅可以中止进程,也可以向进程发送其他信号。与kill函数不同的是,raise()函数向进程自身发送信号#includesys/types.h#includesignal.hintkill(pid_tpid,intsigno);intraise(intsigno);两个函数返回:若成功则为0,若出错则为-1。1.kill与raise函数kill的pid参数有四种不同的情况:(1)pid0将信号发送给进程ID为pid的进程。(2)pid==0将信号发送给其进程组ID等于发送进程的进程组ID,而且发送进程有许可权向其发送信号的所有进程。(3)pid0将信号发送给其进程组ID等于pid绝对值,而且发送进程有许可权向其发送信号的所有进程。如上所述一样,“所有进程”并不包括系统进程集中的进程。(4)pid==-1POSIX.1未定义种情况2.alarm与pause函数使用alarm函数可以设置一个时间值(闹钟时间),在将来的某个时刻时间值会被超过。当所设置的时间被超过后,产生SIGALRM信号,其默认动作是终止该进程。#includeunistd.hunsignedintalarm(unsignedintsecondss);返回:0或以前设置的闹钟时间的余留秒数。参数seconds的值是秒数,经过了指定的seconds秒后产生信号SIGALRM。每个进程只能有一个闹钟时间。如果在调用alarm时,以前已为该进程设置过闹钟时间而且还没有超时,则该闹钟时间的余留值作为本次alarm函数调用的值返回,以前登记的闹钟时间则被新值代换。如果有以前登记的尚未超过的闹钟时间,而且seconds值是0,则取消以前的闹钟时间,其余留值仍作为函数的返回值。2.alarm与pause函数pause函数使用调用进程挂起直至捕捉到一个信号#includeunistd.hintpause(void);返回:-1,errno设置为EINTR只有执行了一信号处理程序并从其返回时,pause才返回。3.signal函数信号的处理:当系统捕捉到某个信号时,可以忽略谁信号或是使用指定的处理函数来处理该信号,或者使用系统默认的方式。信号处理的主要方式有两种,一种是使用简单的signal函数,别一种是使用信号集函数组。#includesignal.hvoid(*signal(intsigno,void(*func)(int)))(int)返回:成功则为以前的信号处理配置,若出错则为SIG_ERR3.signal函数func的值是:(a)常数SIGIGN,或(b)常数SIGDFL,或(c)当接到此信号后要调用的的函数的地址。如果指定SIGIGN,则向内核表示忽略此信号(有两个信号SIGKILL和SIGSTOP不能忽略)。如果指定SIGDFL,则表示接到此信号后的动作是系统默认动作。当指定函数地址时,我们称此为捕捉此信号。我们称此函数为信号处理程序(signalhandler)或信号捕捉函数(signal-catchingfuncgion)。4.sigaction函数sigaction函数的功能是检查或修改(或两者)与指定信号相关联的处理动作。此函数取代了UNIX早期版本使用的signal函数。#includesignal.hintsigaction(intsigno,conststructsigaction*act,structsigaction*oact);返回:若成功则为0,若出错则为-1参数signo是要检测或修改具体动作的信号的编号数。若act指针非空,则要修改其动作。如果oact指针为空,则系统返回该信号的原先动作。4.sigaction函数此函数使用下列结构:structsigaction{void(*sa_handler)(intsigno);sigset_tsa_mask;intsa_flags;void(*sa_restore);};sa_handler是一个函数指针,指定信号关联函数,可以是自定义处理函数,还可以SIG_DEF或SIG_IGN;sa_mask是一个信号集,它可以指定在信号处理程序执行过程中哪些信号应当被阻塞。sa_flags中包含许多标志位,是对信号进行处理的各种选项。5.5.2管道文件►一个管道线就是连接两个进程的一个打开文件►由系统自动处理二者间的同步、调度和缓冲。管道文件允许两个进程按先入先出(FIFO)的方式传送数据,而它们可以彼此不知道对方的存在►每个管道只有一个内存页面用做缓冲区,该页面是按环型缓冲区的方式来使用的。Linux系统也支持命名管道管道主要用于不同进程间通信。实际上,通常先创建一个管道,再通过fork函数创建一个子进程。可以通过打开两个管道来创建一个双向的管道。但需要在子进程中正确地设置文件描述符。必须在系统调用fork()中调用pipe(),否则子进程将不会继承文件描述符。当使用半双工管道时,任何关联的进程都必须共享一个相关的祖先进程。因为管道存在于系统内核之中,所以任何不在创建管道的进程的祖先进程之中的进程都将无法寻址它。而在命名管道中却不是这样。5.pipe函数管道创建与关闭创建一个简单的管道,可以使用系统调用pipe()。它接受一个参数,也就是一个包括两个整数的数组。如果系统调用成功,此数组将包括管道使用的两个文件描述符。创建一个管道之后,一般情况下进程将产生一个新的进程。intpipe(intfd[2]);返回值:如果系统调用成功,返回0。如果系统调用失败返回-1:errno=EMFILE(没有空的文件描述符)EMFILE(系统文件表已满)EFAULT(fd数组无效)注意:fd[0]用于读取管道,fd[1]用于写入管道。6.mkfifo函数命名管道(FIFO)命名管道和一般的管道基本相同,但也有一些显著的不同:A、命名管道是在文件系统中作为一个特殊的设备文件而存在的。B、不同祖先的进程之间可以通过管道共享数据。C、当共享管道的进程执行完所有的I/O操作以后,命名管道将继续保存在文件系统中以便以后使用。管道只能由相关进程使用,它们共同的祖先进程创建了管道。但是,通过FIFO,不相关的进程也能交换数据。#includesys/types.h#includesys/stat.hintmkfifo(constchar*pathname,mode_tmode);返回:若成功则为0,若出错返回-16.mkfifo函数一旦已经用mkfifo创建了一个FIFO,就可用open打开它。一般的文件I/O函数(close,read,write,unlink等)都可用于FIFO。当打开一个FIFO时,非阻塞标(O_NONBLOCK)产生下列影响:(1)在一般情况中(没有说明O_NONBLOCK),只读打开要阻塞到某个其他进程为写打开此FIFO。类似,为写而打开一个FIFO要阻塞到某个其他进程为读而打开它。(2)如果指一了O_NONBLOCK,则只读打开立即返回。但是,如果没有进程已经为读而打开一个FIFO,那么只写打开将出错返回,其errno是ENXIO。类似于管道,若写一个尚无进程为读而打开的FIFO,则产生信号SIGPIPE。若某个FIFO的最后一个写进程关闭了该FIFO,则将为该FIFO的读进程产生一个文件结束标志。FIFO相关出错信息:EACCES(无存取权限)EEXIST(指定文件不存在)ENAMETOOLONG(路径名太长)ENOENT(包含的目录不存在)ENOSPC(文件系统余空间不足)ENOTDIR(文件路径无效)EROFS(指定的文件存在于只读文件系统中)5.5.3SystemVIPC机制►Linux系统也支持UNIXSystemV版本中的三种进程间通信机制,它们是:►消息通信►共享内存►信号量消息队列消息队列(MessageQueues)顾名思义,消息队列就是在系统内核中保存的一个用来保存消息的队列。但这个队列并不是简单的进行“先入先出”的操作,我们可以控制消息用更为灵活的方式流动。msgbuflinux/msg.hstructmsgbuf{longmtype;/*typeofmessage*/charmtext[1];/*messagetext*/};mtype是一个正的长整型量,通过它来区分不同的消息数据类型。mtext是消息数据的内容。在Linux的库文件=linux/msg.h中定义了每个msgbuf结构的最大长度:#defineMSGMAX4056/*=4056*/消息队列消息队列在系统内核中是以消息链表的形式出现的。而完成消息链表每个节点结构定义的就是msg结构。它在Linux的系统库linux/msg.h中的定义是这样的:structmsg{structmsg*msg_next;/*nextmessageonqueue*/longmsg_type;char*msg_spot;/*messagetextaddress*/time_tmsg_stime;/*msgsndtime*/shortmsg_ts;/*messagetextsize*/};msg_next成员是指向消息链表中下一个节点的指针,依靠它对整个消息链表进行访问。msg_type和msgbuf中mtype成员的意义是一样的。msg_spot成员指针指出了消息内容(就是msgbuf结构中的mtext)在内存中的位置。msg_ts成员指出了消息内容的长度。1.msgget()msgget()函数被用来创建新的消息队列或获取已有的消息队列。#includetypes.hipc.hmsg.hintmsgget(key_tkey,intmsgflg)返回值:messagequeueidentifieronsuccessmsgget()函数的第一个参数是消息队列对象的关键字(key),函数将它与已有的消息队列对象的关键
本文标题:linux教程 第07课_进程通信
链接地址:https://www.777doc.com/doc-3265193 .html