Linux進程間通信-命名管道


前面我們講了進程間通信的一種方式,匿名管道
我們知道,匿名管道只能用於父子關系的進程之間。那么沒有這種關系的進程之間該如何進行數據傳遞呢?

1.什么是命名管道

匿名管道是在緩存中開辟的輸出和輸入文件流的空間,只能用於父子關系的進程之間。因為父子進程的輸入和輸出文件描述符是一致的。
命名管道是一種實際存在的FIFO文件,稱作“管道文件”,用於不同進程之間,命名管道進程間打開同一個FIFO文件,進行數據傳遞。
我們可以像普通文件一樣操作FIFO文件。
不同進程,引用同一個FIFO文件,進行數據傳遞。

2.創建命名管道
mkfifo函數:創建一個命名管道

int mkfifo(const char *filename,mode_t mode);

filename:指定FIFO文件的名稱
mode:指定文件的讀寫權限

3.訪問命名管道
打開FIFO文件有四種方式:

open(const char *filename,O_RDONLY);
open(const char *filename,O_RDONLY|O_NONBLOCK);
open(const char *filename,O_WRONLY);
open(const char *filename,O_WRONLY|O_NONBLOCK);

需要注意的是,不能以O_RDWR模式打開FIFO文件
因為這樣一個進程寫入的數據會被該進程讀取,FIFO一般只用做單向的數據傳遞。

open函數的第二個參數,表示是讀管道,還是寫管道。
O_NONBLOCK表示FIFO管道的讀寫是非阻塞的,默認的話,是阻塞的。
那么何為阻塞呢?
一個進程寫模式打開管道的時候,必須有另一個進程以讀模式打開;
或讀模式的時候,必須有另一個進程寫寫模式打開,否則該進程open函數阻塞,直到滿足以上關系。

非阻塞,意味着open函數會立即返回,若沒有其他進程以只讀方式打開,open返回-1,並且FIFO也不會被打開。

4.FIFO管道使用示例
下例有兩個程序,fifowrite.c和fiforead.c分別寫管道和讀管道。
fifowrite.c中將一個文本文件data.txt,寫到管道。
fiforead.c中從管道讀取數據,並寫到dataformfifo.txt文件中。
程序使用了默認的阻塞模式。
示例代碼如下:

fifowrite.c

#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>
#include<fcntl.h>
#include<limits.h>
int main()
{
    const char *fifo_name = "/tmp/my_fifo";
    int pipe_fd = -1;
    int data_fd = -1;
    int res = 0;
    const int open_mode = O_WRONLY;
    char buffer[PIPE_BUF+1];
    if(access(fifo_name,F_OK)==-1)
    {
        res = mkfifo(fifo_name,0777);
        if(res!=0)
        {
            fprintf(stderr,"could not create fifo\n");
            exit(EXIT_FAILURE);
        }
    }
    printf("process %d opening fifo O_WRONLY\n",getpid());
    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!=-1)
    {
        int bytes_read = 0;
        bytes_read = read(data_fd,buffer,PIPE_BUF);
        while(bytes_read>0)
        {
            res = write(pipe_fd,buffer,bytes_read);
            if(res==-1)
            {
                fprintf(stderr,"write error\n");
                exit(EXIT_FAILURE);
            }
            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<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<limits.h>
int main()
{
    const char *fifo_name = "/tmp/my_fifo";
    int pipe_fd = -1;
    int data_fd = -1;
    int res = 0;
    int open_mode = O_RDONLY;
    char buffer[PIPE_BUF+1];
    int bytes_read = 0;
    int bytes_write = 0;
    memset(buffer,'\0',sizeof(buffer));

    printf("process %d opening FIFO O_RDONLY\n",getpid());
    pipe_fd = open(fifo_name,open_mode);
    data_fd = open("dataformfifo.txt",O_WRONLY|O_CREAT,0644);
    printf("process %d result %d\n",getpid(),pipe_fd);
    if(pipe_fd!=-1)
    {
        do{
            res = read(pipe_fd,buffer,PIPE_BUF);
            bytes_write = write(data_fd,buffer,res);
            bytes_read +=res;
        }while(res>0);
        close(pipe_fd);
        close(data_fd);
    }
    else{
        exit(EXIT_FAILURE);
    }
    printf("process %d finished,%d bytes read\n",getpid(),bytes_read);
    exit(EXIT_SUCCESS);
}

輸出結果:

我們在shell中輸入命令 ls -l /tmp/my_fifo查看FIFO管道文件的屬性

可以看到,FIFO文件生成了,第一個字符‘p’,表示該文件是一個管道文件

5.多個進程同時寫管道
當多個進程同時寫管道時,讀管道取得的數據是雜亂的。
此時,我們可以控制每個進程,當要寫入的數據超過某個大小時,才寫管道,另外要以阻塞的方式打開FIFO。確保寫操作的原子性。

 


免責聲明!

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



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