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

Linux系統(tǒng)編程:采用管道的方式實現(xiàn)進程間通信

這篇具有很好參考價值的文章主要介紹了Linux系統(tǒng)編程:采用管道的方式實現(xiàn)進程間通信。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

目錄

一.?進程間通信概述

二.?管道的概念?

三.?通過管道實現(xiàn)進程間通信

3.1?實現(xiàn)原理

3.2?匿名管道創(chuàng)建系統(tǒng)接口pipe

3.3?管道通信的模擬實現(xiàn)

3.4?管道通信的訪問控制規(guī)則

3.5?管道通信的特點

四.?通過匿名管道實現(xiàn)進程池

4.1?進程池的概念

4.2?進程池的模擬實現(xiàn)

五.?命名管道

5.1?命名管道的功能

5.2?命名管道的創(chuàng)建和使用

六.?總結(jié)


一.?進程間通信概述

進程間通信的目的:實現(xiàn)進程之間的數(shù)據(jù)傳輸、共享資源、事件通知、多進程協(xié)同等操作。‘

進程間通信的技術(shù)手段:進程間要實現(xiàn)通信,就必須要讓不同的進程看到同一塊資源(內(nèi)存空間),而由于進程之間具有獨立性,因此這塊資源不能隸屬于任何一個進程,應(yīng)當由操作系統(tǒng)內(nèi)核提供。

進程間通信的方法:管道、SystemV、POSIX

管道:匿名管道、命名管道。

System V:共享內(nèi)存、消息隊列、信號量 。--? ?用于本地計算機進行單機進程間通信

POSIX:消息隊列、共享內(nèi)存、信號量、互斥量、讀寫鎖、條件變量。 --? ?在網(wǎng)絡(luò)中,用于多機之間的進程間通信。

Linux系統(tǒng)編程:采用管道的方式實現(xiàn)進程間通信,Linux系統(tǒng)和網(wǎng)絡(luò),linux,運維,服務(wù)器
圖1.1?進程間通信的方法

二.?管道的概念?

管道,是用于傳輸資源(數(shù)據(jù))的一種媒介,可以實現(xiàn)進程之間的單向通信(也只能單向通信)。

由于進程之間具有相互獨立性,因此,管道只能由操作系統(tǒng)內(nèi)核提供,不能源自任意進程,管道的本質(zhì)是一種內(nèi)存級文件,即:內(nèi)容不會被刷新到磁盤上的文件。

Linux系統(tǒng)編程:采用管道的方式實現(xiàn)進程間通信,Linux系統(tǒng)和網(wǎng)絡(luò),linux,運維,服務(wù)器
圖2.1?管道通信的原理

三.?通過管道實現(xiàn)進程間通信

3.1?實現(xiàn)原理

管道,尤其是匿名管道,一般用于具有親緣關(guān)系的進程之間的通信,其底層實現(xiàn)原理如下:

  1. 父進程以讀和寫的方式創(chuàng)建匿名管道,由于管道的本質(zhì)是文件,因此父進程會有兩個文件描述符fd分別指向管道(一個讀一個寫)。
  2. fork創(chuàng)建子進程,子進程的文件描述符及指向與父進程相同。
  3. 關(guān)閉不需要的文件描述符,一般父進程用于寫數(shù)據(jù),子進程用于讀數(shù)據(jù),因此父進程關(guān)閉用來讀的fd,子進程關(guān)閉用于寫的fd。

經(jīng)過上面的步驟,父進程的寫fd和子進程的讀fd就指向了相同的內(nèi)存級文件(管道),通過父進程向管道中寫的數(shù)據(jù),就能被子進程讀出來。

Linux系統(tǒng)編程:采用管道的方式實現(xiàn)進程間通信,Linux系統(tǒng)和網(wǎng)絡(luò),linux,運維,服務(wù)器
圖3.1?通過管道實現(xiàn)進程間通信的原理

3.2?匿名管道創(chuàng)建系統(tǒng)接口pipe

原型:int pipe(int pipefd[2])

參數(shù):pipefd為輸出型參數(shù),pipefd[0]為讀端文件描述符,pipefd[1]為寫端文件描述符。

返回值:如果成功創(chuàng)建管道返回0,失敗返回-1并設(shè)置全局錯誤碼。

頭文件:#include <sys/fcntl.h>、#include <unistd.h>

一般pipe由父進程來調(diào)用,調(diào)用pipe后父進程要fork創(chuàng)建子進程,在父進程中一般要關(guān)閉pipefd[0],在子進程中一般要關(guān)閉pipefd[1]。

