第20章:进程间通信理论
20.1 进程间通信基础理论
20.1.1 进程与线程的区别:用餐厅来理解
想象一下你去餐厅吃饭:
进程(Process) - 就像一家完整的餐厅
- 有独立的厨房(内存空间)、收银台(文件描述符)、员工(系统资源)
- 每家餐厅都是独立的,不能直接使用隔壁餐厅的资源
- 一个餐厅可以服务多个顾客(线程)
线程(Thread) - 就像餐厅里的服务员
- 多个服务员共享同一家餐厅的厨房、收银台等资源
- 每个服务员有自己的工作区域(栈空间)和工作记录(寄存器)
- 服务员之间可以方便地共享餐厅的资源
资源隔离与共享
cpp
/**
* 进程资源隔离:每个进程就像独立的餐厅
*
* 想象你有两家餐厅(进程):
* 1. 麦当劳和肯德基不能共享厨房(内存空间)
* 2. 每家店有自己的收银系统(文件描述符)
* 3. 员工不能随意到隔壁店工作(进程ID不同)
* 4. 每家店有自己的经营限制(系统资源限制)
*/
class ProcessIsolation {
private:
pid_t pid_; // 进程ID(就像餐厅的营业执照号码)
std::vector<void*> vmas_; // 虚拟内存区域(餐厅的各个区域)
std::vector<int> file_descriptors_; // 文件描述符(收银台编号)
public:
/**
* 创建独立的地址空间
*
* 就像给新餐厅划分独立的区域:
* - 厨房区域(代码段):只能厨师进入,存放制作流程
* - 用餐区域(数据段):顾客可以修改,存放订单信息
* - 储物区域(栈段):临时存放物品,后进先出
*/
bool create_isolated_address_space() {
// 划分代码区域(厨房)- 只能读取和执行
void* code_segment = mmap(nullptr, 4096, PROT_READ | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// 划分数据区域(用餐区)- 可以读写
void* data_segment = mmap(nullptr, 4096, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// 划分栈区域(储物区)- 可以读写,向下增长
void* stack_segment = mmap(nullptr, 8192, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
// 检查区域划分是否成功
if (code_segment == MAP_FAILED || data_segment == MAP_FAILED ||
stack_segment == MAP_FAILED) {
return false; // 地盘划分失败
}
// 记录各个区域
vmas_.push_back(code_segment);
vmas_.push_back(data_segment);
vmas_.push_back(stack_segment);
return true; // 餐厅区域划分成功
}
/**
* 文件描述符隔离
*
* 就像餐厅管理自己的设备:
* - 关闭从原餐厅继承的不必要设备
* - 只保留基本的:收银台(0)、菜单显示屏(1)、错误报警器(2)
*/
bool create_isolated_file_descriptors() {
// 关闭从父进程继承的文件描述符(除了标准设备)
struct rlimit rlim;
if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
for (int fd = 3; fd < rlim.rlim_cur; ++fd) {
close(fd); // 关闭多余的设备
}
}
// 只保留标准设备
file_descriptors_ = {0, 1, 2}; // stdin(收银台), stdout(显示屏), stderr(报警器)
return true;
}
/**
* 获取进程资源使用情况
*
* 就像查看餐厅的经营数据:
* - 厨师工作时间:用户CPU时间
* - 经理工作时间:系统CPU时间
* - 实际占用面积:常驻内存
* - 总租用面积:虚拟内存
*/
struct ResourceUsage {
double cpu_user_time; // 用户CPU时间(厨师工作时间)
double cpu_system_time; // 系统CPU时间(经理工作时间)
size_t memory_rss; // 常驻内存(实际使用面积)
size_t memory_vsize; // 虚拟内存(总租用面积)
size_t io_read_bytes; // 读取字节数
size_t io_write_bytes; // 写入字节数
size_t num_file_descriptors; // 文件描述符数
};
ResourceUsage get_resource_usage() const {
ResourceUsage usage = {};
// 从/proc/[pid]/stat获取CPU和内存信息
std::string stat_path = "/proc/" + std::to_string(pid_) + "/stat";
std::ifstream stat_file(stat_path);
if (stat_file.is_open()) {
std::string line;
std::getline(stat_file, line);
// 解析stat文件(简化版本)
std::istringstream iss(line);
std::vector<std::string> tokens;
std::string token;
while (iss >> token) {
tokens.push_back(token);
}
if (tokens.size() > 23) {
// utime (14), stime (15)
usage.cpu_user_time = std::stoull(tokens[13]) / sysconf(_SC_CLK_TCK);
usage.cpu_system_time = std::stoull(tokens[14]) / sysconf(_SC_CLK_TCK);
// rss (24) - 页数转换为KB
usage.memory_rss = std::stoull(tokens[23]) * (sysconf(_SC_PAGESIZE) / 1024);
// vsize (23)
usage.memory_vsize = std::stoull(tokens[22]) / 1024;
}
}
// 从/proc/[pid]/io获取I/O统计
std::string io_path = "/proc/" + std::to_string(pid_) + "/io";
std::ifstream io_file(io_path);
if (io_file.is_open()) {
std::string line;
while (std::getline(io_file, line)) {
if (line.find("read_bytes:") == 0) {
usage.io_read_bytes = std::stoull(line.substr(12));
} else if (line.find("write_bytes:") == 0) {
usage.io_write_bytes = std::stoull(line.substr(13));
}
}
}
usage.num_file_descriptors = file_descriptors_.size();
return usage;
}
};
20.1.2 进程间通信机制分类:用快递系统来理解
通信模型分类
-
共享内存(Shared Memory):就像共享仓库
- 多个进程可以同时访问同一块内存区域
- 速度快,但需要协调访问(就像多人共用仓库需要排队)
-
消息传递(Message Passing):就像快递服务
- 进程A把消息放入队列,进程B从队列取出
- 不需要共享物理空间,通过中间人传递
-
管道(Pipe):就像水管
- 数据像水一样单向流动
- 一端写入,另一端读取
-
信号(Signal):就像门铃
- 异步通知机制,有人按门铃你就知道有事情发生
- 简单但信息量少
-
套接字(Socket):就像电话系统
- 可以本地或网络通信
- 双向通信,功能最强大
20.2 管道(Pipe)通信理论:用水管理解进程通信
20.2.1 匿名管道:父子进程间的"秘密水管"
想象父子进程之间建立了一条秘密水管:
核心概念
- 单向流动:水只能从一个方向流(半双工通信)
- 家庭专用:只有一家人能用(亲缘关系进程)
- 蓄水池:内核提供了一个缓冲区(64KB的"蓄水池")
- 等待机制:水池满了或空了都要等待(阻塞I/O)
cpp
#include <unistd.h>
#include <sys/wait.h>
#include <iostream>
#include <string>
/**
* 匿名管道:父子进程的秘密通信
*
* 生活化理解:
* 1. 爸爸在厨房做饭,孩子在客厅看电视
* 2. 他们之间有一根水管:爸爸可以往里面放饮料,孩子可以取出来喝
* 3. 水管是单向的:爸爸能给孩子送饮料,但孩子不能通过同根水管送东西给爸爸
* 4. 这根水管只有家里人能用(亲缘关系)
* 5. 有一个蓄水池缓冲,爸爸可以一次性放多瓶饮料进去
*/
class FamilyCommunicationPipe {
private:
int read_fd_; // 读端(孩子的取饮料口)
int write_fd_; // 写端(爸爸的放饮料口)
bool is_parent_; // 当前是爸爸还是孩子
public:
FamilyCommunicationPipe() : read_fd_(-1), write_fd_(-1), is_parent_(false) {
int pipe_fds[2]; // 创建水管的两个端口
// 创建匿名管道(安装家庭水管)
if (pipe(pipe_fds) < 0) {
throw std::runtime_error("Failed to create family communication pipe");
}
read_fd_ = pipe_fds[0]; // 0端:读端(孩子的取口)
write_fd_ = pipe_fds[1]; // 1端:写端(爸爸的放口)
std::cout << "家庭水管安装完成:爸爸端口=" << write_fd_
<< ", 孩子端口=" << read_fd_ << std::endl;
}
* - 阻塞条件:写阻塞当可用空间 < n,读阻塞当数据量 < n
* - 原子性:写操作 ≤ PIPE_BUF(4096 bytes)时保证原子性
*/
class AnonymousPipe {
private:
int read_fd_; // 读端文件描述符
int write_fd_; // 写端文件描述符
bool is_parent_; // 是否为父进程
public:
AnonymousPipe() : read_fd_(-1), write_fd_(-1), is_parent_(false) {
int pipe_fds[2];
// 创建匿名管道
if (pipe(pipe_fds) < 0) {
throw std::runtime_error("Failed to create pipe");
}
read_fd_ = pipe_fds[0];
write_fd_ = pipe_fds[1];
// 设置为非阻塞模式(可选)
set_nonblocking(read_fd_);
set_nonblocking(write_fd_);
}
~FamilyCommunicationPipe() {
close_pipe();
}
/**
* 家庭成员角色分配
*
* 就像家庭分工:
* - 爸爸负责制作饮料(写端)
* - 孩子负责享用饮料(读端)
* - 每个人只能用自己的端口,不能混用
*/
void assign_family_roles() {
pid_t pid = fork();
if (pid < 0) {
throw std::runtime_error("Family creation failed");
} else if (pid > 0) {
// 爸爸进程
is_parent_ = true;
close(read_fd_); // 爸爸不需要取饮料口
read_fd_ = -1;
std::cout << "爸爸角色分配完成,负责制作饮料" << std::endl;
} else {
// 孩子进程
is_parent_ = false;
close(write_fd_); // 孩子不需要制作饮料口
write_fd_ = -1;
std::cout << "孩子角色分配完成,负责享用饮料" << std::endl;
}
}
/**
* 爸爸制作饮料
*
* 制作规则:
* 1. 如果蓄水池满了,需要等待(阻塞模式)
* 2. 一次制作少量饮料(≤4096字节)能保证不被打断
* 3. 如果孩子离开(读端关闭),爸爸会收到通知(SIGPIPE)
*/
bool make_beverage(const std::string& drink_name) {
if (write_fd_ < 0) {
std::cout << "错误:这不是爸爸角色,不能制作饮料" << std::endl;
return false;
}
std::string beverage = drink_name + "\n";
ssize_t result = write(write_fd_, beverage.c_str(), beverage.length());
if (result < 0) {
if (errno == EPIPE) {
std::cout << "孩子已经离开,无法继续提供饮料" << std::endl;
} else if (errno == EAGAIN) {
std::cout << "蓄水池满了,请稍后再制作" << std::endl;
} else {
std::cout << "制作饮料失败:" << strerror(errno) << std::endl;
}
return false;
}
std::cout << "爸爸制作了:" << drink_name << "(" << result << "字节)" << std::endl;
return true;
}
/**
* 孩子享用饮料
*
* 享用规则:
* 1. 如果蓄水池空了且爸爸还在制作,需要等待
* 2. 如果蓄水池空了且爸爸已经离开,知道没有更多饮料了(EOF)
* 3. 一次可能收到多种饮料的混合
*/
ssize_t read_from_pipe(void* buffer, size_t size) {
if (read_fd_ < 0) {
return -1; // 没有读端
}
ssize_t bytes_read = read(read_fd_, buffer, size);
if (bytes_read < 0) {
if (errno == EAGAIN) {
// 非阻塞模式下,管道为空
return 0;
} else {
perror("Read from pipe failed");
}
} else if (bytes_read == 0) {
// 写端已关闭,到达EOF
std::cout << "Pipe EOF - writer closed" << std::endl;
}
return bytes_read;
}
/**
* 获取管道缓冲区状态
*
* 通过FIONREAD ioctl获取管道中可读的字节数
*/
int get_available_bytes() const {
if (read_fd_ < 0) return 0;
int available = 0;
if (ioctl(read_fd_, FIONREAD, &available) < 0) {
perror("FIONREAD failed");
return -1;
}
return available;
}
private:
void set_nonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
void close_pipe() {
if (read_fd_ >= 0) {
close(read_fd_);
read_fd_ = -1;
}
if (write_fd_ >= 0) {
close(write_fd_);
write_fd_ = -1;
}
}
};
/**
* 双向管道通信
*
* 实现父子进程间的双向通信,需要两个匿名管道
* 管道1:父进程写,子进程读
* 管道2:子进程写,父进程读
*/
class BidirectionalPipe {
private:
AnonymousPipe pipe_to_child_; // 父→子通信
AnonymousPipe pipe_to_parent_; // 子→父通信
public:
BidirectionalPipe() {
// 管道已在构造函数中创建
}
void fork_and_setup() {
pid_t pid = fork();
if (pid < 0) {
throw std::runtime_error("Fork failed");
} else if (pid > 0) {
// 父进程:关闭不需要的管道端
setup_parent_end();
} else {
// 子进程:关闭不需要的管道端
setup_child_end();
}
}
// 父进程向子进程发送数据
bool parent_send(const std::string& message) {
return pipe_to_child_.write_to_pipe(message.c_str(), message.size()) > 0;
}
// 父进程从子进程接收数据
std::string parent_receive() {
char buffer[1024];
ssize_t bytes = pipe_to_parent_.read_from_pipe(buffer, sizeof(buffer) - 1);
if (bytes > 0) {
buffer[bytes] = '\0';
return std::string(buffer);
}
return "";
}
// 子进程向父进程发送数据
bool child_send(const std::string& message) {
return pipe_to_parent_.write_to_pipe(message.c_str(), message.size()) > 0;
}
// 子进程从父进程接收数据
std::string child_receive() {
char buffer[1024];
ssize_t bytes = pipe_to_child_.read_from_pipe(buffer, sizeof(buffer) - 1);
if (bytes > 0) {
buffer[bytes] = '\0';
return std::string(buffer);
}
return "";
}
private:
void setup_parent_end() {
// 父进程:保留pipe_to_child的写端和pipe_to_parent的读端
// 关闭pipe_to_child的读端和pipe_to_parent的写端
// 这些操作已在fork_and_separate中完成
}
void setup_child_end() {
// 子进程:保留pipe_to_child的读端和pipe_to_parent的写端
// 关闭pipe_to_child的写端和pipe_to_parent的读端
// 这些操作已在fork_and_separate中完成
}
};
20.2.2 命名管道(Named Pipe/FIFO)
命名管道可以在无亲缘关系的进程间进行通信。
cpp
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/**
* 命名管道理论实现
*
* 核心特性:
* 1. 文件系统可见:存在于文件系统中的特殊文件
* 2. 无亲缘限制:任何进程都可以访问
* 3. 双向通信:可以创建两个FIFO实现双向通信
* 4. 持久性:直到显式删除或系统重启
*
* 数学模型:
* - FIFO容量:同匿名管道,通常64KB
* - 访问权限:遵循Unix文件权限模型 ugo+rwx
* - 阻塞行为:与匿名管道相同
*/
class NamedPipe {
private:
std::string fifo_path_; // FIFO文件路径
int read_fd_; // 读端文件描述符
int write_fd_; // 写端文件描述符
bool is_creator_; // 是否为创建者
public:
NamedPipe(const std::string& path) : fifo_path_(path), read_fd_(-1), write_fd_(-1), is_creator_(false) {
// 创建命名管道
if (mkfifo(fifo_path_.c_str(), 0666) < 0) {
if (errno != EEXIST) {
throw std::runtime_error("Failed to create FIFO");
}
// FIFO已存在,尝试打开
} else {
is_creator_ = true;
}
}
~NamedPipe() {
close_fifo();
// 只有创建者才删除FIFO文件
if (is_creator_) {
unlink(fifo_path_.c_str());
}
}
/**
* 打开FIFO进行读写
*
* 打开模式:
* - O_RDONLY:只读模式,阻塞直到有写者
* - O_WRONLY:只写模式,阻塞直到有读者
* - O_RDWR:读写模式,不阻塞
* - O_NONBLOCK:非阻塞模式
*/
bool open_fifo(int flags = O_RDWR) {
// 以读写模式打开,避免阻塞
int fd = open(fifo_path_.c_str(), flags);
if (fd < 0) {
perror("Failed to open FIFO");
return false;
}
// 根据flags设置读写端
if (flags & O_RDONLY) {
read_fd_ = fd;
} else if (flags & O_WRONLY) {
write_fd_ = fd;
} else { // O_RDWR
read_fd_ = fd;
write_fd_ = fd;
}
return true;
}
/**
* 分离读写端
*
* 为了支持双向通信,需要分离读写端
*/
bool separate_read_write() {
if (read_fd_ >= 0 && write_fd_ >= 0 && read_fd_ == write_fd_) {
// 当前是同一个文件描述符,需要分离
write_fd_ = open(fifo_path_.c_str(), O_WRONLY);
if (write_fd_ < 0) {
perror("Failed to open FIFO for writing");
return false;
}
}
return true;
}
/**
* 写入数据到FIFO
*
* 与匿名管道类似,但支持更灵活的打开模式
*/
ssize_t write_to_fifo(const void* data, size_t size) {
if (write_fd_ < 0) {
std::cerr << "No write descriptor available" << std::endl;
return -1;
}
ssize_t written = write(write_fd_, data, size);
if (written < 0) {
if (errno == EPIPE) {
std::cerr << "FIFO broken - no readers" << std::endl;
} else if (errno == EAGAIN) {
std::cerr << "FIFO full - try again later" << std::endl;
} else {
perror("Write to FIFO failed");
}
}
return written;
}
/**
* 从FIFO读取数据
*/
ssize_t read_from_fifo(void* buffer, size_t size) {
if (read_fd_ < 0) {
std::cerr << "No read descriptor available" << std::endl;
return -1;
}
ssize_t bytes_read = read(read_fd_, buffer, size);
if (bytes_read < 0) {
if (errno == EAGAIN) {
// 非阻塞模式下,FIFO为空
return 0;
} else {
perror("Read from FIFO failed");
}
} else if (bytes_read == 0) {
// 所有写者已关闭
std::cout << "FIFO EOF - no writers" << std::endl;
}
return bytes_read;
}
/**
* 获取FIFO状态信息
*/
struct FifoStatus {
bool exists; // FIFO文件是否存在
mode_t permissions; // 文件权限
size_t size; // 文件大小(通常为0)
time_t creation_time; // 创建时间
time_t modification_time; // 修改时间
int readers_count; // 当前读者数量
int writers_count; // 当前写者数量
};
FifoStatus get_status() const {
FifoStatus status = {};
struct stat st;
if (stat(fifo_path_.c_str(), &st) == 0) {
status.exists = true;
status.permissions = st.st_mode;
status.size = st.st_size;
status.creation_time = st.st_ctime;
status.modification_time = st.st_mtime;
// 通过/proc文件系统获取读者和写者数量
// 这需要更复杂的实现,这里简化处理
status.readers_count = -1; // 未知
status.writers_count = -1; // 未知
} else {
status.exists = false;
}
return status;
}
private:
void close_fifo() {
if (read_fd_ >= 0 && read_fd_ != write_fd_) {
close(read_fd_);
read_fd_ = -1;
}
if (write_fd_ >= 0) {
close(write_fd_);
write_fd_ = -1;
}
}
};
/**
* 双向命名管道通信
*
* 使用两个命名管道实现双向通信
*/
class BidirectionalNamedPipe {
private:
std::unique_ptr<NamedPipe> pipe_a_to_b_; // A→B通信
std::unique_ptr<NamedPipe> pipe_b_to_a_; // B→A通信
std::string fifo_prefix_; // FIFO文件前缀
public:
BidirectionalNamedPipe(const std::string& prefix) : fifo_prefix_(prefix) {
// 创建两个命名管道
std::string fifo1 = fifo_prefix_ + "_a_to_b";
std::string fifo2 = fifo_prefix_ + "_b_to_a";
pipe_a_to_b_ = std::make_unique<NamedPipe>(fifo1);
pipe_b_to_a_ = std::make_unique<NamedPipe>(fifo2);
}
/**
* 进程A初始化:打开读写端
*/
bool initialize_process_a() {
// A进程:pipe_a_to_b的写端,pipe_b_to_a的读端
if (!pipe_a_to_b_->open_fifo(O_WRONLY)) return false;
if (!pipe_b_to_a_->open_fifo(O_RDONLY)) return false;
return true;
}
/**
* 进程B初始化:打开读写端
*/
bool initialize_process_b() {
// B进程:pipe_a_to_b的读端,pipe_b_to_a的写端
if (!pipe_a_to_b_->open_fifo(O_RDONLY)) return false;
if (!pipe_b_to_a_->open_fifo(O_WRONLY)) return false;
return true;
}
// 进程A发送数据给进程B
bool a_send_to_b(const std::string& message) {
return pipe_a_to_b_->write_to_fifo(message.c_str(), message.size()) > 0;
}
// 进程A从进程B接收数据
std::string a_receive_from_b() {
char buffer[1024];
ssize_t bytes = pipe_b_to_a_->read_from_fifo(buffer, sizeof(buffer) - 1);
if (bytes > 0) {
buffer[bytes] = '\0';
return std::string(buffer);
}
return "";
}
// 进程B发送数据给进程A
bool b_send_to_a(const std::string& message) {
return pipe_b_to_a_->write_to_fifo(message.c_str(), message.size()) > 0;
}
// 进程B从进程A接收数据
std::string b_receive_from_a() {
char buffer[1024];
ssize_t bytes = pipe_a_to_b_->read_from_fifo(buffer, sizeof(buffer) - 1);
if (bytes > 0) {
buffer[bytes] = '\0';
return std::string(buffer);
}
return "";
}
};
20.3 System V IPC机制
20.3.1 消息队列
System V消息队列提供了更复杂的进程间通信机制。
cpp
#include <sys/msg.h>
#include <sys/ipc.h>
#include <cstring>
/**
* System V消息队列理论实现
*
* 核心特性:
* 1. 消息类型:支持不同类型的消息
* 2. 优先级:可以按类型优先级接收
* 3. 持久性:直到显式删除或系统重启
* 4. 内核管理:由内核维护消息队列
*
* 数学模型:
* - 队列容量:msg_qbytes(通常受系统限制)
* - 消息大小:0 < msg_sz ≤ MSGMAX(通常为8192字节)
* - 消息数量:0 ≤ msg_qnum ≤ MSGMNB(通常为16384)
*/
// 消息结构体
struct Message {
long mtype; // 消息类型(必须 > 0)
char mtext[1024]; // 消息内容
Message() : mtype(1) {
memset(mtext, 0, sizeof(mtext));
}
Message(long type, const std::string& text) : mtype(type) {
strncpy(mtext, text.c_str(), sizeof(mtext) - 1);
mtext[sizeof(mtext) - 1] = '\0';
}
};
class SystemVMessageQueue {
private:
int msgid_; // 消息队列ID
key_t key_; // 消息队列键
bool is_creator_; // 是否为创建者
public:
SystemVMessageQueue(key_t key = IPC_PRIVATE) : msgid_(-1), key_(key), is_creator_(false) {
// 创建消息队列
create_queue();
}
~SystemVMessageQueue() {
cleanup();
}
/**
* 创建消息队列
*
* 权限模型:
* - IPC_CREAT:如果不存在则创建
* - IPC_EXCL:与IPC_CREAT一起使用,如果存在则失败
* - 权限位:0666(所有用户可读写)
*/
bool create_queue() {
if (key_ == IPC_PRIVATE) {
// 私有队列,每次创建新的
msgid_ = msgget(key_, IPC_CREAT | 0666);
is_creator_ = true;
} else {
// 尝试获取已存在的队列,如果不存在则创建
msgid_ = msgget(key_, IPC_CREAT | IPC_EXCL | 0666);
if (msgid_ < 0 && errno == EEXIST) {
// 队列已存在,尝试打开
msgid_ = msgget(key_, 0666);
} else if (msgid_ >= 0) {
is_creator_ = true;
}
}
if (msgid_ < 0) {
perror("msgget failed");
return false;
}
return true;
}
/**
* 发送消息
*
* 发送规则:
* 1. 如果队列满,阻塞直到有空间(阻塞模式)
* 2. 消息大小不能超过系统限制
* 3. 消息类型必须 > 0
*
* 数学模型:
- 发送延迟:T_send = T_kernel + T_copy + T_sync
* - 吞吐量:Throughput = msg_sz / T_send
*/
bool send_message(long msg_type, const std::string& text, bool blocking = true) {
if (msgid_ < 0) return false;
Message msg(msg_type, text);
size_t msg_size = sizeof(msg.mtext);
int flags = 0;
if (!blocking) {
flags |= IPC_NOWAIT; // 非阻塞模式
}
if (msgsnd(msgid_, &msg, msg_size, flags) < 0) {
if (errno == EAGAIN) {
std::cerr << "Message queue full" << std::endl;
} else if (errno == EINVAL) {
std::cerr << "Invalid message size or type" << std::endl;
} else {
perror("msgsnd failed");
}
return false;
}
return true;
}
/**
* 接收消息
*
* 接收模式:
* 1. msgtyp > 0:接收指定类型的第一个消息
* 2. msgtyp = 0:接收队列中的第一个消息
* 3. msgtyp < 0:接收类型 ≤ |msgtyp| 的最小类型消息
*
* 性能特征:
* - 时间复杂度:O(n) 最坏情况,需要遍历消息队列
* - 空间复杂度:O(1),使用用户提供的缓冲区
*/
std::string receive_message(long msg_type = 0, bool blocking = true) {
if (msgid_ < 0) return "";
Message msg;
size_t msg_size = sizeof(msg.mtext);
int flags = 0;
if (!blocking) {
flags |= IPC_NOWAIT; // 非阻塞模式
}
ssize_t bytes_received = msgrcv(msgid_, &msg, msg_size, msg_type, flags);
if (bytes_received < 0) {
if (errno == ENOMSG) {
// 没有指定类型的消息(非阻塞模式)
return "";
} else if (errno == EAGAIN) {
// 队列为空(非阻塞模式)
return "";
} else if (errno == E2BIG) {
std::cerr << "Message too large for buffer" << std::endl;
return "";
} else {
perror("msgrcv failed");
return "";
}
}
return std::string(msg.mtext, bytes_received);
}
/**
* 获取队列状态信息
*
* 通过msgctl获取队列的统计信息
*/
struct QueueInfo {
long msg_qnum; // 当前消息数量
long msg_qbytes; // 队列最大字节数
long msg_lspid; // 最后发送进程PID
long msg_lrpid; // 最后接收进程PID
time_t msg_stime; // 最后发送时间
time_t msg_rtime; // 最后接收时间
time_t msg_ctime; // 最后修改时间
uid_t msg_perm.uid; // 所有者UID
gid_t msg_perm.gid; // 所有者GID
mode_t msg_perm.mode; // 权限模式
};
QueueInfo get_queue_info() const {
QueueInfo info = {};
if (msgid_ < 0) return info;
struct msqid_ds queue_info;
if (msgctl(msgid_, IPC_STAT, &queue_info) < 0) {
perror("msgctl IPC_STAT failed");
return info;
}
info.msg_qnum = queue_info.msg_qnum;
info.msg_qbytes = queue_info.msg_qbytes;
info.msg_lspid = queue_info.msg_lspid;
info.msg_lrpid = queue_info.msg_lrpid;
info.msg_stime = queue_info.msg_stime;
info.msg_rtime = queue_info.msg_rtime;
info.msg_ctime = queue_info.msg_ctime;
info.msg_perm.uid = queue_info.msg_perm.uid;
info.msg_perm.gid = queue_info.msg_perm.gid;
info.msg_perm.mode = queue_info.msg_perm.mode;
return info;
}
/**
* 修改队列属性
*
* 可以修改队列的权限和最大字节数
*/
bool set_queue_properties(mode_t permissions, size_t max_bytes) {
if (msgid_ < 0) return false;
struct msqid_ds queue_info;
// 获取当前队列信息
if (msgctl(msgid_, IPC_STAT, &queue_info) < 0) {
perror("msgctl IPC_STAT failed");
return false;
}
// 修改属性
queue_info.msg_perm.mode = permissions;
queue_info.msg_qbytes = max_bytes;
// 设置新属性
if (msgctl(msgid_, IPC_SET, &queue_info) < 0) {
perror("msgctl IPC_SET failed");
return false;
}
return true;
}
/**
* 清空消息队列
*
* 通过读取所有消息来清空队列
*/
void clear_queue() {
QueueInfo info = get_queue_info();
// 读取所有消息
for (long i = 0; i < info.msg_qnum; ++i) {
receive_message(0, false); // 非阻塞接收
}
}
private:
void cleanup() {
if (msgid_ >= 0 && is_creator_) {
// 删除消息队列
if (msgctl(msgid_, IPC_RMID, nullptr) < 0) {
perror("msgctl IPC_RMID failed");
}
}
}
};
/**
* 高级消息队列管理器
*
* 支持多进程间的复杂通信模式
*/
class AdvancedMessageQueue {
private:
std::unique_ptr<SystemVMessageQueue> request_queue_; // 请求队列
std::unique_ptr<SystemVMessageQueue> response_queue_; // 响应队列
public:
AdvancedMessageQueue(key_t request_key, key_t response_key) {
request_queue_ = std::make_unique<SystemVMessageQueue>(request_key);
response_queue_ = std::make_unique<SystemVMessageQueue>(response_key);
}
/**
* 客户端-服务器通信模式
*
* 通信流程:
* 1. 客户端发送请求到请求队列
* 2. 服务器从请求队列接收请求
* 3. 服务器处理请求并发送响应到响应队列
* 4. 客户端从响应队列接收响应
*
* 数学模型:
* - 请求延迟:T_req = T_send + T_queue + T_process + T_response
* - 吞吐量:Throughput = 1 / T_req
* - 并发度:取决于服务器处理能力
*/
// 客户端发送请求
bool client_send_request(const std::string& request, long client_id) {
return request_queue_->send_message(client_id, request);
}
// 客户端接收响应
std::string client_receive_response(long client_id) {
return response_queue_->receive_message(client_id);
}
// 服务器接收请求
std::pair<std::string, long> server_receive_request() {
// 接收任意类型的消息(阻塞)
std::string request = request_queue_->receive_message(0, true);
if (!request.empty()) {
// 获取发送者的客户端ID(通过消息类型)
auto info = request_queue_->get_queue_info();
long client_id = info.msg_lrpid; // 最后接收的进程ID
return {request, client_id};
}
return {"", -1};
}
// 服务器发送响应
bool server_send_response(const std::string& response, long client_id) {
return response_queue_->send_message(client_id, response);
}
};
20.3.2 共享内存
共享内存是最快的IPC机制,允许多个进程共享同一块物理内存。
cpp
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/mman.h>
/**
* System V共享内存理论实现
*
* 核心特性:
* 1. 零拷贝:无需内核空间复制
* 2. 高性能:直接内存访问
* 3. 同步需求:需要额外的同步机制
* 4. 持久性:直到显式删除或系统重启
*
* 数学模型:
* - 访问延迟:T_access = T_mmu + T_cache + T_memory
* - 带宽:Bandwidth = min(Memory_bandwidth, Cache_bandwidth)
* - 并发控制:需要信号量或互斥锁
*/
class SystemVSharedMemory {
private:
int shmid_; // 共享内存ID
key_t key_; // 共享内存键
void* shm_addr_; // 共享内存地址
size_t size_; // 共享内存大小
bool is_creator_; // 是否为创建者
public:
SystemVSharedMemory(key_t key, size_t size)
: shmid_(-1), key_(key), shm_addr_(nullptr), size_(size), is_creator_(false) {
create_shared_memory();
}
~SystemVSharedMemory() {
cleanup();
}
/**
* 创建共享内存
*
* 创建标志:
* - IPC_CREAT:创建新的共享内存
* - IPC_EXCL:与IPC_CREAT一起使用,如果存在则失败
* - SHM_R:读权限
* - SHM_W:写权限
*
* 数学分析:
* - 内存分配:按页对齐,size = ceil(size / PAGE_SIZE) * PAGE_SIZE
* - 权限检查:mode & ~umask
*/
bool create_shared_memory() {
int flags = IPC_CREAT | 0666; // 读写权限
if (key_ != IPC_PRIVATE) {
flags |= IPC_EXCL; // 尝试创建新的
}
shmid_ = shmget(key_, size_, flags);
if (shmid_ < 0) {
if (errno == EEXIST && (flags & IPC_EXCL)) {
// 已存在,尝试打开
shmid_ = shmget(key_, 0, 0666);
if (shmid_ >= 0) {
// 获取实际大小
struct shmid_ds shm_info;
if (shmctl(shmid_, IPC_STAT, &shm_info) == 0) {
size_ = shm_info.shm_segsz;
}
}
}
if (shmid_ < 0) {
perror("shmget failed");
return false;
}
} else {
is_creator_ = true;
}
// 附加共享内存到进程地址空间
attach_shared_memory();
return true;
}
/**
* 附加共享内存
*
* 附加标志:
* - SHM_RDONLY:只读附加
* - SHM_RND:地址向下对齐到SHMLBA
* - 0:读写附加
*
* 地址映射:
* - 内核选择地址:shm_addr = nullptr
* - 用户指定地址:shm_addr = user_address
*/
bool attach_shared_memory(void* attach_addr = nullptr, bool read_only = false) {
if (shmid_ < 0) return false;
int flags = 0;
if (read_only) {
flags |= SHM_RDONLY;
}
shm_addr_ = shmat(shmid_, attach_addr, flags);
if (shm_addr_ == reinterpret_cast<void*>(-1)) {
perror("shmat failed");
shm_addr_ = nullptr;
return false;
}
return true;
}
/**
* 分离共享内存
*
* 分离过程:
* 1. 减少引用计数
* 2. 从进程地址空间移除映射
* 3. 如果引用计数为0,标记为销毁(如果已标记IPC_RMID)
*/
bool detach_shared_memory() {
if (shm_addr_ == nullptr) return true;
if (shmdt(shm_addr_) < 0) {
perror("shmdt failed");
return false;
}
shm_addr_ = nullptr;
return true;
}
/**
* 写入数据到共享内存
*
* 内存访问:
* - 直接内存访问,无系统调用开销
* - 需要同步机制保护并发访问
* - 遵循CPU内存模型(缓存一致性)
*
* 性能分析:
* - 延迟:T_write = T_cache + T_memory(约50-100ns)
* - 带宽:受限于内存带宽(约20-50 GB/s)
*/
bool write_data(size_t offset, const void* data, size_t size) {
if (shm_addr_ == nullptr || offset + size > size_) {
return false;
}
// 边界检查
if (data == nullptr || size == 0) {
return false;
}
// 直接内存复制
memcpy(static_cast<char*>(shm_addr_) + offset, data, size);
// 内存屏障:确保写入完成
__sync_synchronize();
return true;
}
/**
* 从共享内存读取数据
*
* 读取过程:
* 1. 检查边界条件
* 2. 直接内存复制
* 3. 可选的内存屏障
*/
bool read_data(size_t offset, void* buffer, size_t size) const {
if (shm_addr_ == nullptr || offset + size > size_ || buffer == nullptr) {
return false;
}
// 内存屏障:确保读取最新数据
__sync_synchronize();
// 直接内存复制
memcpy(buffer, static_cast<const char*>(shm_addr_) + offset, size);
return true;
}
/**
* 获取共享内存信息
*
* 统计信息:
* - 段大小:shmid_ds.shm_segsz
* - 附加进程数:shmid_ds.shm_nattch
* - 最后操作时间:shmid_ds.shm_atime, shm_dtime, shm_ctime
* - 创建者信息:shmid_ds.shm_perm.uid, gid
*/
struct SharedMemoryInfo {
size_t size; // 段大小
int attach_count; // 附加进程数
time_t last_attach_time; // 最后附加时间
time_t last_detach_time; // 最后分离时间
time_t last_change_time; // 最后修改时间
uid_t creator_uid; // 创建者UID
gid_t creator_gid; // 创建者GID
uid_t owner_uid; // 所有者UID
gid_t owner_gid; // 所有者GID
mode_t permissions; // 权限模式
};
SharedMemoryInfo get_info() const {
SharedMemoryInfo info = {};
if (shmid_ < 0) return info;
struct shmid_ds shm_info;
if (shmctl(shmid_, IPC_STAT, &shm_info) < 0) {
perror("shmctl IPC_STAT failed");
return info;
}
info.size = shm_info.shm_segsz;
info.attach_count = shm_info.shm_nattch;
info.last_attach_time = shm_info.shm_atime;
info.last_detach_time = shm_info.shm_dtime;
info.last_change_time = shm_info.shm_ctime;
info.creator_uid = shm_info.shm_perm.cuid;
info.creator_gid = shm_info.shm_perm.cgid;
info.owner_uid = shm_info.shm_perm.uid;
info.owner_gid = shm_info.shm_perm.gid;
info.permissions = shm_info.shm_perm.mode;
return info;
}
/**
* 修改共享内存属性
*
* 可修改属性:
* - 所有者UID/GID
* - 权限模式
* - 段大小(某些系统支持)
*/
bool set_properties(uid_t uid, gid_t gid, mode_t mode) {
if (shmid_ < 0) return false;
struct shmid_ds shm_info;
// 获取当前信息
if (shmctl(shmid_, IPC_STAT, &shm_info) < 0) {
perror("shmctl IPC_STAT failed");
return false;
}
// 修改属性
shm_info.shm_perm.uid = uid;
shm_info.shm_perm.gid = gid;
shm_info.shm_perm.mode = mode;
// 设置新属性
if (shmctl(shmid_, IPC_SET, &shm_info) < 0) {
perror("shmctl IPC_SET failed");
return false;
}
return true;
}
/**
* 获取共享内存地址
*/
void* get_address() const {
return shm_addr_;
}
/**
* 获取共享内存大小
*/
size_t get_size() const {
return size_;
}
private:
void cleanup() {
detach_shared_memory();
if (shmid_ >= 0 && is_creator_) {
// 标记为销毁(当所有进程都分离时)
if (shmctl(shmid_, IPC_RMID, nullptr) < 0) {
perror("shmctl IPC_RMID failed");
}
}
}
};
/**
* POSIX共享内存实现
*
* 相比System V共享内存,POSIX共享内存更现代,使用更简单
*/
class PosixSharedMemory {
private:
std::string name_; // 共享内存名称
int fd_; // 文件描述符
void* addr_; // 映射地址
size_t size_; // 大小
bool is_creator_; // 是否为创建者
public:
PosixSharedMemory(const std::string& name, size_t size)
: name_(name), fd_(-1), addr_(nullptr), size_(size), is_creator_(false) {
create_shared_memory();
}
~PosixSharedMemory() {
cleanup();
}
/**
* 创建POSIX共享内存
*
* 创建标志:
* - O_CREAT:如果不存在则创建
* - O_EXCL:与O_CREAT一起使用,如果存在则失败
* - O_RDWR:读写模式
* - O_RDONLY:只读模式
*
* 权限模式:
* - S_IRUSR:用户读权限
* - S_IWUSR:用户写权限
* - S_IRGRP:组读权限
* - S_IWGRP:组写权限
* - S_IROTH:其他用户读权限
* - S_IWOTH:其他用户写权限
*/
bool create_shared_memory() {
// 创建共享内存对象
fd_ = shm_open(name_.c_str(), O_CREAT | O_EXCL | O_RDWR, 0666);
if (fd_ < 0) {
if (errno == EEXIST) {
// 已存在,尝试打开
fd_ = shm_open(name_.c_str(), O_RDWR, 0666);
if (fd_ >= 0) {
// 获取大小
struct stat st;
if (fstat(fd_, &st) == 0) {
size_ = st.st_size;
}
}
}
} else {
is_creator_ = true;
// 设置大小
if (ftruncate(fd_, size_) < 0) {
perror("ftruncate failed");
close(fd_);
fd_ = -1;
return false;
}
}
if (fd_ < 0) {
perror("shm_open failed");
return false;
}
// 映射到进程地址空间
return map_shared_memory();
}
/**
* 映射共享内存
*
* 映射标志:
* - PROT_READ:读权限
* - PROT_WRITE:写权限
* - MAP_SHARED:共享映射
* - MAP_PRIVATE:私有映射(写时复制)
*/
bool map_shared_memory(void* addr = nullptr, bool read_only = false) {
if (fd_ < 0) return false;
int prot = PROT_READ;
if (!read_only) {
prot |= PROT_WRITE;
}
addr_ = mmap(addr, size_, prot, MAP_SHARED, fd_, 0);
if (addr_ == MAP_FAILED) {
perror("mmap failed");
addr_ = nullptr;
return false;
}
return true;
}
/**
* 取消映射
*/
bool unmap_shared_memory() {
if (addr_ == nullptr) return true;
if (munmap(addr_, size_) < 0) {
perror("munmap failed");
return false;
}
addr_ = nullptr;
return true;
}
/**
* 数据访问方法(与System V共享内存类似)
*/
bool write_data(size_t offset, const void* data, size_t size) {
if (addr_ == nullptr || offset + size > size_ || data == nullptr) {
return false;
}
memcpy(static_cast<char*>(addr_) + offset, data, size);
__sync_synchronize(); // 内存屏障
return true;
}
bool read_data(size_t offset, void* buffer, size_t size) const {
if (addr_ == nullptr || offset + size > size_ || buffer == nullptr) {
return false;
}
__sync_synchronize(); // 内存屏障
memcpy(buffer, static_cast<const char*>(addr_) + offset, size);
return true;
}
/**
* 获取共享内存信息
*/
struct SharedMemoryInfo {
size_t size; // 大小
std::string name; // 名称
bool is_mapped; // 是否已映射
int fd; // 文件描述符
};
SharedMemoryInfo get_info() const {
SharedMemoryInfo info;
info.size = size_;
info.name = name_;
info.is_mapped = (addr_ != nullptr);
info.fd = fd_;
return info;
}
private:
void cleanup() {
unmap_shared_memory();
if (fd_ >= 0) {
close(fd_);
fd_ = -1;
}
if (is_creator_) {
shm_unlink(name_.c_str());
}
}
};
20.4 信号(Signal)通信
20.4.1 信号处理理论
信号是Unix系统中最古老的IPC机制,用于进程间的异步通知。
cpp
#include <signal.h>
#include <cstring>
/**
* 信号处理理论实现
*
* 核心特性:
* 1. 异步通知:信号可以在任何时候到达
* 2. 有限类型:标准信号只有31种(SIGRTMIN之前)
* 3. 不可靠性:标准信号可能丢失
* 4. 可阻塞性:进程可以阻塞特定信号
*
* 数学模型:
* - 信号集合:S = {SIGINT, SIGTERM, SIGUSR1, ..., SIGRTMAX}
* - 信号处理:Handler: S → Action,其中Action = {忽略, 默认, 捕获}
* - 信号掩码:Mask ⊆ S,表示当前被阻塞的信号集合
*/
class SignalHandler {
private:
static SignalHandler* instance_; // 单例实例
sigset_t blocked_signals_; // 被阻塞的信号集合
std::unordered_map<int, std::function<void(int)>> handlers_; // 自定义处理器
public:
SignalHandler() {
// 初始化信号集合
sigemptyset(&blocked_signals_);
instance_ = this;
}
~SignalHandler() {
instance_ = nullptr;
}
/**
* 注册信号处理器
*
* 处理模式:
* 1. SIG_DFL:默认处理
* 2. SIG_IGN:忽略信号
* 3. 自定义函数:用户定义的处理函数
*
* 线程安全:信号处理函数必须是可重入的
*/
bool register_handler(int signum, std::function<void(int)> handler) {
if (signum < 1 || signum >= NSIG) {
return false; // 无效信号编号
}
// 保存自定义处理器
handlers_[signum] = handler;
// 设置信号处理器
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = signal_dispatcher;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; // 自动重启被中断的系统调用
if (sigaction(signum, &sa, nullptr) < 0) {
perror("sigaction failed");
handlers_.erase(signum);
return false;
}
return true;
}
/**
* 阻塞信号
*
* 阻塞机制:
* 1. 信号被挂起,直到解除阻塞
* 2. 被阻塞的信号不会触发处理器
* 3. 可以阻塞多个信号
*
* 数学模型:
* - 阻塞集合:Blocked = {s | s ∈ S ∧ sigismember(&blocked_signals_, s)}
* - 待处理信号:Pending = {s | s ∈ S ∧ sigpending(&pending_signals_)}
* - 有效信号:Effective = S - Blocked
*/
bool block_signal(int signum) {
if (signum < 1 || signum >= NSIG) {
return false;
}
sigaddset(&blocked_signals_, signum);
if (sigprocmask(SIG_BLOCK, &blocked_signals_, nullptr) < 0) {
perror("sigprocmask SIG_BLOCK failed");
sigdelset(&blocked_signals_, signum);
return false;
}
return true;
}
/**
* 解除信号阻塞
*/
bool unblock_signal(int signum) {
if (signum < 1 || signum >= NSIG) {
return false;
}
sigdelset(&blocked_signals_, signum);
if (sigprocmask(SIG_UNBLOCK, &blocked_signals_, nullptr) < 0) {
perror("sigprocmask SIG_UNBLOCK failed");
return false;
}
return true;
}
/**
* 发送信号给进程
*
* 发送规则:
* 1. kill():发送信号给指定进程
* 2. raise():发送信号给当前进程
* 3. sigqueue():发送信号并附带数据
*
* 权限检查:
* - 超级用户可以发送信号给任何进程
* - 普通用户只能发送信号给自己的进程
* - 需要具有CAP_KILL能力
*/
static bool send_signal(pid_t pid, int signum, int value = 0) {
if (signum < 0 || signum >= NSIG) {
return false;
}
union sigval sig_value;
sig_value.sival_int = value;
// 使用sigqueue发送信号和附带数据
if (sigqueue(pid, signum, sig_value) < 0) {
if (errno == EPERM) {
std::cerr << "Permission denied to send signal" << std::endl;
} else if (errno == ESRCH) {
std::cerr << "Target process not found" << std::endl;
} else {
perror("sigqueue failed");
}
return false;
}
return true;
}
/**
* 等待信号
*
* 等待机制:
* 1. pause():等待任意信号
* 2. sigsuspend():临时替换信号掩码并等待
* 3. sigtimedwait():带超时的信号等待
*
* 返回值:
* - 成功:返回信号编号
* - 超时:返回-1,errno = EAGAIN
* - 中断:返回-1,errno = EINTR
*/
int wait_for_signal(const sigset_t* wait_set = nullptr, int timeout_ms = -1) {
sigset_t effective_set;
if (wait_set != nullptr) {
effective_set = *wait_set;
} else {
// 等待所有信号
sigfillset(&effective_set);
}
if (timeout_ms < 0) {
// 无限等待
int signum;
sigwait(&effective_set, &signum);
return signum;
} else {
// 带超时等待
timespec timeout;
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_nsec = (timeout_ms % 1000) * 1000000;
siginfo_t siginfo;
int signum = sigtimedwait(&effective_set, &siginfo, &timeout);
if (signum < 0) {
if (errno == EAGAIN) {
// 超时
return -1;
} else if (errno == EINTR) {
// 被信号中断
return -2;
} else {
perror("sigtimedwait failed");
return -3;
}
}
return signum;
}
}
/**
* 获取待处理信号
*
* 检查当前进程是否有待处理的信号
*/
std::vector<int> get_pending_signals() const {
std::vector<int> pending;
sigset_t pending_signals;
if (sigpending(&pending_signals) < 0) {
perror("sigpending failed");
return pending;
}
// 检查每个信号
for (int signum = 1; signum < NSIG; ++signum) {
if (sigismember(&pending_signals, signum)) {
pending.push_back(signum);
}
}
return pending;
}
private:
/**
* 信号分发器
*
* 静态函数,作为C信号处理器的包装器
* 调用对应的C++成员函数
*/
static void signal_dispatcher(int signum) {
if (instance_ != nullptr) {
auto it = instance_->handlers_.find(signum);
if (it != instance_->handlers_.end() && it->second) {
it->second(signum);
}
}
}
static SignalHandler* instance_;
};
// 静态成员定义
SignalHandler* SignalHandler::instance_ = nullptr;
/**
* 高级信号通信系统
*
* 实现进程间基于信号的复杂通信协议
*/
class AdvancedSignalCommunication {
private:
std::unique_ptr<SignalHandler> handler_;
pid_t peer_pid_; // 对等进程PID
std::atomic<bool> communication_active_;
// 信号协议定义
enum SignalProtocol {
SIG_HANDSHAKE = SIGUSR1, // 握手信号
SIG_DATA_READY = SIGUSR2, // 数据就绪
SIG_ACK = SIGRTMIN, // 确认信号
SIG_NACK = SIGRTMIN + 1, // 否定确认
SIG_HEARTBEAT = SIGRTMIN + 2 // 心跳信号
};
public:
AdvancedSignalCommunication(pid_t peer_pid)
: handler_(std::make_unique<SignalHandler>()),
peer_pid_(peer_pid),
communication_active_(false) {
setup_protocol_handlers();
}
/**
* 建立通信连接
*
* 握手协议:
* 1. 发送握手信号
* 2. 等待对方握手信号
* 3. 确认连接建立
*
* 数学模型:
* - 握手时间:T_handshake = 2 × T_signal + T_process
* - 成功率:P_success = (1 - P_loss)^2,其中P_loss为信号丢失概率
*/
bool establish_connection(int timeout_ms = 5000) {
// 注册握手处理器
std::promise<bool> handshake_promise;
auto handshake_future = handshake_promise.get_future();
handler_->register_handler(SIG_HANDSHAKE,
[&handshake_promise](int sig) {
handshake_promise.set_value(true);
});
// 发送握手信号
if (!SignalHandler::send_signal(peer_pid_, SIG_HANDSHAKE)) {
return false;
}
// 等待对方握手信号
if (handshake_future.wait_for(std::chrono::milliseconds(timeout_ms)) ==
std::future_status::timeout) {
return false;
}
communication_active_ = true;
return true;
}
/**
* 发送数据通知
*
* 通知机制:
* 1. 发送数据就绪信号
* 2. 等待确认信号
* 3. 处理超时和重传
*/
bool notify_data_ready(int timeout_ms = 1000) {
if (!communication_active_) return false;
// 发送数据就绪信号
return SignalHandler::send_signal(peer_pid_, SIG_DATA_READY);
}
/**
* 发送心跳信号
*
* 心跳机制:
* 1. 定期发送心跳信号
* 2. 检测连接状态
* 3. 处理连接中断
*/
bool send_heartbeat() {
if (!communication_active_) return false;
return SignalHandler::send_signal(peer_pid_, SIG_HEARTBEAT);
}
private:
void setup_protocol_handlers() {
// 设置协议相关的信号处理器
handler_->register_handler(SIG_HANDSHAKE,
[this](int sig) {
// 响应握手信号
SignalHandler::send_signal(peer_pid_, SIG_HANDSHAKE);
});
handler_->register_handler(SIG_HEARTBEAT,
[this](int sig) {
// 心跳信号处理
std::cout << "Heartbeat received from " << peer_pid_ << std::endl;
});
}
};
20.5 高级IPC同步机制
20.5.1 信号量同步
信号量用于进程间的同步和互斥。
cpp
#include <sys/sem.h>
#include <sys/ipc.h>
/**
* System V信号量集管理器
*
* 核心特性:
* 1. 信号量集合:包含多个信号量的集合
* 2. 原子操作:PV操作是原子的
* 3. 阻塞等待:可以阻塞等待信号量
* 4. 超时机制:支持超时等待
*
* 数学模型:
* - 信号量集合:S = {s₁, s₂, ..., sₙ},其中sᵢ ≥ 0
* - P操作:sᵢ ← sᵢ - 1,若sᵢ < 0则阻塞
* - V操作:sᵢ ← sᵢ + 1,若有等待进程则唤醒
* - 不变式:∀i, sᵢ ≥ -wᵢ,其中wᵢ是等待进程数
*/
class SystemVSemaphoreSet {
private:
int semid_; // 信号量集ID
int sem_count_; // 信号量数量
key_t key_; // IPC键值
bool is_creator_; // 是否为创建者
public:
/**
* 构造函数
*
* 创建或打开信号量集
*/
SystemVSemaphoreSet(key_t key, int sem_count, int flags = IPC_CREAT | 0666)
: semid_(-1), sem_count_(sem_count), key_(key), is_creator_(false) {
// 创建信号量集
semid_ = semget(key, sem_count, flags);
if (semid_ < 0) {
perror("semget failed");
return;
}
// 初始化信号量(仅当创建时)
if ((flags & IPC_CREAT) && (flags & IPC_EXCL)) {
is_creator_ = true;
initialize_semaphores();
}
}
~SystemVSemaphoreSet() {
if (is_creator_ && semid_ >= 0) {
semctl(semid_, 0, IPC_RMID);
}
}
/**
* 初始化所有信号量
*
* 使用SETALL命令一次性设置所有信号量的值
*/
bool initialize_semaphores(unsigned short initial_value = 1) {
if (semid_ < 0) return false;
std::vector<unsigned short> values(sem_count_, initial_value);
union semun arg;
arg.array = values.data();
if (semctl(semid_, 0, SETALL, arg) < 0) {
perror("semctl SETALL failed");
return false;
}
return true;
}
/**
* P操作(等待)
*
* 原子地减少信号量的值
* 若信号量值为负,则阻塞等待
*
* 参数:
* - sem_num:信号量编号(0到sem_count-1)
* - blocking:是否阻塞等待
* - timeout_ms:超时时间(毫秒),-1表示无限等待
*
* 返回值:
* - true:成功获得信号量
* - false:失败或超时
*/
bool wait(int sem_num = 0, bool blocking = true, int timeout_ms = -1) {
if (semid_ < 0 || sem_num < 0 || sem_num >= sem_count_) {
return false;
}
struct sembuf sb;
sb.sem_num = sem_num;
sb.sem_op = -1; // P操作
sb.sem_flg = blocking ? 0 : IPC_NOWAIT;
if (timeout_ms > 0) {
// 使用semtimedop进行超时等待(需要Linux支持)
timespec timeout;
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_nsec = (timeout_ms % 1000) * 1000000;
#ifdef __linux__
if (semtimedop(semid_, &sb, 1, &timeout) < 0) {
if (errno == EAGAIN) {
return false; // 超时
}
perror("semtimedop failed");
return false;
}
#else
// 非Linux系统使用轮询方式
return wait_with_poll(sem_num, timeout_ms);
#endif
} else {
// 普通semop操作
if (semop(semid_, &sb, 1) < 0) {
if (errno == EAGAIN) {
return false; // 非阻塞模式下信号量不可用
}
perror("semop failed");
return false;
}
}
return true;
}
/**
* V操作(释放)
*
* 原子地增加信号量的值
* 如果有进程在等待该信号量,则唤醒其中一个
*/
bool signal(int sem_num = 0) {
if (semid_ < 0 || sem_num < 0 || sem_num >= sem_count_) {
return false;
}
struct sembuf sb;
sb.sem_num = sem_num;
sb.sem_op = 1; // V操作
sb.sem_flg = 0;
if (semop(semid_, &sb, 1) < 0) {
perror("semop signal failed");
return false;
}
return true;
}
/**
* 获取信号量值
*/
int get_value(int sem_num = 0) const {
if (semid_ < 0 || sem_num < 0 || sem_num >= sem_count_) {
return -1;
}
return semctl(semid_, sem_num, GETVAL);
}
/**
* 获取信号量信息
*/
struct SemaphoreInfo {
int sem_count; // 信号量数量
key_t key; // IPC键值
int creator_pid; // 创建者PID
int last_op_pid; // 最后操作的PID
time_t last_op_time; // 最后操作时间
time_t create_time; // 创建时间
};
SemaphoreInfo get_info() const {
SemaphoreInfo info = {};
if (semid_ < 0) return info;
semid_ds sem_info;
union semun arg;
arg.buf = &sem_info;
if (semctl(semid_, 0, IPC_STAT, arg) < 0) {
perror("semctl IPC_STAT failed");
return info;
}
info.sem_count = sem_count_;
info.key = key_;
info.creator_pid = sem_info.sem_perm.cuid;
info.last_op_pid = sem_info.sem_otime ? sem_info.sem_lpid : 0;
info.last_op_time = sem_info.sem_otime;
info.create_time = sem_info.sem_ctime;
return info;
}
private:
/**
* 轮询等待(用于不支持semtimedop的系统)
*/
bool wait_with_poll(int sem_num, int timeout_ms) {
auto start = std::chrono::steady_clock::now();
while (true) {
struct sembuf sb;
sb.sem_num = sem_num;
sb.sem_op = -1;
sb.sem_flg = IPC_NOWAIT;
if (semop(semid_, &sb, 1) == 0) {
return true; // 成功获得信号量
}
if (errno != EAGAIN) {
perror("semop failed");
return false;
}
// 检查超时
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - start).count();
if (elapsed >= timeout_ms) {
return false; // 超时
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
};
/**
* 基于信号量的进程同步示例
*/
class ProcessSynchronization {
private:
std::unique_ptr<SystemVSemaphoreSet> sem_set_;
public:
/**
* 创建生产者-消费者同步
*
* 信号量分配:
* - sem[0]:互斥信号量(保护缓冲区)
* - sem[1]:空槽位信号量(缓冲区空槽数量)
* - sem[2]:满槽位信号量(缓冲区满槽数量)
*/
bool setup_producer_consumer(int buffer_size) {
key_t key = ftok("/tmp/prod_cons.key", 'P');
if (key < 0) {
perror("ftok failed");
return false;
}
// 创建3个信号量的集合
sem_set_ = std::make_unique<SystemVSemaphoreSet>(key, 3);
if (!sem_set_ || !sem_set_->get_info().sem_count) {
return false;
}
// 初始化信号量
// sem[0] = 1 (互斥)
sem_set_->get_semaphore_set()->initialize_semaphore(0, 1);
// sem[1] = buffer_size (空槽位)
sem_set_->get_semaphore_set()->initialize_semaphore(1, buffer_size);
// sem[2] = 0 (满槽位)
sem_set_->get_semaphore_set()->initialize_semaphore(2, 0);
return true;
}
/**
* 生产者操作
*
* P(empty) → P(mutex) → 生产 → V(mutex) → V(full)
*/
bool produce_item(const std::string& item, int timeout_ms = 5000) {
if (!sem_set_) return false;
// P(empty):等待空槽位
if (!sem_set_->wait(1, true, timeout_ms)) {
std::cerr << "等待空槽位超时" << std::endl;
return false;
}
// P(mutex):进入临界区
if (!sem_set_->wait(0, true, 1000)) {
sem_set_->signal(1); // 回滚
return false;
}
// 生产操作(临界区)
std::cout << "生产项目: " << item << std::endl;
// V(mutex):离开临界区
sem_set_->signal(0);
// V(full):增加满槽位
sem_set_->signal(2);
return true;
}
/**
* 消费者操作
*
* P(full) → P(mutex) → 消费 → V(mutex) → V(empty)
*/
std::string consume_item(int timeout_ms = 5000) {
if (!sem_set_) return "";
// P(full):等待满槽位
if (!sem_set_->wait(2, true, timeout_ms)) {
std::cerr << "等待满槽位超时" << std::endl;
return "";
}
// P(mutex):进入临界区
if (!sem_set_->wait(0, true, 1000)) {
sem_set_->signal(2); // 回滚
return "";
}
// 消费操作(临界区)
std::string item = "消费的项目";
std::cout << "消费项目: " << item << std::endl;
// V(mutex):离开临界区
sem_set_->signal(0);
// V(empty):增加空槽位
sem_set_->signal(1);
return item;
}
};
20.5.2 共享内存同步
共享内存需要额外的同步机制来确保数据一致性。
cpp
/**
* 共享内存同步管理器
*
* 结合共享内存和信号量实现进程间通信
*/
class SharedMemorySynchronization {
private:
std::unique_ptr<SharedMemory> shared_mem_; // 共享内存
std::unique_ptr<SystemVSemaphoreSet> sem_set_; // 同步信号量
public:
/**
* 同步模式定义
*/
enum SyncMode {
SYNC_READ_WRITE, // 读写同步
SYNC_READ_ONLY, // 只读同步
SYNC_WRITE_ONLY // 只写同步
};
/**
* 构造函数
*
* 创建共享内存和同步信号量
*/
SharedMemorySynchronization(const std::string& name, size_t size, SyncMode mode) {
// 创建共享内存
shared_mem_ = std::make_unique<SharedMemory>(name, size);
if (!shared_mem_->create()) {
throw std::runtime_error("创建共享内存失败");
}
// 创建同步信号量
key_t key = ftok(name.c_str(), 'S');
if (key < 0) {
throw std::runtime_error("生成IPC键值失败");
}
// 根据同步模式创建信号量
int sem_count = (mode == SYNC_READ_WRITE) ? 2 : 1;
sem_set_ = std::make_unique<SystemVSemaphoreSet>(key, sem_count);
if (!sem_set_->initialize_semaphores(1)) {
throw std::runtime_error("初始化信号量失败");
}
}
/**
* 同步写操作
*
* 确保写操作的互斥性和数据一致性
*/
bool synchronized_write(size_t offset, const void* data, size_t size, int timeout_ms = 5000) {
if (!shared_mem_ || !sem_set_) return false;
// 获取写锁(信号量0)
if (!sem_set_->wait(0, true, timeout_ms)) {
return false;
}
// 执行写操作
bool result = shared_mem_->write_data(offset, data, size);
// 释放写锁
sem_set_->signal(0);
return result;
}
/**
* 同步读操作
*
* 确保读操作的数据一致性
*/
bool synchronized_read(size_t offset, void* buffer, size_t size, int timeout_ms = 5000) const {
if (!shared_mem_) return false;
// 获取读锁(如果有的话)
if (sem_set_ && sem_set_->get_info().sem_count > 1) {
if (!sem_set_->wait(1, true, timeout_ms)) {
return false;
}
}
// 执行读操作
bool result = shared_mem_->read_data(offset, buffer, size);
// 释放读锁
if (sem_set_ && sem_set_->get_info().sem_count > 1) {
sem_set_->signal(1);
}
return result;
}
/**
* 生产者-消费者模式
*
* 使用共享内存实现高效的生产者-消费者通信
*/
class ProducerConsumerBuffer {
private:
SharedMemorySynchronization* sync_mgr_;
// 缓冲区结构定义
struct BufferHeader {
std::atomic<size_t> write_pos; // 写位置
std::atomic<size_t> read_pos; // 读位置
std::atomic<size_t> data_count; // 数据计数
size_t buffer_size; // 缓冲区大小
char data[]; // 数据区(柔性数组)
};
BufferHeader* header_; // 缓冲区头
char* data_area_; // 数据区指针
public:
ProducerConsumerBuffer(SharedMemorySynchronization* sync_mgr)
: sync_mgr_(sync_mgr), header_(nullptr), data_area_(nullptr) {
// 映射共享内存
auto info = sync_mgr_->shared_mem_->get_info();
if (info.is_mapped) {
void* addr = sync_mgr_->shared_mem_->get_address();
header_ = static_cast<BufferHeader*>(addr);
data_area_ = header_->data;
}
}
/**
* 初始化缓冲区
*/
bool initialize(size_t buffer_size) {
if (!header_) return false;
header_->write_pos = 0;
header_->read_pos = 0;
header_->data_count = 0;
header_->buffer_size = buffer_size;
return true;
}
/**
* 生产数据
*/
bool produce(const void* data, size_t size, int timeout_ms = 5000) {
if (!header_ || !data_area_) return false;
// 检查缓冲区是否有足够空间
size_t available_space = header_->buffer_size - header_->data_count;
if (size > available_space) {
return false;
}
// 获取写锁
sync_mgr_->sem_set_->wait(0, true, timeout_ms);
// 写入数据
size_t write_pos = header_->write_pos.load();
memcpy(data_area_ + write_pos, data, size);
// 更新写位置和计数
header_->write_pos = (write_pos + size) % header_->buffer_size;
header_->data_count += size;
// 释放写锁
sync_mgr_->sem_set_->signal(0);
return true;
}
/**
* 消费数据
*/
bool consume(void* buffer, size_t size, int timeout_ms = 5000) {
if (!header_ || !data_area_) return false;
// 检查是否有足够数据
if (header_->data_count < size) {
return false;
}
// 获取读锁
sync_mgr_->sem_set_->wait(1, true, timeout_ms);
// 读取数据
size_t read_pos = header_->read_pos.load();
memcpy(buffer, data_area_ + read_pos, size);
// 更新读位置和计数
header_->read_pos = (read_pos + size) % header_->buffer_size;
header_->data_count -= size;
// 释放读锁
sync_mgr_->sem_set_->signal(1);
return true;
}
/**
* 获取缓冲区状态
*/
struct BufferStatus {
size_t write_position;
size_t read_position;
size_t data_count;
size_t buffer_size;
size_t available_space;
double utilization_rate;
};
BufferStatus get_status() const {
BufferStatus status = {};
if (header_) {
status.write_position = header_->write_pos.load();
status.read_position = header_->read_pos.load();
status.data_count = header_->data_count.load();
status.buffer_size = header_->buffer_size;
status.available_space = header_->buffer_size - status.data_count;
status.utilization_rate = static_cast<double>(status.data_count) /
header_->buffer_size * 100.0;
}
return status;
}
};
/**
* 创建生产者-消费者缓冲区
*/
std::unique_ptr<ProducerConsumerBuffer> create_producer_consumer_buffer(size_t buffer_size) {
auto buffer = std::make_unique<ProducerConsumerBuffer>(this);
if (buffer->initialize(buffer_size)) {
return buffer;
}
return nullptr;
}
};
20.6 性能分析与优化
20.6.1 IPC机制性能比较
不同IPC机制的性能特征比较:
cpp
/**
* IPC性能测试框架
*/
class IPCPerformanceTest {
public:
struct PerformanceMetrics {
double throughput; // 吞吐量(MB/s)
double latency; // 延迟(微秒)
double cpu_usage; // CPU使用率(%)
size_t message_count; // 消息数量
size_t total_data_size; // 总数据量(字节)
std::chrono::microseconds duration; // 测试持续时间
};
/**
* 测试管道性能
*/
static PerformanceMetrics test_pipe_performance(size_t message_size, size_t message_count) {
PerformanceMetrics metrics = {};
auto start_time = std::chrono::high_resolution_clock::now();
// 创建管道
int pipefd[2];
if (pipe(pipefd) < 0) {
perror("pipe failed");
return metrics;
}
// 准备测试数据
std::vector<char> send_buffer(message_size, 'A');
std::vector<char> recv_buffer(message_size);
// 执行测试
for (size_t i = 0; i < message_count; ++i) {
// 写入数据
if (write(pipefd[1], send_buffer.data(), message_size) != static_cast<ssize_t>(message_size)) {
perror("write failed");
break;
}
// 读取数据
if (read(pipefd[0], recv_buffer.data(), message_size) != static_cast<ssize_t>(message_size)) {
perror("read failed");
break;
}
}
auto end_time = std::chrono::high_resolution_clock::now();
metrics.duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
// 计算性能指标
metrics.message_count = message_count;
metrics.total_data_size = message_size * message_count;
metrics.throughput = (metrics.total_data_size * 1000000.0) / (1024.0 * 1024.0 * metrics.duration.count());
metrics.latency = static_cast<double>(metrics.duration.count()) / message_count;
close(pipefd[0]);
close(pipefd[1]);
return metrics;
}
/**
* 测试共享内存性能
*/
static PerformanceMetrics test_shared_memory_performance(size_t message_size, size_t message_count) {
PerformanceMetrics metrics = {};
auto start_time = std::chrono::high_resolution_clock::now();
// 创建共享内存
SharedMemory shared_mem("/perf_test", message_size * 2);
if (!shared_mem.create()) {
return metrics;
}
// 准备测试数据
std::vector<char> send_buffer(message_size, 'B');
std::vector<char> recv_buffer(message_size);
// 执行测试
for (size_t i = 0; i < message_count; ++i) {
size_t offset = (i % 2) * message_size;
// 写入数据
shared_mem.write_data(offset, send_buffer.data(), message_size);
// 读取数据
shared_mem.read_data(offset, recv_buffer.data(), message_size);
}
auto end_time = std::chrono::high_resolution_clock::now();
metrics.duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
// 计算性能指标
metrics.message_count = message_count;
metrics.total_data_size = message_size * message_count * 2; // 读写双向
metrics.throughput = (metrics.total_data_size * 1000000.0) / (1024.0 * 1024.0 * metrics.duration.count());
metrics.latency = static_cast<double>(metrics.duration.count()) / message_count;
return metrics;
}
/**
* 综合性能测试
*/
static void comprehensive_performance_test() {
std::vector<size_t> message_sizes = {64, 256, 1024, 4096, 16384, 65536};
std::vector<size_t> message_counts = {1000, 10000, 100000};
std::cout << "\n=== IPC性能综合测试 ===" << std::endl;
std::cout << std::setw(12) << "消息大小"
<< std::setw(12) << "消息数量"
<< std::setw(15) << "机制"
<< std::setw(15) << "吞吐量(MB/s)"
<< std::setw(15) << "延迟(μs)"
<< std::endl;
std::cout << std::string(80, '-') << std::endl;
for (size_t msg_size : message_sizes) {
for (size_t msg_count : message_counts) {
// 测试管道性能
auto pipe_metrics = test_pipe_performance(msg_size, msg_count);
std::cout << std::setw(12) << msg_size
<< std::setw(12) << msg_count
<< std::setw(15) << "管道"
<< std::setw(15) << std::fixed << std::setprecision(2) << pipe_metrics.throughput
<< std::setw(15) << std::fixed << std::setprecision(2) << pipe_metrics.latency
<< std::endl;
// 测试共享内存性能
auto shm_metrics = test_shared_memory_performance(msg_size, msg_count);
std::cout << std::setw(12) << msg_size
<< std::setw(12) << msg_count
<< std::setw(15) << "共享内存"
<< std::setw(15) << std::fixed << std::setprecision(2) << shm_metrics.throughput
<< std::setw(15) << std::fixed << std::setprecision(2) << shm_metrics.latency
<< std::endl;
std::cout << std::string(80, '-') << std::endl;
}
}
}
};
20.7 实践练习
20.7.1 基础练习
-
管道通信实现:实现一个父子进程通信系统,父进程发送命令,子进程执行并返回结果。
-
共享内存设计:设计一个多进程共享的环形缓冲区,支持并发读写操作。
-
信号处理程序:编写一个信号处理程序,能够捕获并处理SIGINT、SIGTERM等常见信号。
20.7.2 进阶项目
-
多进程文件处理系统:
- 使用管道进行进程间通信
- 实现文件分块处理
- 支持进度报告和错误处理
- 包含完整的同步机制
-
高性能网络服务框架:
- 基于共享内存的缓存系统
- 多进程并发处理模型
- 进程间状态同步
- 性能监控和统计
-
分布式计算引擎:
- 任务分发和结果收集
- 进程间负载均衡
- 故障检测和恢复
- 资源管理和调度
20.7.3 理论分析题
-
比较分析:对比不同IPC机制的性能特征,分析其适用场景和限制条件。
-
死锁预防:设计一个死锁预防算法,适用于多进程共享资源环境。
-
一致性模型:分析共享内存系统中的一致性问题,提出解决方案。
20.8 学习评估
完成本章节后,你应该能够:
✅ 理解进程间通信的基本概念和分类
✅ 掌握管道、命名管道、消息队列的使用方法
✅ 熟练使用共享内存进行高效数据交换
✅ 理解信号机制和处理方法
✅ 掌握进程同步的基本原理和技术
✅ 能够设计和实现复杂的IPC系统
✅ 具备IPC性能分析和优化能力
参考文献
- Stevens, W. R. (1999). UNIX Network Programming, Volume 2: Interprocess Communications. Prentice Hall.
- Stevens, W. R., & Rago, S. A. (2013). Advanced Programming in the UNIX Environment. Addison-Wesley.
- Butenhof, D. R. (1997). Programming with POSIX Threads. Addison-Wesley.
- Kleiman, S., Shah, D., & Smaalders, B. (1995). Programming with Threads. SunSoft Press.
- ISO/IEC 9945:2009 - IEEE Std 1003.1-2008 (POSIX.1)
- Linux man pages - pipe(2), shm_open(3), semget(2), sigaction(2)