linux进程通信

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

一、linux下进程间通信概述

AT&T的贝尔实验室,对UNIX早期的进程间通信进行了改进和扩充,形成了“system V IPC”,其通信进程主要局限在单个计算机内。

BSD(加州大学伯克利分校伯克利软件发布中心),跳过了该限制,形成了基于套接字(socket)的进程间通信机制。

linux继承了上述所有通信方式:

1、传统通信方式

无名管道(pipe),有名管道(fifo),信号(signal)

2、system V IPC对象

共享内存(share memory),消息队列(message queue),信号灯(又叫信号灯集,与线程中的信号灯要区分清楚,semaphore)

3、BSD

套接字(socket,这个其实主要用于网络间线程通信,所以肯定可以支持本机线程通信)

二、每种通信方式的介绍

1、无名管道(pipe)

无名管道只能用于具有亲缘关系的进程间通信,即父子进程,兄弟进程等。

无名管道由内核来维护,是一种半双工的通信模式,具有固定的读端和写端(读端只能读,写端只能写),可以将其看成文件,使用write和read函数进行读写。

由于子进程完全继承父进程打开的文件描述符,所以父子进程可以通过pipe进行通信

创建无名管道pipe需要用到函数:

int pipe(int fd[2]);

fd[0]固定为读端,fd[1]固定为写端。

举例如下:

int fd[2];

pipe(fd);

这样,一个进程通过fd[1]写入数据,另一个就可以从fd[0]读出来了。

注意无名管道无读者时,写操做将会返回一个信号,如果进程未对此信号进行捕捉,会被信号杀死(即进程退出)。

若无写者,则读操作会返回0

无名管道不能保证原子性,既如果有多个进程同时向同一个管道里面写数据,可能会造成数据交错,得不到我们想要的结果,这点希望读者能够在编程时注意。

2、有名管道fifo

有名管道可以使互不相关的两个进程之间进行通信,有名管道想普通文件一样在linux文件系统中可见,可以通过路径来指定,用open,write,read等函数进行操作,而且他与无名管道不通,他可以保证写入数据的原子性,而且遵循先进先出的原则。

使用模型:

有名管道创建函数mkfifo:

int mkfifo(const char *filename,mode_t mode);

由函数原型可以看出他与open()函数基本类似:

返回值:0(成功)-1(错误)

参数filename: 指定包含文件名的路径(注意绝对路径和相对路径的区分)

参数mode:指定创建管道文件的访问权限,一般为八进制0666

举例如下:

创建:mkfifo(“/home/farsight/share/fifo”,0666);

服务器打开:int fd;

fd=open(“/home/farsight/share/fifo”,O_RDONLY);

客户打开:int fd;

fd=open(“/home/farsight/share/fifo”,O_WRONLY);

客户机write的数据,服务器就可以通过read来获得。

3、信号(signal)

信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式(所谓异步是指,接收方无法提前预知的信号到来),信号可以直接进行用户空间进程和linux内核进程之间交互。

信号的处理方式:

忽略信号,既不对信号做任何处理,但有两个信号不能忽略:SIGKILL,SIGSTOP ,这两个信号到来,无条件执行默认操作,也就是杀死当前进程。

捕捉信号,定义信号处理函数,当信号到来时,执行相应的信号处理函数。

执行缺省操作:linux对每个信号都规定了默认操作(绝大多数都是直接杀死进程)

一个完整的信号生命周期分为三个重要阶段,而这三个重要阶段由四个重要事件来刻画:信号的产生,信号在进程中注册,信号在进程中注销,执行信号处理函数。

信号的发送:

int kill(pid_t pid,int sig);

返回值:0(成功)-1(出错)

参数pid:要接收信号的进程的ID

pid=0,信号发送给所有和本进程在同一进程组的进程

pid=-1,信号发送给所有进程表中的进程(除了进程号最大的进程)

参数sig:信号类型

信号的注册与捕捉:

void(*signal(int signum,void(*handler)(int)))(int);

乍看一下觉得这个函数好复杂,其实很简单:这个函数返回一个函数指针,指向一个void fun(int)类型的函数(此函数实际上是注册之前的信号处理方式),函数有两个参数,第二个参数是一个函数指针,同样指向一个void fun(int)类型的函数(此函数是注册后的信号处理函数),这样是不是就很清楚了。

返回值:-1(错误)

参数signum:指定要注册的信号

参数handler:SIG_IGN 忽略信号

SIG_DFL 按照信号默认方式执行

自定义信号处理函数指针

Kill和signal函数是一对配套使用的函数,还有一对功能加强版的:sigqueue和sigaction 也是一对配套使用的信号发送处理函数,使用较少,不做详细介绍,若读者感兴趣可以自己研究一下。

编程举例:

信号发送:kill(pid,SIG_USR1);

信号注册处理:signal(SIG_USR1,fun);//完成信号注册,当信号到来时,执行fun函数。

void fun(int sig){

//可以添加相应的处理代码

}

4、共享内存(share memory)

顾名思义,共享内存就是两个进程共同使用一段内存区,此段内存区由内核来维护,有一个key来唯一的标识。此种方式最为简洁,高效。

共享内存的创建:

int shmget(key_t key,int size,int shmflg);

返回值:共享内存段标识符(同文件标识符fd)(成功)-1(出错)

参数key:一个唯一标识此内存段的整数,由我们自己指定,为防止指定的整数已经被内核使用,或重复,我们一般使用ftok函数来产生一个整数,一般使用如:ftok(“.”,100) (具体使用不做过多介绍)。

参数size:所要创建的内存区的大小,必须是偶数。

参数shmflg:同open函数的权限位,一般用八进制表示法:0666,例如IPC_CREAT|0666

举例:

key_t key;

key=ftok(“.”,1);

int shmid;

shmid=shmget(key,100,IPC_CREAT|0666);

共享内存的映射:

相关文档
最新文档