我正在从一个开放的过程中尝试一个C shell实现,但是关于输出缓冲的行为有一些有趣的地方。
代码如下(请注意我使用pid =WNOHANG)(-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);
}问题是,当我在终端中执行"ls \排序“时,我经常得到以下输出
6.828$ ls | sort
6.828$ a.out
sh.c
t.sh这表示在打印下一个命令提示符"6828$“之前,子进程的输出仍未刷新到终端。
但是,如果我不使用pid =wait(-1,&r,WNOHANG),并使用pid =wait(-1,&r,0)(或wait()),则输出通常如下:
6.828$ ls | sort
a.out
sh.c
t.sh我想这个问题的原因已经很长时间了,但没有想出可能的原因。有人能提出一些可能的理由吗?
非常感谢!
发布于 2017-05-17 04:42:34
此代码没有明确定义的行为:
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,则可能需要重复它。如果它返回-1和errno既不是EINTR也不是ECHILD,那么就发生了一些硬错误,您可能想要进行日志记录。但这与你的问题无关,亲爱的。
https://stackoverflow.com/questions/44014148
复制相似问题