【Linux】如何实现单机版QQ,来看进程间通信之管道

这篇具有很好参考价值的文章主要介绍了【Linux】如何实现单机版QQ,来看进程间通信之管道。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

学会了管道,就可以实现简单的qq哦~

文章目录

  • 前言
  • 一、匿名管道
  • 总结

前言

为什么要进行进程间通信呢?因为需要以下这些事:

数据传输:一个进程需要将它的数据发送给另一个进程
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

一、管道

1.匿名管道

首先我们前面说过,进程是具有独立性的,但是要实现两个进程通信就必须让这两个进程看到同一份资源,这该怎么办呢两个独立的进程如何看到同一份资源呢?其实这个操作是操作系统直接或间接提供的。下面我们先看看管道,然后再画图讲解如何让进程看到同一份资源。

【Linux】如何实现单机版QQ,来看进程间通信之管道

who这个命令是一个进程,wc也是一个进程通过管道完成了进程间通信,那么如何证明这两个是进程呢?我们证明一下:

【Linux】如何实现单机版QQ,来看进程间通信之管道首先我们用sleep命令创建了3个进程,然后后面加个&符号是让这些进程在后台运行,否则如果在前台运行我们就不能输入指令了,然后通过查看 进程发现三个sleep进程都在并且他们的父进程都是bash。将sleep比作刚刚的who和wc指令就证明了进程间通信。

下面我们讲解一下管道的原理:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 首先先有进程,也就是task_struct,每个进程都有对应的文件描述符表,文件描述符表中有相应的数组,数组中存放了标准输入0,标准输出1,标准错误2,而每个进程描述符都会存放相应struct file的地址,在进程间通信的时候系统会提供一个内存文件,这个内存文件不会在磁盘刷新,这个文件被称为匿名文件,当我们以读和写方式打开一个文件,然后我们fork创建一个子进程,子进程也有task_struct,并且子进程会继承父进程的文件描述符表(但是不会复制父进程打开的文件对象),而文件描述符表中存放文件的地址都是相同的,所以子进程的文件描述符表也指向父进程的文件,正是因为这样,在父进程以读和写打开一份文件,而子进程也同样读和写打开和父进程打开的一样的一份文件,这就让两个进程看到了同一份资源。但是这种管道只能实现单向通信,比如我们关闭父进程的写端,关闭子进程的读端让子进程去写这两个进程就实现单向通信了。管道只能单向通信的原因是文件只有一个缓冲区,一个写入位置一个读取位置所以只能单向通信,要是想双向通信那就打开两个管道!而上面所讲的管道就是匿名管道。

下面我们写一个管道的程序:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 首先0 1 2是默认打开的标准输入,标准输出,标准错误,而3就是读端,4就是写端,我们要实现单向信道一定是父子进程一读一写。

首先我们在VScode中创建等会要用的.cc文件和makefile。

要创建管道,首先我们要知道创建管道的函数pipe:

【Linux】如何实现单机版QQ,来看进程间通信之管道

此函数有一个参数是一个一维数组,这个一维数组只有两个元素,这个参数也叫输出型参数(这两个元素我们的例子中就是读端和写端)。

下面我们完成主体:

