15.進程間通信(一)

一、進程間通信介紹

進程間通信目的:
數據傳輸:一個進程需要將它的數據發送給另?個進程
資源共享:多個進程之間共享同樣的資源。
通知事件:一個進程需要向另一個或一組進程發送消息,通知它(它們)發生了某種事件(如進程終止時要通知父進程)。
進程控制:有些進程希望完全控制另一個進程的執行(如Debug進程),此時控制進程希望能夠攔截另一個進程的所有陷入和異常,并能夠及時知道它的狀態改變。
怎么通信?
進程間通信本質:先讓不同的進程,看到同一份資源(內存)(然后才有通信的條件)
不能由任何一個進程提供,進程間數據隔離->OS提供系統調用->設計統一的通信接口。
什么是通信?

二、具體通信方式

1)基于文件的,管道通信

2)System V 本機通信

1.背景

基于已有的技術,直接進行通信。

2.原理

單獨設計了一個內存級的文件,管道(復用了文件管理的代碼)。

獨特的系統調用:

/* On all other architectures */

int pipe(int pipefd[2]);? //數組第一個參數是讀的fd,第二個參數的寫的fd。

返回值:成功返回0,失敗返回-1,錯誤碼被設置。

管道:通過創建子進程,子進程拷貝一份和父進程一樣的文件描述符表,指向同一個“文件”(管道),關閉相應的讀寫端,使得單向通信。

3.demo代碼,測試接口。

測試代碼如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>void ChildWrite(int wfd)
{std::cout << "子進程wfd:" << wfd << std::endl;char buff[1024];int cnt = 0;while (true){sleep(1);snprintf(buff, sizeof(buff), "第%d次:子進程寫入\n", cnt++);ssize_t ret = write(wfd, buff, strlen(buff));(void)ret;}
}void FatherRead(int rfd)
{std::cout << "父進程rfd:" << rfd << std::endl;char buff[1024];int cnt = 1;while (cnt--){sleep(5);ssize_t ret = read(rfd, buff, sizeof(buff) - 1);buff[ret] = 0;std::cout << buff << std::endl;if (ret == 0){std::cout << "寫關閉了,讀關閉" << std::endl;break;}}
}int main()
{// 1.創建管道int pipefd[2] = {0};int ret = pipe(pipefd);if (ret == -1)exit(1);// 2.創建子進程,子進程寫,父進程讀pid_t pid = fork();if (pid < 0)exit(1);else if (pid == 0){// 子進程close(pipefd[0]);ChildWrite(pipefd[1]);close(pipefd[1]);exit(0);}else{// 父進程close(pipefd[1]);FatherRead(pipefd[0]);close(pipefd[0]);int status;waitpid(pid, &status, 0);std::cout << "子進程 exit code:" << ((status >> 8) & 0xFF) << " exit signal:" << (status & 0x7F) << std::endl; }return 0;
}

5種特性(重點):

1)匿名管道,只能用來進行具有血緣關系的進程進行進程間通信(常用于父子)

2)管道文件,自帶同步機制

3)管道是面向字節流的。(怎么讀和怎么寫沒有必然關系)

4)管道是單向通信的。

屬于半雙工的一種特殊情況。

半雙工:任何時刻,一個發,一個收。

全雙工:任何時刻,可以同時收發(吵架)。

5)(管道)文件的生命周期是隨進程的(引用計數)。

4中通信情況:

1)寫慢,讀快?------ 讀端就要阻塞(進程)

2)寫快,讀慢 ------ 滿了的時候,寫端就要阻塞等待

3)寫關,讀繼續 ------ read讀到返回值為0,表示文件結尾。

4)寫繼續,讀關 ------ 寫端寫入無意義,OS不會做無意義的事情->OS會殺掉寫端進程->發送異常信號 13 SIGPIPE?

在小于pipe_buf時,管道的寫入被要求是原子性的(一次要寫全寫完)。

測試管道容量:一次寫一個字節,寫入測試信息。

4.基于匿名管道 --- 進程池?

原理:

父進程通過向指定的管道寫入的方式來向子進程發送對應的任務。

父進程的wfd關閉,子進程會讀到0,可以退出。

總體結構:

????????要對匿名管道進行管理,先描述在組織,要有對應的類Channel,記錄父進程的wfd和子進程的pid,在有對應管理多個類Channel的數據結構vector,對于匿名管道的管理就轉換成了對于vector的增刪查改。對于任務也做管理,與匿名管道管理類似。

邏輯:

? ? ? ? 父進程創建若干個子進程,讓子進程阻塞在read中,并死循環執行,子進程一直阻塞,等待父進程發送對應的任務碼,根據任務碼執行相應的任務。父進程采用輪詢的方式,確保能夠負載均衡,選擇子進程發送隨機的任務碼,即向對應的匿名管道內寫入任務碼。

