最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Linux中的進程間通信之匿名管道解讀

 更新時間:2025年03月20日 15:01:57   作者:s_little_monster_  
這篇文章主要介紹了Linux中的進程間通信之匿名管道解讀,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

一、基本概念

我們知道多個進程之間是互相獨立的,但是有時候我們需要將一個進程的數(shù)據(jù)傳遞到另一個進程,實現(xiàn)數(shù)據(jù)傳輸的效果,有的時候多個進程之間要共享同樣的資源,有的時候一個進程要對其他進程發(fā)送消息,實現(xiàn)通知事件,還有的時候一個進程要完全控制另一個進程的執(zhí)行,實現(xiàn)進程控制

因為進程間相互獨立,所以進程通信是有較高成本

進程間通信的本質(zhì)就是讓不同的進程看到同一份資源,這份資源一定是由操作系統(tǒng)提供的第三方空間,不能是某個進程的,因為這樣會破壞進程獨立性,我們進程訪問第三方空間本質(zhì)上就是訪問操作系統(tǒng)

一般操作系統(tǒng)會有一個獨立的通信模塊,隸屬于文件系統(tǒng),它被制定者制定了兩個標(biāo)準(zhǔn)system V 和 posix ,其中system V 是本機內(nèi)部進程間的通信,分為消息隊列、共享內(nèi)存、信號量,posix 是網(wǎng)絡(luò)進程通信,分為消息隊列、共享內(nèi)存、信號量、互斥量、條件變量、讀寫鎖

在進程間通信的規(guī)則指定之前,還沒有system V 和 posix 的時候,我們是通過管道進行進程間通信的,這是一種基于文件的通信方式

二、管道

1、溫故知新

我們在之前的學(xué)習(xí)命令行的過程中學(xué)習(xí)過管道,那里的管道與這里的管道是一致的,本質(zhì)上就是一個管子,在兩頭位置處有兩種處理方式,在進入管道前處理一次,在管道中的內(nèi)容就是已經(jīng)被處理過一次的內(nèi)容,然后離開管道后再處理一次,得出的結(jié)果就是一個數(shù)據(jù)被前面的命令處理一次的結(jié)果被后面的命令處理

當(dāng)時學(xué)習(xí)的時候只浮于表面,實際上管道就是起到一個傳遞數(shù)據(jù)流的作用,兩邊為兩個進程,進程A發(fā)出的信息可以通過管道到達進程B,管道本身沒有處理數(shù)據(jù)的功能,只有傳遞數(shù)據(jù)的功能

2、實現(xiàn)方式

我們說管道是一個基于文件的通信方式,我們來看一下我們文件管理的內(nèi)容

進程中的PCB中有一個struct files_struct指針,指向結(jié)構(gòu)體files_struct,files_struct結(jié)構(gòu)體中存在一個文件描述符指針數(shù)組,指向?qū)?yīng)的struct file對象,每個struct file都有inode描述文件屬性,file_operators定義操作文件的函數(shù)接口,文件緩沖區(qū)緩沖文件,硬盤當(dāng)中的文件如果要加載到內(nèi)存中需要先加載到文件緩沖區(qū),如果我們的管道文件在硬盤上,那么IO的速度將非常慢,不利于我們進行進程間的快速通信,那什么地方既速度快又能存放文件呢?答案就是內(nèi)存

我們把寫入或者讀取硬盤的IO操作去掉,將管道文件保存在緩沖區(qū),其他進程再通過文件描述符讀取緩沖區(qū)的內(nèi)容,就可以實現(xiàn)進程間的管道通信,這里的管道文件就是匿名管道

管道文件的存放問題我們解決了,下一個問題就是其他進程怎么通過文件描述符讀取緩沖區(qū)的內(nèi)容