#include <iostream>
#include <string>
#include <cassert>
#include <unistd.h>
#include <vector>
#include "task.hpp"
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;
int main()
{
    //1.1创建管道
    int pipefd[2] = {0};
    int n = pipe(pipefd);
    if (n<0)
    {
       std::cout<<"pipe error,"<<errno<<":"<<strerror(errno)<<std::endl;
       return 1;
    } 
    std::cout<<"pipefd[0]:"<<pipefd[0]<<std::endl;
    std::cout<<"pipefd[1]:"<<pipefd[1]<<std::endl;
    //1.2创建进程
    pid_t id = fork();
    if (id==-1)
    {
       //创建子进程失败
       exit(-1);
    }
    if (id==0)
    {
       //子进程
       close(pipefd[0]);
       const std::string namestr = "hello,我是子进程";
       int cnt = 1;
       char buffer[1024];
       while (true)
       {
           snprintf(buffer,sizeof buffer,"%s,计数器:%d,我的pid:%d\n",namestr.c_str(),cnt++,getpid());
           write(pipefd[1],buffer,strlen(buffer));
           sleep(1);
       }
       //写入成功后将写端关闭
       close(pipefd[1])
       exit(0);
    }
    //父进程
    close(pipefd[1]);
    //关闭不需要的fd,让父进程读取,子进程写入。
    char buffer[1024];
    while (true)
    {
       int n = read(pipefd[0],buffer,sizeof(buffer-1));
       if (n>0)
       {
           buffer[n] = '\0';
           std::cout<<"我是父进程,child give me message:"<<buffer<<std::endl;
       }
    }
    return 0;

}

 我们的程序很简单,首先创建管道,如果创建失败就报错打印错误码,然后我们打印一下读端和写端(如果没问题的话读端是3,写端是4),然后第二步我们创建子进程,创建子进程同样要判断创建失败的情况,如果创建成功我们先关闭子进程的读端,然后搞一个1024的缓冲区让字符串写入到缓冲区里,sleep可以让现象更明显,写完后我们为了保证管道安全所以关闭刚刚使用的写端,在父进程中同理只不过是变成了读,下面我们运行一下代码:

【Linux】如何实现单机版QQ,来看进程间通信之管道

【Linux】如何实现单机版QQ,来看进程间通信之管道

 我们确实看到了两个进程,并且确实也完成了简单的单向通信,因为子进程把自己的数据给了父进程。通过上面的实例代码我们能知道管道的特点吗?

1.单向通信

2.管道的本质是文件,因为fd的生命周期随进程,管道的生命周期也是随进程的。

3.管道通信,通常用来进行具有"血缘"关系的进程,进行进程间通信,常用于父子通信----pipe打开管道,并不清楚管道的名字,所以是匿名管道。

下面我们修改一下代码,刚刚我们让子进程发的慢一点(sleep了1秒),现在我们让父进程读的慢一点看看是是什么样子:

【Linux】如何实现单机版QQ,来看进程间通信之管道

【Linux】如何实现单机版QQ,来看进程间通信之管道 下面我们运行起来:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 通过结果我们发现写入的次数和读取的次数不是严格匹配的。所以这也是管道的一个特点。

 下面我们再将代码修改一下,让父进程读取恢复正常,让子进程写的慢一些我们再看看效果:

【Linux】如何实现单机版QQ,来看进程间通信之管道

【Linux】如何实现单机版QQ,来看进程间通信之管道

 【Linux】如何实现单机版QQ,来看进程间通信之管道

通过程序运行结果我们发现,当写入很慢的时候读取也变慢了,也就是说写入是会影响读取的。

下面我们再将代码修改一下,我们让子进程持续写入一个字符,父进程先等10秒再读:

【Linux】如何实现单机版QQ,来看进程间通信之管道 【Linux】如何实现单机版QQ,来看进程间通信之管道

 【Linux】如何实现单机版QQ,来看进程间通信之管道

通过上图我们可以看到当子进程写入65535个也就是2的16次方的数据后,父进程直接打印了,并且时间并没有到10秒,所以打印的原因是因为管道是有容量的,当我们的写端将管道文件写满了我们就不能继续写了,这与之前read端读完了所有的管道数据,如果写端不发就只能等待是相对应的。所以管道的另一个特点来了:具有一定的协同能力,让reader和writer能够按照一定的步骤进行通信(自带同步机制)。

下面我们再试试将写端关闭了读端会怎么样呢?

【Linux】如何实现单机版QQ,来看进程间通信之管道

【Linux】如何实现单机版QQ,来看进程间通信之管道 read函数的返回值可以判断读取了多少,比如返回值大于0就是将缓冲区的数据都读完了,返回值等于0就是读到了文件结尾,否则就是读取异常,下面我们将代码运行起来:

【Linux】如何实现单机版QQ,来看进程间通信之管道

