当前位置: 首页 > news >正文

关于网站平台建设调研的函北京网络优化

关于网站平台建设调研的函,北京网络优化,男女在床上做暖暖插孔网站,动画设计毕业作品搞笑目录 1、进程间通信基础概念 2、管道的工作原理 2.1 什么是管道文件 3、匿名管道的创建与使用 3.1、pipe 系统调用 3.2 父进程调用 fork() 创建子进程 3.3. 父子进程的文件描述符共享 3.4. 关闭不必要的文件描述符 3.5 父子进程通过管道进行通信 父子进程通信的具体例…

目录

1、进程间通信基础概念

2、管道的工作原理

 2.1 什么是管道文件

3、匿名管道的创建与使用

3.1、pipe 系统调用

3.2  父进程调用 fork() 创建子进程

3.3. 父子进程的文件描述符共享

3.4. 关闭不必要的文件描述符

3.5 父子进程通过管道进行通信

父子进程通信的具体例子

 4.管道的四种场景 

4.1 场景一:父进程不写,子进程尝试读取

4.2 场景二:父进程不断写入,直到管道写满,子进程不读取

4.3 场景三:关闭写端,子进程读取数据

4.4 场景四:关闭读端,父进程写入数据

5、匿名管道实操-进程控制

5.1 逻辑设计


🌇前言

在操作系统中,进程间通信(Interprocess Communication,简称IPC)是两个不同进程之间进行协同工作和信息交换的基础。IPC 允许不同的进程相互协调,协作完成任务。进程间通信的方式有很多种,而管道则是一种非常经典且常用的方式。本文将详细探讨 匿名管道,它在进程间通信中扮演着重要的角色。

🏙️正文

1、进程间通信基础概念

在深入探讨匿名管道之前,我们先来了解一些基本概念。进程间通信的目的是为了使多个独立的进程能够协同工作,进行信息交换。主要有四个目的:

  • 数据传输:不同进程之间需要传输数据。例如,将数据从客户端传送到服务器。

  • 资源共享:多个进程共享系统资源,保证高效使用。

  • 事件通知:某个进程需要通知其他进程某个事件的发生,例如进程的终止。

  • 进程控制:用于进程管理,协调进程的执行和资源的分配。

这些目的的核心是打破进程的独立性,让它们能够共享资源和信息,协同完成任务。 

2、管道的工作原理

管道是一种用于进程间通信的方式,它本质上是一个文件。无论是匿名管道还是命名管道,它们的原理都是通过文件描述符来共享数据。每个管道都有两个端口:一个是写端,另一个是读端。

管道最初是由 Unix 系统引入的,它允许具有“血缘关系”的进程(如父子进程)通过管道进行通信。管道的实现通常会涉及到内核为进程分配文件描述符。父进程在创建管道后,会为其子进程继承文件描述符,并通过关闭不需要的端口,确保通信的流向。

 2.1 什么是管道文件

