有名管道例子

时间:2021-07-19 15:13:13

有名管道


管道没有名字,它的通信只限定于亲缘关系间的通信,有名管道实现了无亲缘关系间的通信,原理是fifo提供了一个路径名与之关联,让fifo的文件存于系统中,只要知道该文件路径,就可以进行访问。fifo指代(fist in, fist out),即按照先进先出的工作。
fifo 创建
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * pathname, mode_t mode);

pathname 为路径名,创建管道的名字

mode 为创建fifo的权限

 

例,该程序让子进程执行了ls-l命令,并将执行结果写入有名管道,父进程从该管道中读出执行结果,输出到屏幕,最后删除该有名管道的文件

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

#define FIFO2 "/tmp/fifo.2"
int main(int argc, int ** argv)
{
    int readfd,writefd;
    pid_t pid;
    char buf[1001];
//建立有名管道    S_IFIFO|0666指名创建有名管道且存取权限为0666,即创建者/其同组用户/其他用户均可读可写
    if(mkfifo(FIFO2, S_IFIFO|0666)< 0)
    {
        printf("mkfifo2 error");
        exit(1);
    }
    if((pid = fork()) == 0) //创建进程 子进程写
    {
        writefd = open(FIFO2, O_WRONLY);  //打开管道以写方式
        dup2(1, writefd); //定向输出的东西到管道输入
        execlp("ls", "ls", "-l", NULL);//子进程执行ls -l 命令, 即执行结果到有名管道中
    }

    readfd = open(FIFO2, O_RDONLY);//打开管道以读方式
    while(read(readfd, buf, 1000) != 0)
    {
        printf("%s", buf);    //读出管道东西到屏幕
    }
    waitpid(pid, NULL, 0);//等待子进程结束
    close(readfd);
    close(writefd);
    unlink(FIFO2);//删除FIFO2文件
    exit(0);

}

注:有名管道需要用open函数打开以后使用,如果以读方式打开,会阻塞到有写方打开管道,同样以写方式打开会阻塞到以读方式打开,若同时打开读写,则不会阻塞,在该程序中一定会先执行子进程,因为父进程读管道时,管道中没有数据,也会阻塞read到有write写入到管道中

 

例2,上一个程序是亲缘关系间的通信,该程序是无亲缘关系间的通信,该程序只写了一个服务器端,写了shell与其通信,题目是shell脚本中的客户进程请求服务器返回指定一个文件的内容,请求格式为进程id、空格、路径名、换行符构成,该程序类型为多个客户,一个服务器

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

#define FIFO  "/tmp/fifo.serv"  
#define MAXLINE 80

int main(int argc, int ** argv)
{
    int readfd,writefd,fd;
    char *ptr, buff[MAXLINE], fifoname[MAXLINE];
    pid_t pid;
    ssize_t n;

    if(mkfifo(FIFO,S_IFIFO|0666 )< 0)//创建管道
    {
        printf("mkfifo error");
        exit(1);
    }

    readfd = open(FIFO, O_RDONLY); //以不阻塞的方式打开服务器管道,
    writefd = open(FIFO, O_WRONLY);
                                                                                     
      while ((n = read(readfd, buff, MAXLINE)) > 0)  //读服务器管道内容,读出客户请求
    {
        if(buff[n-1] == '\n') //去掉换行符
        {
            n--;    
        }
        buff[n] = 0;
        if((ptr = strchr(buff, ' ')) == NULL) //定位空格
        {
            printf("pathname :%s", buff);
            continue;
        }
        *ptr++ = 0;
        printf("ptr = %s\n", ptr); //打印客户要求输出的文件名
        pid = atol(buff);     //取出进程id
        snprintf(fifoname ,sizeof(fifoname), "/tmp/fifo.%ld", (long)pid);  //组织客户管道名称
        printf("fifoname = %s\n", fifoname);        //输出客户管道名称
        if((writefd =  open (fifoname, O_WRONLY, 0)) < 0)  //打开客户管道,以写方式打开
        {
            printf("can't open %s\n", fifoname);
            continue;
        }
        if((fd = open(ptr, O_RDONLY)) < 0) //打开客户请求的文件
        {
            snprintf(buff+n, sizeof(buff) - n, ":can't open, %s\n", strerror(errno));
            n = strlen(ptr);
            write(writefd, ptr, n);
            close(writefd);
        }
        else
        {
            while((n = read(fd, buff, MAXLINE)) > 0)    //将客户请求的文件写入到客户管道中
                write(writefd, buff, n);
            close(fd);
            close(writefd);
        }
    }

    return 0;
}

shell交互内容,我本目录下写了一个test.c文件,
pid=$$                        获取本进程id
mkfifo /tmp/fifo.$pid          创建客户管道
echo "$pid test.c" > /tmp/fifo.serv  将请求写入服务器管道
cat < /tmp/fifo.$pid   输出服务器返回给客户管道内容
rm /tmp/fifo.$pid  删除客户管道

 

该程序中因为while循环中的read会阻塞到有客户进程往服务器管道中写如数据的时候,即实现了只要有客户进程访问数据服务器就会给出应答,否则一直等待。