鍍金池/ 教程/ Linux/ 有名管道通訊
Linux 學(xué)習(xí)記錄--開機(jī)掛載錯(cuò)誤
日志系統(tǒng)
數(shù)據(jù)流重定向
內(nèi)存交換空間的構(gòu)建
文件系統(tǒng)簡介
Linux 學(xué)習(xí)記錄--軟件安裝 RPM|SRPM|YUM
文件特殊權(quán)限
目錄配置 FHS
文件內(nèi)容查閱
Boot Loader
文件壓縮
Linux 學(xué)習(xí)記錄--文件權(quán)限
Linux 命令縮寫
命令與文件的查詢
文件|目錄的默認(rèn)權(quán)限與隱藏權(quán)限
shell script
服務(wù)
Linux 學(xué)習(xí)記錄--程序編譯與函數(shù)庫
正則表達(dá)式與其應(yīng)用
關(guān)機(jī)相關(guān)指令
shell
vim 與 vi 常用命令
系統(tǒng)調(diào)用:進(jìn)程控制
文件系統(tǒng)簡單操作
磁盤掛載與卸載
有名管道通訊
磁盤分區(qū),格式化與檢驗(yàn)
工作管理與進(jìn)程管理
匿名管道通訊
Linux 學(xué)習(xí)記錄--啟動(dòng)流程
文件與目錄管理
管道命令
命名別名與歷史命令
文件備份|還原
shell變量
Linux 學(xué)習(xí)記錄--ACL 權(quán)限控制
內(nèi)核|內(nèi)核模塊編譯
文件管理相關(guān)系統(tǒng)編程

有名管道通訊

什么是有名管道

匿名管道應(yīng)用的一個(gè)重大限制是它沒有名字,因此,只能用于具有親緣關(guān)系的進(jìn)程間通信,在有名管道(named pipe 或 FIFO)提出后,該限制得到了克服。FIFO 不同于管道之處在于它提供一個(gè)路徑名與之關(guān)聯(lián),以 FIFO 的文件形式存在于文件系統(tǒng)中。這樣,即使與 FIFO 的創(chuàng)建進(jìn)程不存在親緣關(guān)系的進(jìn)程,只要可以訪問該路徑,就能夠彼此通過 FIFO 相互通信

有名管道創(chuàng)建

int mkfifo(const char * pathname, mode_t mode)
和普通文件創(chuàng)建一樣 pathname 為文件名稱,mode為權(quán)限

有名管道通信規(guī)則

管道關(guān)閉規(guī)則

int close (int __fd);
1.當(dāng)最后一個(gè)讀進(jìn)程管道關(guān)閉時(shí),寫進(jìn)程無論是阻塞還是非阻塞,都會(huì)將管道寫滿(如果能寫滿)并退出
2.當(dāng)最后一個(gè)寫進(jìn)程管道關(guān)閉時(shí),向管道寫入一個(gè)結(jié)束標(biāo)識,當(dāng)讀進(jìn)程從管道讀到這個(gè)結(jié)束標(biāo)識時(shí),如果是阻塞讀進(jìn)程將結(jié)束阻塞返回讀入數(shù)據(jù)個(gè)數(shù)為0.(對于未阻塞讀進(jìn)程如果管道內(nèi)沒有數(shù)據(jù)則返回-1,如果讀到結(jié)束標(biāo)識則返回讀入數(shù)據(jù)個(gè)數(shù)為0)

規(guī)則分析1

