产品营销网站建设百度关键词搜索量查询
前言
进程通信是指在进程间传输数据(交换信息)。 进程通信根据交换信息量的多少和效率的高低,分为低级通信(只能传递状态和整数值)和高级通信(提高信号通信的效率,传递大量数据,减轻程序编制的复杂度)。其中高级进程通信分为三种方式:共享内存模式、消息传递模式、共享文件模式。
共享内存通讯是指在计算机的内存中开辟一块空间。使这个空间能够被两个进程看见,于是形成通讯。因为实在内存中操作,所以操作系统(OS)不需要和磁盘交换数据,于是效率会更高。
一、命名管道通讯
1. 简介
命名管道就是共享文件模式,也是本篇博客的中心。命名管道的通讯在于建立通讯用的文件,然后两个进程分别以读和写的方式打开文件。于是乎就能让一个进程读到另一个进程写的东西,从而形成通信。
有命名管道通信就会有匿名管道通信,但是匿名管道通信只能用于有血缘关系的进程。为了让服务器和客户端进行交互,我们需要建立命名通信管道。于是命名管道文件诞生了。
2. 实现原理
实现命名管道的通信的主要使用函数包括mkfifo、write、read、open。
2.1. mkfifo
这是我们第一次学习mkfifo函数,下面让我来介绍一下它的功能和使用方法。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
mkfifo用来建立管道文件,它的形式如上所示。其中pathname表示的是文件所在的地址,如果只填写文件名称那么就会在当前文件夹创建文件。mode表示文件的操作权限,会改变文件是否能够读写,一般我们会将其设置为0666。
如果创建管道文件成功那么会返回0,如果出现错误会返回-1(错误码会被存储起来到errno中)。
需要注意的是该函数只能用来建立管道文件,不建议用这个函数建立其他性质的文件。
2.2. 其他文件操作函数
对于其他文件操作函数。open函数用来打开文件、write函数用于client进程向管道内写入数据、read用于server进程读取需要处理的内容。这些函数的使用方法可以参考之前的博客,链接如下:
访问文件和文件重定向_访问代码本地文件,重定向-CSDN博客文章浏览阅读1k次,点赞20次,收藏12次。主讲linux系统中打开文件的接口和对程序的输入输出重定向。_访问代码本地文件,重定向https://blog.csdn.net/2302_81342533/article/details/145203057
2.3. 实现逻辑
2.3.1. 共同部分
首先我们能够建立一个类用来封装我们所需要的功能:创建管道文件,删除管道文件、从管道文件中读取数据、向管道文件中写入数据等操作。在描述中,需要文件的地址和名称。建立文件的方式默认为0666,一次最多读取到的字节数默认为1023个。
#pragma once#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>#define FIFO_FILE "fifo"
#define EXIT_ERROR(error, error_mode) \do{ \std::cerr << error; \exit(error_mode); \}while(0)class MyFifo
{
public:// 初始化管道名称MyFifo(const std::string&& name = FIFO_FILE):_filename(name){_path = "./" + name;_fifoname = "fifoname=" + _filename;}// 展示管道文件名称bool SayFifoName(){std::cout << _fifoname << std::endl;return true;}// 创建对应管道文件bool Create(){umask(0);int n = mkfifo(_path.c_str(), 0666);if(n != 0){EXIT_ERROR("mkdir_fifo_error", 1);}return true;}// 以读得到方式打开管道文件,并读取数据int Read(){int readline = 0;// 打开管道文件int fd = open(_path.c_str(), O_RDONLY);if(fd < 0){EXIT_ERROR("open_fifo_error", 2);}// 读取数据char buffer[1024];while(true){int n = read(fd, buffer, sizeof(buffer) - 1);if(n > 0){buffer[n] = 0;readline++;std::cout << "client say#" << buffer << std::endl;}else if(n < 0){EXIT_ERROR("read_fifo_error", 3);}else{std::cout << "read is over\n";break;}}close(fd);return readline;}// 向管道内写入数据int Write(){int write_line = 0;// 打开文件int fd = open(_path.c_str(), O_WRONLY);if(fd < 0){EXIT_ERROR("open_fifo_error\n", 4);}// 写入数据while(true){std::cout << "Pleause Enter#";std::string message;getline(std::cin, message, '\n');if(message == "close write"){std::cout << message << std::endl;break;}int n = write(fd, message.c_str(), message.size());if(n < 0){EXIT_ERROR("write to fifo error\n", 5);}else if(n != message.size()){std::cerr << "write to fifo had lose byte\n";}else{write_line++;}}close(fd);return write_line;}bool Remake(){int n = unlink(_filename.c_str());if(n == 0){std::cout << "remove fifo success\n";return true;}else{std::cout << "remove fifo failed\n";return false;}}~MyFifo(){}private:std::string _path; // 管道文件地址std::string _filename; // 文件名称std::string _fifoname; // 管道名称
};
2.3.2. server(服务器部分)
服务器部分用来接收管道信息。
#include "comm.hpp"int main()
{MyFifo fifo;// 创建管道fifo.Create();// 读取管道中的数据fifo.Read();// 删除管道文件fifo.Remake();return 0;
}
2.3.3. client(客户端部分)
客户端部分用来发送信息。
#include "comm.hpp"int main()
{MyFifo write_fifo;// 向管道中写入数据write_fifo.Write();return 0;
}
3. 实际使用
因为有管道的存在,所以我们server端能够得到client端传来的数据。但我们现在没有对应的任务能够给server执行。所以我们就打印出来client传给server的数据。比如:
在原有基础上我增加了一些功能,例如用unlink删除管道文件和用“close write”关闭写端。通过增加这样的功能我们也能使客户端执行更负责的操作。但是现在的交流仍然是单向的,如果是双向的话也许我们就需要两个管道才行。
二、 共享内存通信
1. 简介
共享内存通信,就是指在内存中的两个进程都能够看到同一块区域,于是两个进程就能够进行交流了。这通常被用于两个没有血缘关系的进程,而且因为都是在内存中操作所以效率很高。缺点就是为了实现内存共享,需要约定一个方法使进程都能够找到同一块内存。只要能找到了共享内存就实现了。
2. 实现原理
在实现通信之前,我们需要将内存共享。那么我们将其分为两步,一步是创建,另一步是分享。创建内存需要用到两个函数ftok和shmget。接下来将介绍他们的形式和用法:
2.1. shmget
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
函数的作用是在内存中开辟size大小的内存,这就相当于玩游戏时创建房间一样。key代表房间的密码,size表示房间的大小(单位byte),shmflg表示创建房间的方法。其中shmflg有两种常用的命令——IPC_CREAT 、IPC_EXCL,前者表示创建,后者表示唯一。如果需要使用IPC_EXCL就需要和IPC_CREAT一起。
返回值如果>0则表示创建成功。如果为-1则表示创建失败,失败内容会被记录在errno中。
2.2. ftok
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
这个函数就是用于构建shmget中的key中默认的方法。pathname表示地址名,proj_id表示打开文件的文件方式。
如果成功将会返回>0。反之就会返回-1,错误码会被存到errno中。可以使用perror打印。
2.3. 实现逻辑
实现逻辑和命名通讯部分的实现逻辑相似,构建服务端和客户端,描述共享内存。将方法表示为创建内存和链接内存。
实现代码暂无。
作者结语
本节的内容就先到这里,下一节将会是进程池的实现。学习完进程池之后,也能将这一节的两个通讯方式进行池化。