泰安網(wǎng)絡(luò)教育肇慶seo優(yōu)化
文章目錄
- 前言
- 一、匿名管道
- 1.管道原理
- 2.管道的四種情況
- 3.管道的特點(diǎn)
- 二、命名管道
- 1. 特點(diǎn)
- 2.創(chuàng)建命名管道
- 1.在命令行上
- 2.在程序中
- 3.一個(gè)程序執(zhí)行打開管道并不會(huì)真正打卡
- 三、進(jìn)程池簡(jiǎn)易實(shí)現(xiàn)
- 1.makefile
- 2.Task.hpp
- 3.ProcessPool.cpp
前言
一、匿名管道
#include <unistd.h>
功能:創(chuàng)建一無名管道
原型
int pipe(int fd[2]);
參數(shù):
fd:文件描述符數(shù)組,其中fd[0]表示讀端, fd[1]表示寫端
返回值:成功返回0,失敗返回錯(cuò)誤代碼
1.管道原理
本質(zhì)是先讓不同的進(jìn)程看到同一份資源,也就是兩個(gè)進(jìn)程都能對(duì)管道文件的緩沖區(qū)進(jìn)行操作
這里我們pipe的時(shí)候,會(huì)使用兩個(gè)文件描述符,這兩個(gè)文件描述里面存的file結(jié)構(gòu)體是同一個(gè),也就是管道文件的file結(jié)構(gòu)體,file結(jié)構(gòu)體中存儲(chǔ)有inode以及系統(tǒng)緩沖區(qū),此時(shí)fork一個(gè)子進(jìn)程,子進(jìn)程有著和父進(jìn)程一樣的結(jié)構(gòu),
這里有一個(gè)非常重要的點(diǎn)雖然子進(jìn)程有著自己的進(jìn)程地址空間,也有著自己存儲(chǔ)file結(jié)構(gòu)體的指針數(shù)組,但是其數(shù)組里面的內(nèi)容是和父進(jìn)程一樣的,也就是子進(jìn)程里面pipe對(duì)應(yīng)的文件描述符位置指向的file結(jié)構(gòu)體(管道文件)是同一個(gè),至此我們父子進(jìn)程就看到了同一個(gè)資源,可以利用這個(gè)資源進(jìn)行通信
兩個(gè)不同的進(jìn)程打開同一份文件的時(shí)候,在內(nèi)核中,操作系統(tǒng)只會(huì)打開一個(gè)
2.管道的四種情況
1.讀寫端正常,管道如果為空,讀端就要阻塞
讀寫端正常,管道如果被寫滿,寫端就要阻塞
2.讀端正常讀,寫端關(guān)閉,讀端就會(huì)讀到0,表明讀到了管道文件的結(jié)尾,不會(huì)被阻塞,如果我們打印讀端讀到的內(nèi)容,顯示器會(huì)一直顯示0
3.寫端正常寫入,讀端關(guān)閉,操作系統(tǒng)會(huì)殺掉此時(shí)正在寫入的進(jìn)程(通過信號(hào)來殺掉)
4.因?yàn)椴僮飨到y(tǒng)不會(huì)做低效,浪費(fèi)的事情,我讀端都不讀了,你寫入再多數(shù)據(jù)到一個(gè)管道里面有什么用,因?yàn)楣艿啦徽加么疟P內(nèi)存,所以程序結(jié)束后,就沒有管道的存在了。
當(dāng)要寫入的數(shù)據(jù)量不大于PIPE_BUF時(shí),linux將保證寫入的原子性。
當(dāng)要寫入的數(shù)據(jù)量大于PIPE_BUF時(shí),linux將不再保證寫入的原子性。
3.管道的特點(diǎn)
1.只能用于具有共同祖先的進(jìn)程(具有親緣關(guān)系的進(jìn)程)之間進(jìn)行通信;通常,一個(gè)管道由一個(gè)進(jìn)程創(chuàng)建,然后該進(jìn)程調(diào)用fork,此后父、子進(jìn)程之間就可應(yīng)用該管道。
2.管道提供流式服務(wù)
3.一般而言,進(jìn)程退出,管道釋放,所以管道的生命周期隨進(jìn)程
4.一般而言,內(nèi)核會(huì)對(duì)管道操作進(jìn)行同步與互斥
5.管道是半雙工的,數(shù)據(jù)只能向一個(gè)方向流動(dòng);需要雙方通信時(shí),需要建立起兩個(gè)管道
二、命名管道
1. 特點(diǎn)
1.管道應(yīng)用的一個(gè)限制就是只能在具有共同祖先(具有親緣關(guān)系)的進(jìn)程間通信。
2.如果我們想在不相關(guān)的進(jìn)程之間交換數(shù)據(jù),可以使用FIFO文件來做這項(xiàng)工作,它經(jīng)常被稱為命名管道
3.命名管道是一種特殊類型的文件
2.創(chuàng)建命名管道
1.在命令行上
mkfifo +文件名
2.在程序中
int mkfifo(const char *filename,mode_t mode);int main(int argc, char *argv[])
{mkfifo("p2", 0644);return 0;
}
3.一個(gè)程序執(zhí)行打開管道并不會(huì)真正打卡
我們執(zhí)行這個(gè)程序發(fā)現(xiàn)并沒有打印那句話,說明管道文件并沒有真正打開,只有當(dāng)我們執(zhí)行另一個(gè)我們要通信的文件的時(shí)候,管道才會(huì)真正打開
三、進(jìn)程池簡(jiǎn)易實(shí)現(xiàn)
1.makefile
ProcessPool:ProcessPool.cppg++ -o $@ $^ -std=c++11 -g.PHONY:clean
clean:rm -rf ProcessPool
2.Task.hpp
#pragma once
#include<functional>#include<vector>#include<iostream>using namespace std;void task1()
{std::cout << "lol 刷新日志" << std::endl;
}
void task2()
{std::cout << "lol 更新野區(qū),刷新出來野怪" << std::endl;
}
void task3()
{std::cout << "lol 檢測(cè)軟件是否更新,如果需要,就提示用戶" << std::endl;
}
void task4()
{std::cout << "lol 用戶釋放技能,更新用的血量和藍(lán)量" << std::endl;
}void LoadTask(vector<function<void()>>*tasks){
tasks->push_back(task1);
tasks->push_back(task2);
tasks->push_back(task3);
tasks->push_back(task4);
return ;
}
3.ProcessPool.cpp
我們創(chuàng)建processnum個(gè)子進(jìn)程,讓父進(jìn)程來寫,子進(jìn)程來讀,子進(jìn)程讀到任務(wù)號(hào)后進(jìn)行對(duì)應(yīng)的處理。
#include <iostream>
#include "Task.hpp"
#include <assert.h>
#include <vector>
#include <string>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>using namespace std;
vector<function<void()>> tasks;
const int processnum = 10;//創(chuàng)建的子進(jìn)程數(shù)
class channel
{
public:channel( string processname, pid_t slaverid,int cmdcode): _processname(processname), _cmdfd(cmdcode), _slaverid(slaverid){}public:string _processname;//執(zhí)行任務(wù)的進(jìn)程名pid_t _slaverid;//執(zhí)行任務(wù)的進(jìn)程pidint _cmdfd;//朝幾號(hào)管道去操作
};void Menu()
{std::cout << "################################################" << std::endl;std::cout << "# 1. 刷新日志 2. 刷新出來野怪 #" << std::endl;std::cout << "# 3. 檢測(cè)軟件是否更新 4. 更新用的血量和藍(lán)量 #" << std::endl;std::cout << "# 0. 退出 #" << std::endl;std::cout << "#################################################" << std::endl;
}void slaver()
{int cmdcode;while (true){int n = read(0, &cmdcode, sizeof(int));//讀取任務(wù)碼if (n == sizeof(int)){cout << "slaver say get a command " << getpid() << " cmdcode: " << cmdcode << endl;if (cmdcode >= 0 && cmdcode < tasks.size())tasks[cmdcode]();//執(zhí)行任務(wù)}else if (n == 0)//為0,說明讀到文件末尾,之間breakbreak;}
}
void InitProcessPool(vector<channel> *channels)
{for (int i = 0; i < processnum; i++){int pipefd[2] = {0};int n = pipe(pipefd);//使用兩個(gè)文件描述符指向同一個(gè)管道文件assert(!n);pid_t id = fork();if (id == 0)//子進(jìn)程{close(pipefd[1]);//關(guān)閉寫文件dup2(pipefd[0], 0);//將讀文件重定向到標(biāo)準(zhǔn)輸入的位置close(pipefd[0]);//關(guān)閉當(dāng)前讀文件,因?yàn)槲覀兒罄m(xù)用標(biāo)準(zhǔn)輸入的下標(biāo)就行了slaver();//子進(jìn)程讀取任務(wù)碼exit(0);}string name = "processname " + to_string(i);//子進(jìn)程名字channels->push_back(channel(name, id, pipefd[1]));//子進(jìn)程pid,這個(gè)子進(jìn)程//與父進(jìn)程之間的管道文件描述符下標(biāo)記錄下來// fatherclose(pipefd[0]);//關(guān)閉讀文件}
}void ctrlProcess(vector<channel> &channels)
{int which = 0;
//我們循環(huán)調(diào)用各個(gè)子進(jìn)程,which為子進(jìn)程的下標(biāo)while (true){Menu();int select = 0;cin >> select;cout << "Please Enter@ ";if (select <= 0 || select >= 5)break;int cmdcode = select - 1;cout << "father say task have sent to " << channels[which]._processname << " cmdcode : " << cmdcode << endl;write(channels[which]._cmdfd, &cmdcode, sizeof(int));//寫入指令which++;which %= channels.size();}
}void QuitProcess(const vector<channel> channels)
{//方法一:for (const auto &c : channels)close(c._cmdfd);for (const auto &c : channels)waitpid(c._slaverid, nullptr, 0);//方法二://for(int i=channels.size()-1;i>=0;i--){// close(channels[i]._cmdfd);//waitpid(channels[i]._slaverid,nullptr,0);//阻塞等待//}
}int main()
{vector<channel> channels;//管理管道的數(shù)組LoadTask(&tasks);//加載任務(wù)InitProcessPool(&channels);//初始化進(jìn)程池ctrlProcess(channels);//輸入任務(wù)命令QuitProcess(channels);//中止進(jìn)程return 0;
}
如果等待和close在一個(gè)循環(huán)中會(huì)發(fā)生阻塞,因?yàn)槲乙惶?hào)管道雖然父進(jìn)程那里寫關(guān)閉了,但依舊有子進(jìn)程2,3指向這個(gè)管道為寫