linux进程间通信.ppt

上传人:牧羊曲112 文档编号:5438164 上传时间:2023-07-07 格式:PPT 页数:53 大小:308.49KB
返回 下载 相关 举报
linux进程间通信.ppt_第1页
第1页 / 共53页
linux进程间通信.ppt_第2页
第2页 / 共53页
linux进程间通信.ppt_第3页
第3页 / 共53页
linux进程间通信.ppt_第4页
第4页 / 共53页
linux进程间通信.ppt_第5页
第5页 / 共53页
点击查看更多>>
资源描述

《linux进程间通信.ppt》由会员分享,可在线阅读,更多相关《linux进程间通信.ppt(53页珍藏版)》请在三一办公上搜索。

1、linux进程间通信,进程间通信概述管道通信信号共享内存消息队列,1、进程间通信概述,进程间通信有如下一些目的:数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及

2、时知道它的状态改变。,linux进程间通信(IPC)由以下几部分发展而来:早期UNIX进程间通信、基于System V进程间通信、基于Socket进程间通信和POSIX进程间通信。UNIX进程间通信方式包括:管道、FIFO、信号。System V进程间通信方式包括:System V消息队列、System V信号灯、System V共享内存。POSIX进程间通信包括:posix消息队列、posix信号灯、posix共享内存。,现在linux使用的进程间通信方式:(1)管道(pipe)和有名管道(FIFO)(2)信号(signal)(3)消息队列(4)共享内存(5)信号量(6)套接字(socket

3、),2、管道通信,普通的Linux shell都允许重定向,而重定向使用的就是管道。例如:ps|grep vsftpd管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的首端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。管道主要用于不同进程间通信。,2.1 管道创建与关闭,创建一个简单的管道,可以使用系统

4、调用pipe()。它接受一个参数,也就是一个包括两个整数的数组。如果系统调用成功,此数组将包括管道使用的两个文件描述符。创建一个管道之后,一般情况下进程将产生一个新的进程。系统调用:pipe();原型:int pipe(int fd2);返回值:如果系统调用成功,返回0。如果系统调用失败返回-1:errno=EMFILE(没有空闲的文件描述符)EMFILE(系统文件表已满)EFAULT(fd数组无效),注意:fd0 用于读取管道,fd1 用于写入管道。,图1 linux中管道与文件描述符的关系,#include#include#include#include int main()int pip

5、e_fd2;if(pipe(pipe_fd)0)printf(pipe create errorn);return-1;else printf(pipe create successn);close(pipe_fd0);close(pipe_fd1);,2.2 管道读写,管道主要用于不同进程间通信。实际上,通常先创建一个管道,再通过fork函数创建一个子进程。,图2 父子进程管道的文件描述符对应关系,子进程写入和父进程读的命名管道:,图 3 关闭父进程fd1 和 子进程0,2.3 管道读写注意事项,可以通过打开两个管道来创建一个双向的管道。但需要在子进程中正确地设置文件描述符。必须在系统调用f

6、ork()中调用pipe(),否则子进程将不会继承文件描述符。当使用半双工管道时,任何关联的进程都必须共享一个相关的祖先进程。因为管道存在于系统内核之中,所以任何不在创建管道的进程的祖先进程之中的进程都将无法寻址它。而在命名管道中却不是这样。,2.4 标准流管道,与linux中文件操作有文件流的标准I/O一样,管道的操作也支持基于文件流的模式。接口函数如下库函数:popen();原型:FILE*popen(char*command,char*type);返回值:如果成功,返回一个新的文件流。如果无法创建进程或者管道,返回NULL。管道中数据流的方向是由第二个参数type控制的。此参数可以是r或

7、者w,分别代表读或写。但不能同时为读和写。在Linux系统下,管道将会以参数type中第一个字符代表的方式打开。所以,如果你在参数type中写入rw,管道将会以读的方式打开。,使用popen()创建的管道必须使用pclose()关闭。其实,popen/pclose和标准文件输入/输出流中的fopen()/fclose()十分相似。库函数:pclose();原型:int pclose(FILE*stream);返回值:返回调用状态。,#include#include#include#include#define BUFSIZE 1024int main()FILE*fp;char*cmd=ps-

8、ef;char bufBUFSIZE;bufBUFSIZE=0;if(fp=popen(cmd,r)=NULL)perror(popen);while(fgets(buf,BUFSIZE,fp)!=NULL)printf(%s,buf);pclose(fp);exit(0);,命名管道(FIFO),基本概念命名管道和一般的管道基本相同,但也有一些显著的不同:命名管道是在文件系统中作为一个特殊的设备文件而存在的。不同祖先的进程之间可以通过管道共享数据。当共享管道的进程执行完所有的I/O操作以后,命名管道将继续保存在文件系统中以便以后使用。管道只能由相关进程使用,它们共同的祖先进程创建了管道。但是

9、,通过FIFO,不相关的进程也能交换数据。,命名管道创建与操作,名管道创建#include#include int mkfifo(const char*pathname,mode_t mode);返回:若成功则为0,若出错则为-1一旦已经用mkfifo创建了一个FIFO,就可用open打开它。确实,一般的文件I/O函数(close、read、write、unlink等)都可用于FIFO。,当打开一个FIFO时,非阻塞标志(O_NONBLOCK)产生下列影响:(1)在一般情况中(没有说明O_NONBLOCK),只读打开要阻塞到某个其他进程为写打开此FIFO。类似,为写而打开一个FIFO要阻塞到某

10、个其他进程为读而打开它。(2)如果指定了O_NONBLOCK,则只读打开立即返回。但是,如果没有进程已经为读而打开一个FIFO,那么只写打开将出错返回,其errno是ENXIO。类似于管道,若写一个尚无进程为读而打开的FIFO,则产生信号SIGPIPE。若某个FIFO的最后一个写进程关闭了该FIFO,则将为该FIFO的读进程产生一个文件结束标志。,FIFO相关出错信息:EACCES(无存取权限)EEXIST(指定文件不存在)ENAMETOOLONG(路径名太长)ENOENT(包含的目录不存在)ENOSPC(文件系统剩余空间不足)ENOTDIR(文件路径无效)EROFS(指定的文件存在于只读文件

11、系统中),信号通信,信号概述信号是软件中断。信号(signal)机制是Unix系统中最为古老的进程之间的通信机制。它用于在一个或多个进程之间传递异步信号。很多条件可以产生一个信号。当用户按某些终端键时,产生信号。在终端上按DELETE键通常产生中断信号(SIGINT)。这是停止一个已失去控制程序的方法。(第11章将说明此信号可被映射为终端上的任一字符。)硬件异常产生信号:除数为0、无效的存储访问等等。这些条件通常由硬件检测到,并将其通知内核。然后内核为该条件发生时正在运行的进程产生适当的信号。例如,对执行一个无效存储访问的进程产生一个SIGSEGV。,进程用kill(2)函数可将信号发送给另一

12、个进程或进程组。自然,有些限制:接收信号进程和发送信号进程的所有者必须相同,或发送信号进程的所有者必须是超级用户。用户可用kill(1)命令将信号发送给其他进程。此程序是kill函数的界面。常用此命令终止一个失控的后台进程。当检测到某种软件条件已经发生,并将其通知有关进程时也产生信号。这里并不是指硬件产生条件(如被0除),而是软件条件。例如SIGURG(在网络连接上传来非规定波特率的数据)、SIGPIPE(在管道的读进程已终止后一个进程写此管道),以及SIGALRM(进程所设置的闹钟时间已经超时)。,内核为进程生产信号,来响应不同的事件,这些事件就是信号源。主要的信号源如下:异常:进程运行过程

13、中出现异常;其它进程:一个进程可以向另一个或一组进程发送信号;终端中断:Ctrl-C,Ctrl-等;作业控制:前台、后台进程的管理;分配额:CPU超时或文件大小突破限制;通知:通知进程某事件发生,如I/O就绪等;报警:计时器到期。,Linux 中的信号:,1)SIGHUP 2)SIGINT 3)SIGQUIT 4)SIGILL5)SIGTRAP 6)SIGIOT 7)SIGBUS 8)SIGFPE9)SIGKILL 10)SIGUSR1 11)SIGSEGV12)SIGUSR213)SIGPIPE 14)SIGALRM15)SIGTERM17)SIGCHLD18)SIGCONT 19)SIGS

