linux中管道(pipe)一談

/***********************************************
  管道(pipe)是Linux上進程間通信的一種方式,其是半雙工(數據流只能在一個方向上流動(還需要經過內核),及要麼是接收,要麼是發送),並且只能在父子進程 或 具有公共祖先的兩個子進程間通信。
  管道(pipe)一般是在調用fork函數之前調用pipe函數創建,這樣的話,fork後子進程將得到父進程的兩個管道描述符副本(相當於調用了兩次dup)。

/***********************************************
相關函數:
  #include <unistd.h>
  int pipe(int fd[2])
                        返回值:成功返回0,失敗返回-1
***********************************************/
注意: 函數執行成功後爲參數返回兩個描述符,fd[0]爲讀而打                    開,fd[1]爲寫而打開

很多系統在stat結構體st_size成員中儲存管道中可用於讀寫字節數。但這是不可移植的。(對於struct stat不瞭解的請百度)


使用管道有幾點注意事項:
1.當讀(read)一個寫(write)端已經關閉的管道,程序(read)將讀完管道中所有數據後返回0。
2.當寫(write)一個讀(write)端已經關閉的管道,將產SIGPIP信號,程序(write)返回-1,並將錯誤碼置爲EPIPE
3.如果管道中數據爲空,則(read)將永久堵塞直到有數據或寫(write)端關閉。
4.如果管道已被數據填滿,則(write)將會堵塞直到管道中有空與空間 或 寫端(read)關閉,(管道容量可用fpathconf函數得到,使用_PC_PIPE_BUF參數)。
***********************************************/


實例1:
    使用管道實現 ls -l | wc -c 操作

分析:
    執行ls -l後其結果將會被打印到終端(即使用到STDOUT_FILENO)標準輸出,wc -c(統計單詞數量)是從標準輸入中得到內容(即使用到STDIN_FILENO)然後進行統計。

思路:
    子進程中調用exec函數族執行ls -l命令,並調用dup2函數將子進程中的fd[1]描述符複製爲標準輸出(STDOUT_FILENO),這用的話,但向標準輸出中寫數據是就等同於向管道中寫數據。
    對於父進程則調用exec函數族執行wc -c命令,然後調用dup2將fd[0]複製爲標準輸入,

實例代碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char **argv)
{
    pid_t pid;
    int fd[2];

    /**創建管道**/
    if (pipe(fd) < 0) {
        perror("pipe error");
        return EXIT_FAILURE;
    }

    /**調用fork函數創建子進程**/
    if ((pid = fork()) < 0) {
        perror("fork error");
        return EXIT_FAILURE;
    } else if (0 == pid) {  
        close(fd[0]); /**關閉不必要的描述符**/
        if (fd[1] != STDOUT_FILENO) {
            if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
                perror("dup2 error to stdout");
                return EXIT_FAILURE;
            }
            close(fd[1]);
        }

        execlp("ls", "ls", "-l", NULL);
        exit(EXIT_SUCCESS);
    } 

    //sleep(2);
    close(fd[1]);
    if (fd[0] != STDIN_FILENO) {
        if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
            perror("dup2 error to stdin");
            return EXIT_FAILURE;
        }
        close(fd[0]);
    }

    execlp("wc", "wc", "-c", NULL);

    /**回收子進程的退出狀態,避免產生僵死進程**/
    if (waitpid(pid, NULL, 0) < 0) {
        perror("waitpid error");
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
實例2:
    實現分頁功能 即more命令

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

#define PAGE "/bin/more"
#define MAXLINE 1024

int main(int argc, char **argv)
{
    pid_t pid;
    int fd[2];
    char line[MAXLINE];
    char *page = NULL, *temp = NULL, *arg = NULL;
    FILE *fp = NULL;

    if (argc != 2) {
        printf("Usage: %s <filename>\n", argv[0]);
        return EXIT_FAILURE;
    }

    if ((fp = fopen(argv[1], "r")) == NULL) {
        perror("fopen error");
        return EXIT_FAILURE;
    }

    if (pipe(fd) < 0) {
        perror("pipe error");
        return EXIT_FAILURE;
    }

    if ((pid = fork()) < 0) {
        perror("fork error");
        return EXIT_FAILURE;
    } else if (0 == pid) {
        close(fd[0]);

        int len;
        while (fgets(line, MAXLINE, fp) != NULL) {
            len = strlen(line);

            int offset = 0, n = 0;
            while (len) {
                n = write(fd[1], line+offset, len);
                offset += n;
                len -= n;
            }
        }

        if (ferror(fp)) {
            perror("fgets errro");
            return EXIT_FAILURE;
        }

        fclose(fp);
        close(fd[1]);
    }


    close(fd[1]);

    if (fd[0] != STDIN_FILENO) {
        if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
            perror("dup2 error to stdin");
            return EXIT_FAILURE;
        }

        close(fd[0]);
    }

    /***得到PAGE對應的環境參數***/
    if ((page = getenv("PAGE")) != NULL) {
        arg = page;
    } else {
        arg = PAGE;
    }

    /**
    *strrchr函數判斷'\'字符,
    *在arg字符串中最後一次出現的位置
    **/
    if ((temp = strrchr(arg, '/')) != NULL) {
        temp++;
    } else {
        temp = arg;
    }

    execl(arg, temp, NULL);

    if (waitpid(pid, NULL, 0) < 0) {
        perror("waitpid error");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章