我們知道子進程被父進程創(chuàng)建后,如果不做修改,相當(dāng)于是淺拷貝,父進程的PCB復(fù)制一份,files_struct也復(fù)制一份,那么它們就同時指向已經(jīng)同一個struct file,如果父進程fd==3以讀方式打開管道文件,fd==4以寫方式打開管道文件,那么子進程也一樣,然后父進程close(3)子進程close(4)實現(xiàn)父寫子讀,父進程close(4)子進程close(3)實現(xiàn)父讀子寫

因為一個文件是沒法進行讀寫交替一起的,所以匿名管道其實是一種半雙工的通信方式,即單向通信,當(dāng)然我們可以通過建立多個匿名管道來實現(xiàn)雙向通信

管道通信常用于父子進程通信,可用于兄弟進程、爺孫進程等有"血緣"的進程進行通信

3、匿名管道

#include <unistd.h>
int pipe(int pipefd[2]);
//pipefd:文件描述符數(shù)組,其中pipefd[0]表示讀端,pipefd[1]表示寫端,值為對應(yīng)的文件描述符 
//返回值:成功返回0,失敗返回錯誤代碼

在pipe函數(shù)中,int fd[2]是一個輸出型參數(shù)

我們來實現(xiàn)一個父讀子寫這樣一個管道通信

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
#include <cstring>
#include <cstdlib>

#define N 2
#define NUM 1024
using namespace std;

void Writer(int wfd)
{
	//定義要發(fā)送的字符串
    string s = "this is your child";
    //獲取當(dāng)前進程的pid
    pid_t myid = getpid();
    int number = 0;

    char buffer[NUM];
    while(1)
    {
        //此處相當(dāng)于buffer[0] = '\0';意思是將整個數(shù)組當(dāng)做字符串用并且清空字符串
        buffer[0] = 0;
        //將字符串、pid、以及計數(shù)器number按照"%s-%d-%d"格式寫到buffer當(dāng)中
        snprintf(buffer,sizeof(buffer),"%s-%d-%d",s.c_str(),myid,number++);
        //這里傳過來的wfd為對應(yīng)的文件描述符,然后將buffer中的有效內(nèi)容寫到管道文件緩沖區(qū)中
        write(wfd,buffer,strlen(buffer));

        sleep(5);
    }
}

void Reader(int rfd)
{
    char buffer[NUM];
    while(1)
    {
    	//同上
        buffer[0] = 0;
        //將文件描述符rfd讀取的內(nèi)容存儲到buffer中,并返回讀取到的字符個數(shù)n
        ssize_t n = read(rfd,buffer,sizeof(buffer));
        //如果有內(nèi)容則打印出來
        if(n > 0)
        {
            buffer[n] = 0;
            cout << "parent get a message[" << getpid() << "]# " << buffer << endl;
        }
        //沒有內(nèi)容即讀取完成
        else if(n == 0) 
        {
            printf("parent read file done!\n");
            break;
        }
        //其他情況就是有bug了
        else break;
    }
}

int main()
{
	//pipefd用來存放輸出型參數(shù)
    int pipefd[N] = {0};	
    //成功驗證
    int n = pipe(pipefd);
    if(n < 0)
    {
        return 1;
    }
	//創(chuàng)建子進程
    pid_t id = fork();
	//錯誤情況
    if(id < 0)
    {
        return 2;
    }
    //子進程執(zhí)行段,把讀寫函數(shù)打包一下,寫到一個函數(shù)里,立體分明
    else if(id == 0)
    {
        //child
        //子進程要寫不讀,關(guān)掉pipefd[0],寫pipefd[1],寫完再關(guān)掉pipefd[1],然后退出
        close(pipefd[0]);
        Writer(pipefd[1]);
        close(pipefd[1]);
        exit(0);
    }
    //父進程執(zhí)行段
    else{
        //parent
        //父進程要讀不寫,關(guān)掉pipefd[1],讀pipefd[0],等待子進程結(jié)束再關(guān)掉pipefd[0]
        close(pipefd[1]);
        Reader(pipefd[0]);

        pid_t rid = waitpid(id,NULL,0);
        if(rid < 0)
        {
            return 3;
        }
        close(pipefd[0]);
    }
    return 0;
}