管道文件是操作系统中用于实现进程间通信的特殊文件,具有以下几个显著特点:

  • 单向通信:管道是 单向 的通信方式,意味着数据只能从一个端流向另一个端。通常情况下,一个进程写数据到管道,而另一个进程从管道中读取数据。这种方式被称为“半双工通信”,如果需要实现双向通信,需要两个管道。

  • 基于文件的设计,管道本质上是内存中的文件; 管道文件并不是磁盘级别的文件,而是内存级别文件,管道文件没有自己的inode,也没有名字。过内存中的缓冲区进行存储,操作系统会将管道作为文件来处理。

  • 管道分为 匿名管道命名管道。匿名管道没有名字,是由操作系统在内存中创建的,仅限于有血缘关系(如父子进程或兄弟进程)的进程间通信。由于没有名字,匿名管道无法在进程间直接共享。

    与此不同,命名管道(FIFO)则拥有一个系统中的路径名,因此它可以被不具备血缘关系的进程之间共享。这使得命名管道的通信更加灵活。

  • 生命周期与进程绑定,管道的生命周期与创建它的进程生命周期紧密相关。当进程结束时,管道也会被操作系统回收。管道文件的生命周期由打开它的进程的生命周期决定,在进程终止时,管道的资源会被释放。

  • 内存缓冲区,管道的一个重要特点是,它在内存中创建一个缓冲区,用于存储待传输的数据。由于是内存中的缓冲区,管道中的数据并不会被持久化到磁盘中。这使得管道比磁盘文件更为高效,但数据在管道中的存储是临时的,不会在系统重启后保留

  • 阻塞行为与同步机制

    管道的通信遵循 阻塞 和 同步 机制。当读端尝试读取数据时,如果管道为空,进程会阻塞,直到写端写入数据。同样,写端如果尝试写入数据时,如果管道已满,进程也会阻塞,直到读端读取部分数据。

    这种阻塞行为本身提供了一定的同步机制。管道会保证写入数据的顺序,并且数据在被读取之前不会丢失。这使得进程间的通信是同步的,确保数据完整传输。

  • 管道大小限制,管道的大小在不同的操作系统和系统配置中可能有所不同。通常,管道大小会受到系统配置的限制。在 Linux 中,管道大小的默认值通常为 64KB(从 Linux 2.6.11 版本开始),不过在不同的系统或不同的内核版本中,管道的大小也可能有所变化

  • 在管道中,写入 与 读取 的次数并不是严格匹配的,此时读写次数没有强相关关系,管道是面向字节流读写的面向字节流读写又称为 流式服务:数据没有明确的分割,不分一定的报文段;与之相对应的是 数据报服务:数据有明确的分割,拿数据按报文段拿不论写端写入了多少数据,只要写端停止写入,读端都可以将数据读取。
  • 具有一定的协同能力让 读端 和 写端 能够按照一定的步骤进行通信(自带同步机制)当读端进行从管道中读取数据时,如果没有数据,则会阻塞,等待写端写入数据;如果读端正在读取,那么写端将会阻塞等待读端,因此 管道自带 同步与互斥 机制。

3、匿名管道的创建与使用

具体流程:

父进程创建匿名管道,同时以读、写的方式打开匿名管道,此时会分配两个 fd
fork 创建子进程,子进程拥有自己的进程系统信息,同时会继承原父进程中的文件系统信息,此时子进程和父进程可以看到同一份资源:匿名管道 pipe
因为子进程继承了原有关系,因此此时父子进程对于 pipe 都有读写权限,需要确定数据流向,关闭不必要的 fd,比如父进程写、子进程读,或者父进程读、子进程写都可以。

3.1、pipe 系统调用

匿名管道的创建通过 pipe() 系统调用来实现。该函数会创建一个管道,并返回两个文件描述符:一个用于读,另一个用于写。函数原型如下:

#include <unistd.h>int pipe(int pipefd[2]);

 传入一个大小为2的整型数组作为输出型参数操作系统就会生成一个管道文件,并且让进程以读写的方式分别打开进程,并且将进程的读管道文件的文件标识符写道pipe[1],写管到文件描述符写道pipe[1]之中。

int pipefd[2];
pipe(pipefd);  // 创建管道
3.2  父进程调用 fork() 创建子进程

当父进程调用 fork() 时,操作系统会创建一个新的子进程。子进程会继承父进程的文件描述符表,因此,父子进程可以共享父进程所创建的管道文件描述符。也就是说,父进程和子进程都会拥有相同的管道读端和写端。

pid_t pid = fork();
3.3. 父子进程的文件描述符共享

父进程和子进程共享管道的读写端口意味着:

  • 父进程子进程 都可以操作 pipefd[0]pipefd[1]但它们之间的角色(读或写)通常是根据进程的需求来确定的。

  • 父进程和子进程在创建时各自拥有自己的 进程资源但文件描述符表会被子进程继承,指向相同的管道内存资源。

3.4. 关闭不必要的文件描述符

由于管道是单向通信的,所以为了避免数据混乱,父进程和子进程通常会关闭不必要的文件描述符。例如,如果父进程要写数据到管道而子进程读取数据,父进程应该关闭管道的读端,子进程应该关闭管道的写端

