有名管道


管道沒有名字,它的通信只限定於親緣關系間的通信,有名管道實現了無親緣關系間的通信,原理是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的權限

 

例1,該程序讓子進程執行了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會阻塞到有客戶進程往服務器管道中寫如數據的時候,即實現了只要有客戶進程訪問數據服務器就會給出應答,否則一直等待。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM