国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【LInux】進(jìn)程間通信 -- 匿名管道

這篇具有很好參考價(jià)值的文章主要介紹了【LInux】進(jìn)程間通信 -- 匿名管道。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

前言

我們?cè)趯W(xué)習(xí)進(jìn)程管理,進(jìn)程替換時(shí),都強(qiáng)調(diào)了進(jìn)程的獨(dú)立性,那進(jìn)程間通信是什么?這好像和進(jìn)程的獨(dú)立性相矛盾吧?
那么今天,我們就來(lái)學(xué)習(xí)進(jìn)程間通信,和第一種通信方式 – 管道

【LInux】進(jìn)程間通信 -- 匿名管道

一. 進(jìn)程間通信

進(jìn)程間通信,并沒有破壞進(jìn)程的獨(dú)立性這一特點(diǎn),這點(diǎn)我們?cè)?code>管道講解
而進(jìn)程通信的目的有如下幾個(gè):

  1. 數(shù)據(jù)傳輸:一個(gè)進(jìn)程需要將它的數(shù)據(jù)發(fā)送給另一個(gè)進(jìn)程
  2. 資源共享:多個(gè)進(jìn)程之間共享同樣的資源
  3. 通知事件:一個(gè)進(jìn)程需要向另一個(gè)或一組進(jìn)程發(fā)送消息,通知它(它們)發(fā)生了某種事件(如進(jìn)程終止時(shí)要通知父進(jìn)程)
  4. 進(jìn)程控制:有些進(jìn)程希望完全控制另一個(gè)進(jìn)程的執(zhí)行(如Debug進(jìn)程),此時(shí)控制進(jìn)程希望能夠攔截另一個(gè)進(jìn)程的所有陷入和異常,并能夠及時(shí)找到它的狀態(tài)改變。

進(jìn)程間通信的三種常用方法

  1. 管道

匿名管道pipe
命名管道

  1. System V進(jìn)程間通信

System V消息隊(duì)列
System V共享內(nèi)存
System V信號(hào)量

  1. POSIX進(jìn)程間通信

消息隊(duì)列
共享內(nèi)存
信息號(hào)
互斥量
條件變量
讀寫鎖

進(jìn)程依舊具有獨(dú)立性,一個(gè)進(jìn)程不可能可以直接從另一個(gè)進(jìn)程的堆,棧區(qū)獲取數(shù)據(jù),那么進(jìn)程間通信是怎么實(shí)現(xiàn)的呢?
要讓兩個(gè)不同的進(jìn)程,進(jìn)行通信,前提條件是:先讓不同進(jìn)程,看到同一份“資源”

而管道就是,看到同一份“資源”的實(shí)現(xiàn)方式之一
【LInux】進(jìn)程間通信 -- 匿名管道

二. 管道

管道是Unix中最古老的進(jìn)程間通信形式
我們把一個(gè)進(jìn)程連接到另一個(gè)進(jìn)程的一個(gè)數(shù)據(jù)流稱為一個(gè)“管道”
我們?cè)贚inux指令中使用的| 就是管道
who | wc -l,who可以查看有幾個(gè)用戶登錄服務(wù)器,wc可以統(tǒng)計(jì)有幾行文本行
這里就是who創(chuàng)建進(jìn)程,先顯示有幾個(gè)用戶,然后將數(shù)據(jù)傳輸給wc,wc處理完再輸出結(jié)果
【LInux】進(jìn)程間通信 -- 匿名管道

我們?cè)倬呦蟮睦斫夤艿?/p>

當(dāng)我們創(chuàng)建一個(gè)進(jìn)程,OS會(huì)創(chuàng)建task_struct維護(hù),管理進(jìn)程。而該結(jié)構(gòu)體里有一個(gè)strust file_struct*,指向一個(gè)結(jié)構(gòu)體,該結(jié)構(gòu)體是管理文件的,里面存儲(chǔ)了打開文件的文件描述符,默認(rèn)0,1,2分別是標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)錯(cuò)誤
而Linux下一切皆文件,管道也是文件,但是是OS為了實(shí)現(xiàn)進(jìn)程間通信,而臨時(shí)創(chuàng)建的一個(gè)內(nèi)存文件。默認(rèn)是空閑文件描述符的后兩個(gè)。如下圖

【LInux】進(jìn)程間通信 -- 匿名管道

而創(chuàng)建子進(jìn)程,需要重新創(chuàng)建task_struct ,但是struct files_struct的內(nèi)容是拷貝父進(jìn)程的,但也僅是拷貝,拷貝一份文件描述符和文件的映射關(guān)系,不會(huì)重新創(chuàng)建新文件
fork創(chuàng)建子進(jìn)程后,只會(huì)賦值進(jìn)程相關(guān)的數(shù)據(jù)結(jié)構(gòu)對(duì)象,不會(huì)復(fù)制父進(jìn)程曾經(jīng)打開的文件對(duì)象!就像淺拷貝一樣

【LInux】進(jìn)程間通信 -- 匿名管道

這種管道,只支持單向通信,叫做匿名管道。因?yàn)槲募?code>只有一個(gè)緩沖區(qū),所以讀寫同時(shí)只能進(jìn)行一項(xiàng)
所以我們需要手動(dòng)確定數(shù)據(jù)流向,關(guān)閉不需要的文件描述符fd

三. 匿名管道的使用

接下來(lái),我們就來(lái)簡(jiǎn)單模擬一下進(jìn)程間通信。

用匿名管道實(shí)現(xiàn)進(jìn)程通信,需要父進(jìn)程創(chuàng)建匿名管道,創(chuàng)建的方法是使用pipe函數(shù)
【LInux】進(jìn)程間通信 -- 匿名管道

該函數(shù)的參數(shù)較為特殊:是輸出型參數(shù)。類似waitpid的status。
我們傳過去一個(gè)2大小的整型數(shù)組,pipe函數(shù)內(nèi)部會(huì)將創(chuàng)建的管道的讀和寫兩個(gè)文件描述符寫入這個(gè)數(shù)組,返回給我們

返回值:成功調(diào)用,返回0;錯(cuò)誤,返回-1,并設(shè)置錯(cuò)誤碼。

1. pipe的使用

我們首先使用一下pipe函數(shù),看一下其效果

#include<iostream>
#include<cerrno>
#include<unistd.h>
#include<string.h>

using std::cout;
using std::endl;

int main()
{
    //創(chuàng)建管道所需傳參的數(shù)組
    int pipefd[2]={0};
    
    //1.創(chuàng)建管道
    int n=pipe(pipefd);
    if(n<0)
    {
        //如果返回值小于0,即-1,還會(huì)設(shè)置錯(cuò)誤碼
        //我們?cè)侔彦e(cuò)誤碼對(duì)應(yīng)的錯(cuò)誤信息,打印一下
        cout<<"pipe error,"<<errno<<":"<<strerror(errno)<<endl;
        return 1;
    }
    cout<<"pipefd[0]:"<<pipefd[0]<<endl;
    cout<<"pipefd[1]:"<<pipefd[1]<<endl;

    return 0;
}

【LInux】進(jìn)程間通信 -- 匿名管道
正如前面所說(shuō),管道的兩個(gè)文件描述符,默認(rèn)使用當(dāng)前空閑的前兩個(gè)文件描述符
管道創(chuàng)建的兩個(gè)文件描述符,默認(rèn)第一個(gè)是讀,第二個(gè)是寫

記憶法
pipe[0]的是讀端,0 -> 嘴巴 -> 讀
pipe[1]的是寫端,1 -> 筆 -> 寫

2. 準(zhǔn)備通信

我們知道了管道的創(chuàng)建方法,接下來(lái)就可以準(zhǔn)備實(shí)現(xiàn)父子進(jìn)程通信了。
我們上面說(shuō)到,進(jìn)程間通信的前提條件就是:讓不同的進(jìn)程,看到同一份資源。
管道就可以是這份資源,我們模擬父子進(jìn)程通信,讓子進(jìn)程往管道里寫數(shù)據(jù),然后父進(jìn)程接收數(shù)據(jù)

代碼如下:

#include<iostream>
#include<cerrno>
#include<cassert>
#include<unistd.h>
#include<string.h>
#include<string>
#include<sys/types.h>

using std::cout;
using std::endl;

