【问题标题】:Reading in child process在子进程中读取
【发布时间】:2016-01-16 12:06:59
【问题描述】:

我的作业有问题。

在父进程死亡后,我必须在子进程中从终端读取数据。写得很清楚,父进程必须在执行子进程后立即死亡,所以我找到的解决方案(例如使用wait())对我没有用。

我的代码

int main(void)
{ 
    printf("start main\n");
    if(fork() == 0){
        char buffer[64];
        fgets(buffer, 64, stdin);
        printf("Child process: %s\n", buffer);
    }
    else printf("end main\n");
    //Using WAIT() here is not allowed in my assignment.
    return 0;
}

它不会等待我输入数据。似乎父进程结束后,子进程在后台,无法从终端读取任何数据。

结果

damian@damian-Virtualbox:-$ ./testuje
start main
end main
damian@damian-Virtualbox:-$ Child Process: 
echo test | ./testuje
start main
end
damian@damian-Virtualbox:-$ Child Process: test

程序应该做什么

print: start main
print: end main
then it should:
wait for user to type something
print: child process: text_typed_by_user

编辑:有人建议我使用tee 命令。你知道如何使用它来实现我想要的吗?

【问题讨论】:

  • 您的输出与您的代码不匹配。请准确地向我们展示代码,准确地输出,并告诉我们您的期望。
  • 我在 VirtualMachine 上有这个,复制该代码需要付出更多的努力,所以我手动输入了它。除了这个 printf 没有任何区别。
  • 请勿张贴文字图片!
  • 我会记住这一点的。

标签: c unix stdin


【解决方案1】:

您可能想使用vfork 而不是fork(检查documentation here):

pid_t vfork(void);

vfork - create a child process and block parent

【讨论】:

  • 我认为 vfork 的工作方式类似于 fork + wait()。不允许这样做,因为它会延迟父进程。父进程必须在 fork 后立即终止。
【解决方案2】:

使用wait 不是一个选项,因为它旨在供父进程用来监视子进程的退出状态。

使子进程与父退出事件同步的一种可能方法是使用所有打开的文件描述符在进程终止时关闭的事实。因此,使用socketpair 打开一对连接的套接字可用于通知子级父级已退出。默认情况下,socketpair 将创建阻塞套接字,当父级退出且sp[0] 关闭时,它将通知子级中的sp[1]read 将返回 0。未经测试的代码如下:

int sp[2];

socketpair(AF_UNIX,SOCK_STREAM,0,sp); // needs error check
switch (fork()) {
    case -1: // error
        break;
    case 0: // parent
        close(sp[1]);
        ....
        exit; // will close sp[0] too
    default: // child
        close(sp[0]);
        read(sp[1],sp,sizeof(int)); // needs error check
        // here sp[0] was used as temp buffer
        ... // do your read
 }

编辑:还有另一种方法,但它只是看起来更简单而不是更好,因为它使用 CPU 旋转直到父级退出(假设这对于练习来说是可以的)。这可能会导致竞争条件:

// in child
pid_t ppid=getppid();

while (ppid==getppid()); // loop until parent dies

如果父级在第一次调用getppid 之前退出,则可能会发生竞争。

可以通过在父进程中保存父进程pid 并仅在子进程中循环来解决此竞争:

// in parent, before fork
pid_t ppid=getpid();
....
// in child after fork
while (ppid==getppid()); // loop until parent dies

【讨论】:

  • 非常感谢您的回答,但它并没有解决我的问题。这些方法并没有真正改变任何东西。我希望子进程在父进程死亡后处于前台,因此用户可以编写一些文本 - 该进程应该打印用户输入的内容。该程序在 fork 后使用 wait(NULL) 按预期工作,但我无法保持父进程处于活动状态。
  • 当在终端和父出口运行时,孩子不能(据我所知)保持在前台,但仍然有标准输入/标准输出。
  • 那我怎样才能访问后台进程stdin/stdout?
【解决方案3】:

你必须在fork之后在子进程中调用setsid()。这将防止在父级终止时向子级发送 SIGHUP。

if(fork() == 0){
    setsid();
    char buffer[64];
    fgets(buffer, 64, stdin);
    printf("Child process: %s\n", buffer);
    exit(EXIT_SUCCESS);
}

但是,在主线程退出和 setid() 调用之间可能存在竞争条件,您可能需要处理它(丑陋但简单的 hack 将是在 fork( ))。

【讨论】:

  • 现在打印:start main, end main 并且卡住了。
  • 卡住了?不在这里。主进程终止,shell 恢复。子进程保留并从标准输入读取。主要问题是,shell 也从标准输入读取,因此可能需要多次尝试,直到 fgets 读取换行符(按住 enter 一段时间)。但我认为这是无法避免的。
【解决方案4】:

您可以结合使用setsid(与子进程创建新会话)和__fpurge 来清除标准输入并避免丢弃fgets 输入:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio_ext.h>


int main(void)
{
    printf("start main\n");
    if(fork() == 0){
        char buffer[64];

        setsid();
        __fpurge(stdin);

        fgets(buffer, 64, stdin);
        printf("Child reads: '%s'\n", buffer);
    }
    else printf("end main\n");

    return 0;
}

注意:fflush() 的 POSIX 标准声明该行为在标准输入上未定义。所以你应该避免fflush(stdin)__fpurge(在stdio_ext.h中定义)是this topic中提到的一种替代方案。

【讨论】:

    【解决方案5】:

    我已经实现了我想通过运行它来实现的目标: tee | ./myprogram.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-23
      • 1970-01-01
      • 2012-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-14
      相关资源
      最近更新 更多