命名管道的阻塞和非阻塞模式的初步探讨

前言

进程间通讯(IPC, InterProcess Communication)是指在不一样进程之间传播或交换信息。
主要的方式有管道(包括无名管道,高级管道和命名管道),消息队列, 信号量, 共享内存, Socket等。 其中Socket能够用于不一样主机上的进程间通讯。
进程通讯的主要目的以下:html

  • 数据传输:一个进程须要将它的数据发送给另外一个进程,发送的数据量在一个字节到几M字节之间linux

  • 共享数据:多个进程想要操做共享数据,一个进程对共享数据的修改,别的进程应该马上看到。socket

  • 通知事件:一个进程须要向另外一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。函数

  • 资源共享:多个进程之间共享一样的资源。为了做到这一点,须要内核提供锁和同步机制。ui

  • 进程控制:有些进程但愿彻底控制另外一个进程的执行(如Debug进程),此时控制进程但愿可以拦截另外一个进程的全部陷入和异常,并可以及时知道它的状态改变。线程

IPC方式

  • 匿名管道( pipe ):管道是一种半双工的通讯方式,数据只能单向流动,并且只能在具备亲缘关系的进程间使用。进程的亲缘关系一般是指父子进程关系。设计

  • 高级管道(popen):将另外一个程序当作一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方式咱们成为高级管道方式。调试

  • 命名管道 (named pipe) : 有名管道也是半双工的通讯方式,可是它容许无亲缘关系进程间的通讯。rest

  • 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。code

  • 信号量( semophore ) : 信号量是一个计数器,能够用来控制多个进程对共享资源的访问。它常做为一种锁机制,防止某进程正在访问共享资源时,其余进程也访问该资源。所以,主要做为进程间以及同一进程内不一样线程之间的同步手段。

  • 信号 ( sinal ) : 信号是一种比较复杂的通讯方式,用于通知接收进程某个事件已经发生。

  • 共享内存( shared memory ) :共享内存就是映射一段能被其余进程所访问的内存,这段共享内存由一个进程建立,但多个进程均可以访问。共享内存是最快的 IPC 方式,它是针对其余进程间通讯方式运行效率低而专门设计的。它每每与其余通讯机制,如信号两,配合使用,来实现进程间的同步和通讯。

  • 套接字( socket ) : 套解口也是一种进程间通讯机制,与其余通讯机制不一样的是,它可用于不一样机器间的进程通讯。

命名管道

本文主要讲解命令管道。
命名管道是在文件系统中做为一个特殊的设备文件存在的, 不一样祖先的进程之间能够经过管道共享数据,当全部访问管道的进程执行结束后,命名管道将继续存在以便之后使用。

命名管道的建立

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

mkfifo用于建立一个FIFO文件, 参数mode决定文件权限。
The file creation mask of the process (see umask()) modifies the file permission bits of mode. The owner ID of the FIFO is set to the effective user ID of the process. The FIFO group ID is set to the effective group ID of the process.

若是成功,那么mkfifo()返回0;若是失败,那么返回-1, 而且errno被赋为下面的值:

  • EACCES: 某级路径无访问权限,或pathname指向的目录无写权限

  • EEXIST: 同名文件已存在

  • EINVAL: 在NuTCRCKER平台上描述路径不在fifos文录下

  • ENAMETOOLONG: pathname名太长

  • ENOENT: 路径中某一级目录不存在

  • ENOSPC: 磁盘满

  • ENOTDIR: 路径中某一级不是目录

  • EROFS:文件系统只读

