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

進(jìn)程間通信之匿名管道

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

一、進(jìn)程間通信介紹

1.進(jìn)程間為什么要進(jìn)行通信?
進(jìn)程間通信的是為了協(xié)調(diào)不同的進(jìn)程,使之能在一個操作系統(tǒng)里同時運(yùn)行,并相互傳遞、交換信息。

2.進(jìn)程間通信的目的包括:
數(shù)據(jù)傳輸:一個進(jìn)程需要將它的數(shù)據(jù)發(fā)送給另一個進(jìn)程;
資源共享:多個進(jìn)程間共享同樣的資源;
通知事件:一個進(jìn)程需要向另一個或一組進(jìn)程發(fā)送消息,通知它們發(fā)生了某種事情,比如進(jìn)程終止時需要通知其父進(jìn)程;
進(jìn)程間通信(IPC)是一組編程接口,讓程序員能夠協(xié)調(diào)不同的進(jìn)程,使之能在一個操作系統(tǒng)里同時運(yùn)行,并相互傳遞、交換信息;
進(jìn)程控制:有些進(jìn)程希望完全控制另一個進(jìn)程的執(zhí)行(如Debug進(jìn)程),此時控制進(jìn)程希望能夠攔截另一個進(jìn)程的所有陷入和異常,并能夠及時知道它的狀態(tài)改變。

3.進(jìn)程間通信的分類:
(1)管道:1、匿名管道pipe;2、命名管道m(xù)kfifo
(2)System V IPC:1、System V 消息隊列;2、System V 共享內(nèi)存;3、System V 信號量。
(3) POSIX IPC:1、消息隊列;2、共享內(nèi)存;3、信號量;4、互斥量;5、條件變量;6、讀寫鎖。
那么管道是如何進(jìn)行通信的?

二、管道

什么是管道?

管道是一種通信機(jī)制,通常用于進(jìn)程間的通信,它表現(xiàn)出來的形式將前面每一個進(jìn)程的輸出(stdout)直接作為下一個進(jìn)程的輸入(stdin)。

1.匿名管道

匿名管道是一種進(jìn)程間通信機(jī)制,它僅限于本地父子進(jìn)程之間通信,結(jié)構(gòu)簡單,類似于一根水管,一端進(jìn)水另一端出水(單工)也就是只能夠單向通信。匿名管道的作用之一是輸出重定向。
進(jìn)程間通信之匿名管道,linux
所以用匿名管道pipe通信大概分為四部:1、創(chuàng)建匿名管道;2、創(chuàng)建子進(jìn)程;3、關(guān)閉不需要的文件描述符;4、進(jìn)行通信。

pipe函數(shù),linux中輸入 man 2 pipe,可以查看pipe函數(shù),如下:
進(jìn)程間通信之匿名管道,linux

#include <unistd.h>
功能:創(chuàng)建一無名管道
原型
int pipe(int pipefd[2]);
參數(shù)
pipefd:文件描述符數(shù)組,其中fd[0]表示讀端, fd[1]表示寫端
返回值:成功返回0,失敗返回-1和錯誤代碼

1.1父進(jìn)程和一個子進(jìn)程之間的通信

接下來試著寫一下,父進(jìn)程向管道當(dāng)中寫“i am father”, 子進(jìn)程從管道當(dāng)中讀出內(nèi)容, 并且打印到標(biāo)準(zhǔn)輸出;先創(chuàng)建管道, 進(jìn)而創(chuàng)建子進(jìn)程, 父子進(jìn)程使用管道進(jìn)行通信。

#include <iostream>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <cstdio>
#include <unistd.h>
using namespace std;

