本篇博文,旨在讲述进程间通信方式中的管道通信;讲述了匿名管道通信和命名管道通信两种方式,并用代码进行了验证
进程间通信
提出疑问
每个进程有自己独立的用户地址空间,一个进程的全局变量在另一个进程中可以看到吗?
我们用下面这个程序来进行测试
验证代码
验证结果
结论
由此,我们可以论证:在父进程中的全局变量,如果在子进程中去改变这个全局变量,则子进程中被改变的这个值不会去影响父进程,因为子进程中的所有数据都是通过写时拷贝拷自父进程的,这两个进程的地址空间不同
进程间通信概念
然而,如果两个进程需要交换数据,那么需要从内核中开辟一块空间,作为两个进程之间的缓冲区,一个进程可以将数据写入缓冲区,另一个进程可以从该缓冲区拿走数据,这样便实现了两个进程的信息交互。我们将内存提供两个进程交互的机制叫做进程间通信
管道通信
匿名管道
匿名管道的特点
1、单向通信
2、依赖于文件系统,因此管道的生命会因进程退出而退出(PCB含有地址空间)
3、此管道只能用于具有血缘关系的进程间通信,只有共享文件描述符表,才可以用这种方式来通信
4、管道是按照数据流的方式进行读写的
5、管道自带同步机制,同步访问
2、依赖于文件系统,因此管道的生命会因进程退出而退出(PCB含有地址空间)
3、此管道只能用于具有血缘关系的进程间通信,只有共享文件描述符表,才可以用这种方式来通信
4、管道是按照数据流的方式进行读写的
5、管道自带同步机制,同步访问
匿名管道有关的函数
pipe函数可以调用使在内核中开辟一块缓冲区用于通信,它有一个读端一个写端,然后通过fds参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端
验证代码
验证结果
匿名管道的使用步骤
步骤1:进程创建管道,指向读端和写端
步骤2:创建子进程,使两个进程同时指向读端和写端
步骤3:一个进程关闭读端,一个进程关闭写端
验证代码
运行结果
特殊情况1
如果所有指向管道写端的文件秒描述符都关闭了(管道写端的计数为0),而任然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。
运行结果
特殊情况2
如果有指向管道写端的文件描述符没关闭(管道写端的引用计数大于0),⽽持有管道写端的 进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。
运行结果
特殊情况3
如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数等于0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。
运行结果
特殊情况4
如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0),而持有管道读端的 进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再 次write会阻塞,直到管道中有空位置了才写入数据并返回。
运行结果
命名管道
命名管道比匿名管道最大的优势在于可以在不具备血缘关系的两个进程间进行通信。
命名管道的优势
匿名管道的一个不足之处是没有名字,只能用于具有亲缘关系的进程间通信
然而,在命名管道提出后,该限制得到了克服。
FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中。
命名管道是一个设备文件,故而,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。
值得注意的是,FIFO总是按照先进先出的原则工作,第一个被写入的数据将首先从管道中读出。
Linux下创建方法
Linux下有两种方式创建命名管道。
方法一:在Shell下交互地建立一个命名管道,
方法二:在程序中使用系统函数建立命名管道。Shell方式下可使用mknod或mkfifo命令
client.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<fcntl.h>
int main()
{
umask(0);
if(mkfifo("./fifofile",S_IFIFO|0666) == 0)
{
int fd = open("./fifofile",O_WRONLY);
if(fd > 0)
{
while(1)
{
char buf[1024];
printf("client# ");
fflush(stdout);
ssize_t s = read(0,buf,sizeof(buf-1));
if(s > 0)
{
buf[s] = 0;
write(fd,buf,strlen(buf));
}
else
{
perror("read");
return 3;
}
}
}
else
{
perror("open");
return 2;
}
}
else
{
perror("mkfifo");
return 1;
}
return 0;
}
server.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<fcntl.h>
int main()
{
int fd = open("./fifofile",O_RDONLY);
if(fd > 0)
{
while(1)
{
char buf[1024];
ssize_t s = read(fd,buf,sizeof(buf));
if(s > 0)
{
buf[s] = 0;
printf("client# %s",buf);
}
else if(s == 0)
{
printf("client is quit ! server quit !\n");
}
else
{
perror("read");
return 3;
}
}
}
return 0;
}
运行结果
我们在client一端输入信息,server便可以进行显示
符号‘|’——一种匿名管道
在我们经常使用的'|'符号,就是一种匿名管道,因为它没有创建新的文件,而且可以在兄弟进程之间进行通信(仍具备血缘关系)