歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

Linux進程間通信——管道

進程是一個獨立的資源分配單位,不同進程之間的資源是相互獨立的,沒有關聯,不能在一個進程中直接訪問另一個進程中的資源。但是,進程不是孤立的,不同的進程之間需要信息的交換以及狀態的傳遞,因此需要進程間數據傳遞、同步與異步的機制。

此篇博文記錄管道。

管道pipe

  管道是進程間通信的主要手段之一。一個管道實際上就是個只存在於內存中的文件,對這個文件的操作要通過兩個已經打開文件進行,它們分別代表管道的兩端。管道是一種特殊的文件,它不屬於某一種文件系統,而是一種獨立的文件系統,有其自己的數據結構。根據管道的適用范圍將其分為:無名管道和命名管道。

管道分類

無名管道主要用於父進程與子進程之間,或者兩個兄弟進程之間。在linux系統中可以通過系統調用建立起一個單向的通信管道,且這種關系只能由父進程來建立。因此,每個管道都是單向的,當需要雙向通信時就需要建立起兩個管道。管道兩端的進程均將該管道看做一個文件,一個進程負責往管道中寫內容,而另一個從管道中讀取。這種傳輸遵循“先入先出”(FIFO)的規則。

命名管道命名管道是為了解決無名管道只能用於近親進程之間通信的缺陷而設計的。命名管道是建立在實際的磁盤介質或文件系統(而不是只存在於內存中)上有自己名字的文件,任何進程可以在任何時間通過文件名或路徑名與該文件建立聯系。為了實現命名管道,引入了一種新的文件類型——FIFO文件(遵循先進先出的原則)。實現一個命名管道實際上就是實現一個FIFO文件。命名管道一旦建立,之後它的讀、寫以及關閉操作都與普通管道完全相同。雖然FIFO文件的inode節點在磁盤上,但是僅是一個節點而已,文件的數據還是存在於內存緩沖頁面中,和普通管道相同。

管道的讀寫

  寫入管道的數據按到達次序排列。如果管道滿,則對管道的寫被阻塞,直到管道的數據被讀操作讀取。對於寫操作,如果一次write調用寫的數據量小於管道容量,則寫必須一次完成,即如果管道所剩余的容量不夠,write被阻塞直到管道的剩余容量可以一次寫完為止。如果write調用寫的數據量大於管道容量,則寫操作分多次完成。如果用fcntl設置管道寫端口為非阻塞方式,則管道滿不會阻塞寫,而只是對寫返回0。讀操作按數據到達的順序讀取數據。已經被讀取的數據在管道內不再存在,這意味著數據在管道中不能重復利用。如果管道為空,且管道的寫端口是打開狀態,則讀操作被阻塞直到有數據寫入為止。一次read調用,如果管道中的數據量不夠read指定的數量,則按實際的數量讀取,並對read返回實際數量值。如果讀端口使用fcntl設置了非阻塞方式,則當管道為空時,read調用返回0。

如果管道的讀端口關閉,那麼在該管道上的發出寫操作調用的進程將接收到一個SIGPIPE信號。關閉寫端口是給讀端口一個文件結束符的唯一方法。對於寫端口關閉後,在該管道上的read調用將返回0。

無名管道

  用於父進程與子進程之間,或者兩個兄弟進程之間。

函數說明

/* 管道 */

#include <unistd.h>

/* 建立管道 */
int pipe(int filedes[2]);

/*
filedes[0]為管道裡的讀取端    
filedes[1]為管道裡的寫入端

返回值: 成功返回-1,錯誤返回-1,錯誤存於errno中

錯誤代碼:
EMFILE 進程已用完文件描述詞最大量
ENFILE 系統已無文件描述符可用
EFAULT 參數filedes數組地址不合法
*/

/* 阻塞設置 */
fcntl(filedes[0], F_SETFL, O_NONBLOCK);
fcntl(filedes[1], F_SETFL, O_NONBLOCK);

/* 讀寫 */
read(fd, buf, sizeof(buf));
write(fd, buf, sizeof(buf));

實例

#include <unistd.h>
#include <stdio.h>

int main()
{
    int   p[2];
    char  buf[20];
    char  buf0[20];
    pid_t pid;

    strcpy(buf,"Hello World!");

    pipe(p);

    if((pid = fork()) > 0){
        printf("This is father process.\n\n");
        write(p[1], buf, strlen(buf));
        close(p[1]);
        close(p[2]);
    }
    else{
        printf("This is child process.\n");
        read(p[0], buf0, sizeof(buf0));
        printf("%s\n", buf0);
        close(p[0]);
        close(p[1]);
    }

    return 0;
}

命名管道

  能用於近親進程之間通信。

函數說明

/* 命名管道 */

#include <sys/types.h>
#include <sys/stat.h>

/* 建立管道 */
int mkfifo(const  char  *  pathname, mode_t mode);
/*
pathname 路徑,創建管道的位置
mode        打開函數open中的Mode
*/

/* 打開管道 */
int open(const char *pathname,int oflag,... /* mode_t mode */);
/*
mode 可選:O_RDONLY | O_WRONLY | O_NONBLOC
*/

/* 讀寫 */
int read(int fd, void *buf, int nbyte)
int write(int fd, void *buf, int nbyte)

/* 關閉 */
close(int handle)

實例

/* fifo_write.c */
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main()
{
    const char* pathname = "m_fifo";
    int pipe_fd = -1;
    int bytes;
    char buf[20];

    strcpy(buf, "hello world!");

    mkfifo(pathname, 0777);

    pipe_fd = open(pathname, O_WRONLY);

    printf("Process %d result %d\n", getpid(), pipe_fd);

    write(pipe_fd, buf, strlen(buf));

    printf("Process %d finished!\n", getpid());

    return 0;
}

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    const char* pathname = "m_fifo";
    char buf[20];
    int pipe_fd;
    
    pipe_fd = open(pathname, O_RDONLY);

    printf("Process %d result %d\n", getpid(), pipe_fd);

    read(pipe_fd, buf, sizeof(buf));

    close(pipe_fd);

    printf("Process %d finished,\n read content: %s \n", getpid(), buf);

    return 0;
}

Copyright © Linux教程網 All Rights Reserved