首页 > 代码库 > 进程间通信_03命名管道

进程间通信_03命名管道

一 为什么会有命名管道
匿名管道的产生解决了有亲缘关系的进程之间的小量数据传输,但是匿名管道却不能在没有亲缘关系的进程之间进行数据传输

为了解决这个问题,就出现了命名管道。

命名管道也是在内核分配了一块存储区,并且用一个文件名与之关联,以FIFO(First In First Out)的文件形式存放于文件系统之中。
这样其他不相关的进程只要知道管道的名称也能访问管道


二 和匿名管道在使用上的区别
和匿名管道的唯一区别在于管道的创建和打开方式的不同,其他操作方式完全一致。

创建的时候:命名管道用mkfifo函数,打开的时候命名管道open函数
匿名管道,创建和打开用pipe函数一步实现。


三 命名管道的操作
匿名管道的操作和文件的操作类似,可以当成一个特殊的文件
与普通文件唯一的不同就是,创建的时候普通文件用create()函数或者open()函数创建
                                                                匿名管道用mkfifo创建

四 命名管道的创建
方式一:手动创建
$ mkfifo filename

方式二:代码创建
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * pathname,     //带路径的管道名称
                  mode_t mode)        //管道的操作权限
//返回值:0 (成功),-1(打开失败,错误码见errno)

mode参数取值S_I(R/W/X)(USR/GRP/OTH) 、S_IRWX(U/G/O)
其中,R/W/X分别表示读/写/执行权限,
          USR/GRP/OTH分别表示文件所有者/文件所属组/其他用户
          S_IRWXU =  S_IRUSR | S_IWUSR | S_IXUSR
          S_IRWXG =  S_IRGRP | S_IWGRP | S_IXGRP
          S_IRWXO =  S_IROTH | S_IWOTH | S_IXOTH

比如, S_IRUSR表示文件所有者有读权限;
           S_IWUSR表示文件所有者有写权限;
           多个值用‘|‘连接,比如S_IRUSR|S_IWUSR表示文件所有者有读写权限
上面的取值可以也可以直接用八进制为的数字代替,如:00600 表示 文件所有者有读写权限


五 命名管道的打开
负责写命名管道的进程已只写的形式打开命名管道,负责读命名管道的进程已只读的形式打开命名管道。
命名管道的读端 和 写端 都连上才算打开命名管道成功,否则,进程会阻塞在open函数(没有设置不阻塞标志的情况下)。

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
int open(const char* pathname,     //带路径的管道名称
                int flags          //管道打开的方式
                ...);              //其他参数,比如,在创建的时候可以赋权限,这里因为是由mkfifo创建,所以忽略
//返回值:>2 (返回管道的描述符),-1(打开失败,错误码见errno)

flags参数取值

   O_RDONLY 以只读方式打开文件   

        O_WRONLY 以只写方式打开文件

        O_RDWR 以可读写方式打开文件。   

上述三种旗标是互斥的,只能取一个,可与下列选项或运算组合(|)使用。

O_CREAT             若欲打开的文件不存在则自动建立该文件。   

O_EXCL                一般和O_CREAT一起使用,设置之后在创建文件时发现文件已经存在的话就报错。  

O_NONBLOCK     以不可阻断的方式打开文件,也就是无论有无数据读取或等待,都会立即返回进程之中。  

O_TRUNC             若文件存在并且以可写的方式打开时,清空文件中的内容。

O_APPEND          在写数据时将文件光标移到文件尾部。

O_SYNC               以同步的方式打开文件。 

O_NOCTTY           如果欲打开的文件为终端机设备时,则不会将该终端机当成进程控制终端机。     

O_NOFOLLOW     如果参数pathname 所指的文件为一符号连接,则会令打开文件失败。   

O_DIRECTORY     如果参数pathname 所指的文件并非为一目录,则会令打开文件失败。


六 使用实例
/************************************************************************************ 
    > File Name: testfifo.c 
    > Author: qiaozp 
    > Mail: qiaozongpeng@163.com 
    > Created Time: 2014-9-17 14:29:35
    > Step: 1 调用mkfifo函数创建一个命名管道
            2 在写端进程 用open函数以O_WRONLY的方式连接管道,并用write函数读取数据
            3 在读端进程 用open函数以O_RDONLY的方式连接管道,并用read函数读取数据
 ************************************************************************************/  
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <iostream>
#include <sys/stat.h>
#include <sys/types.h>
using namespace std;

#define  BUFF_LEN 100
#define PIPE_NAME "./PipeOne"

int main(int argc, char** argv)
{
    if (argc != 2)
    {
        cout << "请在后面加上参数[r/w], r表示读管道;w表示写管道。" << endl;
        return -1;
    }

    //创建管道
    if ((mkfifo(PIPE_NAME, S_IRWXU) == -1) && (errno != EEXIST))
    {
        cout << "创建管道[" << PIPE_NAME << "]失败,错误信息[" << strerror(errno) << "]." << endl;
        return -1;
    }

    int fd;
    char buff[BUFF_LEN];
    if (strncasecmp(argv[1], "w", 1) == 0)
    {
        //写管道的进程
        if ((fd = open(PIPE_NAME, O_WRONLY)) == -1)
        {
            cout << "连接管道[" << PIPE_NAME << "]失败,错误信息[" << strerror(errno) << "]." << endl;
            return -1;
        }

        do 
        {
            cout << "请输入需要从管道传输的数据: ";
            memset(buff, 0, BUFF_LEN);
            scanf("%s", buff);
            write(fd, buff, sizeof(buff));
        } while (strncasecmp(buff, "end", 3) != 0);
    }
    else if (strncasecmp(argv[1], "r", 1) == 0)
    {
        //读管道的进程
        if ((fd = open(PIPE_NAME, O_RDONLY)) == -1)
        {
            cout << "连接管道[" << PIPE_NAME << "]失败,错误信息[" << strerror(errno) << "]." << endl;
            return -1;
        }
        do 
        {
            memset(buff, 0, BUFF_LEN);
            read(fd, buff, sizeof(buff));
            cout << "从管道读取到的数据为: " << buff << endl;
        } while (strncasecmp(buff, "end", 3) != 0);
    }
    else
    {
        cout << "请在后面加上正确的参数[r/w], r表示读管道;w表示写管道。" << endl;
    }
    return 0;
}





进程间通信_03命名管道