真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

linux管道是什么及怎么使用

本文小編為大家詳細(xì)介紹“l(fā)inux管道是什么及怎么使用”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“l(fā)inux管道是什么及怎么使用”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識吧。

創(chuàng)新互聯(lián)2013年開創(chuàng)至今,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目成都網(wǎng)站制作、成都網(wǎng)站設(shè)計網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元鶴慶做網(wǎng)站,已為上家服務(wù),為鶴慶各地企業(yè)和個人服務(wù),聯(lián)系電話:13518219792

管道是Linux進(jìn)程間的一種通信方式,兩個進(jìn)程可以通過一個共享內(nèi)存區(qū)域來傳遞信息,并且管道中的數(shù)據(jù)只能是單向流動的,也就是說只能有固定的寫進(jìn)程和讀進(jìn)程。目前在任何一個shell中,都可以使用“|”連接兩個命令,shell會將前后兩個進(jìn)程的輸入輸出用一個管道相連,以便達(dá)到進(jìn)程間通信的目的。

什么是管道?

管道,英文為pipe。管道是Linux進(jìn)程間的一種通信方式,兩個進(jìn)程可以通過一個共享內(nèi)存區(qū)域來傳遞信息,并且管道中的數(shù)據(jù)只能是單向流動的,也就是說只能有固定的寫進(jìn)程和讀進(jìn)程。

管道的發(fā)明人是道格拉斯.麥克羅伊,這位也是UNIX上早期shell的發(fā)明人。他在發(fā)明了shell之后,發(fā)現(xiàn)系統(tǒng)操作執(zhí)行命令的時候,經(jīng)常有需求要將一個程序的輸出交給另一個程序進(jìn)行處理,這種操作可以使用輸入輸出重定向加文件搞定,比如:

[zorro@zorro-pc pipe]$ ls  -l /etc/ > etc.txt
[zorro@zorro-pc pipe]$ wc -l etc.txt 
183 etc.txt

但是這樣未免顯得太麻煩了。所以,管道的概念應(yīng)運而生。目前在任何一個shell中,都可以使用“|”連接兩個命令,shell會將前后兩個進(jìn)程的輸入輸出用一個管道相連,以便達(dá)到進(jìn)程間通信的目的:

[zorro@zorro-pc pipe]$ ls -l /etc/ | wc -l
183

對比以上兩種方法,我們也可以理解為,管道本質(zhì)上就是一個文件,前面的進(jìn)程以寫方式打開文件,后面的進(jìn)程以讀方式打開。這樣前面寫完后面讀,于是就實現(xiàn)了通信。實際上管道的設(shè)計也是遵循UNIX的“一切皆文件”設(shè)計原則的,它本質(zhì)上就是一個文件。Linux系統(tǒng)直接把管道實現(xiàn)成了一種文件系統(tǒng),借助VFS給應(yīng)用程序提供操作接口。

雖然實現(xiàn)形態(tài)上是文件,但是管道本身并不占用磁盤或者其他外部存儲的空間。在Linux的實現(xiàn)上,它占用的是內(nèi)存空間。所以,Linux上的管道就是一個操作方式為文件的內(nèi)存緩沖區(qū)。

管道的分類和使用

Linux上的管道分兩種類型:

  • 匿名管道

  • 命名管道

這兩種管道也叫做有名或無名管道。匿名管道最常見的形態(tài)就是我們在shell操作中最常用的”|”。它的特點是只能在父子進(jìn)程中使用,父進(jìn)程在產(chǎn)生子進(jìn)程前必須打開一個管道文件,然后fork產(chǎn)生子進(jìn)程,這樣子進(jìn)程通過拷貝父進(jìn)程的進(jìn)程地址空間獲得同一個管道文件的描述符,以達(dá)到使用同一個管道通信的目的。此時除了父子進(jìn)程外,沒人知道這個管道文件的描述符,所以通過這個管道中的信息無法傳遞給其他進(jìn)程。這保證了傳輸數(shù)據(jù)的安全性,當(dāng)然也降低了管道了通用性,于是系統(tǒng)還提供了命名管道。

