这段 C 代码有什么问题? child 不返回?
What's wrong with this C code? The child is not returning?
这是我试图了解如何在两个 child 进程之间进行正确管道传输的尝试。我只是想将一个 Linux 命令的输出传递给另一个命令(ls 到 cat)并使程序 return 成功。但是,我猜测分叉的第二个 child 卡住了,parent 永远等待这个 child。很长一段时间以来,我一直在摆弄这段代码,试图找出它卡住的原因。在C系统编程方面,我有点菜鸟,但我正在努力学习。
有人知道为什么程序没有退出,而是挂在 cat 上吗?
如有任何帮助,我们将不胜感激。
谢谢。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char *a[2] = {"/bin/ls", NULL};
char *b[2] = {"/bin/cat", NULL};
char *envp[2] = {getenv("PATH"), NULL};
int fd[2], status;
pipe(fd);
int old_std_out = dup(1);
int old_std_in = dup(0);
dup2(fd[1], 1);
int pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
break;
case 0:
execve(a[0], a, envp);
exit(0);
break;
default:
waitpid(-1, &status, 0);
dup2(old_std_out, 1);
break;
}
dup2(fd[0], 0);
pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
break;
case 0:
execve(b[0], b, envp);
exit(0);
break;
default:
waitpid(-1, &status, 0);
dup2(old_std_in, 0);
break;
}
printf("\n");
return 0;
}
你的程序中有两个潜在的死锁。
首先,第一个 child (ls
) 可能会在尝试写入管道时阻塞,在这种情况下 waitpid()
不会 return 直到 ls
终止,而 ls
直到第二个 child (cat
) 开始执行后才会终止,这要到 waitpid()
returns 才会发生。 => 死锁。
其次,cat
将从它的stdin
读取,直到写端的所有文件描述符都关闭。 parent 进程 和 cat
都有一个写入端的副本,cat
有它 而不知道它明确。如果写端的唯一副本在同一个进程中,有些操作系统会read()
不阻塞(避免这种死锁),但这并不能保证。无论哪种方式,因为 parent 进程保留文件描述符的副本,而 parent 进程 waitpid()
s 用于 child,它等待 write-end要关闭的管道,你又遇到了死锁。
通常,简化程序可以解决这样的问题:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char *a[2] = {"/bin/ls", NULL};
char *b[2] = {"/bin/cat", NULL};
char *envp[2] = {getenv("PATH"), NULL};
int fd[2], status;
pipe(fd);
//int old_std_out = dup(1); /*No need to copy stdout...*/
//int old_std_in = dup(0); /*...or stdin...*/
//dup2(fd[1], 1); /*...if you wait dup2()ing until you need to*/
int pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
//break; /*unreachable*/
case 0:
dup2(fd[1], STDOUT_FILENO); /*NOW we dup2()*/
close(fd[0]); /*no need to pass these file descriptors to...*/
close(fd[1]); /*...a program that doesn't expect to have them open*/
execve(a[0], a, envp);
exit(0); /*might want an error message*/
//break; /*unreachable*/
default:
//waitpid(-1, &status, 0); /*don't wait yet*/
//dup2(old_std_out, 1);
close(fd[1]); /*we don't need this in the parent anymore*/
break;
}
//dup2(fd[0], 0); /*not needed anymore*/
pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
/*might want to ensure the first child can terminate*/
exit(1);
//break; /*unreachable*/
case 0:
dup2(fd[0], STDIN_FILENO);
close(fd[0]); /*again, cat doesn't expect a fourth fd open*/
execve(b[0], b, envp);
/*again, error message would be nice*/
exit(0);
//break;
default:
//waitpid(-1, &status, 0);
//dup2(old_std_in, 0);
break;
}
waitpid(-1, &status, 0); /*don't wait until both children are created*/
waitpid(-1, &status, 0);
printf("\n");
return 0;
}
如您所见,我留下了一些改进建议,但如果 execve()
工作正常,现在应该已经可以正常工作了。
这是我试图了解如何在两个 child 进程之间进行正确管道传输的尝试。我只是想将一个 Linux 命令的输出传递给另一个命令(ls 到 cat)并使程序 return 成功。但是,我猜测分叉的第二个 child 卡住了,parent 永远等待这个 child。很长一段时间以来,我一直在摆弄这段代码,试图找出它卡住的原因。在C系统编程方面,我有点菜鸟,但我正在努力学习。
有人知道为什么程序没有退出,而是挂在 cat 上吗?
如有任何帮助,我们将不胜感激。
谢谢。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char *a[2] = {"/bin/ls", NULL};
char *b[2] = {"/bin/cat", NULL};
char *envp[2] = {getenv("PATH"), NULL};
int fd[2], status;
pipe(fd);
int old_std_out = dup(1);
int old_std_in = dup(0);
dup2(fd[1], 1);
int pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
break;
case 0:
execve(a[0], a, envp);
exit(0);
break;
default:
waitpid(-1, &status, 0);
dup2(old_std_out, 1);
break;
}
dup2(fd[0], 0);
pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
break;
case 0:
execve(b[0], b, envp);
exit(0);
break;
default:
waitpid(-1, &status, 0);
dup2(old_std_in, 0);
break;
}
printf("\n");
return 0;
}
你的程序中有两个潜在的死锁。
首先,第一个 child (ls
) 可能会在尝试写入管道时阻塞,在这种情况下 waitpid()
不会 return 直到 ls
终止,而 ls
直到第二个 child (cat
) 开始执行后才会终止,这要到 waitpid()
returns 才会发生。 => 死锁。
其次,cat
将从它的stdin
读取,直到写端的所有文件描述符都关闭。 parent 进程 和 cat
都有一个写入端的副本,cat
有它 而不知道它明确。如果写端的唯一副本在同一个进程中,有些操作系统会read()
不阻塞(避免这种死锁),但这并不能保证。无论哪种方式,因为 parent 进程保留文件描述符的副本,而 parent 进程 waitpid()
s 用于 child,它等待 write-end要关闭的管道,你又遇到了死锁。
通常,简化程序可以解决这样的问题:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char *a[2] = {"/bin/ls", NULL};
char *b[2] = {"/bin/cat", NULL};
char *envp[2] = {getenv("PATH"), NULL};
int fd[2], status;
pipe(fd);
//int old_std_out = dup(1); /*No need to copy stdout...*/
//int old_std_in = dup(0); /*...or stdin...*/
//dup2(fd[1], 1); /*...if you wait dup2()ing until you need to*/
int pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
//break; /*unreachable*/
case 0:
dup2(fd[1], STDOUT_FILENO); /*NOW we dup2()*/
close(fd[0]); /*no need to pass these file descriptors to...*/
close(fd[1]); /*...a program that doesn't expect to have them open*/
execve(a[0], a, envp);
exit(0); /*might want an error message*/
//break; /*unreachable*/
default:
//waitpid(-1, &status, 0); /*don't wait yet*/
//dup2(old_std_out, 1);
close(fd[1]); /*we don't need this in the parent anymore*/
break;
}
//dup2(fd[0], 0); /*not needed anymore*/
pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
/*might want to ensure the first child can terminate*/
exit(1);
//break; /*unreachable*/
case 0:
dup2(fd[0], STDIN_FILENO);
close(fd[0]); /*again, cat doesn't expect a fourth fd open*/
execve(b[0], b, envp);
/*again, error message would be nice*/
exit(0);
//break;
default:
//waitpid(-1, &status, 0);
//dup2(old_std_in, 0);
break;
}
waitpid(-1, &status, 0); /*don't wait until both children are created*/
waitpid(-1, &status, 0);
printf("\n");
return 0;
}
如您所见,我留下了一些改进建议,但如果 execve()
工作正常,现在应该已经可以正常工作了。