当写入速度快于 reader 时 C FIFO 崩溃

C FIFO crashes when writer goes faster than reader

我有一个使用 fork 的父进程和子进程,它们使用 FIFO 管道进行通信。当编写器进程(父进程)比 reader 进程(子进程)运行得更快时,该程序有时会运行,有时会崩溃。

下面的代码是我目前的代码。

void writeprocess(char* text);
void readprocess(void);

#define FIFO_NAME     "MYTESTFIFO"
#define MAX         200

int main(int argc, const char * argv[]) {
    pid_t pid;
    pid = fork();

    if(pid==0){
        printf("Started child process.\n");
        mkfifo(FIFO_NAME, 0666);
        readprocess();
        printf("Child process finished.\n");
         exit(EXIT_SUCCESS);
    }
    else{
        printf("Started parent process.\n");
        mkfifo(FIFO_NAME, 0666);

        writeprocess("Message 1");
        writeprocess("Message 2");
        writeprocess("Message 3");
        writeprocess("Message 4");
        writeprocess("terminate");

        printf("Waiting for child process to finish.\n");
        int returnStatus;
        waitpid(pid, &returnStatus, 0);
        printf("Parent process also finished.\n");
        exit(EXIT_SUCCESS);
    }
}

void writeprocess(char* text){
    FILE *fp;
    char *send_buf;

    fp = fopen(FIFO_NAME, "w");
    asprintf( &send_buf, "%s\n", text);
    if ( fputs( send_buf, fp ) == EOF )
    {
        fprintf( stderr, "Error writing data to fifo\n");
        exit( EXIT_FAILURE );
    }
    printf("Message send: %s", send_buf);
    free( send_buf );

    fclose(fp);
}

void readprocess(){
    sleep(1);
    FILE *fp;
    char *str_result = NULL;
    char recv_buf[MAX];

    int stringdifference = 1;

    while (stringdifference)
    {
        fp = fopen(FIFO_NAME, "r");
        str_result = fgets(recv_buf, MAX, fp);
        if ( str_result != NULL )
        {
            printf("Message received: %s", recv_buf);
        }
        fclose( fp );

        stringdifference = strncmp(str_result, "terminate", 9);
    }
}

当编写器写入 FIFO 管道的速度快于 reader 的读取速度时,我收到一个退出错误并显示以下消息:"Terminated due to signal 13"。如何在让我的程序 运行 保持完整性能的同时避免这种情况发生?

我确实希望父进程能够结束进程,我必须继续使用 FIFO 管道工作。

信号 13 是 SIGPIPE。发生这种情况是因为没有人从 FIFO 中读取数据。

如果当前没有打开 FIFO 以供读取的进程,则无法写入 FIFO。如果您尝试这样做,您的进程将获得 SIGPIPE(可以忽略,将其变成 EPIPE...但无论哪种方式,它都会失败)。

正确处理此问题的一种方法:

  1. 在父级中创建 FIFO。

  2. 分叉。

    • 在写入进程中,打开写入。这将阻塞,直到 reader 打开它。保持打开状态。

    • 在reader进程中,打开写入。这将阻塞,直到作者打开它。保持打开状态。

When the writer writes faster to the FIFO pipe than the reader can read, I get an exit error with the following message: "Terminated due to signal 13".

您错误地描述了问题的性质。正如您的其他答案已经观察到的那样,信号 13 是 SIGPIPE,如果您尝试写入没有 readers.

的管道,则会传送该信号

通常有一些(有限的)保护来防止使用 FIFO 进入这种情况,因为打开 FIFO 的一端会阻塞,直到另一端也被打开。因此,如果一个进程成功打开了一个 FIFO,它就知道最初有另一个进程打开了另一端。写端打开的那一个可以期望写成功的可能性很高(但不一定没有阻塞)。然而,一旦最后一个 reader 关闭 FIFO,进一步的写入尝试将导致 SIGPIPE 被传递给写入器。当然,如果相同的或新的 reader 打开 FIFO,则允许写入恢复——这对于 FIFO 是可能的,但对于普通管道则不行。

所以问题不在于reader没有跟上,而是它不断打开和关闭FIFO。这创建了一个竞争条件,因为有多个间隔,如果 writer 尝试写入,它将在其中引发 SIGPIPE。由于写入器 重复打开和关闭 FIFO,因此要接收 SIGPIPE,写入器必须在 reader 关闭 FIFO 之前重新打开它上一条消息,但这并不意味着作者的速度超过 reader。 reader 无法在作者完成写入之前完成给定消息的阅读,因此他们的行为是交错的。作者在关闭 FIFO 和重新打开它之间没有做任何其他事情,因此它有时会在 reader 关闭之前重新打开也就不足为奇了。

解决方案很简单:让每个进程持续打开管道,直到完成与另一个进程的通信。打开和关闭每条消息没有好处,但有很多缺点。但是,对于您的特定用途,将作者的流置于行缓冲模式可能会有所帮助(setvbuf();默认情况下将是完全缓冲的)。