首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C中奇怪的输出缓冲行为

C中奇怪的输出缓冲行为
EN

Stack Overflow用户
提问于 2017-05-17 01:42:33
回答 1查看 90关注 0票数 0

我正在从一个开放的过程中尝试一个C shell实现,但是关于输出缓冲的行为有一些有趣的地方。

代码如下(请注意我使用pid =WNOHANG)(-1,&r,WNOHANG)的行):

代码语言:javascript
复制
int
main(void)
{
  static char buf[100];
  int fd, r;
  pid_t pid = 0;

  // Read and run input commands.
  while(getcmd(buf, sizeof(buf)) >= 0){
    if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){
      buf[strlen(buf)-1] = 0;  // chop \n
      if(chdir(buf+3) < 0)
        fprintf(stderr, "cannot cd %s\n", buf+3);
      continue;
    }
    if((pid = fork1()) == 0)
      runcmd(parsecmd(buf));
    while ((pid = waitpid(-1, &r, WNOHANG)) >= 0) {
      if (errno == ECHILD) {
          break;
      }
    }
  }
  exit(0);
}

runcmd函数如下(注意,在管道处理中,我创建了2个子进程并等待它们终止):

代码语言:javascript
复制
void
runcmd(struct cmd *cmd)
{
  int p[2], r;
  struct execcmd *ecmd;
  struct pipecmd *pcmd;
  struct redircmd *rcmd;

  if(cmd == 0)
    exit(0);

  switch(cmd->type){
  case ' ':
    ecmd = (struct execcmd*)cmd;
    if(ecmd->argv[0] == 0) {
      exit(0);
    }
    // Your code here ...
    // fprintf(stderr, "starting to run cmd: %s\n", ecmd->argv[0]);
    execvp(ecmd->argv[0], ecmd->argv);
    fprintf(stderr, "exec error !\n");
    exit(-1);

    break;

  case '>':
  case '<':
    rcmd = (struct redircmd*)cmd;
    // fprintf(stderr, "starting to run <> cmd: %s\n", rcmd->file);
    // Your code here ...
    mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    if (rcmd->type == '<') {
      // input
      close(0);
      if (open(rcmd->file, O_RDONLY, mode) != 0) {
        fprintf(stderr, "Opening file error !\n");
        exit(-1);
      }
    } else {
      // output
      close(1);
      if (open(rcmd->file, O_WRONLY|O_CREAT|O_TRUNC, mode) != 1) {
        fprintf(stderr, "Opening file error !\n");
        exit(-1);
      }
    }

    runcmd(rcmd->cmd);
    break;

  case '|':
    pcmd = (struct pipecmd*)cmd;
    // fprintf(stderr, "starting to run pcmd\n");
    // Your code here ...
    pipe(p);

    if (fork1() == 0) {
      // child for read, right side command
      close(0);
      if (dup(p[0]) != 0) {
        fprintf(stderr, "error when dup !\n");
        exit(-1);
      }
      close(p[0]);
      close(p[1]);
      runcmd(pcmd->right);
      fprintf(stderr, "exec error !\n");
    } 
    if (fork1() == 0) {
      // left side command for writing
      close(1);
      if (dup(p[1]) != 1) {
        fprintf(stderr, "dup error !\n");
        exit(-1);
      }
      close(p[0]);
      close(p[1]);
      runcmd(pcmd->left);
      fprintf(stderr, "exec error !\n");
    }
    close(p[0]);
    close(p[1]);
    int stat;
    wait(&stat);
    wait(&stat);

    break;

  default:
    fprintf(stderr, "unknown runcmd\n");
    exit(-1);
  }    
  exit(0);
}

问题是,当我在终端中执行"ls \排序“时,我经常得到以下输出

代码语言:javascript
复制
6.828$ ls | sort
6.828$ a.out
sh.c
t.sh

这表示在打印下一个命令提示符"6828$“之前,子进程的输出仍未刷新到终端。

但是,如果我不使用pid =wait(-1,&r,WNOHANG),并使用pid =wait(-1,&r,0)(或wait()),则输出通常如下:

代码语言:javascript
复制
6.828$ ls | sort
a.out
sh.c
t.sh

我想这个问题的原因已经很长时间了,但没有想出可能的原因。有人能提出一些可能的理由吗?

非常感谢!

EN

回答 1

Stack Overflow用户

发布于 2017-05-17 04:42:34

此代码没有明确定义的行为:

代码语言:javascript
复制
while ((pid = waitpid(-1, &r, WNOHANG)) >= 0) {
  if (errno == ECHILD) {
      break;
  }
}

如果while返回-1,则waitpid循环会立即中断,在出现错误时,这正是它返回的结果。因此,如果输入了循环的主体,waitpid返回一些非负值:0 --指示子程序仍在执行--或者是退出的子程序的pid。这些不是错误条件,因此errno的值没有意义。可能是ECHILD,在这种情况下,循环将不正确地中断。

必须只在值有意义的情况下检查errno的值。或者,更准确地说,引用Posix标准

errno的值只能在调用被显式声明为其设置的函数之后才定义,直到被下一个函数调用更改为止,或者如果应用程序为其分配了一个值。只有当函数的返回值指示为有效时,才应该检查errno的值。

但是,我不明白为什么您认为有必要使用WNOHANG进行繁忙的循环。这是对资源的巨大浪费,因为父进程将反复执行系统调用,直到子进程实际终止为止。由于您确实打算等到子节点终止,所以只调用wait或将0指定为waitpid的标志值就更有意义了。

另一方面,如果wait (或waitpid)返回-1,并将errno设置为EINTR,则可能需要重复它。如果它返回-1errno既不是EINTR也不是ECHILD,那么就发生了一些硬错误,您可能想要进行日志记录。但这与你的问题无关,亲爱的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44014148

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档