进程间通信的作用是为了进行同步和交换数据。
一、管道管道是进程之间的一个单向数据流。
1.1 什么是管道
把一个进程连接到另一个进程的一个数据流称为一个“管道”,通常是用作把一个进程的输出通过管道连接到另一个进程的输入。管道本质上是内核的一块缓存。一个进程在管道的尾部写入数据,另一个进程从管道的头部读出数据。管道包括无名管道和有名管道两种,前者只能用于父进程和子进程间的通信,后者可用于运行于同一系统中的任意两个进程间的通信。
1.2 管道的特点
- 1、管道通讯是单向的,先进先出,有固定的读端和写端。
- 2、数据被进程从管道读出后,在管道中该数据就不存在了。
- 3、当进程去读取空管道的时候,进程会阻塞。
- 4、当进程往满管道写数据时,进程会阻塞。
- 5、管道容量为64KB(#define PIPE_BUFFERS 16 include/linux/pipe_fs_i.h)
管道的小例子
1.3 匿名管道(pipe)
概念
匿名管道是基于文件描述符的通信方式。实现两个进程间的通信时必须通过fork创建子进程,实现父子进程之间的通信。
读写规则
- 1、管道内没有数据时,读端(read)发生阻塞,等待有效数据进行读取
- 2、管道容量被数据填满时,写端(write)发生阻塞,等待进程将数据读走再进行写入
- 3、如果所有管道写端对应的文件描述符被关闭,read返回0,但会将之前管道里的数据读完
- 4、如果所有管道的读端对应的文件描述符被关闭,write操作会产生信号,SIGPIPE,进而导致write进程退出
- 5、当要写入的数据量不大于管道的容量(PIPE_BUF)时,linux将保证写入的原子性
- 6、当要写入的数据量大于管道容量(PIPE_BUF)时,linux将不再保证写入的原子性
特点
- 1、只能够进行单向通信。
- 2、只能够用于有血缘关系(父子,兄弟,爷孙)的进程之间,多常用于父子之间。
- 3、管道内部自带同步机制:子进程写一条,父进程读一条。
- 4、管道在进行通信的时候,对外层提供的服务叫做面向字节流的服务
- 5、当进程退出之时,管道也随之释放,与文件保持一致
- 6、管道的生命周期为随进程,进程结束管道就没了
流:相当于水流,写入数据时,写多少字节和你自己有关系,读的时候没有 格式的要求,完全取决你自己;
字节流:以字节来读取和写入,字节数的大小完全取决于自己
单进程通信原理
父子进程通信原理
代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd[2];//文件描述符数组
char buf[1024];//临时缓冲区
int len;
ssize_t s;
ssize_t w;
if(pipe(fd) == -1)//创建管道
{
perror("pipe");
return -1;
}
while(1)
{
printf("Please enter:");
fflush(stdout);
s = read(0,buf,sizeof(buf));//从屏幕上读取内容
if( s < 0)
{
perror("read");
return -2;
}
len = strlen(buf);
w = write(fd[1],buf,len);//将读取的数据写入管道
if(w != len)
{
perror("write");
return -3;
}
memset(buf,0,sizeof(buf));//将buf清0
s = read(fd[0],buf,sizeof(buf));//从管道当中读取数据
if(s < 0)
{
perror("read");
return -4;
}
w = write(1,buf,len);//将读的数据写入屏幕
if(w != len)
{
perror("write");
return -5;
}
}
return 0;
}
结果:
1.4 命名管道(mkfifo)
概念
命名管道本质上是一个管道文件,可以通过命令创建也可以通过函数创建,用户可以看到。
特点
1. 可以进行不相干进程间的通信。
2. 命名管道是一个文件,对于文件的相关操作对其同样适用。
读写规则
1. 对于管道文件,当前进程操作为只读时,则进行阻塞,直至有进程对其写入数据。
2. 对于管道文件,当前进程操作为只写时,则进行阻塞,直至有进程从管道中读取数据。
示例代码:
write:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
//读取文件,将文件内容写入管道
int main()
{
mkfifo("tp",0644);//创建一个管道文件
int infd = open("123",O_RDONLY);//打开一个文件
if(infd == -1){
perror("open");
return 1;
}
int outfd = open("tp",O_WRONLY);//打开管道文件,将123文件的内容写入管道文件
if(outfd == -1){
perror("open");
return 2;
}
char buf[1024];//用于存放文件内容
ssize_t s;
while( (s = read(infd,buf,sizeof(buf))) >0)
{
write(outfd,buf,s);
}
close(infd);
close(outfd);
}
Read
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//从管道文件里面读取内容,并将内容写入另一个文件中
int main()
{
int infd = open("abc.bak",O_CREAT | O_WRONLY | O_TRUNC,0644);//创建一个新的文件
if(infd == -1){
perror("open");
return 1;
}
//将从管道读取的内容写入到新的文件中
int outfd = open("tp",O_RDONLY);//打开管道文件
if(outfd == -1){
perror("open");
return 2;
}
char buf[1024];//临时数组
ssize_t s;
while( (s = read(outfd,buf,sizeof(buf))) > 0)
{
write(infd,buf,s);
}
close(infd);
close(outfd);
return 0;
}
5.总结
类型 | 进程关系 | 不同点 | 本质 |
---|---|---|---|
匿名管道 | 必须是亲缘关系 | 由pipe创建并打开 | 内核的一块缓存 |
命名管道 | 两个毫不相干进程 | 由mkfifo创建,open打开 | 一个文件 |