Linux進程間通訊——管道

管道

管道是一種最基本的進程間通訊機制,由pipe函數建立:函數

#include <unistd.h>
int pipe(int filedes[2]);
複製代碼

調用pipe函數時在內核中開闢一塊緩衝區(稱爲管道)用於通訊,它有一個讀端一個寫端,而後經過filedes參數傳出給用戶程序兩個文件描述符,filedes[0]指向管道的讀端,filedes[1]指向管道的寫端。向這個文件讀寫數據實際上是在讀寫內核緩衝區。pipe函數調用成功返回0,調用失敗返回-1。ui

管道代碼示例

子進程經過管道向父進程發送數據。限制在父子進程間通訊。spa

#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<string.h>

int main () {
    char* msg;
    char buf[20];
    int pipe_filed[2];
    pipe(pipe_filed);
    pid_t pid = fork();
    if(pid < 0) {
        perror("fork errir.");
        exit(1);
    } else if (0 == pid) {
        msg = "child";
        write(pipe_filed[1], msg, sizeof(msg));
        printf("child process send: %s\n", msg);
    } else {
        read(pipe_filed[0], buf, sizeof(buf));
        printf("parent process recv: %s\n", buf);

        int status;
        wait(&status);
        if (WIFEXITED(status))
            printf("Child exited with code %d\n", WEXITSTATUS(status));
        else if (WIFSIGNALED(status))
            printf("Child terminated abnormally, signal %d\n", WTERMSIG(status));
    }

    return 0;
}
複製代碼

兩個進程經過一個管道只能實現單向通訊,好比上面的例子,子進程寫父進程讀,若是有時候也須要父進程寫子進程讀,就必須另開一個管道。code

命名管道

管道的使用有個限制,就是必須是父子進程間才能夠通訊,若是不是父子進程是不能使用上面的管道通訊的,須要命名管道。orm

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

int mkfifo(const char * pathname,mode_t mode);
複製代碼

依參數pathname創建特殊的FIFO文件,參數mode爲該文件的權限。若成功則返回0,不然返回-1,錯誤緣由存於errno中。進程

命名管道代碼示例

沒必要是父子進程間。進程A向進程B發送信息:事件

/* send process*/
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<string.h>

int main () {
    if (-1 == mkfifo("comm", 0666)) {
        if (EEXIST != errno) {
            perror("mkfifo failure.");
            exit(EXIT_FAILURE);
        }
    }

    int fd = open("comm", O_WRONLY);
    if (fd < 0) {
        perror("open pipe failure.");
    }
    
    char* msg = "process of send.";
    write(fd, msg, strlen(msg));
    close(fd);

    return 0;
}

複製代碼
/* recv process*/
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<string.h>

int main () {
    if (-1 == mkfifo("comm", 0666)) {
        if (EEXIST != errno) {
            perror("mkfifo failure.");
            exit(EXIT_FAILURE);
        }
    }

    int fd = open("comm", O_RDONLY);
	if (fd < 0) {
        perror("open pipe failure.");
    }
    char* buf = (char*)malloc(80);
    bzero(buf, 80);
	read(fd, buf, 80);
	printf("recv from other process: %s\n", buf);
    close(fd);
	free(buf);

    return 0;
}
複製代碼

特殊狀況

使用管道須要注意如下4種特殊狀況(假設都是阻塞I/O操做,沒有設置O_NONBLOCK標誌):ip

  • 若是全部指向管道寫端的文件描述符都關閉了(管道寫端的引用計數等於0),而仍然有進程從管道的讀端讀數據,那麼管道中剩餘的數據都被讀取後,再次read會返回0,就像讀到文件末尾同樣。同步

  • 若是有指向管道寫端的文件描述符沒關閉(管道寫端的引用計數大於0),而持有管道寫端的進程也沒有向管道中寫數據,這時有進程從管道讀端讀數據,那麼管道中剩餘的數據都被讀取後,再次read會阻塞,直到管道中有數據可讀了纔讀取數據並返回。string

  • 若是全部指向管道讀端的文件描述符都關閉了(管道讀端的引用計數等於0),這時有進程向管道的寫端write,那麼該進程會收到信號SIGPIPE,一般會致使進程異常終止。

  • 若是有指向管道讀端的文件描述符沒關閉(管道讀端的引用計數大於0),而持有管道讀端的進程也沒有從管道中讀數據,這時有進程向管道寫端寫數據,那麼在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入數據並返回。

其實和其餘I/O事件的阻塞與同步是同樣的。

相關文章
相關標籤/搜索