进行数据传输,通知事件,资源共享,进程控制等,来进行进程间协同工作。
因此要通信的前提是:两个进程能看到同一块内存资源,并能或读或写这块资源。
POSIX标准 ------>进程通信可以跨主机
System V标准---->进程本地间通信(共享内存,消息队列,信号量)
管道通信----------->进程本地间通信(匿名管道和命名管道,都是利用文件系统进行通信)
匿名管道是血缘间进程通信方法,其原理是利用了创建子进程时继承的文件描述符表,通过文件系统进行通信。因为其并不存在显名的文件,因此称为匿名管道。
父进程创建子进程时,子进程会继承父进程的文件描述符表,其中指向相同的内核文件,这个共享的文件中存在buffer缓冲区。这样通信的前提就有了:父进程和子进程能看到同一块内存资源。
pipe函数是创建匿名管道的函数
一般来说管道只能用来进行单向通信,因此父进程fork后要关闭读或写端,子进程关闭另一个端。
#include
#include
#include
#include
#include
#include
#include
#include
//实现一个父子通信的匿名管道
//子进程写,父进程读,并探究读写特性
int main(){
int fds[2];
int p = pipe(fds);
assert(p == 0);
pid_t id = fork();
if(id == -1){
perror("fork filed");
exit(-1);
}else if(id == 0){//子进程,关闭读fd,使用写fd,实现单向通信
close(fds[0]);
char buffer[1024];
int cnt = 0;
while(true){
sprintf(buffer,"this is child process,now i am calling parent process,%d,%d",++cnt,getpid());
write(fds[1],buffer,strlen(buffer));
std::cout< 0) buffer[r] = 0;
fprintf(stdout,"the message from chiled is : %s\n",buffer);
if(r == 0) break;
}
int status = 2;
waitpid(id,&status,0);//阻塞式等待;
return 0;
//父进程
}
1️⃣如果管道中没数据,读端会阻塞式等待
2️⃣管道缓冲区固定大小,如果写端写满了,写端会阻塞
3️⃣写端关闭,读端读完所有内容后会返回0
4️⃣读端关闭,OS会给写端发送信号终止写端。
1️⃣管道的生命周期依赖于进程
2️⃣匿名管道用于有血缘关系的进程通信
3️⃣管道的互斥与同步机制,读buffer共享资源进行保护
设计原理:用一个进程控制其他进程,完成任务。
父进程创建多个匿名管道并创建多个对应的子进程,父进程向子进程发送Command Code,子进程根据任务码,完成指定任务。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORCESS_NUM 5
#define TASK_NUM 3
#define TASK_COUNT 10
#define Initsrand() srand((unsigned int)time(nullptr) | getpid())
typedef void(*func_t) (); //注意函数指针的写法 返回值(*)(参数类型)!!!;
func_t task_table[TASK_NUM];
void func1(){
std::cout<<"-->下载任务"<安装任务"<启动任务"<& info_table,std::vector& deletefd){
for (int i = 0; i < PORCESS_NUM; i++){
// 父进程打开匿名管道
int fds[2];
int r = pipe(fds);
assert(r == 0);
(void)r;
// 子进程fork后继承匿名管道
pid_t id = fork();
if (id == 0){
for(const auto& x : deletefd) close(x);
// 进入子进程逻辑(接受任务,并完成任务)
close(fds[1]);
while(true){
//接受任务码
int Command_Code;
ssize_t r = read(fds[0],&Command_Code,sizeof(int));
//执行任务
if(r == 0) break;
task_table[Command_Code] ();
}
exit(0);
}
close(fds[0]);
deletefd.push_back(fds[1]);
info_table.push_back(process_info(id,fds[1]));
}
}
void wait_childprocess(std::vector& info_table){
for (size_t i = 0; i < PORCESS_NUM; i++){
waitpid(info_table[i]._pid,NULL,0);
std::cout<<"on sucess exit :" << info_table[i]._pid<& info_table){
int cout = TASK_COUNT;
while(cout--){
int index_process = rand()%PORCESS_NUM;
int index_task = rand()%TASK_NUM;
std::cout< deletefd;
//
std::vector info_table;
load_task();
// 循环创建指定个数的子进程,打开信道,并保存信息
Createporeccss(info_table,deletefd);
Initsrand();
//父进程逻辑:发送Commnade Code
Send_task(info_table);
//等待子进程
wait_childprocess(info_table);
return 0;
}
BUG:父进程创建第二个子进程时,子进程会继承父进程与第一个子进程的通信管道,在后续close时实际上从后往前关闭的。
匿名管道是在血缘间进程的通信,其是依靠继承的方法实现的通信。
命名管道是存在实际的文件,但其buffer区数据并不会刷新到磁盘文件中,只是为我们提供了入口。
命名管道通过不同进程打开同一个文件,进而指向同一块内存空间资源。
利用mkfifo函数创建命名管道文件。
利用unlink删除管道文件
一般是一个进程打开读端, 另一个进程打开写端,进行读写通信
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define FILE_PATH "/tmp/my_named_pipe"
void CreateNamedPipe(const char* path){
umask(0);
int r = mkfifo(path,0600);
//perror("r");
assert(r == 0);
}
void RemoveNamedPipe(const char* path){
unlink(path);
}
#include"Comm.hpp"
int main(){
int fd = open(FILE_PATH,O_RDONLY);
char buffer[1024];
while(true){
std::cout<<"--> ";
ssize_t s = read(fd,buffer,sizeof(buffer));
buffer[s] = 0;
if(s == 0) break;
std::cout<
#include"Comm.hpp"
int main(){
//启动服务
CreateNamedPipe(FILE_PATH);
//进行通信
int fd = open(FILE_PATH,O_WRONLY);
char buffer[1024];
while (true){
std::cout<<"--->";
fgets(buffer,sizeof(buffer),stdin);
buffer[strlen(buffer)-1] = 0;
ssize_t s = write(fd,buffer,strlen(buffer));
assert(s == strlen(buffer));
}
//结束通信
close(fd);
RemoveNamedPipe(FILE_PATH);
return 0;
}