這里父進程只在子進程寫入的時候才讀取,沒有出現(xiàn)子進程寫一半父進程就讀取的情況,所以父子進程直接是會進行協(xié)同的,有同步和互斥性

(一)管道中的四種情況

對管道中可能出現(xiàn)的四種情況做說明:

  • 讀寫端正常,如果管道為空,讀端就要被阻塞(上面印證)
  • 讀寫端正常,如果管道被寫滿,寫端就要被阻塞(在管道特性這里印證)
  • 讀端正常,寫端關(guān)閉,讀端可以讀到0,表明讀到了文件結(jié)尾,不堵塞
  • 寫端正常,讀端關(guān)閉,操作系統(tǒng)會殺死正在寫入的進程,用信號SIGPIPE,也就是kill -13

注釋掉main函數(shù)中子進程中的Writer函數(shù),它會讀到文件結(jié)尾并打印done信息

寫端一秒寫入一次,讀端一秒讀一次,讀端讀5秒后退出讀模式,關(guān)閉讀端,然后靜待5秒,等待子進程結(jié)束,然后打印它的退出碼和收到的信號

int main()
{
	//......
    if(id < 0)
    {
        return 2;
    }
    else if(id == 0)
    {
        //child
        close(pipefd[0]);
        Writer(pipefd[1]);
        close(pipefd[1]);
        exit(0);
    }
    else{
        //parent
        close(pipefd[1]);
        Reader(pipefd[0]);
        close(pipefd[0]);
        cout << "father close read fd: " << pipefd[0] << endl;
        sleep(5);
        int status = 0;
        pid_t rid = waitpid(id,&status,0);
        if(rid < 0)
        {
            return 3;
        }
        cout << "wait child success: " << rid << " exit code: " << ((status>>8)&0xFF) << " exit signal: " << (status&0x7F) << endl;
        sleep(5);
        cout << "parent quit" << endl;
    }
    
    return 0;
}

(二)管道的特性

//子進程一直寫
void Writer(int wfd)
{
    string s = "this is your child";
    pid_t myid = getpid();
    int number = 0;

    char buffer[NUM];
    while(1)
    {
        //buffer[0] = '\0';
        buffer[0] = 0;
        snprintf(buffer,sizeof(buffer),"%s-%d-%d",s.c_str(),myid,number++);
        write(wfd,buffer,strlen(buffer));
    }
}

//父進程5秒讀一次數(shù)據(jù)
void Reader(int rfd)
{
    char buffer[NUM];
    while(1)
    {
        sleep(5);
        buffer[0] = 0;
        ssize_t n = read(rfd,buffer,sizeof(buffer));
        if(n > 0)
        {
            buffer[n] = 0;
            cout << "parent get a message[" << getpid() << "]# " << buffer << endl;
        }
        else if(n == 0) 
        {
            printf("parent read file done!\n");
            break;
        }
        else break;
    }
}

