Linux进程间通信----命名管道

By AverageJoeWang
 标签:

一.引子

上一篇笔记记录了无名管道,也就是匿名管道进行进程间通信必须满足的条件是父子进程,而本篇笔记将记录是是比无名管道通信更加便利的通信方式,也就是命名管道,命名管进程间通信没有父子进程的限制。

二.创建与删除管道文件

FIFO文件,和普通文件操作方式类似,有2种创建和删除FIFO文件的方式

2.1.调用函数

2.1.1.原型

  • 创建管道文件函数原型
    #include <sys/types.h>
    #include <sys/stat.h>
    int mkfifo(const char *pathname, mode_t mode);//创建
    
  • 参数pathname为要创建的FIFO文件的全路径名;
  • 参数mode为文件访问权限
  • 如果创建成功,则返回0,否则-1。
  • 删除管道文件函数原型
#include <unistd.h>
int unlink(const char *pathname);
  • 参数pathname为要创建的FIFO文件的全路径名;

2.1.2.例子

  • 创建
 ///
 /// @file    03_1_mkfifo.cpp
 /// @author  AverageJoeWang(AverageJoeWang@gmail.com)
 ///
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
using std::cout;
using std::endl;
int main()
{
    int ret;
    ret = mkfifo("./pipe", 0666);
    if(-1 == ret)
    {
        perror("mkfifo create failed!\n");
        return -1;
    }else
        printf("success!\n");
    return 0;
}
  • 删除

 ///
 /// @file    04_Orphaned.cpp
 /// @author  AverageJoeWang(AverageJoeWang@gmail.com)
 ///
#include <iostream>
#include <unistd.h>
#include <stdio.h>
using std::cout;
using std::endl;
int main()
{
    unlink("pipe");
    return 0;
}

2.2.使用命令

mkfifo pipename#创建
rm pipename#删除

2.3.测试

创建完毕之后,就可以访问FIFO文件了:

#一个终端:
cat < pipe
#另一个终端:
echo “hello” > pipe

三.访问命名管道

对FIFO类型的文件的打开/关闭跟普通文件一样,都是使用open和close函数。如果打开时使用O_WRONLY选项,则打开FIFO的写入端,如果使用O_RDONLY选项,则打开FIFO的读取端,写入端和读取端都可以被几个进程同时打开。

有四种打开管道的方式

open(const char *path, O_RDONLY);//1
open(const char *path, O_RDONLY | O_NONBLOCK);//2
open(const char *path, O_WRONLY);//3
open(const char *path, O_WRONLY | O_NONBLOCK);//4

在open函数的调用的第二个参数中,选项O_NONBLOCK表示非阻塞,加上这个选项后,表示open调用是非阻塞的,如果没有这个选项,则表示open调用是阻塞的。

阻塞是对于以只读方式(O_RDONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_RDONLY),除非有一个进程以写方式打开同一个FIFO,否则它不会返回;如果open调用是非阻塞的的(即第二个参数为O_RDONLY | O_NONBLOCK),则即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回。

对于以只写方式(O_WRONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_WRONLY),open调用将被阻塞,直到有一个进程以只读方式打开同一个FIFO文件为止;如果open调用是非阻塞的(即第二个参数为O_WRONLY | O_NONBLOCK),open总会立即返回,但如果没有其他进程以只读方式打开同一个FIFO文件,open调用将返回-1,并且FIFO也不会被打开。

四.使用FIFO实现进程间的通信

接下来使用一个例子来说明使用fifo进行进程间通信

  • 首先执行
mkfifo pipe
  • write.cpp文件
 ///
 /// @file    03_2_write_pipe.cpp
 /// @author  AverageJoeWang(AverageJoeWang@gmail.com)
 ///

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
using std::cout;
using std::endl;
int main()
{
    int fdFifo = open("pipe", O_WRONLY);//打开一个管道文件,返回一个文件操作符
    if(-1 == fdFifo)
    {
        perror("open failed");
        return -1;
    }
    int ret = write(fdFifo, "hello", 6);//向管道写入内容
    if(-1 == ret)
    {
        perror("write pipe failed!");
        return -1;
    }
    close(fdFifo);//关闭管道
    return 0;
}
  • read.cpp文件
 ///
 /// @file    03_2_read_pipe.cpp
 /// @author  AverageJoeWang(AverageJoeWang@gmail.com)
 ///

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
using std::cout;
using std::endl;
int main()
{
    int fdFifo = open("pipe", O_RDONLY);//打开管道文件,返回一个文件描述符
    if(fdFifo == -1)
    {
        perror("open failed !");
        return -1;
    }
    char buf[32] = {'\0'};
    if(read(fdFifo, buf, sizeof(buf)) > 0)//读取管道的内容,没有内容的话阻塞状态
        puts(buf);//输出读取到buf的管道内容
    else 
    {
        perror("read pipe failed!");
        close(fdFifo);
        return 0;
    }
    close(fdFifo);
    return 0;
}
  • 运行
g++ 03_2_read_pipe.cpp -o read
g++ 03_2_write_pipe.cpp -o write
#一个终端
./read #此时会阻塞
#另一个终端
./write #向管道写内容

最后读终端会读到写终端的内容。