结果就是子进程写了一个字符退出后,父进程读取到\0就会读到文件结尾而退出。那么如果我们写端一直写,将读端关闭了会发生什么呢?因为这里的行为是无意义的,所以操作系统会杀死这个一直写入的进程(因为操作系统不会维护无意义,低效率,或者浪费资源的事情,操作系统会杀死一直写入的进程,会通过信号来终止进程(13号信号)),那么如何证明会被操作系统杀死呢,下面我们让父进程等待一下子进程然后看看:

【Linux】如何实现单机版QQ,来看进程间通信之管道

【Linux】如何实现单机版QQ,来看进程间通信之管道

 通过结果我们发现确实是这样,子进程被操作系统用13号信号终止了。以上就是匿名管道的相关知识,下面我们用一批管道实现控制进程的代码:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 首先我们创建一个ctrlProecess.cc的文件夹,然后写一个makefile,准备工作做好后我们就可以按照思路写代码了,我们第一步要先进行构建控制结构,父进程写入,子进程读取,并且既然是多个管道我们直接用个循环来控制:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 我们在判断是否成功创建子进程的时候直接用assert断言了,其实这里应该要用if语句判断的,但是由于这个函数基本不会出错并且我们只是演示一下代码所以就用断言了,当返回值等于0的时候那么一定是子进程了,否则就是父进程了,那么我们该如何通信呢,让父进程写入,子进程读取,所以我们先关闭不需要的端口。

【Linux】如何实现单机版QQ,来看进程间通信之管道

现在前期准备工作已经完成了,下面如何让父进程管理自己创建的管道和进程呢?还记得我们之前说过的先描述,在组织吗?答案就是这个。

【Linux】如何实现单机版QQ,来看进程间通信之管道 我们为了描述这些创建的管道所以用一个类来封装,成员变量包括子进程的pid以及父进程写入所需要的端口,然后我们在构造函数中将这些变量初始化一下。

class EndPoint
{
public:
     pid_t _child_id;
     int _write_fd;
     EndPoint(int id,int fd)
          :_child_id(id)
          ,_write_fd(fd)
     {
    
     }
     ~EndPoint()
     {

     }

};

因为是多个管道所以我们将刚刚的自定义对象放到vector中去管理,这不就完成了先描述,在组织的任务吗,所需要的头文件大家记得加上,下面我们将vector定义到函数最开始。

【Linux】如何实现单机版QQ,来看进程间通信之管道

我们的目的是让父进程写入,子进程读取,所以肯定是在父进程的地方来描述这个子进程:

【Linux】如何实现单机版QQ,来看进程间通信之管道 将一个个子进程和写端的对象放入向量后,父进程就拿到了一批Endpoint对象,这个结构里包含了要写入的文件描述符的写端和新的子进程。下面的工作就是要子进程读取指令,我们要让子进程在读取指令的时候,都从标准输入读取,要从标准输入读取我们就要用的以前学的知识了,那就是输入重定向,输入重定向的方式很多,我们主要用函数的方式:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 还记得我们之前学的dup2函数吗,在这里就可以派上用场了。我们的目的是要子进程都要从标准输入读取,所以函数的第一个参数old就是pipefd[0]了因为我们要让pipefd[0]重定向到0(标准输入),所以第二个参数new就是0了,然后我们让子进程开始等待获取命令,这里我们直接写一个函数WaitCommand()将所以的代码写入这个函数里,并且将刚刚创建进程的代码也放入一个creatProcess函数中,让各个函数完成相应的功能会让代码看的更简洁:

【Linux】如何实现单机版QQ,来看进程间通信之管道

【Linux】如何实现单机版QQ,来看进程间通信之管道

 下面在让子进程要执行方法的函数中先写一个read函数和一个死循环我们来看看代码能否正常创建子进程,然后再编写后序的代码:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 【Linux】如何实现单机版QQ,来看进程间通信之管道

【Linux】如何实现单机版QQ,来看进程间通信之管道 运行起来后我们可以看到之前写的创建进程的函数没毛病,下面我们来设计一下让子进程执行的方法的函数。在完成这个函数前先新建一个头文件task.hpp,然后创建一些简单的打印任务:

【Linux】如何实现单机版QQ,来看进程间通信之管道

