fifo linux - write() 函数突然终止程序

问题描述 投票:0回答:3

我正在用 C 实现一个管道,其中多个生产者程序(在我的例子中为 9 个)将数据写入一个消费者程序。

问题是一些生产者(有时是一两个)在调用 write() 函数时突然退出程序。

代码很简单,这里是生产者代码:

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>

#define MSG_SIZE_BYTES 4

void send(unsigned int * msg){

    int fd, msg_size;
    int r;
    char buffer [5];
    char myfifo[50] = "/tmp/myfifo";

    fd = open(myfifo, O_WRONLY);

    if(fd == -1){
        perror("error open SEND to fifo");
    }

    r = write(fd, msg, MSG_SIZE_BYTES);

    if(r == -1){
        perror("error writing to fifo");
     }

    close(fd);
    printf("Message send\n");
}

int main(int argc, char *argv[]){
    int cluster_id = atoi(argv[1]);
    unsigned int msg[1];
    msg[0] = cluster_id;

    while(1){
        printf("Press a key to continue...\n");
        getchar();
        send(msg);
    }
}

这是消费者代码

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>

#define MSG_SIZE_BYTES 4

int receive(unsigned int * received_msg){
    int fd, msg_size;
    int ret_code;
    char buffer [5];
    char myfifo[50] = "/tmp/myfifo";

    fd = open(myfifo, O_RDONLY);

    if(fd == -1) 
       perror("error open RECV to fifo");

    ret_code = read(fd, received_msg, MSG_SIZE_BYTES);

    close(fd);

    if (ret_code == -1){
        printf("\nERROR\n");    
        return 0;
    }

    return 1;
}

void main(){

    mkfifo("/tmp/myfifo", 0666);

    unsigned int msg[1];
    while(1){
       receive(msg);
       printf("receive msg from id %d\n", msg[0]);

    }
}

我使用以下命令编译生产者和消费者:gcc -o my_progam my_program.c

要重现该问题,您需要打开 9 个终端来运行每个生产者,并打开 1 个终端来运行消费者。 执行消费者:./consumer

同时在所有终端中执行生产者,将命令行传递的关联 ID 传递给每个执行。例如:./生产者 0、./生产者 1.

生产者发送消息几次(平均10次)后,任意一个生产者会突然停止执行,从而显示问题。

下图描述了执行过程: Terminals ready to execute

下图描述了生产者 ID 3 上的错误 Error on producer 3

提前致谢

c linux named-pipes fifo
3个回答
4
投票

看起来消费者程序读取数据后关闭了管道的读取端:

fd = open(myfifo, O_RDONLY);

if(fd == -1){
     perror("error open RECV to fifo");
}
ret_code = read(fd, received_msg, MSG_SIZE_BYTES);

close(fd);

当前正在尝试

write()
数据(即在
write()
系统调用中被阻止)的所有其他编写器现在都会收到
SIGPIPE
,这会导致程序终止(如果未指定其他信号处理)。

当生产者写入时,您的消费者程序可能不会关闭文件描述符。只需读取下一条数据即可,无需关闭。


1
投票

问题已解决:

问题是我在每条消息处打开和关闭 FIFO,在一些写入尝试中生成损坏的管道。删除 close() 并在代码请求处插入生产者和消费者的 open() 函数而不是在循环内解决了问题。

这是修复了错误的生产者代码:

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>

#define MSG_SIZE_BYTES 4

int my_fd;

void send(unsigned int * msg){

    int fd, msg_size;
    int r;
    char buffer [5];
    char myfifo[50] = "/tmp/myfifo"

    if(fd == -1){
        perror("error open SEND to fifo");
    }

    r = write(my_fd, msg, MSG_SIZE_BYTES);

    if(r == -1){
        perror("error writing to fifo");
     }

    //close(fd);
    printf("Message send\n");
}

int main(int argc, char *argv[]){
    int cluster_id = atoi(argv[1]);
    unsigned int msg[1];
    msg[0] = cluster_id;

    my_fd = open("/tmp/myfifo", O_WRONLY);

    while(1){
        printf("Press a key to continue...\n");
        getchar();
        send(msg);
    }
}

这是消费者代码:

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>

#define MSG_SIZE_BYTES 4

int my_fd;

int receive(unsigned int * received_msg){
    int fd, msg_size;
    int ret_code;
    char buffer [5];
    char myfifo[50] = "/tmp/myfifo";

    if(fd == -1) 
       perror("error open RECV to fifo");

    ret_code = read(my_fd, received_msg, MSG_SIZE_BYTES);

    //close(fd);

    if (ret_code == -1){
        printf("\nERROR\n");    
        return 0;
    }

    return 1;
}

void main(){

    mkfifo("/tmp/myfifo", 0666);
    my_fd = open("/tmp/myfifo", O_RDONLY);

    unsigned int msg[1];

    while(1){
       receive(msg);
       printf("receive msg from id %d\n", msg[0]);

    }
}

谢谢大家!!


0
投票

请注意,网络也可能发生这种情况,并且并不总是能够修复客户端/消费者。

有时问题是,当调用 write 函数时,如果有 SIGPIPE,write 函数将突然退出 C 程序,完全没有任何警告。如果你用 GDB 调试它,就会很清楚,但否则就不会那么明显发生了什么。

要阻止这种情况发生,请添加代码

signal(SIGPIPE,SIG_IGN)
,如下所示:

#include <signal.h>
...
int main(){
  // this suppresses the program exit behaviour on a SIGPIPE signal
  signal(SIGPIPE, SIG_IGN); 
  ...
  int result=write(...);
  if(result<0){
    puts("Write failed, but rather than the program exiting, you are reading this");
  }
}

根据

man 2 signal
SIG_IGN 会告诉信号处理程序,它不会退出程序,而是会忽略错误。由程序员来读取和解析写入输出上的负结果,并适当地处理它。

© www.soinside.com 2019 - 2024. All rights reserved.