進程間通信1(匿名管道)Linux

1 進程間通信的必要性

首先要明確進程間是相互獨立的(獨享一份虛擬地址空間,頁表,資源),那怎么樣才能使得兩個進程間實現資源的發送?所以,兩個進程一定需要看到同一份資源,并且?個進程需要向另?個或?組進程發送消息,通知它(它們)發生了某種事件(如進程終止時要通知父進程)。

進程間通信的目的:

1.數據傳輸

2.資源共享

3.通知事件(也就是?個進程向另?個或?組進程發送消息)

4.進程控制(一些進程希望完全控制另?個進程的執行)

1.1 進程間通信分類

管道:(基于文件的通信方法)

1.匿名管道pipe

2.命名管道

System V IPC:(單獨設計的通信模塊)

System V 消息隊列?

System V 共享內存

System V 信號量

?POSIX IPC:(網絡間進程通信)

消息隊列

共享內存

信號量

互斥量

條件變量

讀寫鎖

2 管道

管道是從?個進程連接到另?個進程的?個數據流。

管道的本質是一個基于文件系統的一個內存級的單向通信的文件,主要用于進程間通信(IPC)。

所以管道其實也是文件,在前面講的文件系統中,那個管道文件是不是也要創建,要打開,要有對應的路徑解析,要有對應的inode,那么文件使用的接口(write,read等管道也是可以直接使用的),但是這個文件不需要和磁盤進行IO,只需要存在內核中,所以就沒有路徑,沒有文件名,是內核中模擬的一個文件(所以叫匿名管道),創建一個緩沖區,利用緩沖區實現兩個進程看到同一份資源。

3 匿名管道

系統調用:

int pipe(int fd[2]);

這里fd是一個輸出型參數,fd所對應的也就是之前講的文件描述符,fd[0]表示讀端, fd[1]表示寫端,

成功返回0,失敗返回錯誤代碼。

3.1 用fork來共享管道原理

父進程創建管道后,創建子進程,子進程會拷貝父進程的資源,但是文件本身并未拷貝,而是訪問該文件的文件描述符,所對應的地址是同一個文件,這時父進程關閉讀端,子進程關閉寫端,(這里關閉讀寫端的操作可以不做也能使用,但是還是最好做,以防誤操作,畢竟是要實現單向通信)這樣父進程就可以向管道中寫入,子進程就可以從管道中讀取文件。

大致邏輯:

給出一個代碼例子:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>int main(int argc, char *argv[])
{int pipefd[2];if (pipe(pipefd) == -1){perror("pipe");}pid_t pid;pid = fork();if (pid == -1){perror("fork");}  if (pid == 0) {close(pipefd[0]);write(pipefd[1], "hello", 5);close(pipefd[1]);exit(0);}close(pipefd[1]);char buf[10] = {0};read(pipefd[0], buf, 10);printf("buf=%s\n", buf);return 0;
}

這里就是通過子進程向管道寫入“hello”,父進程讀取并打印。

3.2 管道讀寫規則

當沒有數據可讀時:

O_NONBLOCK disable:read調?阻塞,即進程暫停執行,?直等到有數據來到為止。

O_NONBLOCK enable:read調用返回-1,errno值為EAGAIN。

當管道滿的時候 :

O_NONBLOCK disable:write調?阻塞,直到有進程讀?數據?

O_NONBLOCK enable:調用返回-1,errno值為EAGAIN

如果所有管道寫端對應的文件描述符被關閉,則read返回0

如果所有管道讀端對應的文件描述符被關閉,則write操作會產生信號SIGPIPE,進而可能導致 write進程退出。

3.3?管道讀寫的幾種情況

情況一:管道里沒有數據,讀端正常

這時讀端就會阻塞,等待寫端寫入。

情況二:讀端不讀取,寫端一直寫

緩沖區寫滿了,就不會再寫入了。

情況三:寫端關閉(不寫),讀端正常

read之后,就會返回0,表示讀到文件結尾。

情況四:讀端關閉,寫端正常

OS直接殺掉該進程。(讀端關閉了,寫入就沒有意義了,OS不會做浪費時間和空間的事,所以直接殺掉該進程)

3.4 管道的特點

只能用于具有共同祖先的進程(具有親緣關系的進程)之間進行通信;通常,?個管道由?個進 程創建,然后該進程調用fork,此后父,子進程之間就可應用該管道。