14、TOP20)SIGTSTP21)SIGTTIN22)SIGTTOU 23)SIGURG24)SIGXCPU25)SIGXFSZ26)SIGVTALRM 27)SIGPROF 28)SIGWINCH 29)SIGIO30)SIGPWR,下面是几个常见的信号。SIGHUP:从终端上发出的挂起信号;SIGINT:来自键盘的中断信号(Ctrl-C);SIGQUIT:来自键盘的退出信号(Ctrl-);SIGFPE:浮点异常信号(例如浮点运算溢出);SIGKILL:该信号结束接收信号的进程;SIGALRM:进程的定时器到期时,发送该信号;SIGTERM:中止信号;kill的默认发出信号;SIGCHLD:标

15、识子进程停止或结束的信号;SIGSTOP:来自键盘(Ctrl-Z)或调试程序的停止执行信号,可以要求系统在某个信号出现时按照下列三种方式中的一种进行操作。(1)忽略此信号。大多数信号都可使用这种方式进行处理,但有两种信号却决不能被忽略。它们是:SIGKILL和SIGSTOP。这两种信号不能被忽略的原因是:它们向超级用户提供一种使进程终止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号(例如非法存储访问或除以0),则进程的行为是未定义的。(2)捕捉信号。为了做到这一点要通知内核在某种信号发生时,调用一个用户函数。在用户函数中,可执行用户希望对这种事件进行的处理。如果捕捉到SIGCHLD信

16、号,则表示子进程已经终止,所以此信号的捕捉函数可以调用waitpid以取得该子进程的进程ID以及它的终止状态。(3)执行系统默认动作。对大多数信号的系统默认动作是终止该进程。,每一个信号都有一个缺省动作,它是当进程没有给这个信号指定处理程序时,内核对信号的处理。有5种缺省的动作:异常终止(abort):在进程的当前目录下,把进程的地址空间内容、寄存器内容保存到一个叫做core的文件中,而后终止进程。退出(exit):不产生core文件,直接终止进程。忽略(ignore):忽略该信号。停止(stop):挂起该进程。继续(continue):如果进程被挂起,则恢复进程的运行。否则,忽略信号。,3.

17、2 信号发送与捕捉,3.2.1 kill()和raise()kill()不仅可以中止进程,也可以向进程发送其他信号。与kill函数不同的是,raise()函数运行向进程自身发送信号。#include#include int kill(pid_t pid,int signo);int raise(int signo);两个函数返回:若成功则为0,若出错则为-1。,kill的pid参数有四种不同的情况:pid 0 将信号发送给进程ID为pid的进程。pid=0 将信号发送给其进程组ID等于发送进程的进程组ID,而且发送进程有许可权向其发送信号的所有进程。pid 0 将信号发送给其进程组ID等于pi

18、d绝对值,而且发送进程有许可权向其发送信号的所有进程。如上所述一样,“所有进程”并不包括系统进程集中的进程。pid=-1 POSIX.1未定义此种情况。,3.2.2 alarm和pause函数,使用alarm函数可以设置一个时间值(闹钟时间),在将来的某个时刻该时间值会被超过。当所设置的时间值被超过后,产生SIGALRM信号。如果不忽略或不捕捉此信号,则其默认动作是终止该进程。#include unsigned int alarm(unsigned int seconds);返回:0或以前设置的闹钟时间的余留秒数,参数seconds的值是秒数,经过了指定的seconds秒后会产生信号SIGAL

19、RM。每个进程只能有一个闹钟时间。如果在调用alarm时,以前已为该进程设置过闹钟时间,而且它还没有超时,则该闹钟时间的余留值作为本次alarm函数调用的值返回。以前登记的闹钟时间则被新值代换。如果有以前登记的尚未超过的闹钟时间,而且seconds值是0,则取消以前的闹钟时间,其余留值仍作为函数的返回值。,pause函数使调用进程挂起直至捕捉到一个信号。#include int pause(void);返回:-1,errno设置为EINTR只有执行了一个信号处理程序并从其返回时,pause才返回。,3.3 信号的处理,当系统捕捉到某个信号时,可以忽略该信号或是使用指定的处理函数来处理该信号,或