【Linux】如何实现单机版QQ,来看进程间通信之管道 我们在Task类中先定义一个vector,vector中存放的类型为函数指针类型,每一个函数指针都指向一个函数方法,这样我们就可以通过指令来调用不同的方法,对于指令command来讲我们之前也提到过其实就是位图,我们定义三个宏值分别代表三种打印方法,相要调用哪个方法就让command参数&上对应的宏值就完成了。我们在.cc文件中加上头文件,然后定义一个全局的Task类:

【Linux】如何实现单机版QQ,来看进程间通信之管道

接下来我们继续实现子进程任务函数:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 在子进程函数中我们默认指令为int类型,然后通过read函数的返回值判断是否读取成功,如果返回值等于4字节说明读到了指令那么就调用对应的函数方法,如果返回值等于0就说明当前的子进程要退出了直接break,剩下的就是读取失败也是直接退出,当然我们不能只执行一次,所以我们加个循环让子进程周而复始的去执行任务:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 那么接下来的父进程还需要干什么呢?父进程需要选择任务,选择进程然后下发任务,下面我们来实现:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 这里的任务是可以随机选择的,但是我们为了测试就只用了LOG任务,然后选择进程的时候直接用随机函数选择即可,下发任务也就是让父进程写入,写入的是指令对应的方法,然后我们将之前答应的代码加上getpid,把子进程的pid也打印出来:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 以下是运行起来后的代码:

【Linux】如何实现单机版QQ,来看进程间通信之管道

【Linux】如何实现单机版QQ,来看进程间通信之管道 这样我们就完成了要实现的功能,现在我们再把相应的函数优化一下,让选择任务的时候可以是交互式的,首先第一步让用户选择任务,这里我们先实现一个选择面板:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 将选择面板搞定后我们就可以实现让用户选择对应的任务了:

【Linux】如何实现单机版QQ,来看进程间通信之管道

下面我们让选择进程的功能变成按顺序的,这样就不会出现每次随机让一个进程执行任务的情况:

【Linux】如何实现单机版QQ,来看进程间通信之管道 下面我们我们再实现一下每次拿到任务后打印正在执行哪个任务:

【Linux】如何实现单机版QQ,来看进程间通信之管道

【Linux】如何实现单机版QQ,来看进程间通信之管道

  我们先创建一个静态的int变量用来充当第几个进程,然后我们在类外将number初始化为0,string类型的进程名接收缓冲区里的字符,函数name可以返回一个进程名。然后我们在选择任务那块也打印一下:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 然后如果进程退出的话也要有提示,所以我们在子进程那块加上打印提示:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 当n等于0说明父进程将写端关闭了,随之就打印一下告诉用户。然后我们再将代码简化一下,把刚刚main函数中写的控制函数代码都放到ctrlProcess函数中:

【Linux】如何实现单机版QQ,来看进程间通信之管道

【Linux】如何实现单机版QQ,来看进程间通信之管道 写到这里发现124行有个报错,是因为返回的name函数需要加上const让静态变量也能使用(因为我们的ctrlProcess函数的参数是const对象)

 【Linux】如何实现单机版QQ,来看进程间通信之管道

 下面我们运行一下程序:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 程序运行起来后我们发现输入3退出不了,并且输入其他指令程序也没有打印动作,所以我们用if语句判断一下:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 除了这个问题我们还要解决子进程退出问题,还记得我们之前说过的僵尸进程吗,如果子进程没有被父进程等待那么子进程就变成了僵尸状态,为了防止这种情况我们再写一个进程等待函数函数:

【Linux】如何实现单机版QQ,来看进程间通信之管道

【Linux】如何实现单机版QQ,来看进程间通信之管道

 回收子进程之前我们需要将父进程的所有写端都关了,然后等待10秒后我们让父进程把每个子进程都回收了,这样就完成了简单的回收子进程函数。

【Linux】如何实现单机版QQ,来看进程间通信之管道

当我们运行起来后发现可以完成我们的需求,并且在退出后也能正常的回收子进程。

不知道有没有人会有疑问,为什么我们刚刚在同一个循环内不能直接边关闭边回收子进程呢,就像下图这样:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 我们先用这样的代码运行一下:

【Linux】如何实现单机版QQ,来看进程间通信之管道

 我们发现这样写后程序就卡在了回收进程处,这是为什么呢?其实是和子进程有关,我们早就说过子进程会继承父进程打开的文件描述符(这就是管道的原理),这里的坑就在于当第二次及第二次以后的创建子进程时,这个子进程会继承前面所有父进程的端口,也就是说越往后的子进程继承的端口越多,如下图所示:

【Linux】如何实现单机版QQ,来看进程间通信之管道

也就是说刚刚卡主的原因是写端没有全部关完就回收了子进程,就造成了阻塞。而我们用一个循环单独关闭父进程的端口时是不会出现这个问题的,因为我们是先关闭了父进程的写端,都关闭后才去回收子进程。

由于上面的讲解都是通过图片的形式,下面我们将此次的实例代码发出来:

首先是ctrlProcess.cc文件中的:

#include <iostream>
#include <string>
#include <cassert>
#include <unistd.h>
#include <vector>
#include "task.hpp"
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;
const int gnum = 3;
Task t;
class EndPoint
{
private:
     static int number;
public:
     pid_t _child_id;
     int _write_fd;
     std::string processname;
     EndPoint(int id,int fd)
          :_child_id(id)
          ,_write_fd(fd)
     {
         char namebuffer[64];
         snprintf(namebuffer,sizeof(namebuffer),"process-%d[%d:%d]",number++,_child_id,_write_fd);
         processname = namebuffer;
     }
     std::string &name()
     {
        return processname;
     }
     ~EndPoint()
     {

     }

};
int EndPoint::number = 0;
//子进程要执行的方法
void WaitCommand()
{
    while (true)
    {
        int command = 0;
        int n = read(0,&command,sizeof(int));
        if (n==sizeof(int))
        {
            t.Execute(command);
        }
        else if(n==0)
        {
            std::cout<<"父进程让我退出,我就退出了: "<<getpid()<<std::endl;
            break;
        }
        else 
        {
            break;
        }
    }
   
}
void CreatProcess( vector<EndPoint>* end_points)
{
     //1.先进行构建控制结构,父进程写入,子进程读取
    for (int i = 0;i<gnum;i++)
    {
        //1.1创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        assert(n==0);
        (void)n;
        //1.2创建进程
        pid_t id = fork();
        assert(id!=-1);
        //返回值等于0一定是子进程
        if (id==0)
        {
            //先关闭不要的fd
            close(pipefd[1]);
            //我们期望所有的子进程读取指令的时候,都从标准输入读取
            //1.3.1输入重定向
            dup2(pipefd[0],0);  //读0就像读管道一样
            //1.3.2让子进程开始等待获取命令
            WaitCommand();
            close(pipefd[0]);
            exit(0);
        }

        //一定是父进程
        //1.3关闭不要的fd
        close(pipefd[0]);

        //1.4将新的子进程和他的管道写端,构建对象
        end_points->push_back(EndPoint(id,pipefd[1]));

    }
}
int ShowBoard()
{
    std::cout<<"################################"<<std::endl;
    std::cout<<"##0. 执行日志任务 1. 执行数据库任务##"<<std::endl;
    std::cout<<"##2.执行请求任务  3.退出#########"<<std::endl;
    std::cout<<"################################"<<std::endl;
    std::cout<<"请选择#";
    int command = 0;
    std::cin>>command;
    return command;
}
void ctrlProcess(vector<EndPoint>& end_points) 
{
    //2.1我们可以写成自动化的,也可以写成交互式的
    int cnt = 0;
    int num = 0;
    while (true)
    {
        //1.选择任务
        int command = ShowBoard();
        if (command==3)
        {
            break;
        }
        if (command<0|| command>2)
        {
            continue;
        }
        //2.选择进程
        int index = cnt++;
        cnt%=end_points.size();
        std::cout<<"选择了进程:"<<end_points[index].name()<<" | 处理任务"<<command<<std::endl;
        //3.下发任务
        write(end_points[index]._write_fd,&command,sizeof(command));

        //sleep(1);
    }
}
void ExitProcess()
{
    exit(0);
}
void waitProcess(const vector<EndPoint>& end_points)
{
     //1.我们需要让子进程全部退出 -- 只需要让父进程关闭所有的写端
     //for (const auto &ep:end_points)
     for (int end = end_points.size()-1;end>=0;end--)
     {
        std::cout<<"父进程让所有的子进程全部退出"<<std::endl;
        //先关闭最后一个写端倒着一直关闭,因为子进程会继承父进程的文件描述符所以
        //后面的子进程都会链接到第一个父进程的写端,如果关第一个无法全部关闭,会造成
        //阻塞
        close(end_points[end]._write_fd);
        std::cout<<"父进程回收了所有的子进程"<<std::endl;
        waitpid(end_points[end]._child_id,nullptr,0);

     }
     //std::cout<<"父进程让所有的子进程全部退出"<<std::endl;
     sleep(10);
     //2.父进程要回收子进程的僵尸状态
     //for (const auto& ep:end_points)
     //{
     //   waitpid(ep._child_id,nullptr,0);
     //}
     //std::cout<<"父进程回收了所有的子进程"<<std::endl;
     //sleep(10);
}
int main()
{
    //1.先进行构建控制结构,父进程写入,子进程读取
    vector<EndPoint> end_points;
    CreatProcess(&end_points);
    //2.我们得到了什么?
    ctrlProcess(end_points);
    //3.处理所有的退出问题
    waitProcess(end_points);
    return 0;
}

