管道demo
#include <iostream>
#include <unistd.h>
#include <cstring>
#include <cstdlib>
int main() {
int pipefd[2]; // 用于存储管道的文件描述符
pid_t pid;
// 创建管道
if (pipe(pipefd) == -1) {
exit(EXIT_FAILURE);
}
// 创建子进程
pid = fork();
if (pid == -1) {
exit(EXIT_FAILURE);
}
if (pid > 0) { // 父进程
// 关闭管道的写端
close(pipefd[1]);
// 从管道的读端读取数据
char buffer[128];
ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0'; // 确保字符串以 null 结尾
std::cout << "Parent received: " << buffer << std::endl;
} else {
std::cerr << "Failed to read from pipe" << std::endl;
}
// 关闭管道的读端
close(pipefd[0]);
} else { // 子进程
// 关闭管道的读端
close(pipefd[0]);
// 向管道的写端写入数据
const char *msg = "Hello from child process!";
ssize_t bytes_written = write(pipefd[1], msg, strlen(msg));
if (bytes_written < 0) {
std::cerr << "Failed to write to pipe" << std::endl;
}
// 关闭管道的写端
close(pipefd[1]);
}
return 0;
}
命名管道demo
- 创建命名管道
我们首先需要创建一个命名管道。这可以通过命令行工具 mkfifo 或在程序中使用 mkfifo() 系统调用来完成。
创建管道的命令行方式:
mkfifo /tmp/myfifo
创建管道的程序方式:
#include <sys/types.h>
#include <sys/stat.h>
#include <iostream>
int main() {
// 创建命名管道
if (mkfifo("/tmp/myfifo", 0666) == -1) {
perror("mkfifo");
return -1;
}
std::cout << "Named pipe created at /tmp/myfifo" << std::endl;
return 0;
}
- 写入者进程
写入者进程向命名管道中写入数据。
writer.cpp:
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
int main() {
const char *fifo_path = "/tmp/myfifo";
const char *message = "Hello from writer process!\n";
// 打开管道文件(写模式)
int fd = open(fifo_path, O_WRONLY);
if (fd == -1) {
perror("open");
return -1;
}
// 向管道写入数据
if (write(fd, message, strlen(message)) == -1) {
perror("write");
close(fd);
return -1;
}
std::cout << "Message sent: " << message << std::endl;
// 关闭管道文件描述符
close(fd);
return 0;
}
- 读取者进程
读取者进程从命名管道中读取数据。
reader.cpp
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
int main() {
const char *fifo_path = "/tmp/myfifo";
char buffer[128];
// 打开管道文件(读模式)
int fd = open(fifo_path, O_RDONLY);
if (fd == -1) {
perror("open");
return -1;
}
// 从管道读取数据
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read == -1) {
perror("read");
close(fd);
return -1;
}
buffer[bytes_read] = '\0'; // 确保字符串以 null 结尾
std::cout << "Message received: " << buffer << std::endl;
// 关闭管道文件描述符
close(fd);
return 0;
}
编译与运行
1. 创建管道:
如果没有提前创建管道,运行 writer 时会阻塞,直到管道被创建。
可以手动创建管道:bash复制
mkfifo /tmp/myfifo
或者运行创建管道的程序。
- 编译代码:
将 writer.cpp 和 reader.cpp 分别保存为文件。
使用以下命令编译:bash复制
g++ -o writer writer.cpp
g++ -o reader reader.cpp
- 运行程序:
在一个终端运行读取者程序:
./reader
在另一个终端运行写入者程序:
./writer
输出示例
reader终端:Message received: Hello from writer process!
writer终端:Message sent: Hello from writer process!
注意事项
- 阻塞特性:
- 默认情况下,读取者会阻塞,直到有数据可读。
- 写入者会阻塞,直到有读取者打开管道。
- 如果不希望阻塞,可以在 open() 时使用 O_NONBLOCK 标志。
- 管道文件路径:
- 确保管道文件路径一致(这里是 /tmp/myfifo)。
- 如果路径不存在,写入者会阻塞,直到管道被创建。
- 清理:
- 使用完管道后,可以手动删除管道文件:bash复制
rm /tmp/myfifo
- 跨进程通信:
- 命名管道允许不相关的进程之间通信,因此读取者和写入者可以是完全独立的程序。
通过这个示例,你可以看到命名管道如何实现进程间通信,非常适合需要跨进程传递数据的场景。
信号量
信号量(Semaphore)是一种用于进程间或线程间同步的机制,用于控制对共享资源的访问。它通过维护一个计数器来实现同步,当计数器大于零时,表示资源可用;当计数器为零时,表示资源已被占用。信号量通常用于解决互斥(Mutex)和同步(Sync)问题。
以下是信号量的基本操作:
- P操作(Wait/Down/Decrease):将信号量的值减1。如果减1后信号量的值小于0,则调用进程或线程阻塞,等待信号量的值变为非负。
-
V操作(Signal/Up/Increase):将信号量的值加1。如果加1后信号量的值大于0,则唤醒一个等待该信号量的进程或线程。
- 信号量的分类
信号量主要分为两种:
- 二进制信号量(Binary Semaphore):值只能为0或1,用于互斥访问。
- 计数信号量(Counting Semaphore):值可以是任意非负整数,用于控制多个资源的访问。
- 信号量的实现
在C++中,信号量可以通过多种方式实现,包括系统V IPC信号量、POSIX信号量、Boost库以及C++20标准中的std::semaphore。
2.1 系统V信号量
系统V信号量是基于IPC机制的信号量实现,使用semget、semop等函数。
2.2 POSIX信号量
POSIX信号量是另一种实现方式,使用sem_open、sem_wait和sem_post等函数。
2.3 Boost信号量
Boost库提供了跨平台的信号量实现,使用boost::interprocess::named_semaphore。
2.4 C++20 std::semaphore
C++20标准引入了std::semaphore,使得信号量的使用更加简洁。
- 信号量的使用场景
信号量主要用于以下场景:
- 互斥(Mutex):确保多个线程或进程不会同时访问共享资源。
- 同步(Sync):协调线程或进程的执行顺序,例如生产者-消费者问题。
- 注意事项
– 死锁:如果信号量的使用不当,可能会导致死锁。例如,多个线程或进程同时等待同一个信号量。
– 资源泄漏:在使用系统V或POSIX信号量时,需要确保信号量在程序结束时被正确删除,否则可能会导致资源泄漏。
– 性能:信号量的使用会引入上下文切换的开销,因此需要合理设计同步机制。
总结
信号量是一种强大的同步机制,适用于多种并发场景。根据具体需求,可以选择系统V信号量、POSIX信号量、Boost信号量或C++20标准中的std::semaphore。
消息队列demo
在C++中,消息队列是一种常见的进程间通信(IPC)机制,允许不同进程之间以异步方式交换消息。以下是关于C++消息队列的实现和使用方法的总结:
-
系统V消息队列
系统V消息队列是一种传统的IPC机制,基于msgget、msgsnd和msgrcv等函数。 -
POSIX消息队列
POSIX消息队列提供了另一种实现方式,使用mq_open、mq_send和mq_receive等函数。 -
Boost.Interprocess消息队列
Boost库提供了跨平台的消息队列实现,基于共享内存。 -
自定义线程安全消息队列
如果需要在多线程环境中使用消息队列,可以结合std::queue、std::mutex和std::condition_variable实现线程安全的消息队列。
总结
- 系统V和POSIX消息队列适用于进程间通信,但需要处理IPC资源的创建和销毁。
- Boost.Interprocess提供了跨平台的实现,基于共享内存。
- 自定义线程安全消息队列适用于多线程环境。
- 根据具体需求选择合适的消息队列实现方式。