linux系統編程之管道(一)

今天繼續研究linux系統編程,繼上次對信號進行研究以後,此次開始一個新的章節-----管道,在正式開始以前,先把今天讓本身很激動的事說一下,小小的興奮,通過下週後本身的創業產品用戶量就有一個質的飛越了,百萬級的,雖然說離最終的目標還有很遠,可是,這說明本身團隊最近幾個月的辛苦付出是值得的,也讓本身對此次的創業更加有期待了,小小驕傲一下,個人學習任務還得繼續堅持,誰叫咱是搞技術的呢,言歸正傳,正入正題:html

什麼是管道:

實際上,咱們在linux中常常會用到帶"管道"的命令,如:
 
那管道的本質是什麼呢?既然它是一個數據流,那就必定得要有一個緩衝區來保存數據, 因此說,咱們能夠把管道當作是具備 固定大小的一個 內核緩衝區。
管道限制:
關於上面提到的第二點,爲啥只能用於具備共同祖先的進程呢?須要先理解下面的函數才能明白,因此先學習下面的用法,回過頭來再來理解這句話。
匿名管道pipe:
建立管道後示意圖:
回到以前提出的問題來,爲啥管道只能用於具備共同祖先的進程呢?緣由在於: 管道的文件描述符其它進程是沒有辦法獲取,只能經過子進程繼承父進程得來了。
經過管道的這些文件描述符,咱們就能夠實現進程間的通訊了,好比:子進程往管道的寫入端點中寫入數據,父進程能夠從管道的讀端點獲取數據,下面就以實際代碼來講明一下父子進程的數據傳遞:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)


int main(int argc, char *argv[])
{
    int pipefd[2];
    if (pipe(pipefd) == -1)//建立一個管道以後,就會獲得兩個文件描述符
        ERR_EXIT("pipe error");

    pid_t pid;
    pid = fork();//建立父子進程來演示數據通信的目的
    if (pid == -1)
        ERR_EXIT("fork error");

    if (pid == 0)
    {//子進程發送數據
        close(pipefd[0]);//關閉子進程管道的讀端,由於沒有用
        write(pipefd[1], "hello", 5);//向子進程管道的寫端傳入數據
        close(pipefd[1]);//傳遞完以後將其關掉
        exit(EXIT_SUCCESS);
    }

    //父進程讀取數據
    close(pipefd[1]);//關閉父進程的寫端,由於沒有用
    char buf[10] = {0};
    read(pipefd[0], buf, 10);//從管道的讀端讀入數據
    close(pipefd[0]);//關閉管道的讀端
    printf("buf=%s\n", buf);
    
    return 0;

}

編譯運行:linux

這個經過管道達到進程間傳遞數據的例子比較簡單,下面用程序來模擬下面的這個shell命令的效果:web

咱們能夠用子進程來運行ls,父進程來運行wc -w命令,具體代碼以下:shell

第一步:編程

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

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)


int main(int argc, char *argv[])
{
    int pipefd[2];
    if (pipe(pipefd) == -1)
        ERR_EXIT("pipe error");

    pid_t pid;
    pid = fork();
    if (pid == -1)
        ERR_EXIT("fork error");

    if (pid == 0)
    {//子進程運行ls命令,
        execlp("ls", "ls", NULL);
        fprintf(stderr, "error execute ls\n");
        exit(EXIT_FAILURE);
    }

    //父進程運行wc -w命令
    execlp("wc", "wc", "-w", NULL);
    fprintf(stderr, "error execute wc\n");//若是執行execlp運行失敗了,纔會執行到這
    exit(EXIT_FAILURE);

}

 

【說明】:關於execlp函數的使用,能夠參考博文:http://www.cnblogs.com/webor2006/p/3507913.html函數

第二步,重定向文件描述符,這是實現的關鍵:學習

由於ls命令標準是輸出到標準輸出設備(屏幕)當中,wc命令是從標準輸入設備獲取數據,而如今,咱們但願ls命令輸出到管道的寫端,而wc命令是從管道的讀端獲取數據,那該怎麼辦呢?文件描述符的複製既可達到這個目的,具體代碼以下:spa

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

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)


int main(int argc, char *argv[])
{
    int pipefd[2];
    if (pipe(pipefd) == -1)
        ERR_EXIT("pipe error");

    pid_t pid;
    pid = fork();
    if (pid == -1)
        ERR_EXIT("fork error");

    if (pid == 0)
    {//子進程運行ls命令,
        dup2(pipefd[1], STDOUT_FILENO);//將標準輸出複製到管道的寫端,也就是說標準輸出定位到了管道的寫端 close(pipefd[1]);//這時管道的讀寫端都沒用了,將其關閉 close(pipefd[0]);
        execlp("ls", "ls", NULL);//這時ls輸出則爲管道的寫端了,因爲文件描述符重定向了
        fprintf(stderr, "error execute ls\n");
        exit(EXIT_FAILURE);
    }

    //父進程運行wc -w命令
    dup2(pipefd[0], STDIN_FILENO);//將標準輸入重定向管道的讀端,因此wc命令這時就會從管道的讀端來獲取數據嘍 close(pipefd[0]); close(pipefd[1]);
    execlp("wc", "wc", "-w", NULL);
    fprintf(stderr, "error execute wc\n");//若是執行execlp運行失敗了,纔會執行到這
    exit(EXIT_FAILURE);

}

編譯運行:3d

下面再來看一個有關文件描述符複製的程序,先看效果,再來分析其原理:code

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

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)


int main(int argc, char *argv[])
{
    close(0);
    open("Makefile", O_RDONLY);
    close(1);
    open("Makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644);

    execlp("cat", "cat", NULL);

    return 0;
    
}

編譯運行:

爲啥能實現文件的拷貝效果呢?我們來分析一下程序:

默認狀況下:

而下面這句代碼事後:

close(0);

open("Makefile", O_RDONLY);

close(1);

open("Makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644);

而最後一句關鍵代碼以下:

execlp("cat", "cat", NULL);

咱們一般用cat能夠查看一個文件內容:

可是,若是cat不帶參數,那是什麼意思呢?

如效果所示,不帶參數的cat命令其實是從標準輸入獲取數據,寫入到標準輸出當中,因此也就是從Makefile文件獲取數據,寫入到Makefile2文件當中,若是Makefile2文件不存在則會主動建立一個,因此就實現了一個cp命令嘍,是否是頗有技巧。

【說明】:關於文件描述符的複製,能夠參考博文:http://www.cnblogs.com/webor2006/p/3498443.html

好了,今天的內容學到這,雖然說內容很少,可是得好好理解,下回見!!

相關文章
相關標籤/搜索