易錯點:

回收時需注意,每次創建子進程時,子進程會繼承上一次父進程的wfd,導致第1個管道有n個引用,第2個管道有n-1個引用 ... 第n個管道有一個引用。

解決方法1:倒著關閉

解決方法2:隨便關閉。創建子進程后,子進程把之前從父進程繼承下來的wfd全部關閉。

完整代碼如下:

Processpool.hpp

#pragma once#include <vector>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp"class Channel
{
public:Channel(int wfd, int pid): _wfd(wfd), _pid(pid){}~Channel(){}int Wfd() const { return _wfd; }int Pid() const { return _pid; }void Close() const { close(_wfd); }void Wait() const { waitpid(_pid, nullptr, 0); }private:int _wfd; // 控制子進程int _pid; // 拿到子進程pid,方便回收
};class ChannelManager
{
public:ChannelManager() : _next(0){}// 為了保證負載均衡,采用輪詢的方式const Channel& SelectChannel(){const Channel& c = _channels[_next];// 選出下標++_next;_next %= _channels.size();return c;}void InsertChannel(int wfd, int pid){_channels.emplace_back(wfd, pid);}void CloseFd() const{for(const auto &e : _channels){e.Close();}}void CloseChannels() const{// 2.正著關,創建子進程時就關閉從父進程繼承下來的wfdfor (size_t i = 0; i < _channels.size(); i++){// 關閉父進程的寫fd,讓子進程讀到0個字節退出。_channels[i].Close();_channels[i].Wait();printf("等待成功,回收了子進程:%d\n", _channels[i].Pid());}// // 1.倒著關閉// for (int i = _channels.size() - 1; i >= 0; i--)// {//     // 關閉父進程的寫fd,讓子進程讀到0個字節退出。//     _channels[i].Close();//     _channels[i].Wait();//     printf("等待成功,回收了子進程:%d\n", _channels[i].Pid());// }}~ChannelManager(){}private:std::vector<Channel> _channels;int _next;
};class ProcessPool
{
public:ProcessPool(){}~ProcessPool(){}void ChildRead(int rfd) const{int taskcode = 0;while (true){ssize_t ret = read(rfd, &taskcode, sizeof(taskcode));// 父進程寫端關閉了,子進程要結束if (ret == 0){std::cout << "父進程寫端關閉,子進程:" << getpid() << "退出" << std::endl;break;}// 讀到的不是4字節,丟棄,重新讀if (ret != sizeof(taskcode)){printf("丟棄\n");return;}// 執行相應任務printf("進程:%d ExcuteTask開始,ret:%d,taskcode:%d\n", getpid(), (int)ret, taskcode);_tm.ExecuteTask(taskcode);}}void Create(int num){for (int i = 0; i < num; i++){// 1.創建管道int pipefd[2] = {0};int ret = pipe(pipefd);if (ret != 0)exit(1);// 2.創建子進程pid_t pid = fork();// 3.關閉父讀,子寫if (pid < 0)exit(1);else if (pid == 0){// 關掉從父進程繼承下來的wfd_cm.CloseFd();// 子進程關閉寫close(pipefd[1]);// 子進程工作printf("ChildRead開始,進程為%d\n", getpid());ChildRead(pipefd[0]);close(pipefd[0]);// 子進程完成工作,退出exit(0);}// 父進程關閉讀close(pipefd[0]);// emplace_back直接構造,插入到_channels_cm.InsertChannel(pipefd[1], pid);// 循環num次}}// 選擇一個子進程,隨機發送任務void Run(){// 1.選擇一個子進程const Channel &c = _cm.SelectChannel();printf("挑選的子進程為:%d\n", c.Pid());// 2.獲取任務碼int taskcode = _tm.TaskCode();// 3.發送任務碼給子進程,子進程執行(寫給子進程)printf("父進程:%d 寫入taskcode:%d\n", getpid(), taskcode);ssize_t ret = write(c.Wfd(), &taskcode, sizeof(taskcode));}void Close() const{_cm.CloseChannels();}private:TaskManager _tm;ChannelManager _cm;
};

task.hpp

#pragma once#include <iostream>
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <ctime>void Open()
{std::cout << "這是一個打開的任務" << std::endl;
}
void Download()
{std::cout << "這是一個下載的任務" << std::endl;
}
void Upload()
{std::cout << "這是一個上傳的任務" << std::endl;
}typedef void (*task_t)();
class Task
{
public:Task(task_t task) : _task(task){}~Task(){}void operator()() const{_task();}task_t getTask() const {return _task;}
private:task_t _task;
};class TaskManager
{
public:TaskManager(){// 設置種子數,采用隨機任務形式srand((unsigned)time(nullptr));Register(Open);Register(Download);Register(Upload);}// 注冊任務void Register(task_t t){_tasks.emplace_back(t);}// 返回任務碼int TaskCode() const{int ret = rand() % _tasks.size();return ret;}void ExecuteTask(int taskcode) const{if (taskcode < 0 || taskcode >= _tasks.size()){std::cout << "讀取的任務碼無效" << std::endl;return;}printf("進程:%d執行任務,taskcode為:%d\n",getpid(),taskcode);_tasks[taskcode]();printf("任務:%d 執行完畢\n",taskcode);}~TaskManager(){}private:std::vector<Task> _tasks;
};

main.cc

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include "ProcessPool.hpp"int main()
{pid_t pid = fork();if (pid == 0){ProcessPool pool;pool.Create(5);int cnt = 5;while (cnt--){sleep(1);pool.Run();}sleep(10);pool.Close();exit(0);}int status;waitpid(pid, &status, 0);std::cout << "main exit code:" << ((status >> 8) & 0xFF) << " signal code:" << (status & 0x7F) << std::endl;return 0;
}

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

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

相關文章

05-jenkins學習之旅-vue前項目部署實踐

1、創建被管理項目 2、構建流程說明 jenkins其實就是將服務部署拆分成了&#xff1a; 1、拉取代碼(git) 2、打包編譯(npm install) 3、自定義腳本(dist復制、執行啟動腳本) 4、部署成功后的一些通知等 3、demo配置 3.1、General 3.2 源碼管理 添加用戶名密碼方式如下圖 3.2…

服務器中分布式存儲數據技術都包含哪些內容?

隨著大數據時代的到來&#xff0c;企業和組織對于服務器的存儲要求也在不斷地增高&#xff0c;傳統的存儲架構已經無法滿足一些大規模的數據存儲和處理需求&#xff0c;分布式存儲技術應運而生&#xff0c;成為了大數據存儲的重要基礎設施&#xff0c;下面&#xff0c;就來介紹…

從比分滾動到數據革命:體育數據如何重構我們的觀賽體驗?

當凌晨三點的歐冠決賽與鬧鐘沖突時&#xff0c;當世界杯小組賽因時差難以全程跟進時&#xff0c;當代體育迷早已不再依賴電視直播 —— 打開手機里的比分網&#xff0c;實時跳動的體育大數據正構建著全新的觀賽宇宙。這些曾經被視為 "輔助工具" 的平臺&#xff0c;如…

vue2使用element中多選組件el-checkbox-group,數據與UI更新不同步

問題描述 使用element多選checkbox組件&#xff0c;點擊勾選取消勾選&#xff0c;視圖未變化&#xff0c;再次點擊表單其他元素&#xff0c;多選組件勾選狀態發生變化&#xff0c;視圖和數據未同步 第一次嘗試&#xff1a;再el-checkbox-group多選父組件上增加點擊事件&…

CodeTop之LRU緩存

題目鏈接 146. LRU 緩存 - 力扣&#xff08;LeetCode&#xff09; 題目解析 算法原理 我們使用雙向鏈表哈希表的形式來模擬緩存機制 首先我們要自己實現一個雙鏈表, 自己寫一個內部類, 這個內部類記錄了key,value,prev,next(前驅和后繼), 后續我們就通過這個內部類來構造雙…

PyQt學習系列11-綜合項目:多語言文件管理器

PyQt學習系列筆記&#xff08;Python Qt框架&#xff09; 第十一課&#xff1a;綜合項目 - 多語言文件管理器 &#xff08;原課程規劃中的第十五課&#xff0c;按用戶要求調整為第十一課&#xff09; 課程目標 綜合運用PyQt框架開發一個支持多語言的文件管理器實現以下核心功…

【Ubuntu修改串口延時(Latency Timer)為1毫秒(設備拔插或系統重啟后自動生效)】

Ubuntu修改串口延時Latency Timer為1毫秒-設備拔插或系統重啟后自動生效 在Ubuntu系統中&#xff0c;串口設備的延時參數(latency_timer)可以通過udev規則永久修改。以下是完整步驟&#xff1a; 創建udev規則文件 sudo vim /etc/udev/rules.d/99-ftdi-low-latency.rules添加以…

OpenCV CUDA模塊圖像處理------顏色空間處理之GPU 上交換圖像的通道順序函數swapChannels()

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 該函數用于在 GPU 上交換圖像的通道順序&#xff08;例如將 BGR 圖像轉為 RGB&#xff09;。 它適用于多通道圖像&#xff08;如 3 通道或 4 通道…

Linux Ubuntu24.04配置安裝MySQL8.4.5高可用集群主從復制!

MySQL 主從復制&#xff08;Replication&#xff09;是實現數據高可用、讀寫分離及異地容災的核心機制之一。主庫寫、從庫讀&#xff0c;提升并發能力&#xff1b;讀寫分離&#xff0c;減輕主庫壓力。 本地 windows 系統有一個Linux Ubuntu子系統&#xff0c;版本為Ubuntu 24.…

R基于邏輯回歸模型實現心臟病檢測及SHAP值解釋項目實戰

說明&#xff1a;這是一個機器學習實戰項目&#xff08;附帶數據代碼文檔視頻講解&#xff09;&#xff0c;如需數據代碼文檔視頻講解可以直接到文章最后關注獲取。 1.項目背景 心血管疾病是全球范圍內導致死亡的主要原因之一&#xff0c;每年有數百萬人因此失去生命。在眾多的…

嵌入式學習筆記 -函數嵌套時以及異常響應時,LR使用的具體過程

函數嵌套時以及異常響應時&#xff0c;寄存器LR的作用存在顯著區別&#xff0c;理解這個問題對于理解freeRTOS底層代碼的實現大有幫助&#xff0c;具體使用過程如下&#xff1a; 一 函數嵌套時的LR使用的具體過程 在ARM架構(特別是M0處理器)中&#xff0c;函數嵌套調用時LR(L…

Java String函數的使用

文章目錄 String字符串比較字符串查找轉化字符串替換字符串拆分字符串截取&#xff08;常用&#xff09;字符串的不可變性 String str本來是字符串常量的引用&#xff0c;應該打印地址&#xff0c;但是編譯器重寫了toString方法&#xff0c;所以打印hello String 的構造方法 …

Oracle 11G RAC重啟系統異常

vmware安裝centos7環境部署Oracle RAC (11.2.0.4) 部署時所有資源情況都是正常的&#xff0c;關機重啟虛擬機后集群資源狀態異常&#xff0c;請教CSDN大佬 – 部署規劃 域名地址備注rac16192.168.31.16rac17192.168.31.17rac16vip192.168.31.26viprac17vip192.168.31.27vip…

吉林省CCPC與全國邀請賽(東北地區賽)游記

總述&#xff1a; 本次賽段共獲得一銀&#xff08;吉林省賽&#xff09;、一銅&#xff08;東北地區賽&#xff09;、一鐵&#xff08;全國邀請賽的成績&#xff09;。總體成績跟校內賽的情況相比隊伍狀態與發揮水準都有提升&#xff09;&#xff0c;但也體現出很多不足&#x…

「Python教案」循環語句的使用

課程目標 1&#xff0e;知識目標 能使用for循環和while循環設計程序。能使用循環控制語句&#xff0c;break、continue、else設計程序。能使用循環實際問題。 2&#xff0e;能力目標 能根據需求合適的選擇循環結構。能對嵌套循環代碼進行調試和優化。能利用循環語句設計&am…

OpenCV---findCountours

一、基本概念與用途 findContours是OpenCV中用于在二值圖像中查找輪廓的核心函數。輪廓作為連續的點集&#xff0c;能夠精確勾勒出物體的邊界&#xff0c;廣泛應用于目標檢測、形狀分析、圖像分割等領域。 函數核心價值 目標檢測&#xff1a;通過輪廓定位圖像中的物體&#…

20250523-BUG:無法加載“GameLib/Framework.h“頭文件(已解決)

BUG&#xff1a;無法加載"GameLib/Framework.h"頭文件&#xff08;已解決&#xff09; 最近在打開新的C項目時報了這個錯&#xff0c;我是按照以下步驟來排除的BUG&#xff0c;希望對您有所幫助~ 檢查【C/C】-【附加包含目錄】中的路徑有無問題&#xff0c;一般需要加…

商品條形碼查詢接口如何用C#進行調用?

一、什么是商品條碼查詢接口&#xff1f; 1974年6月26日&#xff0c;美國俄亥俄州的一家超市首次使用商品條碼完成結算&#xff0c;標志著商品條碼正式進入商業應用領域。這項技術通過自動識別和數據采集&#xff0c;極大提升了零售行業的作業效率&#xff0c;減少了人工錄入錯…

SD07_NVM的安裝及相關操作

以下是在 Windows 系統 上使用 NVM&#xff08;Node Version Manager&#xff09; 管理多個 Node.js 版本的詳細步驟&#xff0c;從零開始操作&#xff1a; 一、準備工作 卸載舊版 Node.js 打開 控制面板 → 程序和功能&#xff0c;找到已安裝的 Node.js 和 npm&#xff0c;徹底…

OSI 深度安全防御體系架構深度剖析

文章目錄 前言什么是 OSI 深度安全防御體系架構各層的安全防御措施物理層數據鏈路層網絡層傳輸層會話層表示層應用層 OSI 深度安全防御體系架構的優勢全方位防護深度防御靈活性和可擴展性 總結 前言 大家好&#xff0c;我是沛哥兒。今天咱們來深入探討一下 OSI 深度安全防御體…