Linux系統(tǒng)編程:采用管道的方式實現(xiàn)進程間通信,Linux系統(tǒng)和網(wǎng)絡(luò),linux,運維,服務(wù)器
圖3.2?pipe的原理

3.3?管道通信的模擬實現(xiàn)

代碼3.1通過管道,實現(xiàn)父進程向子進程發(fā)生消息,父進程每隔1s寫一次消息,子進程不間斷讀取并輸出消息,由于寫慢讀快,子進程需要阻塞等待父進程寫消息后才能讀。

代碼3.1:模擬實現(xiàn)管道通信

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

#define SIZE 1024

int main()
{
    // 1. 父進程創(chuàng)建管道
    int pipefd[2] = {0};    // 管道讀寫對應(yīng)的文件描述符
    int n = pipe(pipefd);   // 創(chuàng)建管道

    if(n == -1)   // 管道創(chuàng)建失敗
    {
        perror("pipe");
        exit(1);
    }

    // 2. fork子進程
    pid_t id = fork();    // 創(chuàng)建子進程
    
    if(id < 0)   //子進程創(chuàng)建失敗
    {
        perror("fork");
        exit(2);
    }
    else if(id == 0)   //子進程代碼
    {
        // 3. 子進程 -- 用于讀取管道中的數(shù)據(jù)
        // 3.1 關(guān)閉不需要的文件描述符,子進程關(guān)寫pipefd[1]
        close(pipefd[1]);

        // 3.2 讀取數(shù)據(jù)并打印到標準輸出流
        // 如果寫慢讀快,那么讀要等寫
        // 如果寫快讀慢,那么等待管道被寫滿后,就不能再繼續(xù)寫
        char read_buffer[1024] = {0};    // 讀數(shù)據(jù)文件緩沖區(qū)
        while(true)
        {
            ssize_t sz = read(pipefd[0], read_buffer, SIZE - 1);   //數(shù)據(jù)讀取
            
            // 如果寫端退出,那么讀端read讀到0,讀端最終會退出
            // 如果讀端退出,OS會強制終止寫端進程
            if(sz > 0)    // 確實讀到了數(shù)據(jù)
            {
                read_buffer[sz] = '\0';
                std::cout << "Father# " << read_buffer << std::endl;
            }
            else if(sz == 0)   // 寫端關(guān)閉
            {
                std::cout << "Father quit, write end, read end!" << std::endl;
                break;
            }
            else // sz < 0
            {
                perror("read");
                break;
            }
        }

        close(pipefd[0]);
        exit(0);
    }

    // 3. 父進程代碼 -- 用于寫數(shù)據(jù)
    // 3.1 關(guān)閉不需要的文件描述符,父進程關(guān)讀pipdfd[0];
    close(pipefd[0]);

    // 3.2 向管道寫數(shù)據(jù)
    const char* msg = "I am father process, I am sending message";

    char send_buffer[SIZE] = {0};   // 寫數(shù)據(jù)緩沖區(qū)
    int count = 0;

    while(true)
    {
        snprintf(send_buffer, SIZE, "%s,%d", msg, ++count);
        write(pipefd[1], send_buffer, strlen(send_buffer));
        sleep(1);
    }

    close(pipefd[1]);
    return 0;
}

3.4?管道通信的訪問控制規(guī)則

  • 讀快寫慢:讀端需要阻塞等待寫端寫入數(shù)據(jù)后才能讀。
  • 寫快讀慢:管道被寫滿后就不能繼續(xù)寫入,需要等待數(shù)據(jù)被讀出。
  • 寫端關(guān)閉:讀端read讀到0,退出。
  • 讀端關(guān)閉:OS強制終止寫端進程。

3.5?管道通信的特點

  1. 管道(匿名管道),常用于具有親緣關(guān)系的進程的進程間通信。
  2. 管道通信存在訪問控制。
  3. 管道通信是一種面向字節(jié)流式的通信。--?面向流式的通信:可以多次寫入的內(nèi)容一次讀取,也可以一次寫入的內(nèi)容分多次讀取。
  4. 管道的本質(zhì)是文件(內(nèi)存級文件),文件的生命周期隨進程的結(jié)束而結(jié)束,因此進程結(jié)束時,管道關(guān)閉。
  5. 管道通信為單向通信,是半雙工通信的一種特殊形式。

半雙工通信:通信雙方在某一時刻,只能單獨進行寫或讀。(并不是說某一端只能進行寫或讀,而是不能寫和讀同時進行)

全雙工通信:通信雙方可以寫和讀同時進行。

四.?通過匿名管道實現(xiàn)進程池

4.1?進程池的概念

父進程創(chuàng)建N個子進程,按照一定的規(guī)則向子進程派發(fā)任務(wù),父進程只負責向子進程派發(fā)任務(wù),具體的任務(wù)由子進程來完成。

如果父進程將均衡的向每個子進程派發(fā)任務(wù),這種算法稱為單機版負載均衡。

Linux系統(tǒng)編程:采用管道的方式實現(xiàn)進程間通信,Linux系統(tǒng)和網(wǎng)絡(luò),linux,運維,服務(wù)器
圖4.1?進程池

4.2?進程池的模擬實現(xiàn)

采用rand隨機生成來決定選用哪個子進程來執(zhí)行任務(wù)。

task.hpp文件:父進程派發(fā)的任務(wù)

#ifndef __TASK_DEFINE_

#define __TASK_DEFINE_

#include <iostream>
#include <vector>
#include <functional>

typedef std::function<void()> func;    // 類型重定義

std::vector<func> trace_back;   // 回調(diào)函數(shù)
std::vector<std::string> desc;  // 任務(wù)編號及對應(yīng)說明

void execuleUrl()
{
    std::cout << "execuleUrl" << std::endl;
}

void save()
{
    std::cout << "save data" << std::endl;
}

void visitSQL()
{
    std::cout << "visit SQL" << std::endl;
}

void online()
{
    std::cout << "take online" << std::endl;
}

void load()
{
    trace_back.emplace_back(execuleUrl);
    desc.emplace_back("execuleUrl");

    trace_back.emplace_back(save);
    desc.emplace_back("save data");

    trace_back.emplace_back(visitSQL);
    desc.emplace_back("visit SQL");

    trace_back.emplace_back(online);
    desc.emplace_back("take online");
}

void show()
{
    int count = 0;

    for(const auto& msg : desc)
    {
        std::cout << count << ": " << msg << std::endl;
        ++count;
    }
}

int handlerSize()
{
    return trace_back.size();
}

#endif

PipePool.cc文件:進程池的實現(xiàn)源文件

#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "task.hpp"

#define PROCESS_NUM 5

void distributeTask(int who, int fd, uint32_t command)
{
    // 派發(fā)任務(wù)
    ssize_t n = write(fd, &command, sizeof(uint32_t));
    assert(n == sizeof(uint32_t));
}

uint32_t waitCommand(int fd, bool &quit)
{
    uint32_t command = 0;
    ssize_t n = read(fd, &command, sizeof(uint32_t));

    if (n == 0)
    {
        quit = true;
        return -1;
    }

    assert(n == sizeof(uint32_t));
    return command;
}

int main()
{
    load(); // 載入任務(wù)

    // 創(chuàng)建子進程
    int pipefd[2] = {0};
    std::vector<std::pair<pid_t, int>> slot; // 記錄子進程id以及寫端文件描述符

    for (int i = 0; i < PROCESS_NUM; ++i)
    {
        // 創(chuàng)建管道
        int ret = pipe(pipefd);
        if (ret == -1) // 管道創(chuàng)建失敗
        {
            perror("pipe");
            exit(1);
        }

        // 創(chuàng)建子進程
        pid_t id = fork();

        if (id < 0) // 如果子進程創(chuàng)建失敗
        {
            perror("fork");
            exit(2);
        }
        else if (id == 0) // 子進程代碼
        {
            // 子進程代碼
            // 關(guān)閉不需要的文件描述符
            close(pipefd[1]); // 子進程關(guān)寫

            while (true)
            {
                // 阻塞等待指令
                bool quit = false;
                uint32_t command = waitCommand(pipefd[0], quit);

                if (quit)
                {
                    std::cout << "write close, read also close!" << std::endl;
                    break;
                }

                if (command >= 0 && command < handlerSize())
                {
                    trace_back[command]();
                }
                else
                {
                    std::cerr << "choice wrong" << std::endl;
                }
            }

            close(pipefd[0]);
            exit(0);
        }

        // 父進程代碼
        close(pipefd[0]);                 // 關(guān)閉讀
        slot.emplace_back(id, pipefd[1]); // 將子進程id和對應(yīng)寫端文件描述符插入順序表
    }

    srand((unsigned int)time(NULL));

    // 父進程,開始派發(fā)任務(wù)
    int select = 0;

    while (true)
    {
        std::cout << "##############################" << std::endl;
        std::cout << "##   1. show    2. choice   ##" << std::endl;
        std::cout << "##############################" << std::endl;

        std::cout << "Please Select: > ";
        std::cin >> select;

        if (select == 1)
        {
            show();
        }
        else if (select == 2)
        {
            int choice = 0;
            std::cout << "Please chose task: > ";
            std::cin >> choice;

            int proc = rand() % PROCESS_NUM;                             // 選擇子進程完成任務(wù)
            distributeTask(slot[proc].first, slot[proc].second, choice); // 派發(fā)任務(wù)

            usleep(100000);
        }
        else
        {
            std::cerr << "choice error" << std::endl;
            break;
        }
    }

    // 父進程關(guān)閉寫端,子進程退出
    for (const auto &iter : slot)
    {
        close(iter.second);
    }

    // 父進程阻塞等待子進程退出
    for (const auto &iter : slot)
    {
        waitpid(iter.first, NULL, 0);
    }

    return 0;
}

五.?命名管道

5.1?命名管道的功能

一般意義上的管道(匿名管道)只能實現(xiàn)父子進程之間的通信,而命名管道可以實現(xiàn)不相關(guān)進程之間的進程間通信。

命名管道是一種特殊類型的文件,具有以下特性:

  1. 可以被打開,但不會將內(nèi)存中的數(shù)據(jù)刷新到磁盤。
  2. 具有屬于自己的名稱。
  3. 在系統(tǒng)中有唯一的路徑。

兩個不相關(guān)的進程,可以通過訪問同一管道文件,來實現(xiàn)進程間通信。

Linux系統(tǒng)編程:采用管道的方式實現(xiàn)進程間通信,Linux系統(tǒng)和網(wǎng)絡(luò),linux,運維,服務(wù)器
圖5.1?命名管道實現(xiàn)不相關(guān)進程之間的通信

5.2?命名管道的創(chuàng)建和使用

  • 通過指令創(chuàng)建管道文件:mkfifo [文件名]? ?
  • 刪除管道文件:unlink、rm均可
Linux系統(tǒng)編程:采用管道的方式實現(xiàn)進程間通信,Linux系統(tǒng)和網(wǎng)絡(luò),linux,運維,服務(wù)器
圖5.2?通過指令創(chuàng)建管道文件

同樣,C語言庫函數(shù)中也有mkfifo,其功能也是創(chuàng)建管道文件,mkfifo庫函數(shù)的信息如下:

  • 函數(shù)原型:int mkfifo(const char* pathname, mode_t mode);
  • 函數(shù)參數(shù):pathname為創(chuàng)建的管道文件名和路徑,mode為起始權(quán)限。
  • 返回值:創(chuàng)建成功返回0,失敗返回-1并設(shè)置全局錯誤碼。

也存在unlink庫函數(shù),用于刪除管道文件,原型為:int unlink(const char* pathname)

假設(shè)有兩個進程A和B,進程A以寫的方式打開管道文件,進程B以讀的方式打開管道文件,如果進程B先運行,需要等到進程A以寫的方式打開管道文件后,進程B才可以以讀打開的方式打開管道文件,否則,進程B要一直等待管道文件被以讀的方式打開。

代碼5.1通過命名管道,實現(xiàn)服務(wù)端進程(serve.exe)和客戶端進程(client.exe)之間的進程間通信,serve.cc創(chuàng)建管道文件,并以只讀方式打開管道文件,client.cc以只寫的方式打開管道文件,執(zhí)行代碼時先運行serve.exe,等待client.exe運行以只寫打開管道后,serve.exe才能執(zhí)行只讀打開管道文件的代碼,在client中輸入的信息,會顯示到serve中。

代碼5.1:命名管道實現(xiàn)不相關(guān)進程間的通信文章來源地址http://www.zghlxwxcb.cn/news/detail-650081.html

// log.hpp頭文件 -- 日志打印相關(guān)聲明和實現(xiàn)
// 日志操作
#include <iostream>
#include <string>
#include <ctime>

#ifndef __LOG_DEFINE_
#define __LOG_DEFINE_

#define DEBUG   0
#define NOTICE  1
#define WARNING 2
#define ERROR   3   

std::string msg[] = {
    "Debug",
    "Notice",
    "Waring",
    "Error"
};

