Linux學習記錄--有名管道通訊

wKioL1NadFTxwuhZAAP2OPN_yNI834.bmp

有名管道通信


什麼是有名管道


匿名管道應用的一個重大限制是它沒有名字,所以,只能用於具備親緣關係的進程間通訊,在有名管道(named pipeFIFO)提出後,該限制獲得了克服。FIFO不一樣於管道之處在於它提供一個路徑名與之關聯,以FIFO的文件形式存在於文件系統中。這樣,即便與FIFO的建立進程不存在親緣關係的進程,只要能夠訪問該路徑,就可以彼此經過FIFO相互通訊linux

有名管道建立

int mkfifo(const char * pathname, mode_t mode)ide

和普通文件建立同樣pathname爲文件名稱,mode爲權限spa


有名管道通訊規則


管道關閉規則


int close (int __fd);blog

1.當最後一個讀進程管道關閉時,寫進程不管是阻塞仍是非阻塞,都會將管道寫滿(若是能寫滿)並退出進程

2.當最後一個寫進程管道關閉時,向管道寫入一個結束標識,當讀進程從管道讀到這個結束標識時,若是是阻塞讀進程將結束阻塞返回讀入數據個數爲0.(對於未阻塞讀進程若是管道內沒有數據則返回-1,若是讀到結束標識則返回讀入數據個數爲0ip


規則分析1


寫進程get

#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, "不能建立管道文件 %s\n", FIFO_NAME);
            exit(1);
        }
    }
    printf("進程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("寫入數據的大小是%d \n", res);
        close(pipe_fd);
        sleep(1000);
    } else
        exit(1);
    exit(1);
}

讀進程string

#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("進程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("讀入數據的大小是%d \n", bytes_read);
        sleep(10);
        close(pipe_fd);
    } else
        exit(1);
    exit(1);
}

控制檯信息it

讀進程:pip

進程PID 10930打開管道 O_RDONLY

讀入數據的大小是20

寫進程:

進程PID 10918打開管道 O_WRONLY

10S後輸出…..

寫入數據的大小是65536

分析:當讀進程執行到close(pipe_fd);時,寫進程一次性將數據寫滿緩衝區(65536)並退出。


規則分析2


寫進程

#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, "不能建立管道文件 %s\n", FIFO_NAME);
            exit(1);
        }
    }
    printf("進程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("寫入數據的大小是%d \n", res);
        sleep(10);
        close(pipe_fd);
    } else
        exit(1);
    exit(1);
}

讀進程

#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("進程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("讀入數據的大小是%d \n", bytes_read);
        } while (bytes_read != 0);
        close(pipe_fd);
    } else
        exit(1);
    exit(1);
}

控制檯輸出:

讀進程

進程PID 12240打開管道 O_RDONLY

讀入數據的大小是4000.

……

(10S)

讀入數據的大小是0

寫進程

進程PID 12227打開管道 O_WRONLY

寫入數據的大小是80000

分析:

若是讀進程爲阻塞的,當寫進程關閉管道時,讀進程收到寫進程發來的結束符,讀進程結束阻塞(此時bytes_read =0

若是讀進程爲非阻塞的,首先將全部數據讀取出來,而後在讀進程未收到寫進程發來的結束符時,因爲管道沒有數據讀進程不會阻塞且返回-1,由於此例WHILE退出條件是bytes_read =0,所以在未讀到結束符以前返回值一直是-1,直到讀取到結束符才返回0


管道寫端規則

對於設置了阻塞標誌的寫操做:

1.當要寫入的數據量不大於PIPE_BUF時,linux將保證寫入的原子性。若是此時管道空閒緩衝區不足以容納要寫入的字節數,則進入睡眠,直到當緩衝區中可以容納要寫入的字節數時,纔開始進行一次性寫操做。

2.當要寫入的數據量大於PIPE_BUF時,linux將再也不保證寫入的原子性。FIFO緩衝區一有空閒區域,寫進程就會試圖向管道寫入數據,寫操做在寫完全部請求寫的數據後返回。

對於沒有設置阻塞標誌的寫操做:

3.當要寫入的數據量大於PIPE_BUF時,linux將再也不保證寫入的原子性。在寫滿全部FIFO空閒緩衝區後,寫操做返回。

4.當要寫入的數據量不大於PIPE_BUF時,linux將保證寫入的原子性。若是當前FIFO空閒緩衝區可以容納請求寫入的字節數,寫完後成功返回;若是當前FIFO空閒緩衝區不可以容納請求寫入的字節數,則返回EAGAIN錯誤,提醒之後再寫

管道讀端規則


對於設置了阻塞標誌的寫操做:

1.若是有進程寫打開FIFO,且當前FIFO內沒有數據,將一直阻塞。

對於沒有設置阻塞標誌的寫操做:

2.若是有進程寫打開FIFO,且當前FIFO內沒有數據。則返回-1,當前errno值爲EAGAIN,提醒之後再試。

管道讀寫規則代碼舉例


寫進程

#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,"不能建立管道文件 %s\n",FIFO_NAME);
            exit(1);
        }
    }
    printf("進程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("寫入數據的長度是%d \n",res);
        close(pipe_fd);
    }
    else
        exit(1);
    exit(1);
}

讀進程

#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,"不能建立管道文件 %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("進程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次讀入數據,數據的長度是%d \n",num, bytes_read);
        } while (bytes_read != 0);
        close(pipe_fd);
    } else
        exit(1);
    exit(1);
}


上面兩段代碼分別是管道的讀端進程與寫端進程。其中有4個註釋行。分別表明

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種狀況分析:

說明:寫端與讀端不能同時都不阻塞


寫端阻塞,讀端不阻塞


控制檯輸出以下:


讀端進程:

進程PID 5919打開管道 O_RDONLY

1次讀入數據,數據的長度是-1

…………..

5次讀入數據,數據的長度是4000

…………..

26次讀入數據,數據的長度是4000

27次讀入數據,數據的長度是888

28次讀入數據,數據的長度是0


端進程:

進程PID 5906打開管道 O_WRONLY

寫入數據的長度是88888

分析:讀端知足讀端規則2,前面因爲寫進程還未開始寫入數據到管道所以返回-1

寫端知足寫端規則2

寫端不阻塞,讀端阻塞


執行流程:先執行讀端程序,在執行寫端

控制檯輸出以下:


讀端進程:

進程PID 6046打開管道 O_RDONLY

1次讀入數據,數據的長度是4000

…………

16次讀入數據,數據的長度是4000

17次讀入數據,數據的長度是1536

18次讀入數據,數據的長度是0


端進程:

進程PID 6056打開管道 O_WRONLY

寫入數據的長度是65536

分析:讀端知足讀端規則1,讀進程在爲讀取到管道數據時一直處於等待阻塞狀態

寫端知足寫端規則3,寫端寫滿管道後推出,所以寫入數據長度是65535,而不是88888

寫端阻塞,讀端阻塞


控制檯輸出以下:


讀端進程:

進程PID 8386打開管道 O_RDONLY

1次讀入數據,數據的長度是4000

…………

22次讀入數據,數據的長度是4000

23次讀入數據,數據的長度是888

24次讀入數據,數據的長度是0


端進程:

進程PID 8373打開管道 O_WRONLY

寫入數據的長度是88888

分析:讀端知足讀端規則1,讀進程在爲讀取到管道數據時一直處於等待阻塞狀態

寫端知足寫端規則2

相關文章
相關標籤/搜索