学习网站建设好找工作吗,网页视频怎么下载到本地视频手机,凡科互动修改器,大数据平台设计个人主页#xff1a;仍有未知等待探索-CSDN博客 专题分栏#xff1a; Linux 目录
一、通信
1、进程为什么要通信#xff1f;
1.数据的类型
2.父进程和子进程算通信吗#xff1f;
2、进程如何通信#xff1f;
3、进程通信的常见方式#xff1f;
二、管道
1、概念… 个人主页仍有未知等待探索-CSDN博客 专题分栏 Linux 目录
一、通信
1、进程为什么要通信
1.数据的类型
2.父进程和子进程算通信吗
2、进程如何通信
3、进程通信的常见方式
二、管道
1、概念
1.管道的4种情况
2.管道的5种特征
2、匿名管道
1.为什么父子进程会向同一个显示器终端打印数据
2.进程默认会打开三个标准输入输出012 ? 3.为什么我们子进程主动close(0/1/2)不影响父进程继续使用显示器文件呢
4.什么是管道文件
5.管道文件的特点
6.管道文件结构图
7.父子既然要关闭不需要的fd那为什么要打开
8.可以不关不需要的fd吗
9.如果想双向通信怎么办
10.为什么要单向通信
11.管道的使用*
12.read、write的注意点
read的返回值
write
13.命令行中的管道符号‘ | ’
14.管道多次创建的示意图* 3、命名管道
1.原理
2.匿名管道和命名管道的区别
3.怎么保证两个毫不相干的进程打开了同一个文件呢
4.命名管道操作 --- 系统调用篇
5.命名管道操作 --- 指令篇
三、管道的项目 --- 进程池
process_pool.cc
task.hpp
Makefile 一、通信
1、进程为什么要通信 进程也是需要某种协同的所以如何协同的前提是通信。 1.数据的类型 通知就绪的、单纯的要传递的数据、控制相关的信息... 2.父进程和子进程算通信吗 答案不算。 1、一直通信 和 能通信不是同一个概念。 2、父子进程只能读父子进程共享的变量内容而且还不能进行修改修改了就会发生写时拷贝数据不一致。 2、进程如何通信 进程间通信的前提是 让两个进程能同时看到同一份资源也就是操作系统的一段空间。 因为不同的进程之间互相是独立的所以不同的进程独自创建的空间其他进程是不知道的。所以需要第三方的协助也就是操作系统。 但是操作系统不能被用户直接的访问资源所以操作系统需要提供对应的系统调用。 如果对于下列的图不明白的话建议去看看进程相关的知识。 这两篇博客讲的都是和进程相关的内容。 Linux 冯诺依曼体系、操作系统、进程概念、进程状态、进程切换-CSDN博客 Linux 进程优先级、程序地址空间、进程控制-CSDN博客 3、进程通信的常见方式 二、管道
1、概念
1.管道的4种情况 1、如果管道内部时空的 write fd没有关闭读取条件不具备读进程会被阻塞 --- wait --- 等到读取条件具备 --- 再进行写入 --- 之后再进行读数据。管道空且写未关读阻塞 2、如果管道被写满 不读且没有关闭写条件不具备写进程会被阻塞 --- wait --- 等到写条件具备 --- 写入数据。管道满且读未关写阻塞 3、管道一直在读写端关闭了wfd读端read返回值会读到0表示读到文件结尾。读且写关闭读到文件尾结束 4、rfd直接关闭写端wfd一直进行写入。这种管道叫broken pipeos不做浪费时空的事情os会杀掉对应的进程给目标发送13 SIGPIPE信号。写且读关闭写进程被杀掉。 2.管道的5种特征 1、匿名管道只能用来进行具有父子进程之间通信。这里的父子进程是泛泛的爷孙进程也可以。 2、管道内部自带进程之间的同步机制。同步多执行流执行代码的时候具有明显的顺序性。 3、管道的生命周期是随进程的。 4、管道在通信的时候是面向字节流的。 write的次数和读取的次数不是一一匹配的。 5、管道的通信模式是一种特殊的半双工模式。半双工在同一时刻只能有一个方向的数据传输 2、匿名管道
1.为什么父子进程会向同一个显示器终端打印数据 因为父子进程代码和数据在没有修改的时候是属于共享的所以子进程创建的时候会直接将父进程的task_struct拷贝一份当然也包括其中的文件描述符表所以只要父进程的文件描述符表中存储了标准输入、标准输出、标准错误子进程也就会有标准输入、标准输出、标准错误。 bash进程可以想象成树形结构的根节点。所以只要将bash的文件描述符表设置好所有的进程就都会在运行的之后默认打开这三个标准文件。 2.进程默认会打开三个标准输入输出012 ? bash打开了所有子进程默认也就打开了我们只需要做好约定即可 3.为什么我们子进程主动close(0/1/2)不影响父进程继续使用显示器文件呢 因为struct file中也有内存级引用计数。 父子同时打开了显示器文件所以显示器文件的引用计数为2当关闭了子进程的显示器文件引用计数减一只有当引用计数为0的时候才会释放资源。 struct file - ref_count; if (ref_count0) 释放文件资源。 4.什么是管道文件 管道文件管道文件是一种特殊的文件它存在于内存中而不是磁盘上。它允许一个进程的输出直接作为另一个进程的输入从而实现进程间的数据交换和协同工作。 5.管道文件的特点 管道只允许单向通信。 管道文件不需要刷新到磁盘中所以需要单独设计通信接口。 6.管道文件结构图 7.父子既然要关闭不需要的fd那为什么要打开 为了让子进程继承下去。 8.可以不关不需要的fd吗 可以但是建议关闭因为可能会误写。 9.如果想双向通信怎么办 可以建立两个管道。 一个管道父读子写一个管道父写子读。 10.为什么要单向通信 因为简单并且保证数据的安全性。 公共资源可能会存在被多个进程同时访问的情况。数据不一致问题。比如说子进程写了一半父进程就开始读。 11.管道的使用* #include iostream
#include string
#include cerrno // errno.h
#include cstring // string.h
#include unistd.h
#include cstdlib
#include sys/types.h
#include sys/wait.hvoid subprocess_write(int);
void fatherprocess_read(int);// 子写父读
int main()
{// 创建管道int pipefd[2] {0};int exitcode pipe(pipefd);std::cout pipefd[0]: pipefd[0] pipefd[1]: pipefd[1] std::endl;// 创建子进程int id fork();// 子写父读if (id 0){std::cout 子进程关闭不需要的fd, 准备发消息 std::endl;sleep(1);// child// 关闭其他权限close(pipefd[0]);// 子进程写subprocess_write(pipefd[1]);close(pipefd[1]);exit(0);}std::cout 父进程进程关闭不需要的fd, 准备收消息 std::endl;sleep(1);// father// 关闭不需要的fdclose(pipefd[1]);// 父进程读fatherprocess_read(pipefd[0]);close(pipefd[0]);pid_t rid waitpid(id, nullptr, 0);if (rid 0){std::cout wait sucessfully\n std::endl;}return 0;
}std::string other()
{static int cnt 0;std::string messageid std::to_string(cnt);cnt;std::string stringid std::to_string(getpid());std::string message messageid my pid is stringid;return message;
}
void subprocess_write(int wfd)
{std::string message i am child: ;while (true){std::string info message other();write(wfd, info.c_str(), info.size());// 写入管道的时候没有写入\0sleep(1);}
}
void fatherprocess_read(int rfd)
{char inbuffer[1024] {0};while (true){std::cout --------------------------------- std::endl;ssize_t n read(rfd, inbuffer, sizeof(inbuffer) - 1);if(n 0){inbuffer[n] 0;std::cout inbuffer std::endl;}else if (n 0){std::cout 读端关闭 std::endl;break;}else {std::cout 读入失败 std::endl;break;}}
} 12.read、write的注意点 read的返回值 返回值 0读取成功且返回值是读取的字节数。返回值 0读取结束。写端关闭导致读端读到文件尾结束。返回值 0读取失败。 write 如果每次写的数据的大小 小于 pipe_buf( 512Byte在Linux中是4096Byte)写入操作的过程是原子的。原子的意思就是不可再分证明这个操作是线程安全的 13.命令行中的管道符号‘ | ’ 命令行中的 |就是匿名管道‘|’ 左侧命令的stdout 作为 右侧命令的stdin。 14.管道多次创建的示意图* 创建了两个管道 创建了三个管道 3、命名管道
1.原理 是不是感觉很眼熟和匿名管道的文件结构图差不多其实就是差不多。 只不过这两个管道有一个非常显著的不同。 2.匿名管道和命名管道的区别 匿名管道两个通信的进程之间是有血缘关系的。父子进程爷孙进程...命名管道两个通信的进程之间没有血缘关系。匿名管道不需要文件路径。命名管道需要文件路径。 3.怎么保证两个毫不相干的进程打开了同一个文件呢 通过文件的路径来确认。每一个文件都有文件路径唯一性。 4.命名管道操作 --- 系统调用篇 5.命名管道操作 --- 指令篇 三、管道的项目 --- 进程池 有 n 个管道n 个任务要保证每条管道都负载均衡。 process_pool.cc #include Task.hpp// 父进程写子进程读
class Channel
{
private:int _wfd;int _id;public:Channel(int wfd, int id): _wfd(wfd), _id(id){}void close_subprocess(){close(_wfd);}int get_wfd(){return _wfd;}int get_id(){return _id;}
};void work(int rfd);
void create_ChannelAndSubprocess(std::vectorChannel *channels, int child_number, work_t work);
int next_channel(std::vectorChannel channels);
void send_command(Channel channel, int option_task);
void ctrl_channel(std::vectorChannel channels);
void clean_ChannelAndSubprocess(std::vectorChannel *channels);
int main(int argv, char* argc[])
{std::vectorChannel channels;load_task();if (argv ! 2)return -1;int num std::stoi(argc[1]);// 1、创建信道和子进程create_ChannelAndSubprocess(channels, num, work);// 2、通过信道控制子进程ctrl_channel(channels);// 3、回收管道和子进程clean_ChannelAndSubprocess(channels);return 0;
}void create_ChannelAndSubprocess(std::vectorChannel *channels, int child_number, work_t work)
{for (int i 0; i child_number; i){int pipefd[2] {0};int exit_code pipe(pipefd);if (exit_code 0){std::cout errno : exit_code std::endl;exit(1);}pid_t id fork();if (id 0){// child;close(pipefd[1]);work(pipefd[0]);exit(0);}// fatherclose(pipefd[0]);(*channels).emplace_back(pipefd[1], id);}std::cout 创建了 channels-size() 个信道和子进程 std::endl;
}
int next_channel(std::vectorChannel channels)
{static int count 0;int option count;count;count % channels.size();return option;
}
void send_command(Channel channel, int option_task)
{write(channel.get_wfd(), option_task, sizeof(option_task));
}
void ctrl_channel(std::vectorChannel channels)
{// 选择任务srand(time(nullptr));int n task_number;while (n -- ){int option_task rand() % task.size(); // task[option_task]// 选择信道int option_channel next_channel(channels); // channels[option_task]// 发送任务send_command(channels.at(option_channel), option_task);sleep(1);}
}
void clean_ChannelAndSubprocess(std::vectorChannel *channels)
{for (auto channel:*channels){channel.close_subprocess();}for (auto channel:*channels){waitpid(channel.get_id(), 0, 0);}
}
task.hpp
#pragma once#include iostream
#include vector
#include string
#include cstring
#include unistd.h
#include cstdlib
#include ctime
#include sys/types.h
#include sys/wait.htypedef void (*task_t)();
typedef void (*work_t)(int);const int size 4096;
const int task_number 7;
// 加载
void download()
{std::cout This is a downlaod task! std::endl;
}
// 打印
void print()
{std::cout This is a print task! std::endl;
}
// 刷新
void flush()
{std::cout This is a flush task! std::endl;
}std::vectortask_t task;void load_task()
{task.push_back(download);task.push_back(print);task.push_back(flush);
}
void excute_task(int option_task)
{if (option_task 0 || option_task task.size())return;task.at(option_task)();
}void work(int rfd)
{int t task_number;int option_task 0;int n read(rfd, option_task, sizeof(option_task));if (n 0){excute_task(option_task);std::cout -------------------------------- std::endl;}
}
Makefile
process_pool:process_pool.ccg $^ -o $ -stdc11.PHONY:clean
clean:rm -rf process_pool
谢谢大家