我們可以使用mkfifo或mknod命令來創(chuàng)建一個命名管道,這跟創(chuàng)建一個文件沒有什么區(qū)別:

[zorro@zorro-pc pipe]$ mkfifo pipe
[zorro@zorro-pc pipe]$ ls -l pipe 
prw-r--r-- 1 zorro zorro 0 Jul 14 10:44 pipe

可以看到創(chuàng)建出來的文件類型比較特殊,是p類型。表示這是一個管道文件。有了這個管道文件,系統(tǒng)中就有了對一個管道的全局名稱,于是任何兩個不相關(guān)的進(jìn)程都可以通過這個管道文件進(jìn)行通信了。比如我們現(xiàn)在讓一個進(jìn)程寫這個管道文件:

[zorro@zorro-pc pipe]$ echo xxxxxxxxxxxxxx > pipe

此時這個寫操作會阻塞,因為管道另一端沒有人讀。這是內(nèi)核對管道文件定義的默認(rèn)行為。此時如果有進(jìn)程讀這個管道,那么這個寫操作的阻塞才會解除:

[zorro@zorro-pc pipe]$ cat pipe 
xxxxxxxxxxxxxx

大家可以觀察到,當(dāng)我們cat完這個文件之后,另一端的echo命令也返回了。這就是命名管道。

Linux系統(tǒng)無論對于命名管道和匿名管道,底層都用的是同一種文件系統(tǒng)的操作行為,這種文件系統(tǒng)叫pipefs。大家可以在/etc/proc/filesystems文件中找到你的系統(tǒng)是不是支持這種文件系統(tǒng):

[zorro@zorro-pc pipe]$ cat /proc/filesystems |grep pipefs
nodev    pipefs

觀察完了如何在命令行中使用管道之后,我們再來看看如何在系統(tǒng)編程中使用管道。

PIPE

我們可以把匿名管道和命名管道分別叫做PIPE和FIFO。這主要因為在系統(tǒng)編程中,創(chuàng)建匿名管道的系統(tǒng)調(diào)用是pipe(),而創(chuàng)建命名管道的函數(shù)是mkfifo()。使用mknod()系統(tǒng)調(diào)用并指定文件類型為為S_IFIFO也可以創(chuàng)建一個FIFO。

使用pipe()系統(tǒng)調(diào)用可以創(chuàng)建一個匿名管道,這個系統(tǒng)調(diào)用的原型為:

#include 

int pipe(int pipefd[2]);

這個方法將會創(chuàng)建出兩個文件描述符,可以使用pipefd這個數(shù)組來引用這兩個描述符進(jìn)行文件操作。pipefd[0]是讀方式打開,作為管道的讀描述符。pipefd[1]是寫方式打開,作為管道的寫描述符。從管道寫端寫入的數(shù)據(jù)會被內(nèi)核緩存直到有人從另一端讀取為止。我們來看一下如何在一個進(jìn)程中使用管道,雖然這個例子并沒有什么意義:

[zorro@zorro-pc pipe]$ cat pipe.c
#include 
#include 
#include 
#include 

#define STRING "hello world!"

int main()
{
    int pipefd[2];
    char buf[BUFSIZ];

    if (pipe(pipefd) == -1) {
        perror("pipe()");
        exit(1);
    }

    if (write(pipefd[1], STRING, strlen(STRING)) < 0) {
        perror("write()");
        exit(1);
    }

    if (read(pipefd[0], buf, BUFSIZ) < 0) {
        perror("write()");
        exit(1);
    }

    printf("%s\n", buf);

    exit(0);
}

這個程序創(chuàng)建了一個管道,并且對管道寫了一個字符串之后從管道讀取,并打印在標(biāo)準(zhǔn)輸出上。用一個圖來說明這個程序的狀態(tài)就是這樣的:

linux管道是什么及怎么使用

一個進(jìn)程自己給自己發(fā)送消息這當(dāng)然不叫進(jìn)程間通信,所以實際情況中我們不會在單個進(jìn)程中使用管道。進(jìn)程在pipe創(chuàng)建完管道之后,往往都要fork產(chǎn)生子進(jìn)程,成為如下圖表示的樣子:

linux管道是什么及怎么使用

如圖中描述,fork產(chǎn)生的子進(jìn)程會繼承父進(jìn)程對應(yīng)的文件描述符。利用這個特性,父進(jìn)程先pipe創(chuàng)建管道之后,子進(jìn)程也會得到同一個管道的讀寫文件描述符。從而實現(xiàn)了父子兩個進(jìn)程使用一個管道可以完成半雙工通信。此時,父進(jìn)程可以通過fd[1]給子進(jìn)程發(fā)消息,子進(jìn)程通過fd[0]讀。子進(jìn)程也可以通過fd[1]給父進(jìn)程發(fā)消息,父進(jìn)程用fd[0]讀。程序?qū)嵗缦拢?/p>

[zorro@zorro-pc pipe]$ cat pipe_parent_child.c
#include 
#include 
#include 
#include 
#include 
#include 

#define STRING "hello world!"

int main()
{
    int pipefd[2];
    pid_t pid;
    char buf[BUFSIZ];

    if (pipe(pipefd) == -1) {
        perror("pipe()");
        exit(1);
    }

    pid = fork();
    if (pid == -1) {
        perror("fork()");
        exit(1);
    }

    if (pid == 0) {
        /* this is child. */
        printf("Child pid is: %d\n", getpid());
        if (read(pipefd[0], buf, BUFSIZ) < 0) {
            perror("write()");
            exit(1);
        }

        printf("%s\n", buf);

        bzero(buf, BUFSIZ);
        snprintf(buf, BUFSIZ, "Message from child: My pid is: %d", getpid());
        if (write(pipefd[1], buf, strlen(buf)) < 0) {
            perror("write()");
            exit(1);
        }

    } else {
        /* this is parent */
        printf("Parent pid is: %d\n", getpid());

        snprintf(buf, BUFSIZ, "Message from parent: My pid is: %d", getpid());
        if (write(pipefd[1], buf, strlen(buf)) < 0) {
            perror("write()");
            exit(1);
        }

        sleep(1);

        bzero(buf, BUFSIZ);
        if (read(pipefd[0], buf, BUFSIZ) < 0) {
            perror("write()");
            exit(1);
        }

        printf("%s\n", buf);

        wait(NULL);
    }


    exit(0);
}

父進(jìn)程先給子進(jìn)程發(fā)一個消息,子進(jìn)程接收到之后打印消息,之后再給父進(jìn)程發(fā)消息,父進(jìn)程再打印從子進(jìn)程接收到的消息。程序執(zhí)行效果:

[zorro@zorro-pc pipe]$ ./pipe_parent_child 
Parent pid is: 8309
Child pid is: 8310
Message from parent: My pid is: 8309
Message from child: My pid is: 8310

從這個程序中我們可以看到,管道實際上可以實現(xiàn)一個半雙工通信的機(jī)制。使用同一個管道的父子進(jìn)程可以分時給對方發(fā)送消息。我們也可以看到對管道讀寫的一些特點,即:

在管道中沒有數(shù)據(jù)的情況下,對管道的讀操作會阻塞,直到管道內(nèi)有數(shù)據(jù)為止。當(dāng)一次寫的數(shù)據(jù)量不超過管道容量的時候,對管道的寫操作一般不會阻塞,直接將要寫的數(shù)據(jù)寫入管道緩沖區(qū)即可。

