进程间通信-管道通信

1.进程通信的概念

1.1为什么要进程通信

进行数据传输,通知事件,资源共享,进程控制等,来进行进程间协同工作。进程间通信-管道通信_第1张图片

因此要通信的前提是:两个进程能看到同一块内存资源,并能或读或写这块资源。 

1.2常见的进程通信协议

POSIX标准 ------>进程通信可以跨主机

System V标准---->进程本地间通信(共享内存,消息队列,信号量)

管道通信----------->进程本地间通信(匿名管道和命名管道,都是利用文件系统进行通信)

2.匿名管道通信

2.1匿名管道通信原理理解

匿名管道是血缘间进程通信方法,其原理是利用了创建子进程时继承的文件描述符表,通过文件系统进行通信。因为其并不存在显名的文件,因此称为匿名管道。

进程间通信-管道通信_第2张图片

 父进程创建子进程时,子进程会继承父进程的文件描述符表,其中指向相同的内核文件,这个共享的文件中存在buffer缓冲区。这样通信的前提就有了:父进程和子进程能看到同一块内存资源。

2.2匿名管道通信

pipe函数是创建匿名管道的函数

进程间通信-管道通信_第3张图片

一般来说管道只能用来进行单向通信,因此父进程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;
    //父进程

}

2.3读写特性

1️⃣如果管道中没数据,读端会阻塞式等待

2️⃣管道缓冲区固定大小,如果写端写满了,写端会阻塞

3️⃣写端关闭,读端读完所有内容后会返回0

4️⃣读端关闭,OS会给写端发送信号终止写端。

2.4管道特性

1️⃣管道的生命周期依赖于进程

2️⃣匿名管道用于有血缘关系的进程通信

3️⃣管道的互斥与同步机制,读buffer共享资源进行保护

3.基于管道的进程池设计

设计原理:用一个进程控制其他进程,完成任务。

父进程创建多个匿名管道并创建多个对应的子进程,父进程向子进程发送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时实际上从后往前关闭的。

4.命名管道

匿名管道是在血缘间进程的通信,其是依靠继承的方法实现的通信。

命名管道是存在实际的文件,但其buffer区数据并不会刷新到磁盘文件中,只是为我们提供了入口。

命名管道通过不同进程打开同一个文件,进而指向同一块内存空间资源。

进程间通信-管道通信_第4张图片

 利用mkfifo函数创建命名管道文件。

进程间通信-管道通信_第5张图片

 利用unlink删除管道文件

进程间通信-管道通信_第6张图片

一般是一个进程打开读端, 另一个进程打开写端,进行读写通信

#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;
}

你可能感兴趣的:(Linux学习,网络)