// 父进程关闭管道的读端,子进程关闭管道的写端
close(pipefd[0]);  // 父进程关闭读端
close(pipefd[1]);  // 子进程关闭写端
3.5 父子进程通过管道进行通信
  • 父进程写数据:父进程通过管道的写端 pipefd[1] 向管道中写入数据。

    write(pipefd[1], "Hello from parent", 17);
    

    子进程读数据:子进程通过管道的读端 pipefd[0] 从管道中读取数据。

    char buf[128];
    read(pipefd[0], buf, sizeof(buf));
    
  • 父进程写入的数据会通过管道传递给子进程。

  • 子进程从管道中读取数据,通常会按顺序接收父进程写入的数据。

父子进程通信的具体例子

下面是一个完整的示例代码,展示了父子进程如何通过管道进行通信:

#include <iostream>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;int main()
{// 1、创建匿名管道int pipefd[2]; // 数组int ret = pipe(pipefd);assert(ret == 0);(void)ret; // 防止 release 模式中报警告// 2、创建子进程pid_t id = fork();if (id == 0){// 子进程内close(pipefd[1]); // 3、子进程关闭写端// 4、开始通信char buff[64]; // 缓冲区while (true){int n = read(pipefd[0], buff, sizeof(buff) - 1);    //注意预留一个位置存储 '\0'buff[n] = '\0';if (n >= 5 && n < 64){// 读取到了信息cout << "子进程成功读取到信息:" << buff << endl;}else{// 未读取到信息if (n == 0)cout << "子进程没有读取到信息,通信结束!" << endl;// 读取异常(消息过短)elsecout << "子进程读取数据量为:" << n << " 消息过短,通信结束!" << endl;break;}}close(pipefd[0]); // 关闭剩下的读端exit(0);          // 子进程退出}// 父进程内close(pipefd[0]); // 3、父进程关闭读端char buff[64];// 4、开始通信srand((size_t)time(NULL)); // 随机数种子while (true){int n = rand() % 26;for (int i = 0; i < n; i++)buff[i] = (rand() % 26) + 'A'; // 形成随机消息buff[n] = '\0';                    // 结束标志cout << "=============================" << endl;cout << "父进程想对子进程说: " << buff << endl;write(pipefd[1], buff, strlen(buff)); // 写入数据if (n < 5)break; // 消息过短时,不写入sleep(1);}close(pipefd[1]); // 关闭剩下的写端// 父进程等待子进程结束int status = 0;waitpid(id, &status, 0);// 通过 status 判断子进程运行情况if ((status & 0x7F)){printf("子进程异常退出,core dump: %d   退出信号:%d\n", (status >> 7) & 1, (status & 0x7F));}else{printf("子进程正常退出,退出码:%d\n", (status >> 8) & 0xFF);}return 0;
}

 

站在 文件描述符 的角度理解上述代码:

 

 所以,看待 管道 ,就如同看待 文件 一样!管道 的使用和 文件 一致,迎合 Linux一切皆文件思想。

 4.管道的四种场景 

4.1 场景一:父进程不写,子进程尝试读取

情况描述:

  • 父进程没有写入数据到管道。

  • 子进程尝试从管道中读取数据。

结果:

  • 由于管道为空,子进程在尝试读取时会进入阻塞状态。

  • 只有当父进程开始向管道中写入数据后,子进程才会成功读取数据。

形象化理解:

  • 这就像一个垃圾桶,子进程是倒垃圾的工作人员,而父进程是往垃圾桶里扔垃圾。如果垃圾桶为空,子进程(倒垃圾的人)就无法工作,必须等待父进程(扔垃圾的人)开始丢垃圾,才能开始工作。

4.2 场景二:父进程不断写入,直到管道写满,子进程不读取

情况描述:

  • 父进程持续向管道写入数据,直到管道被写满。

  • 子进程不进行读取操作。

结果:

  • 当管道的缓冲区满了,父进程会被阻塞,无法继续写入数据,直到子进程读取数据。

  • 这是因为管道有大小限制,管道满时,写端无法继续写入,必须等待管道中有空间才能继续写入。

形象化理解:

  • 就像垃圾桶满了后,不能继续往里面丢垃圾,必须等到垃圾桶被清空(子进程读取数据)之后,才能继续丢垃圾。

4.3 场景三:关闭写端,子进程读取数据

情况描述:

  • 父进程写入数据到管道,并关闭写端。

  • 子进程从管道中读取数据,并在读取到末尾时判断写端是否关闭。

