大家好,我是练习编程时长两年半的个人练习生昆工第一ikun,昨天分享了线程间通信,今天我们来分享一下进程间通信的一种方法——管道。管道又分为无名管道和有名管道,二者的区别在于是否能在文件系统中可见,下面我将来介绍这两种方式。
目录
一、无名管道
1.特点
①创建之后在文件系统中不可见
②以半双工的方式进行通信
③拥有固定的读端和写端
④只能用于具有亲缘关系的进程间通信
2.创建无名管道
①所用函数:pipe();
#include <unistd.h>
int pipe(int pipefd[2]);
参数:
pipefd:存放无名管道读端和写端的数组首地址
pipefd[0] -- 读端
pipefd[1] -- 写端
返回值:
成功返回0,失败返回-1;
②实例
利用无名管道实现子进程从键盘上输入数据,父进程将数据打印到终端,代码如下:
/*===============================================
* 文件名称:pipe.c
* 创 建 者:
* 创建日期:2022年08月10日
* 描 述:利用无名管道实现子进程从键盘上输入数据,父进程将数据打印到终端
================================================*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int fd[2];
int ret = pipe(fd); //创建无名管道
if(ret < 0)
{
perror("pipe");
exit(-1);
}
pid_t pid = fork(); //创建子进程
if(pid == 0)
{
char buf[64];
fgets(buf, 64, stdin); //从键盘输入数据
buf[strlen(buf)-1] = '\0';
write(fd[1], buf, 64); //将获取到的数据写入无名管道中
}
else
{
char buf1[64];
memset(buf1, 0, 64); //清空缓冲区
read(fd[0], buf1, 64); //读取无名管道中的数据
printf("%s\n", buf1);
}
return 0;
}
3.无名管道的读写特性
①读特性
a.写端存在:
管道有数据:返回读到的字节数
管道无数据:阻塞
b.写端不存在:
管道有数据:返回读到的字节数
管道无数据:返回0
②写特性
a.读端存在:
管道有空间:返回写入的字节数
管道无空间:阻塞,直到有空间为止
b.读端不存在:
无论管道是否有空间,管道破裂
③实例
计算管道大小和使管道破裂并打印管道破裂的信号
/*===============================================
* 文件名称:pipe_size_broken.c
* 创 建 者:
* 创建日期:2022年08月10日
* 描 述:计算管道大小和使管道破裂并打印管道破裂的信号
================================================*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int status;
int fd[2];
int ret = pipe(fd); //创建无名管道
if(ret < 0)
{
perror("pipe");
exit(-1);
}
//close(fd[0]); //使管道破裂则关闭管道的读取端,计算管道空间大小则不关闭
pid_t pid = fork(); //创建子进程
if(pid == 0)
{
int n = 0;
char buf[2];
while(1)
{
strcpy(buf, "a"); //在写入端循环写入数据,将管道空间装满
write(fd[1], buf, 1);
n++; //计数
printf("%d\n", n);
}
exit(0);
}
else
{
char buf1[64];
memset(buf1, 0, 64);
read(fd[0], buf1, 64);
printf("%s\n", buf1);
}
wait(&status);
printf("%d\n", WTERMSIG(status)); //打印破裂信号
exit(0);
return 0;
}
运行程序,我们可以看到由于管道无空间,所以被阻塞,总空间大小为65536
将管道的读取端关闭后,我们可以看到管道破裂,并打印除了破裂信号——13)SIGPIPE
二、有名管道
1.特点
①有名管道创建之后会在文件系统中以管道文件的形式存在
②有名管道可以用于任意两个进程间通信
2.有名管道的创建
①所用函数:mkfifo();
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参数:
pathname:创建管道文件的文件名
mode:创建管道文件的权限
返回值:
成功返回0,失败返回-1
②实例
创建一个有名管道,一个进程向管道中输入数据,另一个进程输出数据
/*===============================================
* 文件名称:mkfifo.c
* 创 建 者:
* 创建日期:2022年08月10日
* 描 述:创建有名管道
================================================*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
int ret = mkfifo("fifo", 0664);
if(ret < 0)
{
perror("mkfifo");
exit(-1);
}
return 0;
}
然后,我们编写两个程序分别用于输入和输出
/*===============================================
* 文件名称:mkfifo.c
* 创 建 者:
* 创建日期:2022年08月10日
* 描 述:输入端
================================================*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd = open("fifo", O_RDWR);
char buf[64];
while(1)
{
fgets(buf, 64, stdin);
buf[strlen(buf)-1] = '\0';
write(fd, buf, 64);
}
close(fd);
return 0;
}
/*===============================================
* 文件名称:cxk.c
* 创 建 者:
* 创建日期:2022年08月10日
* 描 述:输出端
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
char buf[64];
int fd = open("fifo", O_RDWR);
while(1)
{
memset(buf, 0, 64);
read(fd, buf, 64);
printf("%s\n", buf);
}
close(fd);
return 0;
}
然后,我们分别在两个终端运行两个程序,可以发现两个进程间可以实现通信