C

时间:2017-05-17 01:42:34

标签: c linux shell buffer

我正在试用一个开放课程的C shell实现,但是有一些有趣的输出缓冲行为。

代码是这样的(请注意我使用的行 pid = waitpid(-1,& r,WNOHANG)):

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个子进程并等待它们终止):

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);
}

奇怪的是,当我执行&#34; ls |时排序&#34;在终端中,我不断得到以下输出

6.828$ ls | sort
6.828$ a.out
sh.c
t.sh

这表示在下一个命令提示符之前&#34; 6828 $&#34;打印时,子进程的输出仍然没有刷新到终端。

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

6.828$ ls | sort
a.out
sh.c
t.sh

我长期以来一直在考虑问题的原因,但没有提出可能的原因。任何人都可以提出一些可能的原因吗?

非常感谢!

1 个答案:

答案 0 :(得分:1)

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

while ((pid = waitpid(-1, &r, WNOHANG)) >= 0) {
  if (errno == ECHILD) {
      break;
  }
}

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

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

  

errno的值只能在调用明确声明为其设置的函数之后定义,并且直到它被下一个函数调用更改或者应用程序为其赋值。 errno的值只有在函数的返回值被指示有效时才应进行检查。

但是我很困惑为什么你认为有必要使用WNOHANG进行忙碌循环。这是一个巨大的资源浪费,因为您的父进程将重复执行系统调用,直到子实际终止。由于您真的打算等到孩子终止,因此只需拨打wait或将0指定为waitpid的标记值就更有意义了。

另一方面,如果waitwaitpid返回-1且errno设置为EINTR,则可能需要重复-1(或errno)。如果它返回EINTRECHILD既不是 <table class="table table-striped"> @foreach($users as $user) <form method="POST" action="your_controller_function_route"> <tr> <td class="col-md-2"> {!! $user->id !!} </td> <td class="col-md-2"> {!! $user->name !!} </td> <td class="col-md-2"> <input type="hidden" name="email" value="{!! $user->email !!}"> {!! $user->email !!} </td> <td class="col-md-3"> <input type="submit" id ="sub{{ $user->id }}" class="btn btn-success btn-md" value="GET VALUE"> </td> </tr> </form> @endforeach </table> 也不是{{1}},那么您可能想要记录一些硬错误。但那与你的问题没有关系,afaics。