【问题标题】:Trying to communicate between two processes with pipe() breaks the program尝试使用 pipe() 在两个进程之间进行通信会破坏程序
【发布时间】:2020-08-07 08:58:14
【问题描述】:

我正在尝试使用管道在 C 中的两个进程之间进行通信。一切正常,直到它应该打印“hi\n”。输出是

(8841) Child here stopping self
(8841) SAYS: 19
DATA WRITED
C: 8
(8841) CONTINUING

这是该程序的简化版本。我知道事实上阅读部分有效,但写作调用似乎没有,因为它从不打印“hi\n”。有什么线索说明这是为什么?

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

volatile sig_atomic_t sigchld = 0;

void sigchldHandler(){
    sigchld = 1;
    return;
}

int main(){

    sigset_t mask,prev;
    signal(SIGCHLD, sigchldHandler);
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);

    int pid = fork();

    int fd[2];
    pipe(fd);

    sigprocmask(SIG_BLOCK, &mask, &prev);
    if (pid == 0){
        dup2(STDIN_FILENO,fd[0]);
        printf("(%d) Child here stopping self\n",getpid());
        raise(SIGSTOP);
        printf("(%d) CONTINUING\n",getpid());
        char* hello = malloc(sizeof("hi\n"));
        read(STDIN_FILENO,hello,sizeof("hi\n"));
        printf("%s",hello);
        exit(0);
    }
    sleep(0.1);
    sigprocmask(SIG_SETMASK, &prev,NULL);
    while(1){
        if (sigchld){
            int status;
            int p = waitpid(-1,&status,WNOHANG|WUNTRACED);
            if (WIFSTOPPED(status)){
                if (WSTOPSIG(status) == SIGSTOP){
                    printf("(%d) SAYS: %d\n",p, WSTOPSIG(status));
                    kill(pid,SIGCONT);
                    printf("DATA WRITED\n");
                    char* h = "hi\n";
                    int c=write(fd[1],h,sizeof(h));
                    printf("C: %i\n",c);
                    break;
                }
            }
            sigchld = 0;
        }
    }
} 

【问题讨论】:

  • sizeof(h) 是错误的。这实际上是sizeof(char *)。需要使用strlen
  • 您没有在孩子中关闭足够的文件描述符。 经验法则:如果您将管道的一端dup2() 连接到标准输入或标准输出,请尽快关闭pipe() 的两个原始文件描述符。特别是,这意味着在使用任何exec*() 系列函数之前。该规则也适用于dup()fcntl()F_DUPFD

标签: c linux pipe fork


【解决方案1】:

主要问题

你的关键问题是你打电话给pipe()你打电话给fork()。这意味着这两个进程具有完全独立的管道;他们没有互相交谈。

次要问题

当然还有其他问题。

  • 您(在父级中):int c=write(fd[1],h,sizeof(h));。您正在写入 8 个字节(您的输出包括 C: 8,因为变量 h 是大小为 8 的指针(您在 64 位系统上)。但是,该字符串仅指向 4 个字节 - 您应该使用strlen() 左右来限制写入的数据量。

  • 您没有关闭足够多的文件描述符。

  • 您将 dup2() 的参数颠倒了。这也是至关重要的。

  • 仅对 4 个字节的数据使用动态分配似乎很奇怪,但它应该可以工作。

  • 您应该将 PID 连同 hello 中的值一起打印在子节点中(为了保持一致性,如果没有别的)。最好用其他打印方式来做。

  • 父级可能应该在循环之后(关闭管道之后)等待子级。

  • sleep() 函数接受一个整数;调用sleep(0.1) 休眠零秒。对于亚秒级睡眠,您需要nanosleep() 或者可能。 usleep()(较旧,不再是 POSIX 的一部分,但广泛可用且更易于使用)。

这是工作代码:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

volatile sig_atomic_t sigchld = 0;

static void sigchldHandler(int signum)
{
    sigchld = signum;
}

int main(void)
{
    sigset_t mask, prev;
    signal(SIGCHLD, sigchldHandler);
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);

    int fd[2];
    pipe(fd);

    int pid = fork();

    sigprocmask(SIG_BLOCK, &mask, &prev);

    if (pid == 0)
    {
        /* Child */
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        close(fd[1]);
        printf("(%d) Child here stopping self\n", getpid());
        raise(SIGSTOP);
        printf("(%d) CONTINUING\n", getpid());
        char *hello = malloc(sizeof("hi\n"));
        int nbytes = read(STDIN_FILENO, hello, sizeof("hi\n"));
        printf("(%d) received %d bytes: %.*s\n", getpid(), nbytes, nbytes, hello);
        exit(0);
    }

    /* Parent */
    close(fd[0]);
    nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 100000000}, NULL);
    sigprocmask(SIG_SETMASK, &prev, NULL);
    while (1)
    {
        if (sigchld)
        {
            int status;
            int p = waitpid(-1, &status, WNOHANG | WUNTRACED);
            if (WIFSTOPPED(status))
            {
                if (WSTOPSIG(status) == SIGSTOP)
                {
                    printf("(%d) SAYS: %d\n", p, WSTOPSIG(status));
                    kill(pid, SIGCONT);
                    char *h = "hi\n";
                    int c = write(fd[1], h, strlen(h));
                    printf("DATA WRITTEN: %i\n", c);
                    close(fd[1]);
                    break;
                }
            }
            sigchld = 0;
        }
    }
    int corpse;
    int status;
    while ((corpse = wait(&status)) > 0)
        printf("PID %d exited with status 0x%.4X\n", corpse, status);
    return 0;
}

示例输出:

(66589) Child here stopping self
(66589) SAYS: 17
DATA WRITTEN: 3
(66589) CONTINUING
(66589) received 3 bytes: hi

PID 66589 exited with status 0x0000

17(在运行 macOS Mojave 10.14.6 的 Mac 上)和 19(在 Linux 机器上)之间的差异是正常的;信号编号的实际值没有被 POSIX 标准化(尽管信号 1 SIGHUP 到 15 SIGTERM 在不同系统中是相同的,因为它们在 Unix 第七版中是标准的)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-16
    • 1970-01-01
    • 1970-01-01
    • 2011-08-03
    相关资源
    最近更新 更多