管道(Linux)
文章目录
- 前言
- 管道
- 1.匿名管道
- 2.指令管道
- 3.有名管道
- 总结
前言
进程通信是什呢???把一个进程的代码和数据交给另一个进程,而这可以访问同样的代码和数据。
我们可不可以直接让两个进程进行通信呢??不可以,因为进程具有独立性。
为啥子要进行进程通信呢??
🌟数据传输:一个进程需要将自己的代码和数据交给另一个进程,比如:一个进程读数据,一个进程写数据
🌟资源共享:让不同进程看到同样的数据,比如王者中的地图。
🌟获取数据:一个进程希望获取另一个进程的信息,比如父子之间,父进程等待子进程。
🌟进程控制:一个进程可以控制另外一个进程,让他执行相应的任务,或者在他出错时杀掉这个进程。
怎摸样进行通信呢??
通信的数据需要一块空间进行通信,这块空间是由操作系统提供的
进程通信的本质:让不同的进程看到同一份代码和数据
管道
管道是一种Linux中最经典的通信方式。
1.匿名管道
原理实现
父进程要创建自己的task_struct ,同时里面有一个指向struct files_struct的指针,struct files_struct里卖弄有一个struct files*数组,里面存放很多file。
如果我们把一个文件file.c读一下,写一下,这时会存在两个file,而不是一个
父进程创建子进程,要继承父进程的代码和数据,子进程也会创建task_struct,struct files_struct,file,这三个的内容都是一样的,这样两个进程就可以看到同一份代码和数据。
父进程要以读写的方式打开一个文件,这个文件会有两个file对象,创建子进程,子进程也会拥有对这个文件的读写方法,通过关闭父进程的写方法,关闭子进程的读方法,就可以让二者实现进程间通信。
pipe系统调用
我们看一下这个函数
🌟参数:两个fd的组成的数组,这是一个输出型参数,fd[0]表示读端,fd[1]表示写端。
🌟返回值:成功返回0。失败返回-1,错误码被设置。
🌟系统会创建一个匿名的文件,这个文件不会将内容刷新到磁盘中,并且这个文件不会被存放在磁盘中,会随进程销毁而销毁。
pipe代码验证
#include <iostream>
using namespace std;
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
void Write(int wfd)
{
char buffer[64];
int cnt = 0;
while (1)
{
snprintf(buffer, sizeof(buffer), "i am child,cnt:%d\n", cnt);
ssize_t n = write(wfd, buffer, sizeof(buffer));
if (n < 0)
{
cerr << "write error:" << errno << "message:" << strerror(errno) << endl;
break;
}
cnt++;
sleep(1);
}
}
void Read(int rfd)
{
char buffer[64];
while (1)
{
ssize_t n = read(rfd, buffer, sizeof(buffer));
if (n < 0)
{
cerr << "read error:" << errno << "message:" << strerror(errno) << endl;
break;
}
cout << "father mesaasge:" << buffer << endl;
}
}
int main()
{
// 创建管道
int pipefd[2];
int Pipe = pipe(pipefd);
if (Pipe < 0)
{
cerr << "pipe error:" << errno << "message:" << strerror(errno) << endl;
exit(1);
}
// 创建子进程
pid_t fid = fork();
if (fid < 0)
{
cerr << "fork error:" << errno << "message:" << strerror(errno) << endl;
exit(1);
}
// 子进程
if (fid == 0)
{
// 关闭读端
close(pipefd[0]);
// 执行写操作
Write(pipefd[1]);
exit(1);
}
// 父进程
close(pipefd[1]);
Read(pipefd[0]);
return 0;
}
我们看一下运行结果
我们发现确实父子进程之间完成了通信。
匿名管道四种情况
🌟如果管道里面没有内容&&没有关闭写进程,读进程就会阻塞等待。等待管道中有数据
🌟如果管道被写满了&&没有关闭读进程,写进程会阻塞等待,等待管道中数据被读走
🌟对于写端,不写了&&关闭写段,读端会把管道中数据读完,如果读端返回0,表示管道内没有数据。
🌟对于读端,不读了&&关闭读端,OS会对写端发送信号(13),杀死这个进程
匿名管道特征
🌟匿名管道是自带同步机制的
🌟匿名管道是面向字节流的
🌟匿名管道用于有血缘关系的进程
🌟匿名管道的生命周期随进程,进程退出,管道结束
🌟管道只能进行单通道通信,如果想要双通道,就要创建两个管道。
🌟当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性
2.指令管道
我们之前学习的 | 这本质也是管道,就像下面这种例子
这也属于匿名管道的一种,只不过这种是兄弟间进程通信
我们在执行上面操作的时候,OS会给我们创建两个进程和一个通道。
3.有名管道
如果我们想要实现几个没有关系的进程之间通信,就要使用有名管道。
原理实现
我们可以创建一个特殊的管道文件,这个管道是有名字的。这个文件与正常文件没有太大差别。多个进程同时对这个文件进程操作,我们就可以看到相同的资源,而进行进程间通信。
我们是可以保证多个进程看到的是同一个管道,这是通有名管道的文件名和路径唯一确定的。
实现指令管道
我们可以是由指令创建管道---->mkfifo
实现代码管道
第一个参数是文件名,第二个参数是文件权限。
client.cpp
#include "fifo.hpp"
int main()
{
int wfd=open(PATH,O_WRONLY);
if(wfd<0)
{
cerr<<"open fail"<<errno<<"message:"<<strerror(errno)<<endl;
}
//写内容
string str;
while(1)
{
cout<<"please enter you message:"<<endl;
getline(cin,str);
if(str=="quit")
{
cout<<"quit client"<<endl;
}
int n=write(wfd,str.c_str(),str.size());
if(n<0)
{
cerr<<"write fail"<<errno<<"message:"<<strerror(errno)<<endl;
}
}
close(wfd);
return 0;
}
server.cpp
#include "fifo.hpp"
int main()
{
//创建管道
Fifo fifo(PATH);
//打开文件,进行读
int rfd=open(PATH,O_RDONLY);
if(rfd<0)
{
cerr<<"open fail"<<errno<<"message:"<<strerror(errno)<<endl;
}
char buffer[64];
while(1)
{
ssize_t n=read(rfd,buffer,sizeof(buffer));
if(n<0)
{
cerr<<"read fail"<<errno<<"message:"<<strerror(errno)<<endl;
break;
}
else if(n>0)
{
cout<<"client say:"<<buffer<<endl;
}
else
{
cout<<"quit serve"<<endl;
break;
}
}
close(rfd);
return 0;
}
fifo.hpp
#pragma once
#include <iostream>
using namespace std;
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <string>
#include <unistd.h>
#include <fcntl.h>
#define PATH "fifo"
class Fifo
{
public:
Fifo(const string&path)
:_path(path)
{
umask(0);
//创建匿名管道
int ret=mkfifo(_path.c_str(),0666);
if(ret==0)
{
cout<<"mkfifo success\n"<<endl;
}
else
{
cerr<<"mkfifo fail"<<errno<<"message:"<<strerror(errno)<<endl;
}
}
~Fifo()
{
int ret=unlink(_path.c_str());
if(ret==0)
{
cout<<"unlink success"<<endl;
}
else
{
cerr<<"unlink fail"<<errno<<"message:"<<strerror(errno)<<endl;
}
}
private:
string _path;
};
运行看一下实现情况
有名管道和匿名管道的特征和情况基本类似,只不过这个可以用于没有关系的进程
进程池补充
操作系统一次要创建多个进程进行管理,我们称为进程池。
操作系统当然也要对这些进程进行控制,发送命令,就必须对每个进程创建一个管道进行通信。
OS也要对这些进程和管道进行管理,先描述,在组织。
对于每个进程,要尽量实现负载均衡。
总结
以上就是今天要讲的关于linux中管道内容。希望对大家的学习有所帮助,仅供参考 如有错误请大佬指点我会尽快去改正 欢迎大家来评论~~ 😘 😘 😘