如何解决带有管道的多进程程序中的意外 scanf 行为
对于 UNIX 操作系统类中的练习,我应该开发一个程序,其中两个兄弟进程通过管道进行通信。一个进程(生产者)应该从 stdout 读取字符串并将它们发送到另一个进程(消费者),然后该进程必须将字符串转换为大写并再次将其打印到 stdout。字符串“end”终止兄弟进程和父进程。
该程序运行良好,但是在尝试读取用户的整个短语而不是单个字符串时它会崩溃。
这是我的主要内容的一部分:
int main(void) {
int child_status,fd[2];
if (pipe(fd)) {
fprintf(stderr,"Error: Could not create pipe.\n");
return EXIT_FAILURE;
}
if (!fork()) {
// child1
producer(fd);
exit(0);
} else if (!fork()) {
// child2
consumer(fd);
exit(0);
} else {
// father
pid_t pid;
...
生产者进程:
void producer(int *fd) {
char buff[BUFFSIZE];
char *line;
close(fd[0]);
while (1) {
fflush(stdout);
fprintf(stdout,"Insert string:\t");
scanf("%[^\n]s%*c",buff);
fflush(stdout);
line = strdup(buff);
printToPipe(fd[1],line);
if (!strcmp(line,"end")) // Special string terminates process
return;
sleep(1);
}
return;
}
以及消费者进程:
void consumer(int *fd) {
char *buff;
close(fd[1]);
while (1) {
if ( (buff = readFromPipe(fd[0])) == NULL ) {
close(fd[0]);
return;
}
fprintf(stdout,"%s\n",strToUpper(buff));
fflush(stdout);
}
return;
}
printToPipe() 和 readfromPipe() 直接实现了 read() 和 write() 系统调用。
意外行为:
一旦输入第一个短语并转换为大写,程序就会无限循环打印出提示和第一个大写短语(即:Insert string: TEST TEST
)。特别是:
- 尽管我考虑了 \n 字符 (
scanf("%[^\n]s%*c",buff);
),但后续的 scanfs 会被忽略。如果我检查 scanf 返回值,则第一次之后的所有迭代都是 0, - 似乎消费者用它的输出“自我维持”:它不等待通过管道的新输入并不断打印转换后的短语。
解决方法
尽管我考虑了 \n 字符,但后续的 scanfs 仍被忽略
代码从不读取 '\n'
scanf("%[^\n]s%*c",buff);
是个问题@John Bollinger。
-
"%[^\n]"
将无限数量的非'\n
字符读入buff
,可能会溢出buff
。它比gets()
更糟糕。如果至少读取了 1 个非'\n'
字符,则在buff
后附加一个 空字符,否则扫描将停止。 -
"s"
匹配一个's'
,它在上述之后不是预期的 - 只有一个'\n'
,所以扫描停止而不消耗任何'\n'
。 -
格式的其余部分永远不会执行。
代码从不检查输入函数的返回值
通过检查 scanf()
的返回值,buff
的内容可能不变或不确定。
// scanf("%[^\n]s%*c",buff);
if (scanf("%[^\n]s%*c",buff) == 1) {
; // OK to use `buff`
}
OP 的 scanf()
可能会占用第一行的大部分内容,将 '\n'
留在 stdin
中,但是第二个 scanf()
调用肯定会在它尝试读取时立即停止第一行的 '\n'
。这可能会使 buff
保持不变。
使用 fgets()
读取来自 stdin
的 行 输入并保存为 字符串。 @xing
// scanf("%[^\n]s%*c",buff);
if (fgets(buff,sizeof buff,stdin) == NULL) {
; // Handle end-of-file or rare input error
break;
}
要从输入行中删除 '\n'
,请参阅此 answer 和其他内容。
代码可以将 '\n'
保留在字符串中,并且在打印时不附加。
//fprintf(stdout,"%s\n",strToUpper(buff));
fprintf(stdout,"%s",strToUpper(buff));
这将更好地处理超过 BUFFSIZE
的长输入行,因为多余的字符只是留给下一次迭代。
OP 可能需要调整 strcmp(line,"end")
测试以考虑可能的尾随 '\n'
。
一个进程(生产者)应该从标准输出读取字符串......
细节:生产者不是从stdout
读取字符串,而是从stdin
读取行到字符串 em>.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。