有名管道实现进程间通信。

有名管道介绍。

使用pipe创建的无名管道只能用于具有亲缘关系的进程之间,这就大大限制了管道的使用。

有名管道的出现就是为了解决这个限制问题,有名管道可以使互不相关的两个进程实现彼此通信。通信的过程就是通过路径名来指出管道文件,然后在建立了管道联系之后两个进程就可以把它当做一个普通文件一样进行读写操作,但是有名管道是严格遵循FIFO的规则,也就是所谓的先进先出,如果从有名管道中读取数据的话总是从开始出返回数据,显然如果是增加数据的话就是添加到末尾,而且不支持一些定位操作如lseek等。

在我们使用有名管道的时候可以把管道当成一个文件,但是与普通文件不同的是有名管道需要考虑阻塞问题。在有关管道的使用中阻塞与不阻塞的主要区别如下:

对于读进程:

  • 若该管道是阻塞打开,且当前FIFO内没有数据,则对度进程而言将一直阻塞直到有数据写入。
  • 若该管道是非阻塞打开,则不管当前FIFO中有没有数据,读进程都回立即执行操作并返回。

对于写进程:

  • 若该管道是阻塞打开,则写进程而言将一直阻塞直到有读进程读出数据。
  • 若该管道是非阻塞打开,则不管当前FIFO中有没有读操作,都会执行写操作并返回。

简单的说就是,打开一个管道之后,如果是非阻塞模式,则不论管道的两边(一边读一边写)有没有正在操作,都会直接返回。但是如果是阻塞模式,则必须要保证管道两边都要有进程同时操作,否则就会阻塞。


创建有名管道。

  1. 可以通过”mknod 管道名 p”来直接创建一个有名管道,然后就可以直接使用read、write等操作。

  2. 也可以使用mkfifo函数来创建或者打开一个管道。

    mkfifo函数介绍:

    • 所需头文件:#include < sys/types.h> 、#include < sys/stat.h>
    • 函数原型:int mkfifo(const char* filename,mode_t mode)
    • 参数介绍:第一个就是管道名的名称,第二个参数是打开的模式,共有以下几种:
      1. O_RDONLY:读管道。
      2. O_WRONLY:写管道。
      3. O_RDWR:读写管道。
      4. O_NONBLOCK:非阻塞管道。
      5. O_CREAT:如果该文件不存在,就创建一个新的文件。
      6. O_EXCL:如果用O_CREAT时文件存在,那么可能会返回错误信息,如果使用这个参数则可以检测文件是否存在。
    • 返回值:成功:0;错误:-1.

使用FIFO可能返回的错误信息:

  • EACCESS:参数filename所指定的目录路径无可执行的权限。
  • EEXIST:参数filename所指定的文件已存在。
  • ENAMETOOLONG:参数filename的路径名称太长。
  • ENOENT:参数filename包含的目录不存在。
  • ENOSPC:文件系统的剩余空间不足。
  • ENOTDIR:参数filename路径中的目录存在但却非真正的目录。
  • EROFS:参数filename指定的文件存在与只读文件系统内。

使用实例。

读操作:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define FIFO "/usr/leafage/testfifo"//定义一个管道名称

int main(int argc,char** argv) {
    int fd;//管道描述符
    char buf_r[100];//用来读取管道信息
    int nread;//文件描述符

    //创建管道
    if((mkfifo(FIFO,O_CREAT|O_EXCL) < 0) && (errno!=EEXIST))
        printf("cannot create fifoserver\n");
    printf("Preparing for reading bytes...\n");
    //将数组中用0填充。
    memset(buf_r,0,sizeof(buf_r));
    //打开管道
    fd = open(FIFO,O_RDONLY|O_NONBLOCK,0);
    if(fd == -1) {
        perror("open failed");
        exit(1);
    }
    //循环读取
    while(1) {
        //清空buf_r数组
        memset(buf_r,0,sizeof(buf_r));
        //如果读取的长度为0的话说明此时还没有数据,否则从管道中读取出数据并输出。
        if((nread=read(fd,buf_r,100)) == 0) {
            printf("no data yet\n");
        } else {
            printf("read %s from FIFO\n",buf_r);
        }
        sleep(1);
    }
}       

写操作:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define FIFO "/usr/leafage/testfifo"//定义管道名称

int main(int argc, char** argv) {
    int fd;
    char w_buf[100];
    int nwrite;

    //创建管道
    if((mkfifo(FIFO,O_CREAT|O_EXCL) < 0) && (errno!=EEXIST))
        printf("cannot create fifoserver\n");

    //写模式打开管道,非阻塞
    fd = open(FIFO,O_WRONLY|O_NONBLOCK,0);

    if(fd==-1)
        if(errno==ENXIO)
            printf("open error; no reading process\n");

    //参数中作为写入管道的内容
    if(argc==1)
        printf("Please send something\n");
    strcpy(w_buf,argv[1]);

    //写入到管道中
    if((nwrite=write(fd,w_buf,100))==-1) {
        if(errno==EAGAIN)
            printf("The FIFO has not been read yet.Please try later\n");
    }else {
        printf("write %s to the FIFO\n",w_buf);
    }
}
  • 非阻塞模式。
    上述是一个简单的读写操作,采用的是非阻塞模式。

这里来测试一下看看有什么问题,如果先运行读操作,再运行写操作输出内容如下:

有名管道实现进程间通信。_第1张图片

可以看到先启动读操作,此时管道中没有任何数据,所以一直循环下去,直到执行写操作像管道中写入数据,读操作才读取到数据。这个时候是没有问题的,但是如果先执行写操作就会出现问题,会出现如下错误提示:

这里写图片描述

通过代码可以看到该错误对应的就是出现了ENXIO异常,对ENXIO有这样的描述:

ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO and no process has the file open for reading. Or, the file is a device special file and no corresponding device exists.

两种情况,显然属于第一种,打开的是一个FIFO,但是此刻并没有进程在读取,所以出现了该操作,这也是非阻塞模式下可能会出现的问题。

  • 阻塞模式。

如果我们在打开管道的时候去掉“O_NONBLOCK”参数,就是阻塞模式了。

同样的测试的时候先运行读操作,输出内容如下:

这里写图片描述

由于是非阻塞模式,所以先执行读操作的时候会发现管道的另一端并没有写操作进程,所以会一直阻塞起来,也就是执行了open函数时阻塞了,并没有进入while循环。直到写操作执行,然后才会执行下面while循环。

如果是先执行写操作,再执行读操作,输出结果如下:

这里写图片描述

这次我们先执行写操作是并没有出现非阻塞模式中的ENXIO异常,这是因为发现了管道的另一端没有读操作,所以一直阻塞并不返回。然后读操作执行,写操作执行完成才返回。

你可能感兴趣的:(Linux,C/C++)