如何用fork()从Stdin读取输入

时间:2017-03-06 21:01:08

标签: c linux process

我想阅读Stdin的输入。我在C中使用fork()方法。我有子进程和父进程。我的输入是多行的。父进程将等待子进程的终止.Child进程将只读取第一行。在子进程终止后,parent将继续读取。我想要打印线。例如;输入 - >

  1. 星期一
  2. 星期二
  3. 星期三
  4. 子进程打印'星期一',父进程打印'星期二'和'星期三'。一旦文件结束 到达程序终止。

    ./ program< input.txt中

1 个答案:

答案 0 :(得分:0)

行;听起来很直接 - 至少,只要你输入输入。事实上,很难让它出错。您尝试了什么以及尝试时发生了什么?请阅读有关如何创建MCVE(Minimal, Complete, Verfiable Example)的信息。

然而,当您从文件中读取时,它会变得更加棘手。问题是当你从终端读取时,子进程中的标准I / O例程获取第一行数据,然后让进程使用它。孩子在不读第二行的情况下退出,因此父母可以在孩子离开的地方接听。相反,如果您正在读取文件,那么标准I / O例程会读取一个充满数据的缓冲区,这些数据可以是多行。孩子读的是什么,父母不能。因此,对终端输入有效的方法不适用于文件输入。

你怎么能避免这个问题? “很难。一种可能被视为作弊的可能性是让初始(父)过程在分叉之前读取一个字符。如果输入来自终端,则填充缓冲区为一行;如果从文件读取,则填充初始缓冲区为满。分叉后,子进程可以读取第一行(fgets())并打印并退出。同时,父进程在其输入缓冲区的副本中也有第一行数据(记住,分叉的子进程和父进程在分叉后几乎相同),因此它可以读取并忽略第一行(子进程正在处理) ,然后读取并打印剩余的行。然后它可以等待孩子在循环中死亡,最后读取剩余的行。这导致:

/* SO 4263-5451 */

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

int main(void)
{
    pid_t pid;
    int c = getchar();
    if (c == EOF)
    {
        fprintf(stderr, "Standard input was empty!\n");
        exit(1);
    }
    ungetc(c, stdin);
    //setvbuf(stdin, 0, _IOLBF, 0);

    if ((pid = fork()) < 0)
    {
        fprintf(stderr, "Failed to fork()!\n");
        exit(2);
    }
    if (pid == 0)
    {
        /* Child */
        char line[4096];
        if (fgets(line, sizeof(line), stdin) == 0)
        {
            fprintf(stderr, "Failed to read line in child\n");
            exit(3);
        }
        line[strcspn(line, "\n")] = '\0';
        printf("Child: read [%s]\n", line);
        exit(0);
    }
    else
    {
        int corpse;
        int status;
        while ((corpse = wait(&status)) > 0 && corpse != pid)
            printf("Parent: child %d died with status 0x%.4X\n", corpse, status);
        char line[4096];
        if (fgets(line, sizeof(line), stdin) == 0)
        {
            fprintf(stderr, "Failed to read line in parent\n");
            exit(4);
        }
        while (fgets(line, sizeof(line), stdin) != 0)
        {
            line[strcspn(line, "\n")] = '\0';
            printf("Parent: read [%s]\n", line);
        }
    }
    return 0;
}

这产生了:

Child: read [Monday]
Parent: read [Tuesday]
Parent: read [Wednesday]

我尝试使用setvbuf(stdin, 0, _IOLBF, 0);进行变量设置行缓冲但不会影响文件输入(在使用GCC 6.3.0运行macOS Sierra 10.12.3的Mac上),尽管终端也能正常工作输入

一种选择是用文件描述符代码替换标准I / O函数,并使用read(STDIN_FILENO, &c, 1)一次读取一个字符。这比较慢(很多系统调用)但可靠:

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

static int input_char(int fd)
{
    char c;
    if (read(fd, &c, 1) != 1)
        return EOF;
    return c;
}

static size_t input_line(int fd, char *buffer, size_t buflen)
{
    int c;
    size_t bufcnt = 0;
    while (bufcnt < buflen - 1 && (c = input_char(fd)) != EOF)
    {
        if (c == '\n')
            break;
        buffer[bufcnt++] = c;
    }
    buffer[bufcnt] = '\0';
    return bufcnt;
}

int main(void)
{
    pid_t pid;

    if ((pid = fork()) < 0)
    {
        fprintf(stderr, "Failed to fork()!\n");
        exit(2);
    }
    if (pid == 0)
    {
        char line[4096];
        if (input_line(STDIN_FILENO, line, sizeof(line)) == 0)
        {
            fprintf(stderr, "Failed to read line in child\n");
            exit(3);
        }

        printf("Child: read [%s]\n", line);
        exit(0);
    }
    else
    {
        int corpse;
        int status;
        while ((corpse = wait(&status)) > 0 && corpse != pid)
            printf("Parent: child %d died with status 0x%.4X\n", corpse, status);
        char line[4096];
        while (input_line(STDIN_FILENO, line, sizeof(line)) != 0)
        {
            printf("Parent: read [%s]\n", line);
        }
    }
    return 0;
}