int main()
{
    //創(chuàng)建管道所需傳參的數(shù)組
    int pipefd[2]={0};
    
    //1.創(chuàng)建管道
    int n=pipe(pipefd);
    if(n<0)
    {
        //如果返回值小于0,即-1,還會(huì)設(shè)置錯(cuò)誤碼
        //我們?cè)侔彦e(cuò)誤碼對(duì)應(yīng)的錯(cuò)誤信息,打印一下
        cout<<"pipe error,"<<errno<<":"<<strerror(errno)<<endl;
        return 1;
    }
    cout<<"pipefd[0]:"<<pipefd[0]<<endl;//讀端
    cout<<"pipefd[1]:"<<pipefd[1]<<endl;//寫端

    //2.創(chuàng)建子進(jìn)程
    pid_t id = fork();
    //獲取錯(cuò)誤。意料之外,使用if;意料之中,用assert
    //此處應(yīng)該使用if,但為了簡(jiǎn)單一些,使用assert
    assert(id!=-1);

    if(id==0)
    {
        //子進(jìn)程
        
        //3.關(guān)閉不需要的fd
        close(pipefd[0]);//關(guān)閉子進(jìn)程的讀端

        //4.開始通信
        const std::string namestr="hello ,我是子進(jìn)程";
        int cnt=1;//計(jì)數(shù)器

        char buffer[1024];//write的字符數(shù)組

        while(true)
        {
            //將內(nèi)容寫入buffer字符串
            snprintf(buffer,sizeof(buffer)-1,"%s,計(jì)數(shù)器:%d,我的PID:%d\n",namestr.c_str(),cnt++,getpid());
            //將內(nèi)容寫入管道
            write(pipefd[1],buffer,strlen(buffer));
            sleep(1);
        }

        //關(guān)閉子進(jìn)程的寫端,再exit退出
        close(pipefd[1]);
        exit(0);
    }

    //父進(jìn)程

    //3.關(guān)閉不需要的fd
    //讓父進(jìn)程進(jìn)行讀取
    close(pipefd[1]);//關(guān)閉父進(jìn)程的寫端

    //4.開始通信
    char buffer[1024];
    while(true)
    {
        //讀取的大小,至少要留一個(gè)位置寫入\0
        int n = read(pipefd[0],buffer,sizeof(buffer)-1);
        if(n>0)
        {
            buffer[n]='\0';
            cout<<"我是父進(jìn)程, child give me a message: "<<buffer<<endl;
        }
    }

    
    //關(guān)閉父進(jìn)程的讀端,結(jié)束進(jìn)程
    close(pipefd[0]);
    return 0;
}

【LInux】進(jìn)程間通信 -- 匿名管道
這樣我們就實(shí)現(xiàn)了父子進(jìn)程的通信。

3. 匿名管道的特點(diǎn)和場(chǎng)景

  1. 當(dāng)我們將子進(jìn)程寫入數(shù)據(jù)的sleep(1)注釋掉。程序運(yùn)行的結(jié)果就變得不一樣了。
    【LInux】進(jìn)程間通信 -- 匿名管道
    我們看到,子進(jìn)程寫了很多次,父進(jìn)程才進(jìn)行了一次讀取。
    我們?cè)俑淖円幌拢谧舆M(jìn)程寫入后的sleep改成sleep(5)
    在父進(jìn)程讀取數(shù)據(jù)時(shí),我們10個(gè)字節(jié),10個(gè)字節(jié)的讀取
    【LInux】進(jìn)程間通信 -- 匿名管道
    程序運(yùn)行結(jié)果就又變了
    【LInux】進(jìn)程間通信 -- 匿名管道

這兩個(gè)實(shí)驗(yàn)驗(yàn)證出了這樣一個(gè)結(jié)論:
在匿名管道的通信中,寫入的次數(shù),和讀取的次數(shù),不是嚴(yán)格匹配的,讀寫次數(shù)的多少?zèng)]有強(qiáng)相關(guān) — 因?yàn)榫彌_區(qū)的讀寫都是以字節(jié)為單位 — 字節(jié)流

  1. 接下來(lái),我們?cè)僮鰝€(gè)實(shí)驗(yàn):
    我們讓父進(jìn)程正常的讀取數(shù)據(jù),但子進(jìn)程每次寫入間隔10秒。
    【LInux】進(jìn)程間通信 -- 匿名管道
    觀察運(yùn)行,我們發(fā)現(xiàn),我們讓子進(jìn)程寫入變慢,但是父進(jìn)程的讀取也變慢了。這是怎么回事呢?

首先,管道文件的數(shù)據(jù)類似隊(duì)列,寫入一次是入隊(duì)列,讀取是出隊(duì)列,只要讀取,數(shù)據(jù)就沒了。
所以,子進(jìn)程寫入變慢,父進(jìn)程沒有東西可讀,就進(jìn)入了阻塞狀態(tài)。

  1. 我們?cè)僮尭高M(jìn)程的讀取變慢,子進(jìn)程正常寫入
    【LInux】進(jìn)程間通信 -- 匿名管道
    【LInux】進(jìn)程間通信 -- 匿名管道
    【LInux】進(jìn)程間通信 -- 匿名管道