我們發(fā)現(xiàn)它的讀取是雜亂無章的,說明管道是面向字節(jié)流的,這里與前面并不矛盾,有人說這里不是沒寫完就讀取嗎,你看這個句子一段一段的,其實這里是緩沖區(qū)寫滿了,寫不下了,寫入端堵塞導(dǎo)致的,在讀取端讀取之后寫入端才繼續(xù)寫入,正好也印證了上面的說法

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • linux mount報錯:you must specify the filesystem type的解決方法

    linux mount報錯:you must specify the filesystem type的解決方法

    這篇文章主要介紹了linux mount報錯:you must specify the filesystem type的解決方法,文中給出了詳細的解決方法示例,對大家具有一定的參考價值,需要的朋友們下面來一起看看吧。
    2017-03-03
  • Linux?SNMP團體號配置指定IP地址訪問的方法

    Linux?SNMP團體號配置指定IP地址訪問的方法

    這篇文章主要介紹了如何配置SNMP服務(wù)以限制對服務(wù)器的訪問,包括使用snmpd.conf文件和iptables防火墻,建議使用復(fù)雜的團體號,實施最小權(quán)限原則,并限制IP地址,通過這些方法,可以顯著提高SNMP服務(wù)的安全性,需要的朋友可以參考下
    2025-11-11
  • CentOS7中使用shell腳本安裝python3.8環(huán)境(推薦)

    CentOS7中使用shell腳本安裝python3.8環(huán)境(推薦)

    這篇文章主要介紹了CentOS7中使用shell腳本安裝python3.8環(huán)境,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • Linux雙網(wǎng)卡綁定實現(xiàn)負載均衡詳解

    Linux雙網(wǎng)卡綁定實現(xiàn)負載均衡詳解

    這篇文章主要為大家詳細介紹了Linux雙網(wǎng)卡綁定實現(xiàn)負載均衡,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • 在Linux中部署并初始化MySQL的多種方式

    在Linux中部署并初始化MySQL的多種方式

    MySQL是常用的關(guān)系型數(shù)據(jù)庫,被廣泛用于學(xué)習(xí)與實際工作中,本篇文章介紹在Linux系統(tǒng)中使用多種方式部署并初始化MySQL數(shù)據(jù)庫,包括源碼安裝、rpm安裝以及yum安裝,需要的朋友可以參考下
    2025-11-11
  • linux中xargs命令的各種使用技巧

    linux中xargs命令的各種使用技巧

    xargs是給命令傳遞參數(shù)的一個過濾器,也是組合多個命令的一個工具。下面這篇文章主要給大家介紹了關(guān)于linux中xargs命令用法的相關(guān)資料,需要的朋友可以參考借鑒,下面來跟著小編一起看看吧。
    2017-05-05
  • CentOS 6.5中SSH免密碼登錄配置教程

    CentOS 6.5中SSH免密碼登錄配置教程

    這篇文章主要介紹了CentOS 6.5中SSH免密碼登錄配置的相關(guān)資料,包括環(huán)境設(shè)置,本機生成公鑰和私鑰等知識點,本文給大家介紹的非常詳細具有參考借鑒價值,需要的朋友可以參考下
    2016-10-10
  • ubuntu18.04 安裝qt5.12.8及環(huán)境配置的詳細教程

    ubuntu18.04 安裝qt5.12.8及環(huán)境配置的詳細教程

    這篇文章主要介紹了ubuntu18.04 安裝qt5.12.8及環(huán)境配置的教程,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-05-05
  • linux安裝jdk并設(shè)置環(huán)境變量的方法教程(看這一篇夠了)

    linux安裝jdk并設(shè)置環(huán)境變量的方法教程(看這一篇夠了)

    這篇文章主要介紹了linux安裝jdk并設(shè)置環(huán)境變量的相關(guān)資料,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-02-02
  • Ubuntu徹底卸載MySQL、Apache2和Php的方法教程

    Ubuntu徹底卸載MySQL、Apache2和Php的方法教程

    這篇文章主要給大家介紹了關(guān)于在Ubuntu系統(tǒng)下徹底卸載MySQL、Apache2和Php的方法教程,文中通過示例代碼介紹的非常詳細,對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面跟著小編來一起看看吧。
    2017-08-08

最新評論

盐池县| 北京市| 山东| 莎车县| 崇礼县| 金昌市| 黔东| 股票| 正阳县| 杨浦区| 兖州市| 利川市| 万荣县| 鹤壁市| 青河县| 金山区| 广汉市| 黑河市| 旅游| 云霄县| 新龙县| 高淳县| 宁明县| 体育| 屏东县| 武汉市| 奉节县| 当涂县| 丰台区| 全州县| 昔阳县| 五峰| 渑池县| 兰溪市| 驻马店市| 丰顺县| 怀安县| 康平县| 梅州市| 突泉县| 栖霞市|