【发布时间】:2017-03-06 21:01:08
【问题描述】:
我想从标准输入读取输入。我在 C 中使用 fork() 方法。我有子进程和父进程。我的输入是多行的。父进程将简单地等待子进程终止。子进程将读取第一行。子进程终止后,父进程将继续读取。我想要打印线。例如;输入->
- 星期一
- 星期二
- 星期三
子进程打印'Monday',父进程打印'Tuesday'和'Wednesday'。一旦文件结束是 到达程序终止。
./程序
【问题讨论】:
我想从标准输入读取输入。我在 C 中使用 fork() 方法。我有子进程和父进程。我的输入是多行的。父进程将简单地等待子进程终止。子进程将读取第一行。子进程终止后,父进程将继续读取。我想要打印线。例如;输入->
子进程打印'Monday',父进程打印'Tuesday'和'Wednesday'。一旦文件结束是 到达程序终止。
./程序
【问题讨论】:
好的;听起来很直截了当——至少,只要你在输入输入。事实上,很难让它出错。您尝试了什么以及尝试时发生了什么?请阅读如何创建 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); 进行变体设置行缓冲,但这并不影响文件输入(在运行 macOS Sierra 10.12.3 和 GCC 6.3.0 的 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;
}
【讨论】: