目录
1.进程间通信方式
2.无名管道(pipe)
(1)无名管道的特点
①只能用于具有亲缘关系的进程之间的通信;
②半双工的通信模式,具有固定的读端和写端;
③可以看作是一种特殊的文件,可以使用文件IO函数对其进行读写;
③通信方式基于文件描述符,管道建立时会产生两个文件描述符fd[0]和fd[1],其中fd[0]用于读管道,fd[1]用于写管道;
记忆方式:读写->01;
(2)函数接口
int pipe(int fd[2]);
功能:创建无名管道;
参数:文件描述符,fd[0],fd[1];
返回值:成功为0,失败为-1;
//使用示范
//1.创建存储文件描述符的数组
int fd[2] = {0};
//创建管道并错误判断
if(pipe(fd) < 0)
{
perror("pipe create err");
return -1;
}
(3)无名管道使用注意事项
①当管道中没有数据时,读操作阻塞;
②管道中装满数据时,写操作阻塞;管道内存为64K,即65536位,当管道存满后,不能继续写内容;当管道内有4K,即4096位的空间时,才能继续写内容。
③管道中必须有读端存在,写操作才有意义,没有读端时进行写操作的话,会造成管道破裂,内核传递SIGPIPE信号(管道破裂)。
代码演示1:使用代码验证管道内存大小。
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//创建无名管道的读端fd[0]和写端fd[1];
int fd[2] = {0};
char buf[65536] = "";
//创建无名管道
if(pipe(fd) < 0)
{
perror("pipe create err");
return -1;
}
//fd[0]和fd[1]是两个文件标识符,所以打印出来应该是3和4
printf("%d %d\n",fd[0],fd[1]);
//管道中写满64K->65536数据时,写堵塞
write(fd[1],buf,65536);
printf("full\n");
//管道中至少有4K->4096的空间,才能继续写
read(fd[0],buf,4096);
write(fd[1],"a",1);//写入的参数是字符串,所以就算是只写一个a,也要用双引号括起
close(fd[0]);//读端关闭,继续写的话,会导致管道破裂;
return 0;
}
代码演示2:用管道实现,循环在父进程中终端输入,在子进程中终端输出,输入“quit”时循环结束;
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char buf[32] = "";
//先创建管道
int fd[2] = {0};
if(pipe(fd) < 0)
{
perror("pipe create err");
return -1;
}
//再创建子进程
pid_t pid;
pid = fork();
if(pid < 0)
{
perror("fork err");
return -1;
}
else if(pid == 0) //子进程中读取管道内容并输出
{
//使用while实现循环
while(1)
{
memset(buf,0,sizeof(buf));
//从管道中读取内容后,读取的内容就不存在于管道中了
read(fd[0],buf,32);
//字符串的比较,使用函数strcmp();
//quit后面加\n的原因是,fgets获取的是在终端中输入的所有字符和操作
if(strcmp(buf,"quit\n") == 0)
{
break;
}
printf("buf:%s\n",buf);
}
//跳出循环后退出子进程
exit(0);
}
else
{
while(1)
{
/*fgets()将在终端输入的字符串获取到buf数组中后,
然后将buf数组中的数据写入管道时,但是buf数组中仍然存在原本输入的字符串,
从管道中读出数据时,也是将数据读入到了buf数组中,然后打印,
所以每次写入管道和读出的时候,都要将buf数组清空,清空使用函数memset()*/
memset(buf,0,sizeof(buf));
//fgets()可以获取输入的所有字符
fgets(buf,32,stdin);
write(fd[1],buf,strlen(buf));
if(strcmp(buf,"quit\n") == 0)
{
break;
}
}
//跳出循环后等待子进程结束;
wait(NULL);
}
return 0;
}
3.有名管道(mkfifo)
(1)有名管道的特点
①有名管道支持两个互不相关的进程之间相互通信;
②有名管道可以通过路径名指出文件位置,并且管道文件存在于文件系统中,但是管道文件的内存为0,文件内容存储在进程的3G到4G的内核内存中;
③有名管道同样通过文件IO函数来操作;
④有名管道遵循先进先出的规则;
⑤不支持lseek()定位操作;
(2)函数接口
int mkfifo(const char *filename,mode_t mode);
功能:创建有名管道
参数:filename->管道文件名,mode->权限
返回值:成功为0,返回为-1并设置errno号;
//使用示范
//管道创建时,返回值为-1,则创建失败
if(mkfifo("./fifo",0666) < 0)
{
//如果返回的错误码是管道文件已存在,则打印信息
if(errno == EEXIST)
{
printf("fifo file exist\n");
}
else
{
//否则管道创建失败
perror("mkfifo err");
return -1;
}
}
//打印管道创建成功
printf("mkfifo sucess\n");
(3)有名管道使用注意事项
使用open()函数打开管道文件时,
①只写方式打开,写阻塞,直到另一个进程将读打开;
②只读方式打开,读阻塞,直到另一个进程将写打开;
③可读可写的方式打开,如果管道中没有数据,读阻塞。
代码演示1:在一个进程中打开管道,实现写入数据,读取数据并在终端打印的操作;
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
//创建管道文件
if(mkfifo("./fifo",0666)<0)
{
if(errno == EEXIST)
{
printf("fifo file exist\n");
}
else
{
perror("mkfifo err");
return -1;
}
}
printf("mkfifo sucess\n");
char buf[32] = "";
//以可读可写的方式打开管道文件
int fd;
fd = open("./fifo",O_RDWR);
if(fd < 0)
{
printf("fifo open err");
return -1;
}
//向管道中写入数据
write(fd,"hello",5);
//从管道中读取数据并输出
read(fd,buf,32);
printf("%s\n",buf);
return 0;
}
代码演示2:实现在一个文件中对管道写入字符串hello,在另一个文件中读取管道内的字符串hello并输出;
//写操作程序
int main(int argc, char const *argv[])
{
//创建管道文件
if(mkfifo("./fifo",0666)<0)
{
if(errno == EEXIST)
{
printf("fifo file exist\n");
}
else
{
perror("mkfifo err");
return -1;
}
}
printf("mkfifo sucess\n");
char buf[32] = "";
int fd;
//以只写方式打开管道文件
fd = open("./fifo",O_WRONLY);
if(fd < 0)
{
printf("fifo open err");
return -1;
}
//向管道中写入字符串hello
write(fd,"hello",5);
return 0;
}
//读操作程序
//程序中不需要再创建管道文件,直接在文件中打开相同的管道文件即可
int main(int argc, char const *argv[])
{
char buf[32] = "";
//以只读方式打开管道文件
int fd;
fd = open("./fifo",O_RDONLY);
if(fd < 0)
{
printf("fifo open err");
return -1;
}
//读取管道中的内容并打印
read(fd,buf,32);
printf("%s\n",buf);
return 0;
}
执行过程:
打开两个终端,运行程序,
写:
读:
4.无名管道和有名管道的区别
1.无名管道用于具有亲缘关系之间的进程通信,有名管道可以用于没有亲缘关系进程之间的通信;
2.无名管道具有固定的读端和写端,有名管道不需要固定的读端和写端;
3.无名管道不需要路径来创建管道文件,有名管道需要指定路径来创建管道文件,文件大小为0,文件的内容存储在进程的3G-4G的内核存储空间中。
如果本文中存在概念错误或代码错误的情况,请批评指正。