进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。
IPC的方式通常有管道(包括匿名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。
一、匿名管道(pipe)
管道,通常指匿名管道,是 UNIX 系统IPC最古老的形式。
1、特点:
管道是IPC最基本的一种实现机制。我们都知道在Linux下“一切皆文件”,其实这里的管道就是一个文件。管道实现进程通信就是让两个进程都能访问该文件。
管道的特征:
①只提供单向通信,也就是说,两个进程都能访问这个文件,假设进程1往文件内写东西,那么进程2 就只能读取文件的内容。
②只能用于具有血缘关系的进程间通信,通常用于父子进程建通信
③管道是基于字节流来通信的
④依赖于文件系统,它的生命周期随进程的结束结束(随进程)
⑤其本身自带同步互斥效果
2、原型
#include <unistd.h>
int pipe(int fd[2]); // 返回值:若成功返回0,失败返回-1
注释:调用pipe函数时,首先在内核中开辟一块缓冲区用于通信,它有一个读端和一个写端,然后通过fd参数传出给用户进程两个文件描述符,fd[0]指向管道的读端,fd[1]指向管道的写段。在用户层面看来,打开管道就是打开了一个文件,通过read()或者write()向文件内读写数据,读写数据的实质也就是往内核缓冲区读写数据。
返回值:成功返回0,失败返回-1。
既然管道只能用于具有血缘关系的进程间通信,因此在这里我们可以调用fork函数,创建一个子进程,然后让父子进程通过管道进行通信。如下图:
3、例子
单个进程中的管道几乎没有任何用处。所以,通常调用 pipe 的进程接着调用 fork,这样就创建了父进程与子进程之间的 IPC 通道。如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/dafec61094b2f454b50f130fff7f08ec.png)
若要数据流从父进程流向子进程,则关闭父进程的读端(fd[0]
)与子进程的写端(fd[1]
);反之,则可以使数据流从子进程流向父进程。要关闭管道只需将这两个文件描述符关闭即可。
#include<stdio.h>
#include<unistd.h>
int main()
{
int fd[2]; // 两个文件描述符
pid_t pid;
char buff[20];
if(pipe(fd) < 0) // 创建管道
printf("Create Pipe Error!\n");
if((pid = fork()) < 0) // 创建子进程
printf("Fork Error!\n");
else if(pid > 0) // 父进程
{
close(fd[0]); // 关闭读端
write(fd[1], "hello world\n", 12);
}
else
{
close(fd[1]); // 关闭写端
read(fd[0], buff, 20);
printf("%s", buff);
}
return 0;
}
使用管道需要注意的4种特殊情况:
- 如果所有指向管道写端的文件描述符都关闭了,而仍然有进程从管道的读端读数据,那么文件内的所有内容被读完后再次read就会返回0,就像读到文件结尾。
- 如果有指向管道写端的文件描述符没有关闭(管道写段的引用计数大于0),而持有管道写端的进程没有向管道内写入数据,假如这时有进程从管道读端读数据,那么读完管道内剩余的数据后就会阻塞等待,直到有数据可读才读取数据并返回。
- 如果所有指向管道读端的文件描述符都关闭,此时有进程通过写端文件描述符向管道内写数据时,则该进程就会收到SIGPIPE信号,并异常终止。
- 如果有指向管道读端的文件描述符没有关闭(管道读端的引用计数大于0),而持有管道读端的进程没有从管道内读数据,假如此时有进程通过管道写段写数据,那么管道被写满后就会被阻塞,直到管道内有空位置后才写入数据并返回。
二、FIFO
FIFO,也称为命名管道,它是一种文件类型
1、特点:
上述管道虽然实现了进程间通信,但是它具有一定的局限性:首先,这个管道只能是具有血缘关系的进程之间通信;第二,它只能实现一个进程写另一个进程读,而如果需要两者同时进行时,就得重新打开一个管道。为了使任意两个进程之间能够通信,就提出了命名管道(named pipe 或 FIFO)。
1、与管道的区别:提供了一个路径名与之关联,以FIFO文件的形式存储于文件系统中,能够实现任何两个进程之间通信。而匿名管道对于文件系统是不可见的,它仅限于在父子进程之间的通信。
2、FIFO是一个设备文件,在文件系统中以文件名的形式存在,因此即使进程与创建FIFO的进程不存在血缘关系也依然可以通信,前提是可以访问该路径。
3、FIFO(first input first output)总是遵循先进先出的原则,即第一个进来的数据会第一个被读走。
2、原型:
#include <sys/stat.h>
int mknod(const char* path, mode_t mod, dev_t dev);
int mkfifo(const char* path, mode_t mod);
注释:这两个函数都能创建一个FIFO文件,该文件是真实存在于文件系统中的。函数 mknod 中参数 path 为创建命名管道的全路径; mod 为创建命名管道的模式,指的是其存取权限; dev为设备值,改值取决于文件创建的种类,它只在创建设备文件是才会用到。
返回值:这两个函数都是成功返回 0 ,失败返回 -1
命名管道创建完成后就可以使用,其使用方法与管道一样,区别在于:命名管道使用之前需要使用open()打开。这是因为:命名管道是设备文件,它是存储在硬盘上的,而管道是存在内存中的特殊文件。但是需要注意的是,命名管道调用open()打开有可能会阻塞,但是如果以读写方式(O_RDWR)打开则一定不会阻塞;以只读(O_RDONLY)方式打开时,调用open()的函数会被阻塞直到有数据可读;如果以只写方式(O_WRONLY)打开时同样也会被阻塞,知道有以读方式打开该管道。
3、例子
FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清除数据,并且“先进先出”。下面的例子演示了使用 FIFO 进行 IPC 的过程:
write_fifo.c
#include<stdio.h>
#include<stdlib.h> // exit
#include<fcntl.h> // O_WRONLY
#include<sys/stat.h>
#include<time.h> // time
int main()
{
int fd;
int n, i;
char buf[1024];
time_t tp;
printf("I am %d process.\n", getpid()); // 说明进程ID
if((fd = open("fifo1", O_WRONLY)) < 0) // 以写打开一个FIFO
{
perror("Open FIFO Failed");
exit(1);
}
for(i=0; i<10; ++i)
{
time(&tp); // 取系统当前时间
n=sprintf(buf,"Process %d's time is %s",getpid(),ctime(&tp));
printf("Send message: %s", buf); // 打印
if(write(fd, buf, n+1) < 0) // 写入到FIFO中
{
perror("Write FIFO Failed");
close(fd);
exit(1);
}
sleep(1); // 休眠1秒
}
close(fd); // 关闭FIFO文件
return 0;
}
read_fifo.c
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
int main()
{
int fd;
int len;
char buf[1024];
if(mkfifo("fifo1", 0666) < 0 && errno!=EEXIST) // 创建FIFO管道
perror("Create FIFO Failed");
if((fd = open("fifo1", O_RDONLY)) < 0) // 以读打开FIFO
{
perror("Open FIFO Failed");
exit(1);
}
while((len = read(fd, buf, 1024)) > 0) // 读取FIFO管道
printf("Read message: %s", buf);
close(fd); // 关闭FIFO文件
return 0;
}
上述例子可以扩展成 客户进程—服务器进程 通信的实例,write_fifo
的作用类似于客户端,可以打开多个客户端向一个服务器发送请求信息,read_fifo
类似于服务器,它适时监控着FIFO的读端,当有数据时,读出并进行处理,但是有一个关键的问题是,每一个客户端必须预先知道服务器提供的FIFO接口,下图显示了这种安排:
参考:1. http://songlee24.github.io/2015/04/21/linux-IPC/
2.https://blog.csdn.net/qq_33951180/article/details/68959819