int main()
{
	//創(chuàng)建匿名管道
    int pipefd[2] = {0}; //文件描述符數(shù)組
    int n = pipe(pipefd);
    if (n < 0) //如果失敗,則打印錯誤碼
    {
        cout << "pipe error, " << errno << ": " << strerror(errno) << endl;
    }
    cout << "讀端:" << pipefd[0] << endl;
    cout << "寫端:" << pipefd[1] << endl; //打印出讀寫端的文件描述符
	//創(chuàng)建子進(jìn)程
    pid_t id = fork(); //子進(jìn)程中返回0,父進(jìn)程返回子進(jìn)程id,出錯返會-1
    assert(id != -1);
    if (id == 0)
    {
        // child(子進(jìn)程)
        close(pipefd[1]); //關(guān)閉不需要的文件描述符,子進(jìn)程需要讀,所以關(guān)閉管道寫端
                          //pipefd數(shù)組中0表示讀端;1表示寫端
        char buffer[1024];//不管讀數(shù)據(jù)還是寫數(shù)據(jù),都需要緩沖區(qū),所以buffer相當(dāng)于一個緩沖區(qū)
        while (true)
        {
            sleep(1);
            int n = read(pipefd[0], buffer, sizeof(buffer) - 1); //read函數(shù)
            //sizeof為什么-1? 從文件中讀取數(shù)據(jù)到buffer中,因?yàn)閏語言中字符串的結(jié)束
            //標(biāo)志是'\0',而文件中的數(shù)據(jù)結(jié)束尾不需要'\0',所以buffer的內(nèi)存要比讀出
            //數(shù)據(jù)內(nèi)存大'\0';相反,往文件中寫入數(shù)據(jù)時,要注意去掉字符串末尾的'\0',
            //字符串末尾的'\0'在文件中會變成亂碼(^@)
            if (n > 0)
            {
                buffer[n] = '\0';
                cout << "我是子進(jìn)程,讀取到父進(jìn)程的數(shù)據(jù): " << buffer << endl;
            }
            else if (n == 0)
            {
                cout << "我是子進(jìn)程,讀到了文件結(jié)尾" << buffer << endl;
                break;
            }
            else
            {
                cout << "我是子進(jìn)程,讀取異常" << endl;
            }
        }
        close(pipefd[0]); //關(guān)閉子進(jìn)程中管道的讀端
        exit(0);
    }
    // father(父進(jìn)程)
    close(pipefd[0]); //父進(jìn)程寫入,關(guān)閉讀端
    int cnt = 1; //用來記錄寫了多少次
    const string str = "i am father"; //要寫入的內(nèi)容
    char buffer[1024];
    while (true)
    {
        sleep(1);
        snprintf(buffer, sizeof(buffer), "%s, 計數(shù)器:%d", str.c_str(), cnt++);//將數(shù)據(jù)寫入到buffer中
        write(pipefd[1], buffer, strlen(buffer));//再將buffer中的數(shù)據(jù)寫入管道中
    }
    close(pipefd[1]); //關(guān)閉父進(jìn)程中管道的寫端
    
    return 0;
}

下面是一些open,write,close等函數(shù)和文件描述符等有關(guān)概念:
系統(tǒng)方面對文件的打開,讀寫和關(guān)閉(open,write,close等用法)
文件描述符
運(yùn)行結(jié)果如下:
進(jìn)程間通信之匿名管道,linux
由下述代碼看現(xiàn)象:

#include <iostream>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <cstdio>
#include <unistd.h>
using namespace std;

int main()
{
    int pipefd[2] = {0};
    int n = pipe(pipefd);
    if (n < 0)
    {
        cout << "pipe error, " << errno << ": " << strerror(errno) << endl;
    }

    pid_t id = fork();
    assert(id != -1);
    if (id == 0)
    {
        // child
        int cnt = 5;
        close(pipefd[1]);
        char buffer[1024];
        while (--cnt)
        {
            sleep(1);
            int n = read(pipefd[0], buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = '\0';
                cout << buffer << endl;
            }
            else if (n == 0)
            {
                cout << "我是子進(jìn)程,讀到了文件結(jié)尾" << buffer << endl;
                break;
            }
            else
            {
                cout << "我是子進(jìn)程,讀取異常" << endl;
            }
        }
        close(pipefd[0]);
        exit(0);
    }
    // father
    close(pipefd[0]);
    int cnt = 1;
    const string str = "i am father";
    char buffer[1024];
    while (true)
    {
        sleep(1);
        snprintf(buffer, sizeof(buffer), "%s, 計數(shù)器:%d", str.c_str(), cnt++);
        write(pipefd[1], buffer, strlen(buffer));
    }
    close(pipefd[1]);
    return 0;
}

進(jìn)程間通信之匿名管道,linux

由上可知:
寫端一直寫,讀端關(guān)閉OS會殺死一直在寫入的進(jìn)程。
將上述代碼進(jìn)行小的修改,就可以得到如下現(xiàn)象,代碼就不在展示了,四種現(xiàn)象如下:
1.如果read讀取完畢了所有的管道數(shù)據(jù),如果對方不發(fā),那么就只能等待;
2.如果我們writer端將管道寫滿了,那么就不能再寫。管道的內(nèi)存是有一定大小的;
3.如果我關(guān)閉了寫端,讀取完畢管道數(shù)據(jù),再讀,就會read返回0,表明讀到了文件結(jié)尾;
4.寫端一直寫,讀端關(guān)閉,會發(fā)生什么呢?沒有意義。OS不會維護(hù)無意義,低效率,或者浪費(fèi)資源的事情。OS會殺死一直在寫入的進(jìn)程! OS會通過信號來終止進(jìn)程,13) SIGPIPE。

1.2父進(jìn)程和多個子進(jìn)程之間的通信

父進(jìn)程寫數(shù)據(jù),子進(jìn)程讀取數(shù)據(jù),父進(jìn)程和多個子進(jìn)程之間的通信,首先創(chuàng)建管道,代碼如下:

void createProcess(vector<EndPoint> &end_points)
{
    for (int i = 0; i < CHILDNUMS; ++i)
    {
        // 1.先創(chuàng)建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        assert(n == 0); // pipe函數(shù)成功返回0
        (void)n;        // linux中,定義一個變量,但是沒使用,所以強(qiáng)轉(zhuǎn)為void。(gcc編譯可能報錯)
        // 2.創(chuàng)建子進(jìn)程
        pid_t id = fork();
        assert(id != -1); // fork失敗返回-1
        if (id == 0)
        {
            // 子進(jìn)程
            // 關(guān)閉不需要的文件描述符,假設(shè)父進(jìn)程寫,子進(jìn)程讀
            close(pipefd[1]); // pipe中1 為寫端
            char buffer[1024];
            while (true)
            {
                //...進(jìn)行讀取
                ......
                
            }
            close(pipefd[0]); // 關(guān)閉讀端
            exit(0);          // 關(guān)閉讀端,子進(jìn)程退出
        }
        // 父進(jìn)程
        close(pipefd[0]);
        end_points.push_back(EndPoint(id, pipefd[1])); //將文件描述符管理起來
    }
}

其中需要注意的是創(chuàng)建子進(jìn)程,子進(jìn)程是父進(jìn)程的拷貝,所以當(dāng)多次fork后,子進(jìn)程中含有父進(jìn)程指向管道的寫端,如下圖:
進(jìn)程間通信之匿名管道,linux
所以在創(chuàng)建管道時,需要關(guān)閉多余的管道,改進(jìn)代碼如下:

void createProcess(vector<EndPoint> &end_points)
{
    vector<int> fds;
    for (int i = 0; i < CHILDNUMS; ++i)
    {
        // 1.先創(chuàng)建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        assert(n == 0); // pipe函數(shù)成功返回0
        (void)n;        // linux中,定義一個變量,但是沒使用,所以強(qiáng)轉(zhuǎn)為void。(gcc編譯可能報錯)
        // 2.創(chuàng)建子進(jìn)程
        pid_t id = fork();
        assert(id != -1); // fork失敗返回-1
        if (id == 0)
        {
            //關(guān)閉子進(jìn)程多余的fd
            for(auto &fd : fds)
                close(fd);
            // 子進(jìn)程
            // 關(guān)閉不需要的文件描述符,假設(shè)父進(jìn)程寫,子進(jìn)程讀
            close(pipefd[1]); // pipe中1 為寫端
            char buffer[1024];
            while (true)
            {
                //...進(jìn)行讀取
                ......
                
            }
            close(pipefd[0]); // 關(guān)閉讀端
            exit(0);          // 關(guān)閉讀端,子進(jìn)程退出
        }
        // 父進(jìn)程
        close(pipefd[0]);
        end_points.push_back(EndPoint(id, pipefd[1]));
        
        fds.push_back(pipefd[1]);
    }
}

完整代碼如下:

#include <iostream>
#include <vector>
#include <string>
#include <string.h> //strlen
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;

#define CHILDNUMS 3 // 子進(jìn)程的個數(shù)
class EndPoint
{
public:
    EndPoint(pid_t id, int fd)
        : _child_id(id), _write_fd(fd)
    {
    }

public:
    pid_t _child_id;
    int _write_fd;
};
void createProcess(vector<EndPoint> &end_points)
{
    vector<int> fds;
    for (int i = 0; i < CHILDNUMS; ++i)
    {
        // 1.先創(chuàng)建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        assert(n == 0); // pipe函數(shù)成功返回0
        (void)n;        // linux中,定義一個變量,但是沒使用,所以強(qiáng)轉(zhuǎn)為void。(gcc編譯可能報錯)
        // 2.創(chuàng)建子進(jìn)程
        pid_t id = fork();
        assert(id != -1); // fork失敗返回-1
        if (id == 0)
        {
            //關(guān)閉子進(jìn)程多余的fd
            for(auto &fd : fds)
                close(fd);
            // 子進(jìn)程
            // 關(guān)閉不需要的文件描述符,假設(shè)父進(jìn)程寫,子進(jìn)程讀
            close(pipefd[1]); // pipe中1 為寫端
            char buffer[1024];
            while (true)
            {
                int n = read(pipefd[0], buffer, sizeof(buffer) - 1);
                if (n > 0)
                {
                    buffer[n] = '\0';
                    cout << "我是pid=%d的子進(jìn)程," << getpid() << ";接受到父進(jìn)程的指令是:" << buffer << endl;
                }
                else if (n == 0)
                {
                    cout << "我是子進(jìn)程pid=" << getpid() << ",讀到了文件結(jié)尾,并且父進(jìn)程寫端已關(guān)閉" << endl;
                    break;
                }
                else
                {
                    cout << "我是子進(jìn)程,讀取異常" << endl;
                }
                sleep(1);
            }
            close(pipefd[0]); // 關(guān)閉讀端
            exit(0);          // 關(guān)閉讀端,子進(jìn)程退出
        }
        // 父進(jìn)程
        close(pipefd[0]);
        end_points.push_back(EndPoint(id, pipefd[1]));

        fds.push_back(pipefd[1]);
    }
}
void ctrlProcess(const vector<EndPoint> &end_points)
{
    // 對每個子進(jìn)程下發(fā)任務(wù)
    char buffer[1024];
    int command = 0;
    int cnt = 0;
    while (true)
    {
        cnt %= end_points.size();
        cout << "輸如命令號:";
        cin >> command;
        if (command == 0)
            break;
        snprintf(buffer, sizeof(buffer), "pid=%d的子進(jìn)程處理%d任務(wù)", end_points[cnt]._child_id, command);
        write(end_points[cnt]._write_fd, buffer, strlen(buffer));
        ++cnt;
        sleep(1);
    }
}
void waitProcess(const vector<EndPoint> &end_points)
{
    // 需要讓子進(jìn)程全部退出 --- 只需要讓父進(jìn)程關(guān)閉所有的write_fd并且要回收子進(jìn)程的僵尸狀態(tài)
    for (int end = 0; end < end_points.size(); ++end)
    {
        cout << "父進(jìn)程讓子進(jìn)程退出:" << end_points[end]._child_id << endl;
        close(end_points[end]._write_fd);

        waitpid(end_points[end]._child_id, nullptr, 0); //waitpid不需要查看子進(jìn)程退出信息,所以傳nullptr
        cout << "父進(jìn)程回收了子進(jìn)程:" << end_points[end]._child_id << endl;
    }
}
int main()
{
    cout << "命令號為0,則退出進(jìn)程" << endl;
    // 需要記錄子進(jìn)程的pid和父進(jìn)程的寫端
    vector<EndPoint> end_points;
    // 1. 先進(jìn)行構(gòu)建控制結(jié)構(gòu), 父進(jìn)程寫入,子進(jìn)程讀取
    createProcess(end_points);
    // 2.控制每個子進(jìn)程
    ctrlProcess(end_points);
    // 3. 處理所有的退出問題
    waitProcess(end_points);
    return 0;
}

運(yùn)行結(jié)果如下:
進(jìn)程間通信之匿名管道,linux
管道的特點(diǎn):文章來源地址http://www.zghlxwxcb.cn/news/detail-565217.html

  1. 只能用于具有共同祖先的進(jìn)程(具有親緣關(guān)系的進(jìn)程)之間進(jìn)行通信;
  2. 通常,一個管道由一個進(jìn)程創(chuàng)建,然后該進(jìn)程調(diào)用fork,此后父、子進(jìn)程之間就可應(yīng)用該管道;
  3. 管道提供流式服務(wù);
  4. 一般而言,進(jìn)程退出,管道釋放,所以管道的生命周期隨進(jìn)程;
  5. 內(nèi)核會對管道操作進(jìn)行同步與互斥;
  6. 管道是半雙工的,數(shù)據(jù)只能向一個方向流動;需要雙方通信時,需要建立起兩個管道。

到了這里,關(guān)于進(jìn)程間通信之匿名管道的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

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

相關(guān)文章

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包