结果:

  • 当父进程关闭写端后,子进程可以继续读取管道中的数据,直到数据读取完。

  • 子进程在读取到数据末尾时会收到 read的,表示已经没有更多数据可读取,且写端已关闭。

形象化理解:

  • 这类似于垃圾桶的垃圾已经被倒空,子进程(倒垃圾的人)会看到垃圾桶已经没有垃圾了。即使它继续尝试“倒垃圾”,也不会有新的垃圾,显示读取到了文件末尾。

4.4 场景四:关闭读端,父进程写入数据

情况描述:

  • 父进程是写端,子进程是读端。父进程写入数据。

  • 父进程在读取五次后关闭读端。

结果:

  • 当关闭读端后,写端(父进程)会收到 SIGPIPE 信号,通常导致进程终止。

  • 因为操作系统会发现,写端已没有可用的读取端(读端关闭了),它会强制终止写端进程以防止资源浪费。

形象化理解:

这就像垃圾桶的“倒垃圾的人”(写端)发现没有“垃圾桶”(读端)可以丢垃圾,因此操作系统会终止写端,避免无意义的行为继续发生。


5、匿名管道实操-进程控制

匿名管道作为 IPC 的其中一种解决方案,那么肯定有它的实战价值

场景:父进程创建了一批子进程,并通过多条匿名管道与它们链接,父进程选择某个子进程,并通过匿名管道与子进程通信,并下达指定的任务让其执行

5.1 逻辑设计

首先创建一批子进程及匿名管道 -> 子进程(读端)阻塞,等待写端写入数据 -> 选择相应的进程,并对其写入任务编号(数据)-> 子进程拿到数据后,执行相应任务

1.创建一批进程及管道

首先需要先创建一个包含进程信息的类,最主要的就是子进程的写端 fd,这样父进程才能通过此 fd 进行数据写入
循环创建管道、子进程,进行相应的管道链接操作,然后子进程进入任务等待状态,父进程将创建好的子进程信息注册
假设子进程获取了任务代号,那么应该根据任务代号,去执行相应的任务,否则阻塞等待
注意: 因为是创建子进程,所以存在关系重复继承的情况,此时应该统计当前子进程的写端 fd,在创建下一个进程时,关闭无关的 fd

具体体现为:每次都把 写端 fd 存储起来,在确定关系前 “清理” 干净

关于上述操作的危害,需要在编写完进程等待函数后,才能演示其作用 。

完整代码如下:

Task.hpp

#pragma once 
#include<iostream>
#include<vector>typedef void (*task_t)();void task1()
{std::cout << "lol 刷新日志" << std::endl;
}
void task2()
{std::cout << "lol 更新野区,刷新出来野怪" << std::endl;
}
void task3()
{std::cout << "lol 检测软件是否更新,如果需要,就提示用户" << std::endl;
}
void task4()
{std::cout << "lol 用户释放技能,更新用的血量和蓝量" << std::endl;
}
void LoadTask(std::vector<task_t>& task)
{task.push_back(task1);task.push_back(task2);task.push_back(task3);task.push_back(task4);
}

 processpool.cc