下面是头文件task.hpp中的:

#include <iostream>
#include <vector>
#include <unistd.h>
typedef void(*fun_t)();  //函数指针


void PrintLog()
{
    std::cout<<"pid:"<<getpid()<<",打印日志任务,正在被执行..."<<std::endl;
}
void InsertMySQL()
{
    std::cout<<"执行数据库任务,正在被执行..."<<std::endl;
}
void NetRequest()
{
    std::cout<<"执行网络请求任务,正在被执行..."<<std::endl;
}
#define COMMAND_LOG 0
#define COMMAND_MYSQL 1
#define COMMAND_REQUEST 2
class Task
{
public:
    Task()
    {
        funcs.push_back(PrintLog);
        funcs.push_back(InsertMySQL);
        funcs.push_back(NetRequest);
    }
    ~Task()
    {

    }
    void Execute(int command)
    {
        if (command >= 0&& command < funcs.size())
        {
            funcs[command]();
        }
    }

public:
    std::vector<fun_t> funcs;
};

总结

管道读写规则:文章来源地址https://www.toymoban.com/news/detail-431926.html

当没有数据可读时:
O_NONBLOCK disableread调用阻塞,即进程暂停执行,一直等到有数据来到为止。
O_NONBLOCK enableread调用返回-1errno值为EAGAIN
当管道满的时候:
O_NONBLOCK disable write调用阻塞,直到有进程读走数据
O_NONBLOCK enable:调用返回-1errno值为EAGAIN
如果所有管道写端对应的文件描述符被关闭,则read返回0
如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致writ进程退出
当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
管道特点:
只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
管道提供流式服务
一般而言,进程退出,管道释放,所以管道的生命周期随进程
一般而言,内核会对管道操作进行同步与互斥
管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