我們發(fā)現(xiàn),管道進(jìn)行了65536(從0開始)次寫入之后就沒有寫入了。等父進(jìn)程時(shí)間一到,讀取了很多的X
而每次寫入,我們都只寫入1個(gè)字節(jié)。所以管道最多可以寫入65536字節(jié),也就是2的16次方,64kb,16個(gè)數(shù)據(jù)塊。

  1. 如果我們將子進(jìn)程的寫端關(guān)閉,父進(jìn)程的讀端繼續(xù),會(huì)發(fā)生什么呢?
    我們讓子進(jìn)程寫入一次數(shù)據(jù)就關(guān)閉寫端,父進(jìn)程仍然一直讀取數(shù)據(jù),但要對(duì)read的返回值多作一個(gè)判斷
    【LInux】進(jìn)程間通信 -- 匿名管道
    運(yùn)行結(jié)果如下
    【LInux】進(jìn)程間通信 -- 匿名管道

當(dāng)我們關(guān)閉子進(jìn)程的寫端,父進(jìn)程再讀取就會(huì)讀到文件尾,就會(huì)返回0,父進(jìn)程就終止了。

  1. 如果我們關(guān)閉父進(jìn)程的讀端,結(jié)果又會(huì)是這樣呢?

直接說(shuō)結(jié)論:
當(dāng)一個(gè)管道只有寫端,沒有讀端,代表著無(wú)論怎么寫,都不會(huì)有人獲取,這是沒有意義的事,而操作系統(tǒng)不會(huì)維護(hù)無(wú)意義的,低效率的,或者浪費(fèi)資源的事情。OS會(huì)殺死一直在寫入的這個(gè)進(jìn)程!通過13號(hào)信號(hào) SIGPIPE,殺死進(jìn)程

接下來(lái),我們總結(jié)一下匿名管道的特點(diǎn)和場(chǎng)景

特點(diǎn)

  1. 單向通信半雙工的一種情況,雙方同時(shí)只能一方寫入
    因?yàn)槟涿艿?code>只有一個(gè)緩沖區(qū),同時(shí)只能有一方進(jìn)行讀寫。
    全雙工,雙方可以同時(shí)寫入
  2. 匿名管道的本質(zhì)是文件,因?yàn)?code>fd的生命周期隨進(jìn)程,所以管道的生命周期也是隨進(jìn)程的。
  3. 匿名管道通信,通常用來(lái)進(jìn)行具有“血緣關(guān)系”的進(jìn)程之間的進(jìn)程通信,因?yàn)槟涿艿朗?code>內(nèi)存級(jí)文件,所以只有創(chuàng)建的子進(jìn)程可以獲得父進(jìn)程創(chuàng)建的匿名管道,所以常用于父子間通信 -- pipe 打開匿名管道。
    4.在匿名管道的通信中,寫入的次數(shù),和讀取的次數(shù),不是嚴(yán)格匹配的,讀寫次數(shù)的多少?zèng)]有強(qiáng)相關(guān) — 因?yàn)榫彌_區(qū)的讀寫都是以字節(jié)為單位 — 字節(jié)流
  4. 具有一定的協(xié)同能力,讓read和write能夠按照一定的步驟進(jìn)行通信 — 自帶同步機(jī)制

場(chǎng)景

  1. 如果我們r(jià)ead,讀端,讀取完畢了所有的管道數(shù)據(jù),如果對(duì)方不發(fā),讀端就會(huì)堵塞
  2. 如果我們write,寫端,將管道寫滿了,那就暫時(shí)不能繼續(xù)寫了,需要讀端讀取數(shù)據(jù),被讀取的數(shù)據(jù)從管道中去除,就可以繼續(xù)寫入
  3. 如果我們關(guān)閉了寫端讀取完畢管道數(shù)據(jù),再讀,就會(huì)讀到文件尾,read就會(huì)返回0
  4. 寫端一直寫,讀端關(guān)閉,操作系統(tǒng)會(huì)發(fā)送13號(hào) SIGPIPE信號(hào)殺死寫端的進(jìn)程。

當(dāng)單次寫入的數(shù)據(jù)量不大于PIPE_BUF時(shí),LInux將保證寫入的原子性
反之大于PIPE_BUF時(shí),Linux不再保證寫入的原子性。
目前的理解是,保證寫入時(shí)不會(huì)被讀取

結(jié)束語(yǔ)

本篇博客的內(nèi)容到此就結(jié)束了。

如果覺得本篇文章對(duì)你有所幫助的話,不妨點(diǎn)個(gè)贊支持一下博主,拜托啦,這對(duì)我真的很重要。
【LInux】進(jìn)程間通信 -- 匿名管道文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-417891.html

到了這里,關(guān)于【LInux】進(jìn)程間通信 -- 匿名管道的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包