當(dāng)然寫操作也不會再所有情況下都不阻塞。這里我們要先來了解一下管道的內(nèi)核實現(xiàn)。上文說過,管道實際上就是內(nèi)核控制的一個內(nèi)存緩沖區(qū),既然是緩沖區(qū),就有容量上限。我們把管道一次最多可以緩存的數(shù)據(jù)量大小叫做PIPESIZE。內(nèi)核在處理管道數(shù)據(jù)的時候,底層也要調(diào)用類似read和write這樣的方法進(jìn)行數(shù)據(jù)拷貝,這種內(nèi)核操作每次可以操作的數(shù)據(jù)量也是有限的,一般的操作長度為一個page,即默認(rèn)為4k字節(jié)。我們把每次可以操作的數(shù)據(jù)量長度叫做PIPEBUF。POSIX標(biāo)準(zhǔn)中,對PIPEBUF有長度限制,要求其最小長度不得低于512字節(jié)。PIPEBUF的作用是,內(nèi)核在處理管道的時候,如果每次讀寫操作的數(shù)據(jù)長度不大于PIPEBUF時,保證其操作是原子的。而PIPESIZE的影響是,大于其長度的寫操作會被阻塞,直到當(dāng)前管道中的數(shù)據(jù)被讀取為止。

在Linux 2.6.11之前,PIPESIZE和PIPEBUF實際上是一樣的。在這之后,Linux重新實現(xiàn)了一個管道緩存,并將它與寫操作的PIPEBUF實現(xiàn)成了不同的概念,形成了一個默認(rèn)長度為65536字節(jié)的PIPESIZE,而PIPEBUF只影響相關(guān)讀寫操作的原子性。從Linux 2.6.35之后,在fcntl系統(tǒng)調(diào)用方法中實現(xiàn)了F_GETPIPE_SZ和F_SETPIPE_SZ操作,來分別查看當(dāng)前管道容量和設(shè)置管道容量。管道容量容量上限可以在/proc/sys/fs/pipe-max-size進(jìn)行設(shè)置。

#define BUFSIZE 65536

......

ret = fcntl(pipefd[1], F_GETPIPE_SZ);
if (ret < 0) {
    perror("fcntl()");
    exit(1);
}

printf("PIPESIZE: %d\n", ret);

ret = fcntl(pipefd[1], F_SETPIPE_SZ, BUFSIZE);
if (ret < 0) {
    perror("fcntl()");
    exit(1);
}

......

PIPEBUF和PIPESIZE對管道操作的影響會因為管道描述符是否被設(shè)置為非阻塞方式而有行為變化,n為要寫入的數(shù)據(jù)量時具體為:

O_NONBLOCK關(guān)閉,n <= PIPE_BUF:

n個字節(jié)的寫入操作是原子操作,write系統(tǒng)調(diào)用可能會因為管道容量(PIPESIZE)沒有足夠的空間存放n字節(jié)長度而阻塞。

O_NONBLOCK打開,n <= PIPE_BUF:

如果有足夠的空間存放n字節(jié)長度,write調(diào)用會立即返回成功,并且對數(shù)據(jù)進(jìn)行寫操作??臻g不夠則立即報錯返回,并且errno被設(shè)置為EAGAIN。

O_NONBLOCK關(guān)閉,n > PIPE_BUF:

對n字節(jié)的寫入操作不保證是原子的,就是說這次寫入操作的數(shù)據(jù)可能會跟其他進(jìn)程寫這個管道的數(shù)據(jù)進(jìn)行交叉。當(dāng)管道容量長度低于要寫的數(shù)據(jù)長度的時候write操作會被阻塞。

O_NONBLOCK打開,n > PIPE_BUF:

如果管道空間已滿。write調(diào)用報錯返回并且errno被設(shè)置為EAGAIN。如果沒滿,則可能會寫入從1到n個字節(jié)長度,這取決于當(dāng)前管道的剩余空間長度,并且這些數(shù)據(jù)可能跟別的進(jìn)程的數(shù)據(jù)有交叉。