std::ostream& log(const std::string& message, int level)
{
    std::cout << (unsigned int)time(NULL) << " | " << msg[level] << " | " << message << std::endl;
}

#endif


// common.hpp頭文件 -- 聲明宏,包含庫文件
#ifndef __COMMON_DEF_
#define __COMMON_DEF_

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

#include "log.hpp"

#define MODE 0666
#define SIZE 1024
#define PROCESS_NUM 3

std::string ipcPath = "fifo.ipc";

#endif


// serve.cc -- 服務(wù)端源文件代碼
#include "commom.hpp"

// 服務(wù)端函數(shù)
int main()
{
    // 1. 以只寫的方式打開管道文件
    int fd = open(ipcPath.c_str(), O_WRONLY);
    if(fd < 0)
    {
        perror("client open");
        exit(1);
    }

    // 2. 開始執(zhí)行進程間通信
    std::string send_buffer;   // 信息發(fā)送緩沖區(qū)

    while(true)
    {
        std::cout << "請輸入要發(fā)送的信息: > ";
        std::getline(std::cin, send_buffer);  // 逐行讀取信息
        write(fd, send_buffer.c_str(), send_buffer.size());   //寫數(shù)據(jù)
    }

    // 3. 關(guān)閉文件
    close(fd);
    return 0;
}


// client.cc -- 客戶端源文件代碼
#include "commom.hpp"

// 信息讀取函數(shù)
void GetMessage(int fd)
{
    char read_buffer[SIZE] = {0};    //讀數(shù)據(jù)緩沖區(qū)

    while(true)
    {
        ssize_t n = read(fd, read_buffer, SIZE - 1);    //從管道文件讀數(shù)據(jù)
        if(n > 0)
        {
            read_buffer[n] = '\0';
            std::cout << "[ " << getpid() << " ]  client say# " << read_buffer << std::endl;
        }
        else if(n == 0)
        {
            std::cout << "client quit, read end, serve quit too! " << std::endl;
            break;
        }
        else  // n < 0 -- 讀取出錯
        {
            perror("read");
            break;
        }
    }
}

int main()
{
    // 1. 創(chuàng)建命名管道文件
    if(mkfifo(ipcPath.c_str(), MODE) < 0)
    {
        perror("mkfifo");
        exit(1);
    }

    log("管道創(chuàng)建成功", DEBUG);

    // 2. 服務(wù)端以只讀方式打開文件
    int fd = open(ipcPath.c_str(), O_RDONLY);
    if(fd < 0)  //檢驗打開是否成功
    {
        perror("serve fopen");
        exit(2);
    }

    log("文件打開成功", DEBUG);

    // 3. 創(chuàng)建子進程,進行進程間通信(讀數(shù)據(jù))
    for(int i = 0; i < PROCESS_NUM; ++i)
    {
        pid_t id = fork();
        if(id == 0)   //子進程代碼
        {
            GetMessage(fd);   //信息讀取函數(shù)
            exit(0);
        }
    }

    // 4. 阻塞等待子進程退出
    for(int i = 0; i < PROCESS_NUM; ++i)
    {
        waitpid(-1, NULL, 0);
    }

    log("子進程全部退出", NOTICE);

    // 5. 關(guān)閉文件,刪除命名管道文件
    close(fd);
    unlink(ipcPath.c_str());

    log("文件關(guān)閉成功", NOTICE);
    // std::cout << "文件關(guān)閉成功" << std::endl;

    return 0;
}
Linux系統(tǒng)編程:采用管道的方式實現(xiàn)進程間通信,Linux系統(tǒng)和網(wǎng)絡(luò),linux,運維,服務(wù)器
圖5.3?代碼5.1運行情況

六.?總結(jié)

  • 實現(xiàn)進程間通信的方式有三種,分別為管道、System V、POSIX,其中System V用于本地單機進程間通信,POSIX用于網(wǎng)絡(luò)進程間通信。
  • 管道的本質(zhì)是內(nèi)存級文件,由OS內(nèi)核提供,管道一般用于具有親緣關(guān)系的進程間通信,管道通信為單向通信,是面向字節(jié)流式的通信,存在訪問控制,管道的生命周期隨進程的終止而終止。
  • 通過命名管道,可以實現(xiàn)不相關(guān)進程間的通信。

到了這里,關(guān)于Linux系統(tǒng)編程:采用管道的方式實現(xiàn)進程間通信的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

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

相關(guān)文章

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包