20、者使用系统默认的方式。信号处理的主要方法有两种,一种是使用简单的signal函数,另一种是使用信号集函数组。,3.3.1 signal(),#include void(*signal(int signo,void(*func)(int)(int)返回:成功则为以前的信号处理函数指针,若出错则为SIG_ERRfunc的值是:(a)常数SIG_IGN,或(b)常数SIG_DFL,或(c)当接到此信号后要调用的函数的地址。如果指定SIG_IGN,则向内核表示忽略此信号(有两个信号SIGKILL和SIGSTOP不能忽略)。如果指定SIG_DFL,则表示接到此信号后的动作是系统默认动作。当指定函数地址时

21、,我们称此为捕捉此信号。我们称此函数为信号处理程序(signal handler)或信号捕捉函数(signal-catching function)。,3.3.2 信号集函数组,我们需要有一个能表示多个信号信号集(signal set)的数据类型。将在sigprocmask()这样的函数中使用这种数据类型,以告诉内核不允许发生该信号集中的信号。信号集函数组包含几大模块:创建函数集、登记信号集、检测信号集。,图4 信号操作一般流程,SigemptysetSigaddset,Sigprocmask,定义信号集,设置信号屏蔽位,sa_masksa_handlersigaction,Sigpendin

22、g,定义信号处理函数,测试信号,3.3.2.1 创建函数集,#include int sigemptyset(sigset_t*set);int sigfillset(sigset_t*set);int sigaddset(sigset_t*set,int signo);int sigdelset(sigset_t*set,int signo);四个函数返回:若成功则为0,若出错则为-1int sigismember(const sigset_t*set,int signo);返回:若真则为1,若假则为0。,sigemptyset:初始化信号集合为空。sigfillset:初始化信号集合为所有

23、信号的集合。sigaddset:将指定信号添加到现存集中。sigdelset:从信号集中删除指定信号。sigismember:查询指定信号是否在信号集合中。,3.3.2.2 登记信号集,登记信号处理机主要用于决定进程如何处理信号。首先要判断出当前进程阻塞能不能传递给该信号的信号集。这首先使用sigprocmask函数判断检测或更改信号屏蔽字,然后使用sigaction函数改变进程接受到特定信号之后的行为。,一个进程的信号屏蔽字可以规定当前阻塞而不能递送给该进程的信号集。调用函数sigprocmask可以检测或更改(或两者)进程的信号屏蔽字。#include int sigprocmask(in

24、t how,const sigset_t*set,sigset_t*oset);返回:若成功则为0,若出错则为-1oset是非空指针,进程的当前信号屏蔽字通过oset返回。其次,若set是一个非空指针,则参数how指示如何修改当前信号屏蔽字。,用sigprocmask更改当前信号屏蔽字的方法,how参数设定:SIG_BLOCK该该进程新的信号屏蔽字是其当前信号屏蔽字和set指向信号集的并集。set包含了我们希望阻塞的附加信号。SIG_UNBLOCK该该进程新的信号屏蔽字是其当前信号屏蔽字和set所指向信号集的交集。set包含了我们希望解除阻塞的信号。SIG_SETMASK该该进程新的信号屏蔽是

25、set指向的值。如果set是个空指针,则不改变该进程的信号屏蔽字,how的值也无意义。,sigaction函数的功能是检查或修改(或两者)与指定信号相关联的处理动作。此函数取代了UNIX早期版本使用的signal函数。#include int sigaction(int signo,const struct sigaction*act,struct sigaction*oact);返回:若成功则为0,若出错则为-1 参数signo是要检测或修改具体动作的信号的编号数。若act指针非空,则要修改其动作。如果oact指针非空,则系统返回该信号的原先动作。此函数使用下列结构:struct sigac

26、tion void(*sa_handler)(int signo);sigset_t sa_mask;int sa_flags;void(*sa_restore);,sa_handler是一个函数指针,指定信号关联函数,可以是自定义处理函数,还可以SIG_DFL或 SIG_IGN。sa_mask是一个信号集,它可以指定在信号处理程序执行过程中哪些信号应当被阻塞。sa_flags中包含许多标志位,是对信号进行处理的各种选项。具体如下:SA_NODEFERSA_NOMASK:当捕捉到此信号时,在执行其信号捕捉函数时,系统不会自动阻塞此信号。SA_NOCLDSTOP:进程忽略子进程产生的任何SIGS

