进程间通信之有名管道

概述

对于进程间通信,由于管道的种种约束与不便,我们聪明的人类必然会想办法对其进行完善,以便使程序更加完美,然后就有了有名管道的诞生。

有名管道

有名管道也被称为FIFO,它的提出克制了管道在使用上面的不足,管道只能在有亲缘关系的进程间使用,而有名管道可以使不同的两个进程进行通信。

有名管道的实现原理

有名管道提供了一个路径名使之与自己关联,并且以FIFO的文件形式存储在文件系统中。有名管道就是一个设备文件,因此就算进程之间不存在 亲缘关系,只要访问该路径,就可以相互通信。由于FIFO是按照先进先出的原则工作,所以第一个被写入有名管道的数据总是第一个被读出。

有名管道的创建和读写

shell下创建有名管道

shell命令:mknod, mkfifo
mknod用法:mknod Name { b | c } Major Minor
b表示块文件,c表示字符文件。
后面第一个参数指定主设备数目,第二个参数指定次设备数目。

系统函数创建有名管道
#include<sys/types.h>
#include<sys/stat.h>
int mknod(const char *path, mode_t mod, dev_t dev)
int mkfifo(const char *path, mode_t mode)
//mknod参数中path为有名管道的全路径名称,mod为创建有名管道的模式,指明其存取权限,dev为设备值,该值取决于文件创建的种类,它只在创建设备文件时才会用到。
//函数调用成功返回0,失败返回-1。
//mkfifo前两个参数的意义和mknod一样。
有名管道的使用

有名管道的使用基本和管道的一样,只是在使用的时候先要用open函数进行打开,因为有名管道是存于硬盘上的一个文件,而管道是存于内存中的特殊文件。
需要注意的是以open打开的有名管道有可能会进行阻塞,以写方式或以读方式打开的有名管道一定会阻塞到直到有读方式或写方式打开有名管道。而已读写方式打开的有名管道则不会进行阻塞。

有名管道使用实例

下面我给出一个以有名管道进行进程间通信的源代码,对于代码的解释会和注释一起出现。
Server:(写端)

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

#define FIFO_WRITE "writefifo"
#define FIFO_READ "readfifo"
#define BUFSIZE 1024

int main()
{
    int rfd;
    int wfd;
    int len;
    char buf[BUFSIZE];

    umask(0);

    if(mkfifo(FIFO_WRITE, S_IFIFO | 0666) == -1)    //创建有名管道失败
    {
        printf("can't create fifo %s because %s\n", FIFO_WRITE, strerror(errno));
        exit(1);
    }

    umask(0);

    if((wfd = open(FIFO_WRITE, O_WRONLY)) == -1)
    {
        perror("open");
        exit(1);
    }

    while((rfd = open(FIFO_READ, O_RDONLY)) == -1)  //打开客户端创建的有名管道,打开失败则进行阻塞
    {
        printf("debug\n");
        sleep(1);
    }

    while(1)
    {
        printf("Server: ");
        fgets(buf, BUFSIZE, stdin);
        buf[strlen(buf)-1] = '\0';
        if(strncmp(buf, "quit", 4) == 0)
        {
            close(wfd);
            unlink(FIFO_WRITE);            //将写端关闭后要调用unlink函数
            close(rfd);
            exit(0);
        }
        write(wfd, buf, BUFSIZE);          //将服务器端的东西写入管道

        len = read(rfd, buf, BUFSIZE);     //读客户端的东西
        if(len > 0)
        {
            buf[len] = '\0';
            printf("Client: %s\n", buf);
        }
    }

    return 0;
}

Client:(读端)

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

#define WRITE_FIFO "writefifo"
#define READ_FIFO "readfifo"
#define BUFSIZE 1024

int main()
{
    int rfd;
    int wfd;
    int len;
    char buf[BUFSIZE];

    umask(0);

    if(mkfifo(READ_FIFO, S_IFIFO | 0666) == -1)           //创建一个有名管道
    {
        printf("can't create fifo %s,because %s\n", READ_FIFO, strerror(errno));
        exit(1);
    }

    if((rfd = open(READ_FIFO, O_WRONLY)) < 0)             //打开这个有名管道
    {
        perror("open");
        exit(1);
    }

    while((wfd = open(WRITE_FIFO, O_RDONLY)) == -1)       //打开服务端创建的有名管道,打开失败则进行阻塞
    {
        sleep(1);
    }

    while(1)
    {
        len = read(wfd, buf, BUFSIZE);                    //将服务器端的东西读出来
        if(len > 0)
        {
            buf[len] = '\0';
            printf("Server: %s\n", buf);
        }

        printf("Client: \n");
        fgets(buf, BUFSIZE, stdin);
        buf[strlen(buf) -1] = '\0';

        if(strncmp(buf, "quit", 4) == 0)
        {
            close(rfd);
            unlink(READ_FIFO);
            close(wfd);
            exit(0);
        }
        write(rfd, buf, BUFSIZE);                         //将客户端的东西写入管道
    }

    return 0;
}

这个程序也可以实现进程间的双向通信。

你可能感兴趣的:(进程间通信,有名管道)