操作系统:进程通信实践-用管道实现进程之间的通信(详解)
目录
3.pipe_fd = open("my_pipe", O_WRONLY);
4.int bytes_read = read(pipe_fd, buffer, BUFFER_SIZE);
5.snprintf(buffer, BUFFER_SIZE, "Message %d", i);
1. (简答题) 请编写程序,用管道实现进程之间的通信。即server进程实现从管道中读取数据(输出数据及其长度),client进程实现从管道中写入数据。可设置读取或写入次数,便于让程序正常执行完毕。(给出源代码、调试过程及运行截图)
server代码
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define BUFFER_SIZE 100
int main() {
int pipe_fd;
char buffer[BUFFER_SIZE];
// 尝试创建管道,如果管道已经存在,mkfifo会返回 - 1并设置errno为EEXIST
if (mkfifo("my_pipe", 0666)== - 1 && errno!=EEXIST) {
std::cerr << "Error creating pipe" << std::endl;
return 1;
}
// 以只读方式打开管道
pipe_fd = open("my_pipe", O_RDONLY);
if (pipe_fd == -1) {
std::cerr << "Error opening pipe for reading" << std::endl;
return 1;
}
while (true) {
// 从管道读取数据
int bytes_read = read(pipe_fd, buffer, BUFFER_SIZE);
if (bytes_read == 0) {
break;
} else if (bytes_read == -1) {
std::cerr << "Error reading from pipe" << std::endl;
return 1;
}
std::cout << "Server received: " << buffer << ", length: " << bytes_read << std::endl;
}
// 关闭管道
close(pipe_fd);
return 0;
}
client代码
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#define BUFFER_SIZE 100
#define WRITE_TIMES 5
int main() {
int pipe_fd;
char buffer[BUFFER_SIZE];
// 创建命名管道,如果管道已经存在则不会创建新的
mkfifo("my_pipe", 0666);
// 以只写方式打开管道
pipe_fd = open("my_pipe", O_WRONLY);
if (pipe_fd == -1) {
std::cerr << "Error opening pipe for writing" << std::endl;
return 1;
}
for (int i = 0; i < WRITE_TIMES; ++i) {
// 构造要写入的数据
snprintf(buffer, BUFFER_SIZE, "Message %d", i);
// 写入数据到管道
write(pipe_fd, buffer, strlen(buffer) + 1);
std::cout << "Client wrote: " << buffer << std::endl;
sleep(1);
}
// 关闭管道
close(pipe_fd);
// 删除命名管道(可选)
unlink("my_pipe");
return 0;
}
【记忆语法】
1.mkfifo("my_pipe", 0666);
一个用于在Linux系统中创建命名管道的函数调用。它接受两个参数:
-
"my_pipe"
:这是要创建的命名管道的名称。在这个例子中,管道的名称是 "my_pipe"。 -
0666
:这是一个八进制的文件权限模式,表示该管道可以被所有用户(所有者、所属组和其他用户)读写。具体来说,0666
表示所有者具有读/写/执行权限(6),所属组具有读/写权限(6),其他用户也具有读/写权限(6)。
这个函数调用会创建一个名为 "my_pipe" 的命名管道,并设置相应的文件权限。命名管道是一种特殊类型的文件,允许一个进程将数据写入管道,而另一个进程可以从管道中读取数据。这种通信方式常用于进程间通信(IPC)。
2.std::cerr
输出错误信息的语句。它使用了std::cerr
对象,该对象表示标准错误流(standard error stream),通常用于输出错误信息或诊断信息。
3.pipe_fd = open("my_pipe", O_WRONLY);
打开一个名为 "my_pipe" 的文件,并以只写方式打开。
4.int bytes_read = read(pipe_fd, buffer, BUFFER_SIZE);
读取管道数据。它使用了read
函数,该函数从指定的文件描述符(pipe_fd
)读取数据并将其存储到缓冲区(buffer
)中。BUFFER_SIZE
是缓冲区的大小,表示一次最多读取的字节数。bytes_read
是一个整数变量,用于存储实际读取的字节数。如果读取成功,bytes_read
将大于0;如果到达文件末尾或发生错误,bytes_read
将为-1。
5.snprintf(buffer, BUFFER_SIZE, "Message %d", i);
将格式化的字符串 "Message %d" 写入名为 buffer
的字符数组中,其中 %d
会被替换为变量 i
的值。BUFFER_SIZE
是 buffer
的大小,确保不会溢出。
例如,如果 i
的值为 42,那么 buffer
的内容将会是 "Message 42"。如果BUFFER_SIZE
为10,而格式化后的字符串长度超过10(不包括字符串结束符'\0'
),snprintf
会截断字符串以适应缓冲区大小。
snprintf
是一个 C 语言标准库函数,用于将格式化的字符串写入一个字符数组中。
int snprintf(char *str, size_t size, const char *format, ...);
参数解释:
str
: 指向目标字符数组的指针,该数组将被填充为格式化后的字符串。size
: 指定目标字符数组的大小(包括最后的空字符 '\0')。format
: 格式化字符串,包含要插入到目标字符数组中的文本和格式说明符。...
: 可变参数列表,根据format
中的格式说明符提供相应的值。
【易错点】忘记先创建管道
如果在server
尝试打开管道之前,没有创建管道(例如在client
或者其他进程中创建管道的代码没有执行),会出现“Error opening pipe for reading”错误(这表明server
进程在尝试以只读方式打开管道时遇到了问题)。
if (mkfifo("my_pipe", 0666)== - 1 && errno!=EEXIST) {
std::cerr << "Error creating pipe" << std::endl;
return 1;
}
由图可见先打开server报错:Error opening pipe for reading,但是先打开client创建管道后可以正常运行代码。
(另外:当你在Ubuntu系统中执行./server2.cpp
时出现Permission denied
(权限被拒绝)错误,这是因为.cpp
文件是C++ 源文件,它不是可执行文件。(~.~!) Wrong again!)
从职责分离的角度来看,server
进程的主要职责是从管道读取数据并处理,将创建管道的操作放在client
进程中是一种简化示例的方式。在更复杂的系统中,创建管道可能是由专门的初始化模块或者脚本完成,这样可以使server
和client
进程的功能更加纯粹,专注于数据的读写和处理,便于代码的维护和理解。然而,这种设计没有充分考虑到先启动server
进程的场景,导致在这种情况下会出现管道不存在而无法打开读取的问题。
调试过程
运行截图
原文地址:https://blog.csdn.net/m0_73631172/article/details/143033271
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!