为什么我的 C 语言管道不工作?

3

作为一项练习,我需要使用信号处理程序和管道在两个进程之间发送一些消息。下面是我的源代码。当我运行它时,只要在它们的主方法(在这种情况下是process1()和process2())中调用pipe,我就可以让管道工作,两个进程都可以通信。但我想在信号处理程序中使用管道。但现在管道不起作用了。这是我得到的一些输出:

3 - 4 and 5 - 6
Segv at 8825
USR1 at 8824
898 sent to 4
130 received on 3
130

“898”和“130”应该相等,但实际上并不相等。 我知道管道(pipes)正在正确工作,所以我认为这与信号(signal)有关。但是,具体是什么原因呢?
源代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

int fd1[2], fd2[2], status;
int cpid, cpoid;

void process1() {   
    cpid = getpid();        /*What's my process ID?*/
    cpoid = cpid + 1;       /*And what's the other process ID?*/

    close(fd1[0]);
    close(fd2[1]);

    while (1) {}
}

void process2() {   
    cpid = getpid();
    cpoid = cpid - 1;

    close(fd1[1]);
    close(fd2[0]);

    raise(SIGSEGV);         /*Start with a SegV signal*/

    while (1) {}
}

/*Method to send a message to the other process, by pipe*/
void send (int msg) {
    if (cpid < cpoid) {
        write(fd1[1], &msg, 1);
        printf("%d sent to %d\n", msg, fd1[1]);
    } else {
        write(fd2[1], &msg, 1);
        printf("%d sent to %d\n", msg, fd2[1]);
    }
}

/*Method to receive a message from the other process*/
int receive () {
    int msg = 0;
    if (cpid < cpoid) {
        read(fd2[0], &msg, 1);
        printf("%d received on %d\n", msg, fd2[0]);
    } else {
        read(fd1[0], &msg, 1);
        printf("%d received on %d\n", msg, fd1[0]);
    }
    return msg;
}

/*The SegV Signal handler*/
void segvHandler() {
    int y = -1;
    printf("Segv at %d\n", cpid);
    kill(cpoid, SIGUSR1);           /*Send an USR1 Signal to the other proces*/

    while (y != 898) {
        y = receive();
        printf("%d\n", y);
    }
}

/*The Usr1 Signal handler*/
void usr1Handler() {
    int x = 898;
    printf("USR1 at %d\n", cpid);

    send(x);
}

int main (int argc, char *argv[]) {

    if (pipe(fd1) < 0) {
        fprintf (stderr, "Could not make pipe\n");
        return (EXIT_FAILURE);
    }
    if (pipe(fd2) < 0) {
        fprintf (stderr, "Could not make pipe\n");
        return (EXIT_FAILURE);
    }
    printf("%d - %d and %d - %d\n", fd1[0], fd1[1], fd2[0], fd2[1]);    /*Pipe numbers*/

    signal(SIGUSR1, usr1Handler);   /*Signal handlers*/
    signal(SIGSEGV, segvHandler);

    if (fork() != 0) {
        process1();
    } else {
        process2();
    }
    waitpid(-1, &status, 0);

    return EXIT_SUCCESS;
}

2
你不应该在信号处理程序中进行任何重要的I/O操作。只应该设置一个标志位,然后有一个主循环来处理它。不确定这是否是你的问题,但这是标准指导。 - chrisdowney
1
作为一个引子,898 是 0x382,130 是 0x82。编辑:因此,如果发送和接收仅操作单个字节(char 类型),那么整数将被截断,因为它被隐式转换为 char 类型。 - Tobias Wärre
3
printf()不是异步安全的,在信号处理函数中不要使用它。 - Šimon Tóth
2
你不能仅仅依赖于pid只相差一个的情况。为了获取另一个进程的pid,你必须在fork时跟踪它或通过管道写入它。 - William Pursell
当然,这并不是什么出色的代码,只是为了让我看到它是如何工作的,以及为什么它不能正常运行。但我会在继续编写时记住你的建议。谢谢! - Sietse Ludger G
2个回答

3

快速查看后发现一些问题。

  • printf()不是异步信号安全的,不要在信号处理程序中调用它。

  • 您正在读写1个字节,这很可能小于sizeof(int)。

  • 您不能假设PID是连续的。在父进程中,fork()的返回值给出了子进程的PID。在子进程中,如果父进程在fork()之前存储了getpid()的返回值,那么就可以使用该值;否则请参阅getppid()。


2

正如评论中所提到的,你不应该在信号处理程序中调用printf,但这可能不是问题所在。除非你的机器上int类型只占一个字节,否则问题在于你没有写入或读取整个int,因为你只将一个字节写入了管道。(将代码更改为:write(fd[1], &msg, sizeof msg)并在读取时进行同样的更改。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接