dup2() 正在阻塞子进程? C
dup2() is blocking with child processes? C
我正在编写一个函数,将输入回显到一个 sed,然后再回显到另一个 sed。我想我以正确的方式使用了我所有的等待信号,但我能得到的最后一个打印是在 echo 中我的第一个子进程中调用 dup2() 之前。
void sendbc (char * str_ ) {
int fd[2];
int fd1[2];
int pid,pid1;
char* echo[] = {"echo", str_,NULL};
char* sed1[] = {"sed","s/[^:]*;"" " "//",NULL};
char* sed2[] = {"sed","s/[^:]*."" " "//",NULL};
int status,er;
FILE *f;
if(pipe(fd) < 0){
exit(100);
}
if(pipe(fd1) < 0){
exit(100);
}
pid = fork();
if (pid == 0) {
dup2(fd[1], 1) //last command before blocking
close(fd[1]);
close(fd[0]);
execvp(echo[0], echo);
printf("Error in execvp1\n");
}else{
wait(&status);
pid = fork();
if (pid == 0){
dup2(fd[0], 0);
dup2(fd1[1], 1);
dup2(fd1[1], 2);
close(fd[1]);
close(fd[0]);
close(fd1[1]);
close(fd1[0]);
execvp(sed1[0],sed1);
printf("Error in execvp2\n");
}else{
wait(&status);
dup2(fd1[0],0);
dup2(1,2);
//dup2(1,1);
close(fd1[1]);
close(fd1[0]);
execvp(sed2[0],sed2);
printf("Error in execvp3\n");
}
}
if(pid!=0)
wait(&status);
close(fd[0]);
close(fd[1]);
close(fd1[1]);
close(fd1[0]);
}
我可以想象 2 种可能性...dup2 正在阻塞,或者我需要创建更多进程,因为它会在调用时结束进程,但是在快速阅读他的手册页后这听起来不正确...可能是什么?
一般问题
您在各个进程中没有关闭足够的文件描述符。
经验法则:如果你
dup2()
管道的一端连接到标准输入或标准输出,同时关闭
返回的原始文件描述符
pipe()
尽早。
特别是,您应该在使用任何
exec*()
函数族。
如果您使用以下任一方式复制描述符,则该规则也适用
dup()
或者
fcntl()
F_DUPFD
或 F_DUPFD_CLOEXEC
.
如果 parent 进程将不与其任何 children 通过
管道,它必须确保尽早关闭管道的两端
足够(例如,在等待之前)以便其 children 可以接收
EOF 指示读取(或获取 SIGPIPE 信号或写入错误
写),而不是无限期地阻塞。
即使 parent 使用管道而不使用 dup2()
,它也应该
通常至少关闭管道的一端——这种情况极为罕见
在单个管道的两端读写的程序。
请注意 O_CLOEXEC
选项
open()
,
并且 fcntl()
的 FD_CLOEXEC
和 F_DUPFD_CLOEXEC
选项也可以考虑因素
进入这个讨论。
如果你使用
posix_spawn()
及其广泛的支持功能系列(总共 21 个功能),
您将需要查看如何在生成的进程中关闭文件描述符
(posix_spawn_file_actions_addclose()
,
等)。
请注意,使用 dup2(a, b)
比使用 close(b); dup(a);
更安全
由于各种原因。
一个是如果你想强制文件描述符大于
通常的数字,dup2()
是唯一明智的方法。
另一个是如果 a
与 b
相同(例如两者都是 0
),则 dup2()
正确处理它(它在复制 a
之前不会关闭 b
)
而单独的 close()
和 dup()
则非常失败。
这种情况不太可能,但并非不可能。
具体问题
- 出于安全考虑,您没有关闭足够多的文件描述符。
- 你的正则表达式有问题。
- 不应让管道中的进程相互等待。
烦心事:当我有两个密切相关的变量(如成对的管道文件描述符)时,我更喜欢使用 fd1
和 fd2
;我发现 fd
和 fd1
之类的东西很傻。但是,您可以选择忽略它。
工作代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static void dump_argv(char **argv)
{
printf("%d:\n", getpid());
while (*argv != NULL)
{
printf("%d: <<%s>>\n", getpid(), *argv++);
}
}
static void sendbc(char *str)
{
int fd1[2];
int fd2[2];
int pid;
char *echo[] = {"echo", str, NULL};
char *sed1[] = {"sed", "s/[^:]*[;]//", NULL};
char *sed2[] = {"sed", "s/[^:]*[.]//", NULL};
if (pipe(fd1) < 0)
exit(100);
if (pipe(fd2) < 0)
exit(101);
printf("%d: at work\n", getpid());
pid = fork();
if (pid < 0)
exit(102);
else if (pid == 0)
{
printf("%d: child 1 - echo\n", getpid());
dump_argv(echo);
dup2(fd1[1], 1);
close(fd1[1]);
close(fd1[0]);
close(fd2[0]);
close(fd2[1]);
execvp(echo[0], echo);
fprintf(stderr, "Error in execvp1\n");
exit(103);
}
else
{
printf("%d: parent - before second fork\n", getpid());
pid = fork();
if (pid == 0)
{
printf("%d: child 2 - sed 1\n", getpid());
dump_argv(sed1);
dup2(fd1[0], 0);
dup2(fd2[1], 1);
close(fd1[1]);
close(fd1[0]);
close(fd2[1]);
close(fd2[0]);
execvp(sed1[0], sed1);
fprintf(stderr, "Error in execvp2\n");
exit(104);
}
else
{
printf("%d: parent - sed 2\n", getpid());
dump_argv(sed1);
dup2(fd2[0], 0);
close(fd1[1]);
close(fd1[0]);
close(fd2[1]);
close(fd2[0]);
execvp(sed2[0], sed2);
fprintf(stderr, "Error in execvp3\n");
exit(105);
}
}
fprintf(stderr, "Reached unexpectedly\n");
exit(106);
}
int main(void)
{
char message[] =
"This is the first line\n"
"and this is the second - with a semicolon ; here before a :\n"
"and the third line has a colon : before the semicolon ;\n"
"but the fourth line has a dot . before the colon\n"
"whereas the fifth line has a colon : before the dot .\n"
;
sendbc(message);
return 0;
}
示例输出
$ ./pipe29
74829: at work
74829: parent - before second fork
74829: parent - sed 2
74829:
74829: <<sed>>
74829: <<s/[^:]*[;]//>>
74830: child 1 - echo
74830:
74830: <<echo>>
74830: <<This is the first line
and this is the second - with a semicolon ; here before a :
and the third line has a colon : before the semicolon ;
but the fourth line has a dot . before the colon
whereas the fifth line has a colon : before the dot .
>>
74831: child 2 - sed 1
74831:
74831: <<sed>>
74831: <<s/[^:]*[;]//>>
This is the first line
here before a :
and the third line has a colon :
before the colon
whereas the fifth line has a colon :
$
除了诊断打印之外,主要区别在于此代码严格关闭了管道的所有未使用端,并且不包含对 wait()
或其亲属的调用——它们不是必需的,通常是当它们阻止管道中进程的并发执行时是有害的。
我正在编写一个函数,将输入回显到一个 sed,然后再回显到另一个 sed。我想我以正确的方式使用了我所有的等待信号,但我能得到的最后一个打印是在 echo 中我的第一个子进程中调用 dup2() 之前。
void sendbc (char * str_ ) {
int fd[2];
int fd1[2];
int pid,pid1;
char* echo[] = {"echo", str_,NULL};
char* sed1[] = {"sed","s/[^:]*;"" " "//",NULL};
char* sed2[] = {"sed","s/[^:]*."" " "//",NULL};
int status,er;
FILE *f;
if(pipe(fd) < 0){
exit(100);
}
if(pipe(fd1) < 0){
exit(100);
}
pid = fork();
if (pid == 0) {
dup2(fd[1], 1) //last command before blocking
close(fd[1]);
close(fd[0]);
execvp(echo[0], echo);
printf("Error in execvp1\n");
}else{
wait(&status);
pid = fork();
if (pid == 0){
dup2(fd[0], 0);
dup2(fd1[1], 1);
dup2(fd1[1], 2);
close(fd[1]);
close(fd[0]);
close(fd1[1]);
close(fd1[0]);
execvp(sed1[0],sed1);
printf("Error in execvp2\n");
}else{
wait(&status);
dup2(fd1[0],0);
dup2(1,2);
//dup2(1,1);
close(fd1[1]);
close(fd1[0]);
execvp(sed2[0],sed2);
printf("Error in execvp3\n");
}
}
if(pid!=0)
wait(&status);
close(fd[0]);
close(fd[1]);
close(fd1[1]);
close(fd1[0]);
}
我可以想象 2 种可能性...dup2 正在阻塞,或者我需要创建更多进程,因为它会在调用时结束进程,但是在快速阅读他的手册页后这听起来不正确...可能是什么?
一般问题
您在各个进程中没有关闭足够的文件描述符。
经验法则:如果你
dup2()
管道的一端连接到标准输入或标准输出,同时关闭
返回的原始文件描述符
pipe()
尽早。
特别是,您应该在使用任何
exec*()
函数族。
如果您使用以下任一方式复制描述符,则该规则也适用
dup()
或者
fcntl()
F_DUPFD
或 F_DUPFD_CLOEXEC
.
如果 parent 进程将不与其任何 children 通过
管道,它必须确保尽早关闭管道的两端
足够(例如,在等待之前)以便其 children 可以接收
EOF 指示读取(或获取 SIGPIPE 信号或写入错误
写),而不是无限期地阻塞。
即使 parent 使用管道而不使用 dup2()
,它也应该
通常至少关闭管道的一端——这种情况极为罕见
在单个管道的两端读写的程序。
请注意 O_CLOEXEC
选项
open()
,
并且 fcntl()
的 FD_CLOEXEC
和 F_DUPFD_CLOEXEC
选项也可以考虑因素
进入这个讨论。
如果你使用
posix_spawn()
及其广泛的支持功能系列(总共 21 个功能),
您将需要查看如何在生成的进程中关闭文件描述符
(posix_spawn_file_actions_addclose()
,
等)。
请注意,使用 dup2(a, b)
比使用 close(b); dup(a);
更安全
由于各种原因。
一个是如果你想强制文件描述符大于
通常的数字,dup2()
是唯一明智的方法。
另一个是如果 a
与 b
相同(例如两者都是 0
),则 dup2()
正确处理它(它在复制 a
之前不会关闭 b
)
而单独的 close()
和 dup()
则非常失败。
这种情况不太可能,但并非不可能。
具体问题
- 出于安全考虑,您没有关闭足够多的文件描述符。
- 你的正则表达式有问题。
- 不应让管道中的进程相互等待。
烦心事:当我有两个密切相关的变量(如成对的管道文件描述符)时,我更喜欢使用 fd1
和 fd2
;我发现 fd
和 fd1
之类的东西很傻。但是,您可以选择忽略它。
工作代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static void dump_argv(char **argv)
{
printf("%d:\n", getpid());
while (*argv != NULL)
{
printf("%d: <<%s>>\n", getpid(), *argv++);
}
}
static void sendbc(char *str)
{
int fd1[2];
int fd2[2];
int pid;
char *echo[] = {"echo", str, NULL};
char *sed1[] = {"sed", "s/[^:]*[;]//", NULL};
char *sed2[] = {"sed", "s/[^:]*[.]//", NULL};
if (pipe(fd1) < 0)
exit(100);
if (pipe(fd2) < 0)
exit(101);
printf("%d: at work\n", getpid());
pid = fork();
if (pid < 0)
exit(102);
else if (pid == 0)
{
printf("%d: child 1 - echo\n", getpid());
dump_argv(echo);
dup2(fd1[1], 1);
close(fd1[1]);
close(fd1[0]);
close(fd2[0]);
close(fd2[1]);
execvp(echo[0], echo);
fprintf(stderr, "Error in execvp1\n");
exit(103);
}
else
{
printf("%d: parent - before second fork\n", getpid());
pid = fork();
if (pid == 0)
{
printf("%d: child 2 - sed 1\n", getpid());
dump_argv(sed1);
dup2(fd1[0], 0);
dup2(fd2[1], 1);
close(fd1[1]);
close(fd1[0]);
close(fd2[1]);
close(fd2[0]);
execvp(sed1[0], sed1);
fprintf(stderr, "Error in execvp2\n");
exit(104);
}
else
{
printf("%d: parent - sed 2\n", getpid());
dump_argv(sed1);
dup2(fd2[0], 0);
close(fd1[1]);
close(fd1[0]);
close(fd2[1]);
close(fd2[0]);
execvp(sed2[0], sed2);
fprintf(stderr, "Error in execvp3\n");
exit(105);
}
}
fprintf(stderr, "Reached unexpectedly\n");
exit(106);
}
int main(void)
{
char message[] =
"This is the first line\n"
"and this is the second - with a semicolon ; here before a :\n"
"and the third line has a colon : before the semicolon ;\n"
"but the fourth line has a dot . before the colon\n"
"whereas the fifth line has a colon : before the dot .\n"
;
sendbc(message);
return 0;
}
示例输出
$ ./pipe29
74829: at work
74829: parent - before second fork
74829: parent - sed 2
74829:
74829: <<sed>>
74829: <<s/[^:]*[;]//>>
74830: child 1 - echo
74830:
74830: <<echo>>
74830: <<This is the first line
and this is the second - with a semicolon ; here before a :
and the third line has a colon : before the semicolon ;
but the fourth line has a dot . before the colon
whereas the fifth line has a colon : before the dot .
>>
74831: child 2 - sed 1
74831:
74831: <<sed>>
74831: <<s/[^:]*[;]//>>
This is the first line
here before a :
and the third line has a colon :
before the colon
whereas the fifth line has a colon :
$
除了诊断打印之外,主要区别在于此代码严格关闭了管道的所有未使用端,并且不包含对 wait()
或其亲属的调用——它们不是必需的,通常是当它们阻止管道中进程的并发执行时是有害的。