进程通信——管道

什么是进程通信?

进程通信是实现进程间传递数据信息的机制。要实现数据信息传递就要进程间共享资源——内存空间。那么是哪块内存空间呢?进程间是相互独立的,一个进程不可能访问其他进程的内存空间,那么这块空间只能由操作系统提供。进程通信的方式有多种,管道就是一种。

管道是一种最简单的通信机制,管道分为匿名管道和命名管道。匿名管道通常用于父子进程之间。命名管道可实现任意两个进程通信。

匿名管道

原理

父进程打开管道文件流(读写两个流),分配描述符到文件描述符表。两个新的文件描述符分别指向管道的读端和写端。父进程创建子进程时,子进程的文件描述符表和父进程指向相同。父子进程指向的文件流都是共享的。

父子进程通信时,如果子进程写,父进程读,子进程关闭pipe_r,父进程关闭pipe_w;如果子进程读,父进程写,子进程关闭pipe_w,父进程关闭pipe_r。

使用样例

pipefd是一个输出型参数,pipefd[0] 表示的是读端的文件描述符,pipefd[1]表示的是写端文件描述符

创建管道->创建子进程->子进程关闭读端,写;父进程关闭写端,读->父进程等待子进程 

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;
void Write(int fd)
{
    char buffer[1024] = {0};
    pid_t id = getpid();
    int n = 0;
    string s = "I am child";
    while(1)
    {
        snprintf(buffer,sizeof(buffer),"%s-%d-%d",s.c_str(),id,n);
        n++;
        write(fd,buffer,sizeof(buffer));
        sleep(1);
        if(n == 5) break;
    }
}

void Read(int fd)
{
    char buffer[1024] = {0};
    while(1)
    {
        ssize_t s = read(fd,buffer,sizeof(buffer));
        if(s > 0)
        {
            cout << buffer << endl;
        }
        else if(s == 0)
        {
            cout << "read ending" << endl;
            break;
        }
        else{
            cout << "read fail" << endl;
        }
    }
}


int main()
{
    int pipefd[2] = {0};
    pipe(pipefd);
    pid_t id = fork();
    if(id < 0) return -1;
    if(id == 0)
    {
        //child
        close(pipefd[0]);//关闭读端
        Write(pipefd[1]);

        //close(pipefd[1]);
        exit(0);
    }
    //father
    close(pipefd[1]);//关闭写端
    Read(pipefd[0]);
    
    pid_t ret = waitpid(id,nullptr,0);
    if(ret==id) cout << "wait success" << endl;
    else cout << "wait fail" << endl;
    //close(pipefd[0]);
    return 0;
}

命名管道

命名管道原理和匿名管道一样,区别就是命名管道会创建一个管道文件,两个进程分别对文件读写就可以了

使用样例

创建管道文件

 

销毁管道文件

 

 

//communicate.hpp 创建销毁管道进行封装
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

class Init
{
public:
    Init()
    {
        int n = mkfifo("./myfifo", 0777);
        if (n == -1)
        {
            perror("fifo:");
            exit(-1);
        }
    }

    ~Init()
    {
        int n = unlink("./myfifo");
        if(n == -1)
        {
            perror("unlink:");
            exit(-1);
        }
    }
};

//sever.cpp 服务器负责维护管道,读客户端数据
#include "communicate.hpp"
using namespace std;
int main()
{
    // 创建管道
    Init init;
    // 打开管道
    int fd = open("./myfifo", O_RDONLY);
    if (fd == -1)
    {
        cout << strerror(errno) << endl;
        return -1;
    }

    // 开始通信
    while (1)
    {
        char buffer[1024] = {0};
        ssize_t ret = read(fd, buffer, sizeof(buffer));
        if (ret > 0)
        {
           cout << "sever get inf:" << buffer << endl;
        }
        else if (ret == 0)
        {
            cout << "client quit, sever quit too" << endl;
            break;
        }
        else
            break;
    }

    // 结束通信
    close(fd);
}

//client.cpp 客户端负责向服务器写数据
#include "communicate.hpp"

using namespace std;

int main()
{
    //打开管道
    int fd = open("./myfifo", O_WRONLY);
    if(fd < 0)
    {
        cout << strerror(errno) << endl;
        return -1;
    }

    //进行通信
   while(1)
   {
        string ord;
        cout << "Input:";
        getline(cin, ord);
        write(fd, ord.c_str(), ord.size());
   }

   close(fd);
   return 0;
}

管道特征

  • 只有具有血缘关系的进程可以匿名管道通信

因为只有血缘关系的进程的可以指向同一个匿名管道,达成资源(内存空间)共享,这是通信的前提

  • 管道只能单向通信
  • 通信的进程会进行协同,同步与互斥,为了保证管道文件数据的安全

例如上面的样例:写端每写一次都会等待1s,写端没有写完时,读端会等待进行协同

  • 管道是面向字节流的

管道以字节为单位传输数据,而不是以消息或记录为单位。这意味着数据在传输过程中没有特定的结构或边界,发送方可以连续写入任意数量的字节,接收方则按照字节顺序读取数据。

  • 管道是基于文件的,文件的生命周期是随进程的

管道会随着进程结束而关闭,例如基础IO流并不需要用户手动关闭。在上面的例子中,子进程是写端,没有close(pipdf[1])也不会有错误,同样的父进程是读端,没有close(pipdf[0])不会有错误,因为进程结束这个管道流会自动关闭。

管道的4种情况

  1. 读写端正常,管道为空,读端就要阻塞
  2. 读写端正常,管道为满,写端就要阻塞
  3. 写端关闭,读端正常,读端读到0,表明读到了管道文件的结尾
  4. 读端关闭,写端正常,写端会被异常终止
  • 22
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值