Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG REPORT] 命名管道行为与Linux不一致 #886

Closed
fslongjin opened this issue Aug 9, 2024 · 1 comment · Fixed by #1061
Closed

[BUG REPORT] 命名管道行为与Linux不一致 #886

fslongjin opened this issue Aug 9, 2024 · 1 comment · Fixed by #1061
Assignees
Labels
A-IPC Area: 进程间通信 bug-report 这是一个bug报告(如果确认是一个bug,请管理人员添加`bug` label)

Comments

@fslongjin
Copy link
Member

描述错误

  • 问题1:对于O_NONBLOCK处理的不正确,如果读端未打开,写端应该要返回ENXIO。具体见参考链接。
  • 问题2:当读端未打开/已经关闭时,就往管道写,需要给写端进程发送SIGPIPE

参考: https://man7.org/linux/man-pages/man7/fifo.7.html
请填写您的电脑的信息:

期望行为
行为符合 https://man7.org/linux/man-pages/man7/fifo.7.html 的规定

@fslongjin fslongjin added A-IPC Area: 进程间通信 bug-report 这是一个bug报告(如果确认是一个bug,请管理人员添加`bug` label) labels Aug 9, 2024
@dragonosbot dragonosbot added the needs-triage 这个问题可能需要分类处理。如果已经完成分类,请移除它。 label Aug 9, 2024
@fslongjin fslongjin removed the needs-triage 这个问题可能需要分类处理。如果已经完成分类,请移除它。 label Aug 9, 2024
@xiaolin2004 xiaolin2004 self-assigned this Nov 26, 2024
@xiaolin2004
Copy link
Collaborator

xiaolin2004 commented Nov 27, 2024

使用以下程序测试命名管道行为

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/wait.h>

#define FIFO_PATH "/bin/test_fifo"

// 信号处理函数
void sigpipe_handler(int signo) {
    if (signo == SIGPIPE) {
        printf("Received SIGPIPE signal. Write operation failed.\n");
    }
}

void test_fifo_write(const char *scenario_desc, int nonblocking) {
    int fd;
    char *data = "Hello, FIFO!";
    printf("\n--- Testing: %s ---\n", scenario_desc);

    // 设置 O_NONBLOCK 标志
    int flags = O_WRONLY;
    if (nonblocking) {
        flags |= O_NONBLOCK;
    }

    // 打开 FIFO 写端
    fd = open(FIFO_PATH, flags);
    if (fd == -1) {
        if (errno == ENXIO) {
            printf("Result: Failed to open FIFO for writing (ENXIO: No readers).\n");
        } else {
            perror("Failed to open FIFO for writing");
        }
        return;
    }

    // 写入数据
    ssize_t bytes_written = write(fd, data, sizeof(data));
    if (bytes_written == -1) {
        if (errno == EPIPE) {
            printf("Result: Write failed with EPIPE (no readers available).\n");
        } else if (errno == ENXIO) {
            printf("Result: Write failed with ENXIO (FIFO never had readers).\n");
        } else {
            perror("Write failed with an unexpected error");
        }
    } else {
        printf("Result: Write succeeded. Bytes written: %zd\n", bytes_written);
    }

    // 关闭 FIFO 写端
    close(fd);
}

void run_tests() {
    pid_t reader_pid;

    // Case 1: Test with no readers (FIFO never had readers)
    test_fifo_write("No readers (FIFO never had readers)", 1);

    // Case 2: Test with a reader that disconnects
    reader_pid = fork();
    if (reader_pid == 0) {
        // 子进程充当读端
        int reader_fd = open(FIFO_PATH, O_RDONLY);
        if (reader_fd == -1) {
            perror("Reader failed to open FIFO");
            exit(EXIT_FAILURE);
        }
        sleep(2); // 模拟读端短暂存在
        close(reader_fd);
        exit(EXIT_SUCCESS);
    }

    sleep(1); // 确保读端已打开
    test_fifo_write("Reader exists but disconnects", 0);
    waitpid(reader_pid, NULL, 0); // 等待读端子进程退出

    // Case 3: Test with an active reader
    reader_pid = fork();
    if (reader_pid == 0) {
        // 子进程充当读端
        int reader_fd = open(FIFO_PATH, O_RDONLY);
        if (reader_fd == -1) {
            perror("Reader failed to open FIFO");
            exit(EXIT_FAILURE);
        }
        sleep(5); // 保持读端存在
        close(reader_fd);
        exit(EXIT_SUCCESS);
    }

    sleep(1); // 确保读端已打开
    test_fifo_write("Active reader exists", 0);
    waitpid(reader_pid, NULL, 0); // 等待读端子进程退出
}

int main() {
    // 设置 SIGPIPE 信号处理
    signal(SIGPIPE, sigpipe_handler);

    // 创建 FIFO
    if (mkfifo(FIFO_PATH, 0666) == -1 && errno != EEXIST) {
        perror("mkfifo failed");
        exit(EXIT_FAILURE);
    }

    // 运行测试
    run_tests();

    // 删除 FIFO
    unlink(FIFO_PATH);

    printf("\nAll tests completed.\n");
    return 0;
}

测试结果:
不论读端是否开启,是否曾经开启,都能成功写入
image

通过对Pipe::open的printf debug,发现在创建管道时,创建进程自身会成为管道的第一个读者
image

调换Pipe::open中判断O_WRONLY和O_RDONLY的判断顺序之后,出现了幽默一幕
image

可以大致将错误定位到mode判断错误

修改Pipe::open中对O_WRONLY模式的判断块,并将其置于O_RDONLY块上方(优先判断)

if mode.contains(FileMode::O_WRONLY) {
            if guard.reader == 0 {
                if mode.contains(FileMode::O_NONBLOCK) {
                    return Err(SystemError::ENXIO);
                }
            }
            guard.writer += 1;
        }

问题一解决,行为符合手册规范
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-IPC Area: 进程间通信 bug-report 这是一个bug报告(如果确认是一个bug,请管理人员添加`bug` label)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants