您现在的位置是:首页 >技术教程 >【Linux】如何实现单机版QQ,来看进程间通信之管道网站首页技术教程
【Linux】如何实现单机版QQ,来看进程间通信之管道
学会了管道,就可以实现简单的qq哦~
前言
为什么要进行进程间通信呢?因为需要以下这些事:
一、管道
1.匿名管道
首先我们前面说过,进程是具有独立性的,但是要实现两个进程通信就必须让这两个进程看到同一份资源,这该怎么办呢两个独立的进程如何看到同一份资源呢?其实这个操作是操作系统直接或间接提供的。下面我们先看看管道,然后再画图讲解如何让进程看到同一份资源。
who这个命令是一个进程,wc也是一个进程通过管道完成了进程间通信,那么如何证明这两个是进程呢?我们证明一下:
首先我们用sleep命令创建了3个进程,然后后面加个&符号是让这些进程在后台运行,否则如果在前台运行我们就不能输入指令了,然后通过查看 进程发现三个sleep进程都在并且他们的父进程都是bash。将sleep比作刚刚的who和wc指令就证明了进程间通信。
下面我们讲解一下管道的原理:
首先先有进程,也就是task_struct,每个进程都有对应的文件描述符表,文件描述符表中有相应的数组,数组中存放了标准输入0,标准输出1,标准错误2,而每个进程描述符都会存放相应struct file的地址,在进程间通信的时候系统会提供一个内存文件,这个内存文件不会在磁盘刷新,这个文件被称为匿名文件,当我们以读和写方式打开一个文件,然后我们fork创建一个子进程,子进程也有task_struct,并且子进程会继承父进程的文件描述符表(但是不会复制父进程打开的文件对象),而文件描述符表中存放文件的地址都是相同的,所以子进程的文件描述符表也指向父进程的文件,正是因为这样,在父进程以读和写打开一份文件,而子进程也同样读和写打开和父进程打开的一样的一份文件,这就让两个进程看到了同一份资源。但是这种管道只能实现单向通信,比如我们关闭父进程的写端,关闭子进程的读端让子进程去写这两个进程就实现单向通信了。管道只能单向通信的原因是文件只有一个缓冲区,一个写入位置一个读取位置所以只能单向通信,要是想双向通信那就打开两个管道!而上面所讲的管道就是匿名管道。
下面我们写一个管道的程序:
首先0 1 2是默认打开的标准输入,标准输出,标准错误,而3就是读端,4就是写端,我们要实现单向信道一定是父子进程一读一写。
首先我们在VScode中创建等会要用的.cc文件和makefile。
要创建管道,首先我们要知道创建管道的函数pipe:
此函数有一个参数是一个一维数组,这个一维数组只有两个元素,这个参数也叫输出型参数(这两个元素我们的例子中就是读端和写端)。
下面我们完成主体:
#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
",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] = '