27、TOP、SIGTSTP、SIGTTIN和SIGTTOU信号SA_RESTART:可让重启的系统调用重新起作用。SA_ONESHOTSA_RESETHAND:自定义信号只执行一次,在执行完毕后恢复信号的系统默认动作。,3.3.2.3 检测信号集,检测信号是信号处理的后续步骤,但不是必须的。sigpending函数运行进程检测“未决”信号(进程不清楚他的存在),并进一步决定对他们做何处理。sigpending返回对于调用进程被阻塞不能递送和当前未决的信号集。#include int sigpending(sigset_t*set);返回:若成功则为0,若出错则为-1,4、共享内存,共享内存区域是被

28、多个进程共享的一部分物理内存。如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信。共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。,图5 共享内存原理示意图,共享内存实现分为两个步骤:一、创建共享内存,使用shmget函数。二、映射共享内存,将这段创建的共享内存映射到具体的进程空间去,使用shmat函数。,系统调用:shmget();原型:int shmget(key_t key,int size,int shmflg);返回值:如果成功,返回共

29、享内存段标识符。如果失败,则返回-1:errno=EINVAL(无效的内存段大小)EEXIST(内存段已经存在,无法创建)EIDRM(内存段已经被删除)ENOENT(内存段不存在)EACCES(权限不够)ENOMEM(没有足够的内存来创建内存段),系统调用:shmat();原型:int shmat(int shmid,char*shmaddr,int shmflg);返回值:如果成功,则返回共享内存段连接到进程中的地址。如果失败,则返回-1:errno=EINVAL(无效的IPC ID 值或者无效的地址)ENOMEM(没有足够的内存)EACCES(存取权限不够),当一个进程不在需要共享的内存段

30、时,它将会把内存段从其地址空间中脱离。系统调用:shmdt();调用原型:int shmdt(char*shmaddr);返回值:如果失败,则返回-1:errno=EINVAL(无效的连接地址),5.消息队列,消息队列就是消息的一个链表,它允许一个或多个进程向它写消息,一个或多个进程从中读消息。具有一定的FIFO的特性,但是可实现消息的随即查询。这些消息存在于内核中,由“队列ID”来标识。消息队列的实现包括创建和打开队列、添加消息、读取消息和控制消息队列这四种操作。msgget:创建和打开队列,其消息数量受系统限制。msgsnd:添加消息,将消息添加到消息队列尾部。msgrcv:读取消息,从消

31、息队列中取走消息。msgctl:控制消息队列。,int msgget(key_t key,int flag)key:返回新的或已有队列的ID,IPC_PRIVATEint msgsnd(int msqid,struct msgbuf*msgp,size_t msgsz,int flag)其中:msqid是消息队列的队列ID;msgp是消息内容所在的缓冲区;msgsz是消息的大小;msgflg是标志,IPC_NOWAIT若消息并没有立交发送而调用进程会立即返回。,struct msgbuf long mtype;/*type of message*/char mtext1;/*message t

32、ext*/;,int msgrcv(int msqid,struct msgbuf*msgp,size_t msgsz,long msgtyp,int flag)msqid是消息队列的引用标识符;msgp是接收到的消息将要存放的缓冲区;msgsz是消息的大小;msgtyp是期望接收的消息类型;msgflg是标志。,int msgctl(int msqid,int cmd,struct msqid_ds*buf)msqid是消息队列的引用标识符;cmd是执行命令;buf是一个缓冲区。cmd参数指定对于由msqid规定的队列要执行的命令:IPC_STAT取此队列的msqid_ds结构,并将其存放在

33、buf指向的结构中。IPC_SET按由buf指向的结构中的值,设置与此队列相关的结构中的下列四个字段:msg_perm.uid、msg_perm.gid、msg_perm;mode和msg_qbytes。此命令只能由下列两种进程执行:一种是其有效用户ID等于msg_perm.cuid或msg_perm.uid;另一种是具有超级用户特权的进程。只有超级用户才能增加msg_qbytes的值。IPC_RMID 从系统中删除该消息队列以及仍在该队列上的所有数据。这种删除立即生效。仍在使用这一消息队列的其他进程在它们下一次试图对此队列进行操作时,将出错返回EIDRM。此命令只能由下列两种进程执行:一种是其有效用户ID等于msg_perm.cuid或msg_perm.uid;另一种是具有超级用户特权的进程。,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号