详细信息能够查看: [这里](http://man7.org/linux/man-pag...

file mode以下:

  • S_IRWXU: 00700 read, write, execute/search by owner

  • S_IRUSR: 00400 read permission, owner

  • S_IWUSR: 00200 write permission, owner

  • S_IXUSR: 00100 execute/search permission, owner

  • S_IRWXG: 00070 read, write, execute/search by group

  • S_IRGRP: 00040 read permission, group

  • S_IWGRP: 00020 write permission, group

  • S_IXGRP: 00010 execute/search permission, group

  • S_IRWXO: 00007 read, write, execute/search by others

  • S_IROTH: 00004 read permission, others

  • S_IWOTH: 00002 write permission, others

  • S_IXOTH: 00001 execute/search permission, others

  • S_ISUID: 0004000 set-user-ID on execution

  • S_ISGID: 0002000 set-group-ID on execution

  • S_ISVTX: 0001000 on directories, restricted deletion flag

系统I/O函数如open, close, read, write均可以操做FIFO文件

读/写,阻塞/非阻塞

阻塞

默认不指定O_NONBLOCK时即为阻塞模式

  • open以只读方式打开FIFO时, 要阻塞到某个进程为写而打开此FIFO

  • open以只写方式打开FIFO时,要阻塞到某个进程为读而打开此FIFO

  • open以只读+只写方式打开FIFO时,调用read函数时read会阻塞

  • 调用write函数向FIFO写数据时,当缓冲区已满时write会阻塞

  • 通讯过程当中若写进程先退出了,则调用read函数从FIFO里读数据时不阻塞;若写进程又从新运行,则调用read函数从FIFO里读数据时又恢复阻塞

  • 通讯过程当中若读进程先退出, 则写进程向FIFO写数据,会收到SIGPIPE信号而退出

  • open以读写方式打开FIFO时,open不阻塞

非阻塞

  • open以只读方式打开FIFO,若是没有进程为写而打开FIFO, 只读open成功,而且open不阻塞

  • open以只写方式打开FIFO, 若是没有进程为读而打开FIFO,只写open将出错返回-1 (ENXIO: No Such device or address)

  • read, write读写FIFO中数据时不阻塞

  • 通讯过程当中, 读进程退出后, 写进程向命名管道内写数据时,写进程会收到SIGPIPE信号而退出

Examples

Non-Blocking

reader

//
// Created by         : Harris Zhu
// Filename           : fifo_read.cpp
// Author             : Harris Zhu
// Created On         : 2017-08-17 16:46
// Last Modified      :
// Update Count       : 2017-08-17 16:46
// Tags               :
// Description        :
// Conclusion         :
//
//=======================================================================


#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define FIFO_SERVER "myfifo"
#define OPEN_MODE O_RDONLY | O_NONBLOCK
#define FIFO_MODE O_CREAT|O_RDWR|O_NONBLOCK

int main(int argc, char** argv) {
    char buf_r[100];
    int fd;
    int nread;
    int res;
    if (((res=mkfifo(FIFO_SERVER, FIFO_MODE)) < 0) && (errno != EEXIST))
    {
        printf("can not creat fifoserver %d :\n", res, errno);
        exit(1);
    }
    printf("preparing for reading bytes...\n");
    char cmd[100];
    sprintf(cmd, "chmod 704 %s", FIFO_SERVER);
    system(cmd);
    fd = open(FIFO_SERVER, OPEN_MODE);
    if (fd == -1) {
        perror("error in openning fifo server");
        exit(1);
    }
    int i=0;
    int len;
    while (i++<21)
    {
        memset(buf_r, 0, sizeof(buf_r));
        if ((nread = read(fd, buf_r, sizeof(buf_r))) < 0) {
            if (errno == EAGAIN)
            {
                printf("no data yet\n");
                sleep(1);
            }
        } else {
            if(nread > 0)
            {
                printf("read %s from FIFO %d \n", buf_r, i);
            }
            sleep(1);
        }
    }
//    pause();
    close(fd);
    unlink(FIFO_SERVER);
    return 0;
}

writer

//
// Created by         : Harris Zhu
// Filename           : fifo_write.c
// Author             : Harris Zhu
// Created On         : 2017-08-17 18:04
// Last Modified      :
// Update Count       : 2017-08-17 18:04
// Tags               :
// Description        :
// Conclusion         :
//
//=======================================================================

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>

#define FIFO_SERVER "myfifo"
#define FIFO_MODE O_CREAT|O_NONBLOCK|O_RDWR
#define FILE_MODE O_WRONLY | O_NONBLOCK

int main(int argc, char** argv)
{
    int fd;
    char w_buf[100];
    char w_t_buf[50];
    const char *hstr = "hello world";
    if(mkfifo(FIFO_SERVER, FIFO_MODE<0)&& (errno != EEXIST))
    {
        perror("failed to create fifo server");
        exit(1);
    }

    char cmd[100];
    sprintf(cmd, "chmod 704 %s", FIFO_SERVER);
    system(cmd);

    int nwrite;
    fd = open(FIFO_SERVER, FILE_MODE);
    if (fd == -1)
    {
        if (errno == ENXIO)
        {
            printf("open errop;no reading process\n");
        } else {
            perror("open error");
            exit(EXIT_FAILURE);
        }
    }

    if (argc >= 2)
    {
        strcpy(w_t_buf, argv[1]);
    } else {
        strcpy(w_t_buf, hstr);
    }
    int i=0;
    int n;
    time_t tp;
    while(i++<20)
    {
        time(&tp);
        n=sprintf(w_buf, "Process %d is sending %s at %s", getpid(), w_t_buf, ctime(&tp));
        if ((nwrite = write(fd, w_buf, n)) < 0)
        {
            if (errno == EAGAIN)
            {
                printf("the fifo has not been read yet.Please try later\n");
            } else {
                exit(1);
            }
        }
        printf("Send Message to FIFO: %s \n", w_buf);
        sleep(1);
    }
    close(fd);
    return 0;
}

先运行reader, 而后运行writer
Makefile内容以下

build:
    gcc -g read.c -o read
    gcc -g write.c -o write

run:
    xterm -e ./read &
    sleep 1
    xterm -e ./write &

clean:
    rm -rf read write

由于reader先开读,当writer开始的时候,reader已经在while里走过和个循环了,因此reader会先结束,这时writer还想向FIFO里写数据,就会遇到“Broken pipe”问题
若是把writer的等待时间改小, 那么收端比较慢,因此fifo里的数据会比较多,reader收到的数据就不是一行一行

harriszh Fri 17:30@ ~/trunk/cpp/fifo1$ ./fifo_read
preparing for reading bytes...
read Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello worl from FIFO
read d at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Proce from FIFO
read ss 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at  from FIFO
read Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11 from FIFO
read 863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri A from FIFO
read ug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 i from FIFO
read s sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 from FIFO
read  17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sen from FIFO
read ding hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:3 from FIFO
read 1:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending  from FIFO
read hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57  from FIFO
read 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello from FIFO
read  world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
 from FIFO

Blocking

看完非阻塞再看阻塞就简单点了
代码以下
Reader

//
// Created by         : Harris Zhu
// Filename           : write.c
// Author             : Harris Zhu
// Created On         : 2017-08-17 22:05
// Last Modified      :
// Update Count       : 2017-08-17 22:05
// Tags               :
// Description        :
// Conclusion         :
//
//=======================================================================

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>
#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

int main(int argc, char *argv[])
{

    int outfd = open("Makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (outfd == -1)
        ERR_EXIT("open Makefile2 error");

    int infd;
    infd = open("tp", O_RDONLY );
    if (infd == -1)
        ERR_EXIT("open tp error");

    char buf[1024];
    int n;
    while ((n = read(infd, buf, 1024)) > 0)
        write(outfd, buf, n);

    close(infd);
    close(outfd);
    unlink("tp"); // delete a name and possibly the file it refers to
    return 0;
}

Writer

//
// Created by         : Harris Zhu
// Filename           : read.c
// Author             : Harris Zhu
// Created On         : 2017-08-17 22:05
// Last Modified      :
// Update Count       : 2017-08-17 22:05
// Tags               :
// Description        :
// Conclusion         :
//
//=======================================================================

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>
#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

int main(int argc, char *argv[])
{
    mkfifo("tp", 0644);
    int infd = open("Makefile", O_RDONLY );
    if (infd == -1)
        ERR_EXIT("open Makefile error");

    int outfd;
    outfd = open("tp", O_WRONLY );
    if (outfd == -1)
        ERR_EXIT("open tp error");

    char buf[1024];
    int n;
    while ((n = read(infd, buf, 1024)) > 0)
        write(outfd, buf, n);
    printf("Makefile2 is created successfully!\n")

    close(infd);
    close(outfd);

    return 0;
}

先运行writer, 再运行reader, 缘由是FIFO由writer建立
Makefile

build:
    gcc -g read.c -o read
    gcc -g write.c -o write

clean:
    rm -rf Makefile2
    rm -rf ./read ./write

run:
    xterm -e ./write &
    sleep 0.1
    xterm -e ./read &

.PHONY: clean

运行后,reader端会把从writer写入的内容保存到Makefile2里

后言

非阻塞模式的理解有点绕,在编写程序时也要注意二者的通信机制以及关闭条件。
能够本身先写个例子,看看能不能正确工做。通常第一个例子是很是难以正确工做的,须要在调试的过程当中,来加深了解。
若是有任何疑问,欢迎发邮件给我。

相关文章
相关标签/搜索