天天看點

linux程序間通訊-命名管道1、什麼是命名管道2、命名管道的使用3、命名管道的安全問題4、使用例子

1、什麼是命名管道

  命名管道也被稱為FIFO檔案,它是一種特殊類型的檔案,它在檔案系統中以檔案名的形式存在,但是它的行為卻和之前所講的沒有名字的管道(匿名管道)類似。

  有名管道是有名有形的,為了使用這種管道Linux中設立了一個專門的特殊檔案系統–管道檔案,它存在于檔案系統中,任何程序可以在任何時候通過有名管道的路徑和檔案來通路管道,但是在磁盤上的隻是一個節點,而檔案的資料則隻存在于記憶體緩沖頁面中與普通管道一樣

2、命名管道的使用

1. 命名管道的建立

  我們可以使用兩下函數之一來建立一個命名管道,他們的原型如下:
int mkfifo(const char *filename, mode_t mode);  
int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t));
           

  這兩個函數都能建立一個FIFO檔案,注意是建立一個真實存在于檔案系統中的檔案,filename指定了檔案名,而mode則指定了檔案的讀寫權限。

  mknod是比較老的函數,而使用mkfifo函數更加簡單和規範,是以建議在可能的情況下,盡量使用mkfifo而不是mknod。

2. 命名管道的通路

  與打開其他檔案一樣,FIFO檔案也可以使用open調用來打開。注意,mkfifo函數隻是建立一個FIFO檔案,要使用命名管道還得将其打開。

  但是有兩點要注意:

  1、就是程式不能以O_RDWR模式打開FIFO檔案進行讀寫操作,其行為未明确定義,因為如一個管道以讀/寫方式打開,程序就會讀回自己的輸出,同時我們通常使用FIFO隻是為了單向的資料傳遞。

  2、就是傳遞給open調用的是FIFO的路徑名,而不是正常的檔案。

  打開FIFO檔案通常有四種方式:

open(const char *path, O_RDONLY);//1  
open(const char *path, O_RDONLY | O_NONBLOCK);//2  
open(const char *path, O_WRONLY);//3  
open(const char *path, O_WRONLY | O_NONBLOCK);//4 
           

  在open函數的調用的第二個參數中,你看到一個陌生的選項O_NONBLOCK,選項O_NONBLOCK表示非阻塞,加上這個選項後,表示open調用是非阻塞的,如果沒有這個選項,則表示open調用是阻塞的。

  open調用的阻塞是什麼一回事呢?很簡單,對于以隻讀方式(O_RDONLY)打開的FIFO檔案,如果open調用是阻塞的(即第二個參數為O_RDONLY),除非有一個程序以寫方式打開同一個FIFO,否則它不會傳回;如果open調用是非阻塞的的(即第二個參數為O_RDONLY | O_NONBLOCK),則即使沒有其他程序以寫方式打開同一個FIFO檔案,open調用将成功并立即傳回。

  對于以隻寫方式(O_WRONLY)打開的FIFO檔案,如果open調用是阻塞的(即第二個參數為O_WRONLY),open調用将被阻塞,直到有一個程序以隻讀方式打開同一個FIFO檔案為止;如果open調用是非阻塞的(即第二個參數為O_WRONLY | O_NONBLOCK),open總會立即傳回,但如果沒有其他程序以隻讀方式打開同一個FIFO檔案,open調用将傳回-1,并且FIFO也不會被打開。

3、命名管道的安全問題

  試想這樣一個問題,隻使用一個FIFO檔案,如果有多個程序同時向同一個FIFO檔案寫資料,而隻有一個讀FIFO程序在同一個FIFO檔案中讀取資料時,會發生怎麼樣的情況呢,會發生資料塊的互相交錯?

  為了解決這一問題,就是讓寫操作的原子化。怎樣才能使寫操作原子化呢?答案很簡單,系統規定:在一個以O_WRONLY(即阻塞方式)打開的FIFO中, 如果寫入的資料長度小于等待PIPE_BUF,那麼或者寫入全部位元組,或者一個位元組都不寫入。如果所有的寫請求都是發往一個阻塞的FIFO的,并且每個寫請求的資料長度小于等于PIPE_BUF位元組,系統就可以確定資料決不會交錯在一起。

