🎊【进程通信与并发】专题正在持续更新中,进程,线程,IPC,线程池等的创建原理与运用✨,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏
🪔本系列专栏 - 进程通信与并发
🍻欢迎大家 🏹 点赞👍 评论📨 收藏⭐️
📌个人主页 - 勾栏听曲_0的博客📝
🔑希望本文能对你有所帮助,如有不足请指正,共同进步吧🏆
🎇善治病者,必医其受病之处;善救弊者,必塞其起弊之源。📈
无名管道(pipe)
介绍
它在文件系统中没有名字(没有inode),它的内容在内核中,访问pipe的方式都是通过文件系统的API(read/write)
它不能用open,但是read/write又需要一个文件描述符!!!
所以在创建这个pipe的时候,就必须要返回文件描述符!!!
pipe在创建时,在内核中开辟一块缓冲区,作为pipe文件的内容的存储空间,同时返回两个文件描述符(一个用来读,一个时用来写)
特点
(1)pipe有两端,一端时用来写,一端是用来读;
(2)按顺序读;不支持lseek(光标移动)
(3)内容读走了,就没有啦
(4)pipe(无名管道) 随内核持续性
接口
头文件
#include <unistd.h>
函数功能
pipe用来在内核中创建一个无名管道,pipefd用来保存创建好的无名管道的两个文件描述符,pipe创建的管道,默认是"阻塞方式"
函数原型
int pipe(int pipefd[2]);
函数参数
int pipefd[2] //数组。
pipefd[0] 保存读的文件描述符;
pipefd[1] 保存写的文件描述符;
函数返回值
成功返回0
失败返回-1,同时errno被设置。
注意事项
pipe(无名管道)的使用范围:只要两个进程可以获取这个pipe(无名管道)的文件描述符,就可以使用pipe(无名管道)来通信。
pipe(无名管道)本身是全双工通信,但是两个进程使用一个管道进行全双工通信就必须要有某种方式同步,否则就可能自己读到自己写入的数据。所以在工程项目中,我们一般使用两个或以上的管道来实现全双工通信,一个读,一个写,人为的把它看成半双工通信。
上面说到了pipe(无名管道)的使用范围,而这个使用范围一般又是进程间有亲缘关系的进程,除非通过某些方式将pipe(无名管道)告知其他进程。因此pipe(无名管道)一般都用于有亲缘关系的进程间通信。为什么呢?其原因就是pipe(无名管道)它没有名字,只能靠创建pipe(无名管道)时的文件描述符来确定pipe(无名管道)。记住这个特点,我们再看FIFO(有名管道)。
代码实例
一下代码实现创建一个pipe(无名管道),子进程先往pipe(无名管道)中写入数据,然后再由父进程向pipe(无名管道)读取子进程写入的数据。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char * argv [])
{
int fd[2];
int p = pipe(fd);
if(p == -1)
{
perror("pipe failed\n");
}
pid_t pid = fork();
if(pid == -1)
{
perror("fork failed\n");
}
if(pid > 0)
{
int statues;
int w = wait(&statues);
if(w == -1)
{
perror("wait failed\n");
return -1;
}
char buf[6] = {0};
int r = read(fd[0],buf,5);
if(r != 5)
{
perror("read fd[0] failed\n");
}
printf("read's content is %s\n",buf);
}
else if(pid == 0)
{
int w = write(fd[1],"hello",5);
if(w != 5)
{
perror("write fd[1] failed\n");
}
}
return 0;
}
有名管道(fifo)
介绍
fifo是在pipe的基础上,给fifo在文件系统中创建一个inode(它会在文件系统中有一个文件名),但是fifo文件的内容却是在内核中!!!
fifo的文件名随文件系统持续性的;
fifo的文件内容存在于内核,随内核持续性的。
fifo同pipe一样,出除了fifo在文件系统中有一个文件名。所以特点也与pipe(无名管道)一样。
接口
fifo文件怎么创建呢?
通过mkfifo函数接口
头文件
#include <sys/types.h>
#include <sys/stat.h>
函数功能
用来在文件系统中创建一个fifo(有名管道)
函数原型
int mkfifo(const char *pathname, mode_t mode);
函数参数
const char *pathname //要创建的有名管道,在文件系统中的名字
mode_t mode //创建的有名管道的权限,有两种方式指定:
(1)0660 0777 ...
(2)S_IRUSR
函数返回值
成功:返回0
失败:返回-1,同时errno被设置。
注意事项
FIFO(有名管道)它和PIPE(无名管道)类似,除了它再文件系统中有一个名字。它可以被多个进程打开用来读或写。当进程用FIOF来交换数据时,内核根本没有把数据写到文件系统中去,而是保存在内核的内部,因此FIFO在文件系统中没有内容,它仅作为文件系统的一个引用入口,提供一个文件名,给其它进程去open它。
在数据交换前,FIFO的两端(read,write)必须都被打开。通常情况下,你打开FIFO的一端,会阻塞,直到另外一端也被打开。
一个进程也可能以"非阻塞"方式(O_NONBLOCK)去打开。
在这种情况下(以“非阻塞方式”),只读打开总会成功,即便写端没有被打开;只写打开总会失败,并且errno == EENXIO,除非读端已经打开。
阻塞与非阻塞
阻塞方式:
阻塞地读或写
读的时候,如果没有数据,则read会阻塞
写的时候,如果没有空间,则write会阻塞
非阻塞方式
以非阻塞方式读或写
读的时候,如果没有数据,立即返回,设置相应的错误码
写的时候,如果没有空间,立即返回,设置相应的错误码
代码实例
一下代码实现两个无亲缘关系的进程间使用FIFO(有名管道)进程通信。一个进程用于读,一个进程用于写。
第一个进程,用于将数据写进管道。
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char * argv [ ])
{
//创建一个fifo有名管道文件
int m = mkfifo("./test.fifo", 0660);
if(m == -1)
{
perror("mkfifo failed\n");
}
//打开管道文件
int fd = open("./test.fifo",O_RDWR);
if(fd == -1)
{
perror("open failed\n");
}
//写数据到管道文件
char buf[11] = "I LOVE YOU";
int w = write(fd, buf, 11);
if(w != 11)
{
perror("write failed\n");
}
//关闭文件
int c = close(fd);
if(c == -1)
{
perror("close failed\n");
}
return 0;
}
第二个进程,用于将管道中的数据读出来。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
int main(int argc, char * argv [ ])
{
int fd = open("./test.fifo",O_RDONLY);
if(-1 == fd)
{
perror("open failedn");
}
char buf[6] = {0};
int r = read(fd,buf,5);
if(r != 5)
{
perror("read fd failed\n");
}
printf("read's content is %s\n",buf);
itn c = close(fd);
if(c == -1)
{
perror("close fd failed\n");
}
return 0;
}