自学内容网 自学内容网

操作系统:进程通信实践-用管道实现进程之间的通信(详解)

目录

1. (简答题) 请编写程序,用管道实现进程之间的通信。即server进程实现从管道中读取数据(输出数据及其长度),client进程实现从管道中写入数据。可设置读取或写入次数,便于让程序正常执行完毕。(给出源代码、调试过程及运行截图)

server代码

client代码

【记忆语法】

1.mkfifo("my_pipe", 0666);

2.std::cerr

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系统中创建命名管道的函数调用。它接受两个参数:

  1. "my_pipe":这是要创建的命名管道的名称。在这个例子中,管道的名称是 "my_pipe"。

  2. 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_SIZEbuffer 的大小,确保不会溢出。

        例如,如果 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进程中是一种简化示例的方式。在更复杂的系统中,创建管道可能是由专门的初始化模块或者脚本完成,这样可以使serverclient进程的功能更加纯粹,专注于数据的读写和处理,便于代码的维护和理解。然而,这种设计没有充分考虑到先启动server进程的场景,导致在这种情况下会出现管道不存在而无法打开读取的问题。

调试过程

运行截图


原文地址:https://blog.csdn.net/m0_73631172/article/details/143033271

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!