4、使用例子

  這裡有兩個源檔案,一個fifowrite.c,它在需要時建立管道,然後向管道寫入資料,資料由檔案Data.txt提供,大小為10M,内容全是字元‘0’。另一個源檔案為fiforead.c,它從FIFO中讀取資料,并把讀到的資料儲存到另一個檔案DataFormFIFO.txt中。
/***fifowrite.c檔案***/
    #include <unistd.h>  
    #include <stdlib.h>  
    #include <fcntl.h>  
    #include <limits.h>  
    #include <sys/types.h>  
    #include <sys/stat.h>  
    #include <stdio.h>  
    #include <string.h>  

    int main()  
    {  
        const char *fifo_name = "/tmp/my_fifo";  
        int pipe_fd = -;  
        int data_fd = -;  
        int res = ;  
        const int open_mode = O_WRONLY;  
        int bytes_sent = ;  
        char buffer[PIPE_BUF + ];  

        if(access(fifo_name, F_OK) == -)  
        {  
            //管道檔案不存在  
            //建立命名管道  
            res = mkfifo(fifo_name, );  
            if(res != )  
            {  
                fprintf(stderr, "Could not create fifo %s\n", fifo_name);  
                exit(EXIT_FAILURE);  
            }  
        }  

        printf("Process %d opening FIFO O_WRONLY\n", getpid());  
        //以隻寫阻塞方式打開FIFO檔案,以隻讀方式打開資料檔案  
        pipe_fd = open(fifo_name, open_mode);  
        data_fd = open("Data.txt", O_RDONLY);  
        printf("Process %d result %d\n", getpid(), pipe_fd);  

        if(pipe_fd != -)  
        {  
            int bytes_read = ;  
            //向資料檔案讀取資料  
            bytes_read = read(data_fd, buffer, PIPE_BUF);  
            buffer[bytes_read] = '\0';  
            while(bytes_read > )  
            {  
                //向FIFO檔案寫資料  
                res = write(pipe_fd, buffer, bytes_read);  
                if(res == -)  
                {  
                    fprintf(stderr, "Write error on pipe\n");  
                    exit(EXIT_FAILURE);  
                }  
                //累加寫的位元組數,并繼續讀取資料  
                bytes_sent += res;  
                bytes_read = read(data_fd, buffer, PIPE_BUF);  
                buffer[bytes_read] = '\0';  
            }  
            close(pipe_fd);  
            close(data_fd);  
        }  
        else  
            exit(EXIT_FAILURE);  

        printf("Process %d finished\n", getpid());  
        exit(EXIT_SUCCESS);  
    }  
           
/***fiforead.c檔案***/
    #include <unistd.h>  
    #include <stdlib.h>  
    #include <stdio.h>  
    #include <fcntl.h>  
    #include <sys/types.h>  
    #include <sys/stat.h>  
    #include <limits.h>  
    #include <string.h>  

    int main()  
    {  
        const char *fifo_name = "/tmp/my_fifo";  
        int pipe_fd = -;  
        int data_fd = -;  
        int res = ;  
        int open_mode = O_RDONLY;  
        char buffer[PIPE_BUF + ];  
        int bytes_read = ;  
        int bytes_write = ;  
        //清空緩沖數組  
        memset(buffer, '\0', sizeof(buffer));  

        printf("Process %d opening FIFO O_RDONLY\n", getpid());  
        //以隻讀阻塞方式打開管道檔案,注意與fifowrite.c檔案中的FIFO同名  
        pipe_fd = open(fifo_name, open_mode);  
        //以隻寫方式建立儲存資料的檔案  
        data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, );  
        printf("Process %d result %d\n",getpid(), pipe_fd);  

        if(pipe_fd != -)  
        {  
            do  
            {  
                //讀取FIFO中的資料,并把它儲存在檔案DataFormFIFO.txt檔案中  
                res = read(pipe_fd, buffer, PIPE_BUF);  
                bytes_write = write(data_fd, buffer, res);  
                bytes_read += res;  
            }while(res > );  
            close(pipe_fd);  
            close(data_fd);  
        }  
        else  
            exit(EXIT_FAILURE);  

        printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);  
        exit(EXIT_SUCCESS);  
    }  
           

繼續閱讀