到了这里,关于【Linux】如何实现单机版QQ,来看进程间通信之管道的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Redis单机版搭建

    redis版本redis-5.0.7 虚拟机系统centos7.4 用户 root 下载地址 http://download.redis.io/releases/ 进入安装目录 cd /usr/local/redis (编译目录) 下载redis并解压到指定目录 wget http://download.redis.io/releases/redis-5.0.7.tar.gz tar zxvf redis-5.0.7.tar.gz cd redis-5.0.7/ 编译安装redis cd /usr/local/redis/redis-5.0.7 make make

    2024年02月07日
    浏览(50)
  • 安装配置 zookeeper(单机版)

    目录 一 准备并解压安装包 二 修改zoo.cfg文件 三 创建相应两个目录 四 创建文件myid  五 修改环境变量 六 启动 zookeeper         这里提供了网盘资源  http://链接: https://pan.baidu.com/s/1BybwSQ_tQUL23OI6AWxwFw?pwd=d4cf         提取码: d4cf         这里的安装包是放在/opt/install 目

    2024年02月08日
    浏览(59)
  • Elasticsearch单机版本安装

    OS: CentOS Linux release 7.9.2009 (Core) 机器: 10.28.19.107 ES、Kibana安装版本: 7.15.2 Elasticsearch:https://www.elastic.co/cn/downloads/past-releases#elasticsearch Kibana: https://www.elastic.co/cn/downloads/past-releases#kibana 提前下载好 elasticsearch-7.15.2-linux-x86_64.tar.gz 、 kibana-7.15.2-linux-x86_64.tar.gz ,上传至/opt目录下。

    2023年04月14日
    浏览(52)
  • zookeeper单机版的搭建

    1.解压压缩包 [root@localhost export]# tar -zxvf zookeeper-3.7.0-bin.tar.gz 2.创建data文件夹 [root@localhost export]# cd apache-zookeeper-3.7.0-bin/ [root@localhost apache-zookeeper-3.7.0-bin]# ls bin  conf  docs  lib  LICENSE.txt  NOTICE.txt  README.md  README_packaging.md [root@localhost apache-zookeeper-3.7.0-bin]# pwd /root/export/apache-

    2024年02月05日
    浏览(45)
  • 杂记 | 在Linux上使用Docker-compose安装单机版Milvus向量数据库并配置访问控制和可视化面板(Attu)

    Milvus是一款开源的向量数据库,它专为AI应用设计,用于管理和检索海量的特征向量。Milvus的优势主要包括: 高效的向量检索性能 :Milvus采用了多种先进的索引算法,如IVF, HNSW, ANNOY等,能够在大规模数据集上实现高效的近似最近邻搜索。 易于扩展和维护 :Milvus支持水平和垂

    2024年01月19日
    浏览(54)
  • HBase单机版安装详细步骤

    目录 一、安装HBase前置环境 1.jdk 2.hadoop 3.zookeeper 二、安装HBase步骤 (一)解压 (二)修改文件名 (三)修改配置文件 (四)配置HBase的环境变量 (五)启动HBase 1.启动hadoop 2.启动zookeeper 3.启动hbase 4.jps出现HMaster和HRegionServer 5.HBase的WebUI (六)HBase的逻辑结构: (七)HBase基本命令  1.进入HBase

    2024年02月07日
    浏览(61)
  • Nacos单机版安装启动流程

    安装地址:https://github.com/alibaba/nacos/ 1、选择自己需要的版本  2、选择自己的环境下在对应的压缩包  3、下载后放到自己的文件目录中解压 4、window环境修改startup.cmd文件,改为单机部署后,就可以直接点击cmd文件直接启动了 mac通过终端进入cd ~/nacos/bin 目录下,通过命令 

    2024年02月11日
    浏览(58)
  • 【大数据】安装 Zookeeper 单机版

    下面安装 Zookeeper,由于它是 Apache 的一个顶级项目,所以域名是 zookeeper.apache.org ,所有 Apache 的顶级项目的官网都是以项目名 .apache.org 来命名的。 点击 Download 即可下载,这里我们选择的版本是 3.5.10 ,下载之后扔到服务器上。由于 Zookeeper 是基于 Java 语言编写的,所以还需要

    2024年02月03日
    浏览(49)
  • docker 安装zookeeper单机版

    1. 安装版本3.5.7 ,  也可以自己去官网找到自己需要的版本复制命令即可    https://hub.docker.com/_/zookeeper/tags   2. 创建映射文件夹 :        3. docker命令创建容器 :     4.centos 打开防火墙2181端口,如果是云服务器的话,阿里云控制台打开云服务器2181端口就可以了。 5. 下载客

    2024年02月13日
    浏览(51)
  • milvus实战 | docker部署单机版

    单机版部署总体而言是比较简单的,但是本着鸡过拔毛,码过留痕的当下人生信条,还是即时记录下。部署过程中有些小的细节也是可以拿捏下,希望可以给有缘人一缕顺风 本文主要三部分: milvus部署 milvus可视化工具attu部署 移植到内网部署 需要有一个能连互联网的linux环

    2024年02月06日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包