寫進(jìn)程

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<limits.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#define FIFO_NAME "/tmp/my_fifo"
#define BUF_SIZE 80000
intmain(int argc, char *argv[]) {
    unlink(FIFO_NAME);
    int pipe_fd;
    int res;
    char buf[BUF_SIZE];
    memset(buf, 3, BUF_SIZE);
    if (access(FIFO_NAME, F_OK) == -1) {
        res = mkfifo(FIFO_NAME, 0766);
        if (res != 0) {
            fprintf(stderr, "不能創(chuàng)建管道文件 %s\n", FIFO_NAME);
            exit(1);
        }
    }
    printf("進(jìn)程 PID %d 打開管道 O_WRONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, O_WRONLY);
    if (pipe_fd != -1) {
        res = write(pipe_fd, buf, sizeof(buf));
        printf("寫入數(shù)據(jù)的大小是%d \n", res);
        close(pipe_fd);
        sleep(1000);
    } else
        exit(1);
    exit(1);
}

讀進(jìn)程

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<limits.h>
#include<sys/types.h>
#include<sys/stat.h>
#define FIFO_NAME "/tmp/my_fifo"
#define BUF_SIZE 20
intmain(int argc, char *argv[]) {
    char buf[BUF_SIZE];
    memset(buf, 0, BUF_SIZE);
    int pipe_fd;
    int res;
    int bytes_read = 0;
    printf("進(jìn)程 PID %d 打開管道 O_RDONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, O_RDONLY);
    if (pipe_fd != -1) {
        bytes_read = read(pipe_fd, buf, sizeof(buf));
        printf("讀入數(shù)據(jù)的大小是%d \n", bytes_read);
        sleep(10);
        close(pipe_fd);
    } else
        exit(1);
    exit(1);
}

控制臺信息
讀進(jìn)程:
進(jìn)程 PID 10930打開管道 O_RDONLY
讀入數(shù)據(jù)的大小是20
寫進(jìn)程:
進(jìn)程 PID 10918打開管道 O_WRONLY
(10S后輸出…..)
寫入數(shù)據(jù)的大小是65536
分析:當(dāng)讀進(jìn)程執(zhí)行到 close(pipe_fd);時(shí),寫進(jìn)程一次性將數(shù)據(jù)寫滿緩沖區(qū)(65536)并退出。

規(guī)則分析2

寫進(jìn)程

#define FIFO_NAME "/tmp/my_fifo"
#define BUF_SIZE 80000
int main(int argc, char *argv[]) {
    unlink(FIFO_NAME);
    int pipe_fd;
    int res;
    char buf[BUF_SIZE];
    memset(buf, 3, BUF_SIZE);
    if (access(FIFO_NAME, F_OK) == -1) {
        res = mkfifo(FIFO_NAME, 0766);
        if (res != 0) {
            fprintf(stderr, "不能創(chuàng)建管道文件 %s\n", FIFO_NAME);
            exit(1);
        }
    }
    printf("進(jìn)程 PID %d 打開管道 O_WRONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, O_WRONLY);
    if (pipe_fd != -1) {
        res = write(pipe_fd, buf, sizeof(buf));
        printf("寫入數(shù)據(jù)的大小是%d \n", res);
        sleep(10);
        close(pipe_fd);
    } else
        exit(1);
    exit(1);
}

讀進(jìn)程

#define FIFO_NAME "/tmp/my_fifo"
#define BUF_SIZE 4000
int main(int argc, char *argv[]) {
    char buf[BUF_SIZE];
    memset(buf, 0, BUF_SIZE);
    int pipe_fd;
    int bytes_read = 0;
    printf("進(jìn)程 PID %d 打開管道 O_RDONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, O_RDONLY);
    if (pipe_fd != -1) {
        do {
            bytes_read = read(pipe_fd, buf, sizeof(buf));
            printf("讀入數(shù)據(jù)的大小是%d \n", bytes_read);
        } while (bytes_read != 0);
        close(pipe_fd);
    } else
        exit(1);
    exit(1);
}

控制臺輸出:

讀進(jìn)程
進(jìn)程 PID 12240打開管道 O_RDONLY
讀入數(shù)據(jù)的大小是4000.
……
(10S后)
讀入數(shù)據(jù)的大小是0
寫進(jìn)程
進(jìn)程 PID 12227打開管道 O_WRONLY
寫入數(shù)據(jù)的大小是80000
分析:
如果讀進(jìn)程為阻塞的,當(dāng)寫進(jìn)程關(guān)閉管道時(shí),讀進(jìn)程收到寫進(jìn)程發(fā)來的結(jié)束符,讀進(jìn)程結(jié)束阻塞(此時(shí)bytes_read =0)
如果讀進(jìn)程為非阻塞的,首先將所有數(shù)據(jù)讀取出來,然后在讀進(jìn)程未收到寫進(jìn)程發(fā)來的結(jié)束符時(shí),由于管道沒有數(shù)據(jù)讀進(jìn)程不會(huì)阻塞且返回-1,因?yàn)榇死?WHILE 退出條件是 bytes_read =0,因此在未讀到結(jié)束符之前返回值一直是-1,直到讀取到結(jié)束符才返回0

管道寫端規(guī)則

對于設(shè)置了阻塞標(biāo)志的寫操作:

1.當(dāng)要寫入的數(shù)據(jù)量不大于 PIPE_BUF 時(shí),linux 將保證寫入的原子性。如果此時(shí)管道空閑緩沖區(qū)不足以容納要寫入的字節(jié)數(shù),則進(jìn)入睡眠,直到當(dāng)緩沖區(qū)中能夠容納要寫入的字節(jié)數(shù)時(shí),才開始進(jìn)行一次性寫操作。
2.當(dāng)要寫入的數(shù)據(jù)量大于 PIPE_BUF 時(shí),linux 將不再保證寫入的原子性。FIFO 緩沖區(qū)一有空閑區(qū)域,寫進(jìn)程就會(huì)試圖向管道寫入數(shù)據(jù),寫操作在寫完所有請求寫的數(shù)據(jù)后返回。

對于沒有設(shè)置阻塞標(biāo)志的寫操作:

3.當(dāng)要寫入的數(shù)據(jù)量大于 PIPE_BUF 時(shí),linux 將不再保證寫入的原子性。在寫滿所有 FIFO 空閑緩沖區(qū)后,寫操作返回。
4.當(dāng)要寫入的數(shù)據(jù)量不大于 PIPE_BUF 時(shí),linux 將保證寫入的原子性。如果當(dāng)前 FIFO 空閑緩沖區(qū)能夠容納請求寫入的字節(jié)數(shù),寫完后成功返回;如果當(dāng)前 FIFO 空閑緩沖區(qū)不能夠容納請求寫入的字節(jié)數(shù),則返回 EAGAIN 錯(cuò)誤,提醒以后再寫

管道讀端規(guī)則

對于設(shè)置了阻塞標(biāo)志的寫操作:

1.如果有進(jìn)程寫打開 FIFO,且當(dāng)前 FIFO 內(nèi)沒有數(shù)據(jù),將一直阻塞。

對于沒有設(shè)置阻塞標(biāo)志的寫操作:

2.如果有進(jìn)程寫打開 FIFO,且當(dāng)前 FIFO 內(nèi)沒有數(shù)據(jù)。則返回-1,當(dāng)前 errno 值為 EAGAIN,提醒以后再試。

管道讀寫規(guī)則代碼舉例

寫進(jìn)程

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<limits.h>
#include<sys/types.h>
#include<sys/stat.h>
#include <errno.h>
#define FIFO_NAME "/tmp/my_fifo"
#define BUF_SIZE 88888
int main(int argc,char *argv[])
{
    int pipe_fd;
    int res;
    char buf[BUF_SIZE];
    memset(buf,3,BUF_SIZE);
    if(access(FIFO_NAME,F_OK)==-1)
    {
        res=mkfifo(FIFO_NAME,0766);
        if(res!=0)
        {
            fprintf(stderr,"不能創(chuàng)建管道文件 %s\n",FIFO_NAME);
            exit(1);
        }
    }
    printf("進(jìn)程 PID %d 打開管道 O_WRONLY\n",getpid());
    pipe_fd=open(FIFO_NAME,O_WRONLY|O_TRUNC);//1
   // pipe_fd=open(FIFO_NAME,O_WRONLY|O_TRUNC|O_NONBLOCK);//2
    if(pipe_fd!=-1)
    {
        res=write(pipe_fd,buf,sizeof(buf));
        printf("寫入數(shù)據(jù)的長度是%d \n",res);
        close(pipe_fd);
    }
    else
        exit(1);
    exit(1);
}

讀進(jìn)程

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<limits.h>
#include<sys/types.h>
#include<sys/stat.h>
#define FIFO_NAME "/tmp/my_fifo"
#define BUF_SIZE 4000
int main(int argc, char *argv[]) {
    int res;
    if(access(FIFO_NAME,F_OK)==-1)
    {
        res=mkfifo(FIFO_NAME,0766);
        if(res!=0)
        {
            fprintf(stderr,"不能創(chuàng)建管道文件 %s\n",FIFO_NAME);
            exit(1);
        }
    }
    char buf[BUF_SIZE];
    memset(buf, 0, BUF_SIZE);
    int pipe_fd;
 int num=0;
    int bytes_read = 0;
    printf("進(jìn)程 PID %d 打開管道 O_RDONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, O_RDONLY);//3
    //pipe_fd = open(FIFO_NAME, O_RDONLY|O_NONBLOCK);//4
    if (pipe_fd != -1) {
        do {
            num++;
            bytes_read = read(pipe_fd, buf, sizeof(buf));
            printf("第%d次讀入數(shù)據(jù),數(shù)據(jù)的長度是%d \n",num, bytes_read);
        } while (bytes_read != 0);
        close(pipe_fd);
    } else
        exit(1);
    exit(1);
}

上面兩段代碼分別是管道的讀端進(jìn)程與寫端進(jìn)程。其中有4個(gè)注釋行。分別代表

  1. pipe_fd=open(FIFO_NAME,O_WRONLY|O_TRUNC);//1阻塞寫端
  2. pipe_fd= open (FIFO_NAME,O_WRONLY|O_TRUNC|O_NONBLOCK);//2非阻塞寫端
  3. pipe_fd =open(FIFO_NAME, O_RDONLY);//3阻塞讀端
  4. pipe_fd =open(FIFO_NAME, O_RDONLY|O_NONBLOCK);//4非阻塞讀端

可以分以下3種情況分析:
說明:寫端與讀端不能同時(shí)都不阻塞

寫端阻塞,讀端不阻塞

控制臺輸出如下:

讀端進(jìn)程:
進(jìn)程 PID 5919打開管道 O_RDONLY
第1次讀入數(shù)據(jù),數(shù)據(jù)的長度是-1
…………..
第5次讀入數(shù)據(jù),數(shù)據(jù)的長度是4000
…………..
第26次讀入數(shù)據(jù),數(shù)據(jù)的長度是4000
第27次讀入數(shù)據(jù),數(shù)據(jù)的長度是888
第28次讀入數(shù)據(jù),數(shù)據(jù)的長度是0

寫端進(jìn)程:
進(jìn)程 PID 5906打開管道 O_WRONLY
寫入數(shù)據(jù)的長度是88888
分析:讀端滿足讀端規(guī)則2,前面由于寫進(jìn)程還未開始寫入數(shù)據(jù)到管道因此返回-1
寫端滿足寫端規(guī)則2

寫端不阻塞,讀端阻塞

執(zhí)行流程:先執(zhí)行讀端程序,在執(zhí)行寫端
控制臺輸出如下:

讀端進(jìn)程:
進(jìn)程 PID 6046打開管道 O_RDONLY
第1次讀入數(shù)據(jù),數(shù)據(jù)的長度是4000
…………
第16次讀入數(shù)據(jù),數(shù)據(jù)的長度是4000
第17次讀入數(shù)據(jù),數(shù)據(jù)的長度是1536
第18次讀入數(shù)據(jù),數(shù)據(jù)的長度是0

寫端進(jìn)程:
進(jìn)程 PID 6056打開管道 O_WRONLY
寫入數(shù)據(jù)的長度是65536
分析:讀端滿足讀端規(guī)則1,讀進(jìn)程在為讀取到管道數(shù)據(jù)時(shí)一直處于等待阻塞狀態(tài)
寫端滿足寫端規(guī)則3,寫端寫滿管道后推出,因此寫入數(shù)據(jù)長度是65535,而不是88888

寫端阻塞,讀端阻塞

控制臺輸出如下:

讀端進(jìn)程:
進(jìn)程 PID 8386打開管道 O_RDONLY
第1次讀入數(shù)據(jù),數(shù)據(jù)的長度是4000
…………
第22次讀入數(shù)據(jù),數(shù)據(jù)的長度是4000
第23次讀入數(shù)據(jù),數(shù)據(jù)的長度是888
第24次讀入數(shù)據(jù),數(shù)據(jù)的長度是0

寫端進(jìn)程:
進(jìn)程 PID 8373打開管道 O_WRONLY
寫入數(shù)據(jù)的長度是88888
分析:讀端滿足讀端規(guī)則1,讀進(jìn)程在為讀取到管道數(shù)據(jù)時(shí)一直處于等待阻塞狀態(tài)
寫端滿足寫端規(guī)則2