以上是在使用半雙工管道的時候要注意的事情,因為在這種情況下,管道的兩端都可能有多個進(jìn)程進(jìn)行讀寫處理。如果再加上線程,則事情可能變得更復(fù)雜。實際上,我們在使用管道的時候,并不推薦這樣來用。管道推薦的使用方法是其單工模式:即只有兩個進(jìn)程通信,一個進(jìn)程只寫管道,另一個進(jìn)程只讀管道。實現(xiàn)為:

[zorro@zorro-pc pipe]$ cat pipe_parent_child2.c
#include 
#include 
#include 
#include 
#include 
#include 

#define STRING "hello world!"

int main()
{
    int pipefd[2];
    pid_t pid;
    char buf[BUFSIZ];

    if (pipe(pipefd) == -1) {
        perror("pipe()");
        exit(1);
    }

    pid = fork();
    if (pid == -1) {
        perror("fork()");
        exit(1);
    }

    if (pid == 0) {
        /* this is child. */
        close(pipefd[1]);

        printf("Child pid is: %d\n", getpid());
        if (read(pipefd[0], buf, BUFSIZ) < 0) {
            perror("write()");
            exit(1);
        }

        printf("%s\n", buf);

    } else {
        /* this is parent */
        close(pipefd[0]);

        printf("Parent pid is: %d\n", getpid());

        snprintf(buf, BUFSIZ, "Message from parent: My pid is: %d", getpid());
        if (write(pipefd[1], buf, strlen(buf)) < 0) {
            perror("write()");
            exit(1);
        }

        wait(NULL);
    }


    exit(0);
}

這個程序?qū)嶋H上比上一個要簡單,父進(jìn)程關(guān)閉管道的讀端,只寫管道。子進(jìn)程關(guān)閉管道的寫端,只讀管道。整個管道的打開效果最后成為下圖所示:

linux管道是什么及怎么使用

此時兩個進(jìn)程就只用管道實現(xiàn)了一個單工通信,并且這種狀態(tài)下不用考慮多個進(jìn)程同時對管道寫產(chǎn)生的數(shù)據(jù)交叉的問題,這是最經(jīng)典的管道打開方式,也是我們推薦的管道使用方式。另外,作為一個程序員,即使我們了解了Linux管道的實現(xiàn),我們的代碼也不能依賴其特性,所以處理管道時該越界判斷還是要判斷,該錯誤檢查還是要檢查,這樣代碼才能更健壯。

FIFO

命名管道在底層的實現(xiàn)跟匿名管道完全一致,區(qū)別只是命名管道會有一個全局可見的文件名以供別人open打開使用。再程序中創(chuàng)建一個命名管道文件的方法有兩種,一種是使用mkfifo函數(shù)。另一種是使用mknod系統(tǒng)調(diào)用,例子如下:

[zorro@zorro-pc pipe]$ cat mymkfifo.c
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{

    if (argc != 2) {
        fprintf(stderr, "Argument error!\n");
        exit(1);
    }

/*
    if (mkfifo(argv[1], 0600) < 0) {
        perror("mkfifo()");
        exit(1);
    }
*/
    if (mknod(argv[1], 0600|S_IFIFO, 0) < 0) {
        perror("mknod()");
        exit(1);
    }

    exit(0);
}

我們使用第一個參數(shù)作為創(chuàng)建的文件路徑。創(chuàng)建完之后,其他進(jìn)程就可以使用open()、read()、write()標(biāo)準(zhǔn)文件操作等方法進(jìn)行使用了。其余所有的操作跟匿名管道使用類似。需要注意的是,無論命名還是匿名管道,它的文件描述都沒有偏移量的概念,所以不能用lseek進(jìn)行偏移量調(diào)整。

讀到這里,這篇“l(fā)inux管道是什么及怎么使用”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領(lǐng)會,如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


分享名稱:linux管道是什么及怎么使用
本文URL:http://weahome.cn/article/jssjpe.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部