#include "Task.hpp"
#include <string>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>using namespace std;const int processnum =4;
vector<task_t> tasks; //把所有的任务,装进去class channel
{public:channel(int &cmdfd ,int &mypid,string &name):_name(name),_cmdfd(cmdfd),_mypid(mypid){}public:string _name;//子进程的名字int _cmdfd;//发信号的文件描述符  int _mypid;//我的PID
};void Menu()
{std::cout << "################################################" << std::endl;std::cout << "# 1. 刷新日志             2. 刷新出来野怪        #" << std::endl;std::cout << "# 3. 检测软件是否更新      4. 更新用的血量和蓝量  #" << std::endl;std::cout << "#                         0. 退出               #" << std::endl;std::cout << "#################################################" << std::endl;
}void slaver(int pool)
{while(true){int cmdcode=0;//通过调用码,去领任务int n=read(pool,&cmdcode,sizeof(int));cout <<"slaver say@ get a command: "<< getpid() << " : cmdcode: " <<  cmdcode << endl;if(cmdcode>0&&cmdcode<=tasks.size()) tasks[cmdcode-1]();if(n==0) break;}
}void InitProcesspool(vector<channel>&channels)
{vector<int> d;//把父进程的所有打开的写管道存储到里面for(int i=1;i<=processnum;i++){int pipeid[2];int n=pipe(pipeid);assert(!n);int pid=fork();if(pid==0){cout<<"创建的"<< i<<"号子进程"<<endl;for(auto &t:d){close(t);//关掉所有不相关的管道}close(pipeid[1]);//关闭写管道slaver(pipeid[0]);//读操作// close(pipeid[0]);//多此一举cout<<"关闭的"<< i<<"号子进程"<<endl;exit(0);}close(pipeid[0]);string name="创建的子进程"+to_string(i);channels.push_back({pipeid[1],pid,name});//那个管道发数据记录下来,d.push_back(pipeid[1]);//把一会发数据的管道号记下来sleep(1);}
}void setslaver(vector<channel>&channels)
{int which=0;int cnt=4;while(cnt--){int slect=0;Menu();cin>>slect;     if(!slect) break;   // rand((void)time(nullptr));// int i=srand()%5;cout<<"farher message"<<channels[which]._name<<endl;write(channels[which]._cmdfd,&slect,sizeof(int));     which++;which%=channels.size();sleep(1);}
}void Quitpool(vector<channel>&channels)
{for(auto& t:channels)//关闭所有的写管道{close(t._cmdfd);waitpid(t._mypid,nullptr,0);}
}int main()
{vector<channel> channels;//把打开的子进程装进来LoadTask(tasks);InitProcesspool(channels);//创建子进程setslaver(channels);//发配任务,采用轮询Quitpool(channels);//关闭写管道,等到read=0子进程退出,全部关闭return 0;
}

总体来说,在使用这个小程序时,以下关键点还是值得多注意的

注册子进程信息时,存储的是 写端 fd,目的是为了通过此 fd 向对应的子进程写数据,即使用不同的匿名管道

创建管道后,需要关闭父、子进程中不必要的 fd

需要特别注意父进程写端 fd 被多次继承的问题,避免因写端没有关干净,而导致读端持续阻塞关闭读端对应的写端后,读端会读到 0,可以借助此特性结束子进程的运行

在选择进程 / 任务 时,要做好越界检查

等待子进程退出时,需要先关闭写端,子进程才会退出,然后才能正常等待。

http://www.cotm.com.cn/news/502.html

相关文章:

  • 页面设计要求伊春seo
  • 工商局网站如何做网登谷歌官方seo入门指南
  • 辽宁省建设厅网站广州抖音推广公司
  • 凡科轻站小程序收费吗福州百度关键词优化
  • 商业网站建设软件微信平台推广方法
  • 做网站推广的一般都是什么公司小程序制作费用一览表
  • web是什么意思动漫seo网络推广公司排名
  • 做seo要明白网站内网站seo公司
  • 有没有建网站的app网站推广的公司
  • asp.net网站开发上武汉seo搜索优化
  • 洛阳网站建设建站系统百度电话号码查询
  • 优化就是开除吗前端seo怎么优化
  • 直播开放平台公会廊坊seo排名优化
  • 网站用的空间2024年阳性什么症状
  • 网站模板王线上培训
  • 做查询网站 发布数据域名是什么意思呢
  • 亚马逊建站服务最打动人心的广告语
  • 长沙网站建设哪个公司好市场营销公司有哪些
  • 怎么做视频网站赚钱吗搜索引擎成功案例分析
  • 做网站在哪里做上海seo推广平台
  • 购物网站图片的放大怎么做的seo优化专家
  • 青岛网站设计品牌企业微信软文怎么写
  • 做网站从哪方面入门教育培训报名
  • 万州微网站建设网易游戏推广代理加盟
  • 南宁网站开发制作百度云资源搜索入口
  • html购物网站模板网站开发建设步骤
  • 四川汉舟电力建设有限公司网站首页关键词怎么排名靠前
  • 做一个网站需要多少人利尔化学股票股吧
  • 建设部网站最新政策军事新闻
  • 做词云的在线网站百度100%秒收录