gpt4 book ai didi

c - 强制刷新或读取未刷新的输出

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:13:25 25 4
gpt4 key购买 nike

这是工具 dc 的前端;这个想法是键入一个中缀表达式 (2 + 3),将其映射到相应的后缀符号 (2 3 +) 并将其发送到 dc。这就是 bc 所做的。

我正在使用管道执行此操作,但前端挂起等待输出。
这是代码;我会在下面继续评论它。我的 dc 中的“h”命令未实现,因此我正在寻找作为 dc 输出结束的内容。

TL;DR:进程挂起是因为 dc 中的 stdout 没有刷新,或者这就是我认为已经发现的。我怎样才能不顾一切地读取它或在每次写入后强制刷新?

我的发现在下面评论。

#define DC_EOF_RCV "dc: 'h' (0150) unimplemented"
#define DC_EOF_SND "h\n"

static FILE *sndfp, *rcvfp;

int dcinvoke() {
int pfdout[2], pfdin[2];
pid_t pid;

pipe(pfdout);
pipe(pfdin);
switch (pid = fork()) {
case -1: exit(1);
case 0:
dup2(pfdout[0], STDIN_FILENO);
dup2(pfdin[1], STDOUT_FILENO);
close(pfdout[0]);
close(pfdout[1]);
close(pfdin[0]);
close(pfdin[1]);
execlp("dc", "dc", "-", NULL);
}
close(pfdout[0]);
close(pfdin[1]);
sndfp = fdopen(pfdout[1], "w");
rcvfp = fdopen(pfdin[0], "r");
return 1;
}

void dcsnd(const char *s) {
fputs(s, sndfp);
fflush(sndfp);
}

void dcrcv(char *buf, size_t max) {
fgets(buf, max, rcvfp); // <<<<< HANGS HERE
}

int turnaround() {
dcsnd(DC_EOF_SND); fflush(sndfp);
}

int rcvall() {
char buf[256];
turnaround();
for (;;) {
dcrcv(buf, sizeof(buf));
if (! strcmp(buf, DC_EOF_RCV)) {
break;
}
printf("%s", buf);
}
return 1;
}

int prompt(const char *msg, char *res, size_t resmax, int *eofp) {
char *p;
printf("\n%s ", msg);
if (!fgets(res, resmax, stdin)) {
*eofp = 1;
} else {
if (p = strrchr(res, '\n')) {
*p = 0;
}
*eofp = 0;
}
return 1;
}

int main() {
char buf[128], line[128];
int eof;

dcinvoke();
dcsnd("2 3 +\n");
dcsnd(DC_EOF_SND);
rcvall();
for (;;) {
prompt("Expression", buf, sizeof(buf), &eof);
if (eof) {
break;
}
snprintf(line, sizeof(line), "%s\n", buf);
dcsnd(line);
rcvall();
}
dcsnd("q\n");
return 0;
}

为了简化这个问题,我删除了错误检查。

前端卡在一行:

fgets(buf, max, rcvfp);

因为我真的不知道如何找到问题,所以我写了我自己的 dc 它除了正确响应“h”命令并将它得到的所有内容输出到一个文件之外什么都不做(所以我可以调试它;我不知道任何其他方式)。如果有用,我可以添加到这里。

我发现在 (my) dc 中调用 fflush(stdout) 可以恢复前端,就像调用 fgets终于回来了。

我不能改变 dc 本身。怎么能不卡在fgets上呢?

我的一些想法:

  • 使用另一个线程刷新rcvfp(dcstdout)

  • 使用伪终端编写

我正在寻找避免这两种情况的建议或简单方法。

我尝试过:

  • 使用read()fileno(rcvfp)

  • 阅读thisthis类似的帖子

  • 使用setlinebuf() ( man )

最佳答案

dc 将错误消息发送到 stderr,而不是 stdout,因此您永远不会看到它们——它们直接发送到终端(或您环境中连接到的任何 stderr)。为了能够在您的程序中读取它们,您还需要重定向 stderr。添加

dup2(pfdin[1], STDERR_FILENO);

在子分支代码中。


为了使您的代码真正起作用,我还必须在 DC_EOF_RCV 字符串的末尾添加一个换行符(以匹配 dc 发送的内容),并删除额外的 dcsnd(DC_EOF_SND); 调用main,因为您永远不会在任何地方进行匹配的接收。即使这样,您仍然可以在写入 stdout 和 stderr 之间获得 dc 内的竞争条件,这可能导致输出行混合,导致与您的 DC_EOF_RCV 字符串不匹配的响应。您可以通过在 turnaround 函数中添加一个小 sleep 来避免这些。


另一种选择是在主循环中使用 poll 来同时从 stdin 和 dc 读取数据。有了这个,您就不需要奇怪的“发送 h 命令来触发错误”机制来确定 dc 何时完成——您只需循环即可。您的主循环最终看起来像:

struct pollfd polling[2] = { { STDIN_FILENO, POLLIN, 0 }, { fileno(rcvfp), POLLIN, 0 } };
printf("Expression ");
for(;;) {
if (poll(polling, 2, -1) < 0) {
perror("poll");
exit(1); }
if (polling[0].revents) {
/* stdin is ready */
if (!fgets(buf, sizeof(buf), stdin))
break;
dcsnd(buf); }
if (polling[1].revents) {
/* dc has something for us */
printf("\r \r"); /* erase the previsouly printed prompt */
dcrcv(buf, sizeof(buf));
printf("%s\n", buf); }
printf("Expression "); /* new prompt */
}

这里有更多关于管理错误情况的详细信息(revents 可能是 POLLERRPOLLHUP 表示错误或挂断,而不是POLLIN 用于正常输入——所有这些都是非零值,因此仅针对非零值进行测试是合理的)。

关于c - 强制刷新或读取未刷新的输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57732605/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com