管道提供流式服務

進程退出,管道釋放,所以管道的生命周期隨進程

內核會對管道操作進行同步與互斥

管道是半雙工的,數據只能向?個方向流動;需要雙方通信時,需要建立起兩個管道

3.5 創建進程池處理任務

進程池是用父進程創建一批子進程,通過父進程向管道里面寫入信息通知對應的進程完成對應的任務。

創建函數:

using callback_t =std::function<void(int fd)>;
bool InitProcessPool(callback_t cb){for (int i = 0; i < _processnum; i++){int pipefd[2] = {0};int n = pipe(pipefd);if (n < 0){perror("pipe");return false;}pid_t id = fork();if (id < 0){perror("fork");return false;}else if (id == 0){close(pipefd[1]);cb(pipefd[0]);exit(0);}close(pipefd[0]);std::string name = "channel-" + std::to_string(i);_channels.emplace_back(pipefd[1], name, id);}return true;}

通過一個數組將這一批子進程(管道寫端)進行(channel)類的管理,這里的cb是父進程通知子進程后的處理對應任務的函數。

channel類:

class channel
{
public:channel(){}channel(int wfd, std::string name, pid_t id): _wfd(wfd), _name(name), _sub_target(id){}int fd() { return _wfd; }std::string name() { return _name; }pid_t target() { return _sub_target; }void Close(){close(_wfd);}void Wait(){pid_t rid = waitpid(_sub_target, nullptr, 0);(void)rid;}~channel(){}
private:int _wfd;std::string _name;pid_t _sub_target;
};

成員包括寫端的文件描述符,管道名稱,對應pid。以及關閉寫端的函數,回收子進程的函數。

任務處理函數:

這里通過一批打印函數來模擬任務的調用

#pragma once#include <iostream>
#include <string>
#include <vector>
#include <functional>
// 4種任務
// task_t[4];using task_t = std::function<void()>;void Download()
{std::cout << "我是一個downlowd任務" << std::endl;
}void MySql()
{std::cout << "我是一個 MySQL 任務" << std::endl;
}void Sync()
{std::cout << "我是一個數據刷新同步的任務" << std::endl;
}void Log()
{std::cout << "我是一個日志保存任務" << std::endl;
}std::vector<task_t> tasks;class Init
{
public:Init(){tasks.push_back(Download);tasks.push_back(MySql);tasks.push_back(Sync);tasks.push_back(Log);}
};Init ginit;
while(true){int code = 0;//std::cout << "子進程阻塞: " << getpid() << std::endl;ssize_t n = read(fd, &code, sizeof(code));if(n == sizeof(code)) // 任務碼{std::cout << "子進程被喚醒: " << getpid() << std::endl;if(code >= 0 && code < tasks.size()){tasks[code]();}else{std::cerr << "父進程給我的任務碼是不對的: " << code << std::endl;}}else if(n == 0){std::cout << "子進程應該退出了: " << getpid() << std::endl;break;}else{std::cerr << "read fd: " << fd << ", error" << std::endl;break;}}

讀取成功進行對應任務處理,為0說明寫端關閉了,子進程可以退出了。

控制發送任務的函數:

void PollingCtrlSubProcess(int count){if (count < 0)return;int index = 0;while (count){CtrlSubProcessHelper(index);count--;}}void CtrlSubProcessHelper(int &index){// 1. 選擇一個通道(進程)int who = index;index+=rand();index %= _channels.size();// 2. 選擇一個任務,隨機int x = rand() % tasks.size(); // [0, 3]// 3. 任務推送給子進程std::cout << "選擇信道: " << _channels[who].name() << ", subtarget : " <<                                      _channels[who].target() << std::endl;write(_channels[who].fd(), &x, sizeof(x));sleep(1);}

count表示需要處理的任務個數。

回收函數和回收子進程:

 void WaitSubProcesses(){for (auto &c : _channels){c.Close();c.Wait();}}

注意這里關閉管道是并沒有關閉完的,是有問題的?

注意創建子進程時,會拷貝父進程的資源,創建第一個子進程時,父進程關閉讀端,第一個子進程會關閉寫端,再創建一個新的子進程,會繼承父進程的寫端,子進程關閉對應的寫端(但是關閉的是第二次創建管道的寫端,而第一次的寫端被這個新的子進程繼承下來了),所以往后再創建子進程,也會發送同樣的問題,所以需要再每次子進程關閉寫端時,需要將之前繼承下來的寫端也關閉掉。

加入這段代碼:

for(auto&e:_channels)
{std::cout  << e.fd() << " ";e.Close();
}

這里也是對進程池進行了封裝:

processpool.hpp

#pragma once
#include"task.hpp"
#include <iostream>
#include<functional>
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <sys/wait.h>
#include <vector>const int gprocess_num = 5;
using callback_t =std::function<void(int fd)>;class channel
{
public:channel(){}channel(int wfd, std::string name, pid_t id): _wfd(wfd), _name(name), _sub_target(id){}int fd() { return _wfd; }std::string name() { return _name; }pid_t target() { return _sub_target; }void Close(){close(_wfd);}void Wait(){pid_t rid = waitpid(_sub_target, nullptr, 0);(void)rid;}~channel(){}
private:int _wfd;std::string _name;pid_t _sub_target;
};class Processpool
{
private:void CtrlSubProcessHelper(int &index){// 1. 選擇一個通道(進程)int who = index;index++;index %= _channels.size();// 2. 選擇一個任務,隨機int x = rand() % tasks.size(); // [0, 3]// 3. 任務推送給子進程std::cout << "選擇信道: " << _channels[who].name() << ", subtarget : " << _channels[who].target() << std::endl;write(_channels[who].fd(), &x, sizeof(x));sleep(1);}
public:Processpool(int num=gprocess_num) : _processnum(num){srand(time(0));}bool InitProcessPool(callback_t cb){for (int i = 0; i < _processnum; i++){int pipefd[2] = {0};int n = pipe(pipefd);if (n < 0){perror("pipe");return false;}pid_t id = fork();if (id < 0){perror("fork");return false;}else if (id == 0){std::cout << "進程:" << getpid() << ", 關閉了: ";for(auto&e:_channels)//處理關閉之前的讀端{std::cout  << e.fd() << " ";e.Close();}std::cout<<std::endl;close(pipefd[1]);cb(pipefd[0]);exit(0);}sleep(1);close(pipefd[0]);std::string name = "channel-" + std::to_string(i);_channels.emplace_back(pipefd[1], name, id);}return true;}void PollingCtrlSubProcess(){int index = 0;while (true){CtrlSubProcessHelper(index);}}void PollingCtrlSubProcess(int count){if (count < 0)return;int index = 0;while (count){CtrlSubProcessHelper(index);count--;}}void WaitSubProcesses(){for (auto &c : _channels){c.Close();c.Wait();}}
private:std::vector<channel> _channels;int _processnum;
};

main.cc

#include"processpool.hpp"
#include<vector>int main()
{Processpool pool(5);pool.InitProcessPool([](int fd){while(true){int code = 0;//std::cout << "子進程阻塞: " << getpid() << std::endl;ssize_t n = read(fd, &code, sizeof(code));if(n == sizeof(code)) // 任務碼{std::cout << "子進程被喚醒: " << getpid() << std::endl;if(code >= 0 && code < tasks.size()){tasks[code]();}else{std::cerr << "父進程給我的任務碼是不對的: " << code << std::endl;}}else if(n == 0){std::cout << "子進程應該退出了: " << getpid() << std::endl;break;}else{std::cerr << "read fd: " << fd << ", error" << std::endl;break;}}});sleep(5);// 3. 控制進程池pool.PollingCtrlSubProcess(10);// 4. 結束線程池pool.WaitSubProcesses();std::cout << "父進程控制子進程完成,父進程結束" << std::endl;}

task.hpp

#pragma once#include <iostream>
#include <string>
#include <vector>
#include <functional>
// 4種任務
// task_t[4];using task_t = std::function<void()>;void Download()
{std::cout << "我是一個downlowd任務" << std::endl;
}void MySql()
{std::cout << "我是一個 MySQL 任務" << std::endl;
}void Sync()
{std::cout << "我是一個數據刷新同步的任務" << std::endl;
}void Log()
{std::cout << "我是一個日志保存任務" << std::endl;
}std::vector<task_t> tasks;class Init
{
public:Init(){tasks.push_back(Download);tasks.push_back(MySql);tasks.push_back(Sync);tasks.push_back(Log);}
};Init ginit;

運行結果:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/87614.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/87614.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/87614.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CAN2.0、DoIP、CAN-FD汽車協議詳解與應用

一、CAN2.0 協議詳解與應用示例 1. 技術原理與特性 協議架構&#xff1a;基于 ISO 11898 標準&#xff0c;采用載波監聽多路訪問 / 沖突檢測&#xff08;CSMA/CD&#xff09;機制&#xff0c;支持 11 位&#xff08;CAN2.0A&#xff09;或 29 位&#xff08;CAN2.0B&#xff…

使用nvm管理npm和pnpm

1.使用nvm管理npm // 查看nvm版本 nvm -v // 查看可安裝的 node 版本 nvm ls-remote // 安裝指定 node 版本 nvm install 24.0.0 // 查看當前已安裝的 node 版本及當前使用的版本 nvm list // 使用某個版本 node nvm use 24.0.0 // 卸載指定 node 版本 nvm uninstall 16.20.1…

YOLO11+QT6+Opencv+C++訓練加載模型全過程講解

實現效果&#xff1a; Yolov11環境搭建&#xff08;搭建好的可以直接跳過&#xff09; 最好使用Anconda進行包管理&#xff0c;安裝可參考【文章】。下面簡單過一下如何快速部署環境。如果搭建過或可以參考其他文章可以跳過Yolo11環境搭建這一章節。總體來說Yolov11環境搭建越…

Python 腳本,用于將 PDF 文件高質量地轉換為 PNG 圖像

import os import fitz # PyMuPDF from PIL import Image import argparse import logging from tqdm import tqdm# 配置日志 logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) logger logging.getLogger(PDF2PNG)def convert_pdf_…

【CUDA GPU 支持安裝全攻略】PyTorch 深度學習開發者指南

PyTorch 的 CUDA GPU 支持 安裝五條鐵律&#xff08;最新版 2025 修訂&#xff09;&#xff08;適用于所有用戶&#xff09;-CSDN博客 是否需要預先安裝 CUDA Toolkit&#xff1f;——按使用場景分級推薦及進階說明-CSDN博客 “100% 成功的 PyTorch CUDA GPU 支持” 安裝攻略…

Cyberith 運動模擬器Virtualizer2:提升虛擬現實沉浸體驗

奧地利Cyberith公司是一家專注于虛擬現實&#xff08;VR&#xff09;互動解決方案的創新型科技企業&#xff0c;以其研發的Virtualizer虛擬現實步態模擬設備而聞名。該公司的核心技術體現在其設計和制造的全方位跑步機式VR交互平臺上&#xff0c;使得用戶能夠在虛擬環境中實現自…

常見的數據處理方法有哪些?ETL中的數據處理怎么完成

在數字化轉型縱深推進的背景下&#xff0c;數據作為新型生產要素已成為驅動企業戰略決策、科研創新及智能化運營的核心戰略資產。數據治理價值鏈中的處理環節作為關鍵價值節點&#xff0c;其本質是通過系統化處理流程將原始觀測數據轉化為結構化知識產物&#xff0c;以支撐預測…

WHAT - 為甲方做一個官網(二)- 快速版

文章目錄 一、明確需求優先級&#xff08;快速決策&#xff09;二、推薦零代碼/低代碼工具&#xff08;附對比&#xff09;方案1&#xff1a;低代碼建站平臺&#xff08;適合無技術用戶&#xff0c;拖拽式操作&#xff09;方案2&#xff1a;CMS系統&#xff08;適合內容更新頻繁…

音視頻之H.264視頻編碼傳輸及其在移動通信中的應用

系列文章&#xff1a; 1、音視頻之視頻壓縮技術及數字視頻綜述 2、音視頻之視頻壓縮編碼的基本原理 3、音視頻之H.264/AVC編碼器原理 4、音視頻之H.264的句法和語義 5、音視頻之H.264/AVC解碼器的原理和實現 6、音視頻之H.264視頻編碼傳輸及其在移動通信中的應用 7、音視…

C#語言入門-task2 :C# 語言的基本語法結構

下面從四個方面對C#的基本語法進行簡單介紹&#xff1a; 1. 數據類型 C#的類型可分為值類型和引用類型。值類型變量直接存儲數據&#xff0c;引用類型變量則存儲對象的引用。 值類型&#xff1a;涵蓋整數類型&#xff08;像int、long&#xff09;、浮點類型&#xff08;例如…

c#筆記之類的常量、字段和屬性

學習內容: 一、字段 字段是為了對象或者類型存儲數據的,可以表達一個對象或者類型的狀態;也叫做成員變量;注意字段是在類里面聲明的;在方法里聲明的是局部變量; 1.1實例字段 用來表示每個實例的狀態;比如一個students類;要了解一個學生一般看名字和成績;所以名字和…

Linux 常用命令(入門)

Linux 常用命令 一、Linux 命令基礎 (一)命令格式 Linux 命令的一般格式為:command [-options] [parameter1] … 。其中,command 是命令名,通常是相應功能的英文單詞或其縮寫;[-options] 是選項,用于對命令進行控制,可省略;parameter1 … 是傳給命令的參數,可以是…

CppCon 2016 學習:Parallelism in Modern C++

這段介紹的是 HPX (High Performance ParalleX)&#xff0c;一個現代C的通用并行運行時系統&#xff0c;重點包括&#xff1a; 通用性&#xff1a;適用于各種規模的應用&#xff0c;從小型到超大規模分布式系統。統一標準API&#xff1a;符合C標準&#xff0c;方便編寫異步、并…

機器學習監督學習實戰七:文本卷積神經網絡TextCNN對中文短文本分類(15類)

本文介紹了一個基于TextCNN模型的文本分類項目&#xff0c;使用今日頭條新聞數據集進行訓練和評估。項目包括數據獲取、預處理、模型訓練、評估測試等環節。數據預處理涉及清洗文本、中文分詞、去除停用詞、構建詞匯表和向量化等步驟。TextCNN模型通過卷積層和池化層提取文本特…

iot-dc3 項目Bug修復保姆喂奶級教程

一.Uncaught (in promise) ReferenceError: TinyArea is not defined 1.觸發場景 前端設備模塊,點擊關聯模板、關聯位號、設備數據,無反應,一直切不過去,沒有報錯通知,F12查看控制臺報錯如下: 2.引起原因 前端導入的庫為"@antv/g2": "^5.3.0",在 P…

Spring Boot + MyBatis Plus + SpringAI + Vue 畢設項目開發全解析(源碼)

前言 前些天發現了一個巨牛的人工智能免費學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到網站 Spring Boot MyBatis Plus SpringAI Vue 畢設項目開發全解析 目錄 一、項目概述與技術選型 項目背景與需求分析技術棧選擇…

Vitess數據庫部署與運維深度指南:構建可伸縮、高可用與安全的云原生數據庫

摘要 Vitess是一個為MySQL和MariaDB設計的云原生、水平可伸縮的分布式數據庫系統&#xff0c;它通過分片&#xff08;sharding&#xff09;實現無限擴展&#xff0c;同時保持對應用程序的透明性&#xff0c;使其無需感知底層數據分布。該項目于2019年從云原生計算基金會&#…

SpringAI+DeepSeek大模型應用開發——6基于MongDB持久化對話

持久化對話 默認情況下&#xff0c;聊天記憶存儲在內存中ChatMemory chatMemory new InMemoryChatMemory()。 如果需要持久化存儲&#xff0c;可以實現一個自定義的聊天記憶存儲類&#xff0c;以便將聊天消息存儲在你選擇的任何持久化存儲介質中。 MongoDB 文檔型數據庫&…

Mac電腦-音視頻剪輯編輯-Final Cut Pro X(fcpx)

Final Cut Pro Mac是一款專業的視頻剪輯工具&#xff0c;專為蘋果用戶設計。 它具備強大的視頻剪輯、音軌、圖形特效和調色功能&#xff0c;支持整片輸出&#xff0c;提升創作效率。 經過Apple芯片優化&#xff0c;利用Metal引擎動力&#xff0c;可處理更復雜的項目&#xff…

不同程度多徑效應影響下的無線通信網絡電磁信號仿真數據生成程序

生成.mat數據&#xff1a; %創建時間&#xff1a;2025年6月19日 %zhouzhichao %遍歷生成不同程度多徑效應影響的無線通信網絡拓撲推理數據用于測試close all clearsnr 40; n 30;dataset_n 100;for bias 0.1:0.1:0.9nodes_P ones(n,1);Sampling_M 3000;%獲取一幀信號及對…