前言说明:
宿主主机系统:window 7 旗舰版
虚拟机:VMware 10.0.4
操作平台:Ubuntu 12.04
本文代码在个人电脑可以运行,不确保在其他平台可以正确运行
这两天在学进程通信,学到管道通信,弄了有两天,今天才算能弄个明白,赶紧写来保存方便然后查看,如果能帮到大家就更好了。
无名管道:是指没有文件节点的管道文件,只能用于有亲缘关系的进程间的通信
有名管道:是指有文件节点的管道文件,能用于没有亲缘关系和有亲缘关系的进程间的通信
划重点:什么是文件节点(看的很多博客都没有说到这个问题提一下)
linux下的管道文件是一种树形结构的,树形结构我们都知道,要找到当前内容的下一个内容我们需要知道一个根节点,通过根节点来遍历找到下一个对象。而我们上面说的文件加点就是这个根节点。因为无名管道没有根节点,要想找到一个对象,必须直接找到她,或者通过其中的亲缘关系找到她,就好比如,你跟你父亲之间不需要第三个中间的人就可以直接认识通信一样。(关于无名管道会专门写一篇blog,这里主要介绍有名管道)
有名管道有文件节点的意思即是说,两个进程不需要任何关系也可以通信,因为他们有节点联系,可以通过节点遍历找到需要通信的两个进程,然后进行通信。这个关系就好比如,你跟我认识,但是我不认识你朋友,我跟你的朋友交流我需要通过你这个节点来找到你朋友,才能建立通信。
2、创建有名管道
mkfifo函数
头文件
#include
#include
原型
int mkfifo(const char *pathname, mode_t mode)
成功返回0,失败返回-1
第一个参数是存放的有名管道的路径,包含了有名管道文件的名字,例如:"/tmp/myfifo"的意思就是,在tmp目录下创建了一个名为myfifo的管道文件。但是,如果你写成"/mnt/hgfs/share/myfifo"就会失败,这个是共享目录,链接到windows所以会失败。
第二个参数是读写权限,是个八进制的数,这个以后会花时间写,本文的读写权限为0777(可读可写可执行)
创建成功的管道文件,就可以把他当做是普通文件一样进行操作(但是本质上两者不同)可操作如下:
open()
read()
write()
close()
3、实例 有不少的文章说管道文件只能以读写的方式打开,这个说法是正确的
1、假设有名管道文件以读或写的方式打开。我们知道管道文件是有出入口的(有名管道和普通文件一样共用一个出入口,只要一个文件标识符,无名管道有两个标识符,一个入口一个出口,分开的。)读写的方式打开,就代表,一个进程自己把东西写进管道,再读出来给自己用,这个意义就不大了,管道是用来给两个进程的通信,不是这样用的。网上的说法说“只能以读或写的方式打开“是对的
2、但是有些人在代码实现上面就出现了错误 点击打开链接
写操作
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
const char *fifo_name = "/tmp/my_fifo";
int pipe_fd = -1;
int data_fd = -1;
int res = 0;
const int open_mode = O_WRONLY | O_NONBLOCK;
int bytes_sent = 0;
char buffer[PIPE_BUF + 1];
int bytes_read = 0;
if(access(fifo_name, F_OK) == -1)
{
printf ("Create the fifo pipe.\n");
res = mkfifo(fifo_name, 0777);
if(res != 0)
{
fprintf(stderr, "Could not create fifo %s\n", fifo_name);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIFO O_WRONLY\n", getpid());
pipe_fd = open(fifo_name, open_mode);
printf("Process %d result %d\n", getpid(), pipe_fd);
printf("%d\n",pipe_fd);
if(pipe_fd != -1)
{
bytes_read = 0;
data_fd = open("Data.txt", O_RDONLY);
if (data_fd == -1)
{
close(pipe_fd);
fprintf (stderr, "Open file[Data.txt] failed\n");
return -1;
}
bytes_read = read(data_fd, buffer, PIPE_BUF);
buffer[bytes_read] = '\0';
while(bytes_read > 0)
{
res = write(pipe_fd, buffer, bytes_read);
if(res == -1)
{
fprintf(stderr, "Write error on pipe\n");
exit(EXIT_FAILURE);
}
bytes_sent += res;
bytes_read = read(data_fd, buffer, PIPE_BUF);
buffer[bytes_read] = '\0';
}
close(pipe_fd);
close(data_fd);
}
else
exit(EXIT_FAILURE);
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}
pipe_fd 是定义的文件操作符,我们知道,open函数打开成功返回的是0,失败返回-1;上面返回-1说明文件打开失败
为什么会打开失败呢?因为程序没有阻塞在open()函数里面,程序一直执行到后面直接close文件了。有名管道文件关闭了就不存在内容了,所以返回失败。
说明:源代码没有该打印语句,个人加上去测试的,具体点击链接查看别人的源代码
怎么修改呢?如下:
const int open_mode = O_WRONLY | O_NONBLOCK;
修改为
const int open_mode = O_WRONLY
结果:
可以看见,程序暂停了,阻塞在open函数里面,这才算是成功打开的操作
读操作
#include
#include
#include
#include
#include
#include
#include
int main()
{
const char *fifo_name = "/tmp/my_fifo";
int pipe_fd = -1;
int data_fd = -1;
int res = 0;
int open_mode = O_RDONLY | O_NONBLOCK;
char buffer[PIPE_BUF + 1];
int bytes_read = 0;
int bytes_write = 0;
memset(buffer, '\0', sizeof(buffer));
printf("Process %d opening FIFO O_RDONLY\n", getpid());
pipe_fd = open(fifo_name, open_mode);
data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644);
if (data_fd == -1)
{
fprintf(stderr, "Open file[DataFormFIFO.txt] failed\n");
close(pipe_fd);
return -1;
}
printf("Process %d result %d\n",getpid(), pipe_fd);
if(pipe_fd != -1)
{
do
{
res = read(pipe_fd, buffer, PIPE_BUF);
bytes_write = write(data_fd, buffer, res);
bytes_read += res;
}while(res > 0);
close(pipe_fd);
close(data_fd);
}
else
exit(EXIT_FAILURE);
printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}
读操作同样是这个问题,
int open_mode = O_RDONLY | O_NONBLOCK;
修改为:
int open_mode = O_RDONLY;
最后再开一个终端,运行读操作(注意不要把之前写操作的终端关闭),最后结过如下:
当然他的最后结果对不对不是我谈论的范围,我只是告诉读者正确打开方式
总结一下:
管道文件的特殊性,要求管道文件要在读写的时候,先阻塞在写操作的open函数,当有读操作进来的时候,通信链路建立,写操作开始往管道里写东西,写完成之后,在读操作会把东西读出来,最后两个进程都正常结束退出。注意当你读完一次之后再去读管道文件的时候,是会失败的,因为我前面说过,管道就像是水管,流出去之后就没有了,read完成里面就没东西了。
声明:本文在参考他人blog情况下原创,转载请说明。如有侵权联系删除。
参考文献
1、https://www.cnblogs.com/fangshenghui/p/4039805.html
2、https://blog.csdn.net/best_fiends_zxh/article/details/52923560
3、https://blog.csdn.net/xqhrs232/article/details/53636364