作者:我有岁月619 | 来源:互联网 | 2023-08-11 12:48
我有一个简单的程序,我想模拟没有足够的分叉容量的情况,因此在执行管道任务时会限制分叉数。
让用C ++编写的类似于shell的管道作业:
ls | cat | cat | cat | cat | cat | cat | cat | cat
我有运行pipe()
和fork()
的代码:
#include
#include
#include
#include
#include
#include
const int fork_limit = 3;
int fork_counter = 0;
static void sig_chld_handler(int signo) {
int status;
pid_t pid;
while ((pid = waitpid(-1,&status,WNOHANG)) > 0) {
printf("received SIGCHLD from child process %d\n",pid);
fork_counter -= 1;
fprintf(stdout,"counter --,%d\n",fork_counter);
}
}
int main(int argc,char **argv) {
signal(SIGCHLD,sig_chld_handler);
char **cmds[9];
char *p1_args[] = {"ls",NULL};
char *p2_args[] = {"cat",NULL};
cmds[0] = p1_args;
cmds[1] = p2_args;
cmds[2] = p2_args;
cmds[3] = p2_args;
cmds[4] = p2_args;
cmds[5] = p2_args;
cmds[6] = p2_args;
cmds[7] = p2_args;
cmds[8] = p2_args;
int pipes[16];
pipe(pipes); // sets up 1st pipe
pipe(pipes + 2); // sets up 2nd pipe
pipe(pipes + 4);
pipe(pipes + 6);
pipe(pipes + 8);
pipe(pipes + 10);
pipe(pipes + 12);
pipe(pipes + 14);
pid_t pid;
for (int i = 0; i <9; i++) {
// === comment this part to run correctly ===
while (fork_limit usleep(10000);
}
// ===
pid = fork();
if (pid == 0) {
fprintf(stdout,"fork p%d\n",i);
// read
if (i != 0) {
if (dup2(pipes[(i - 1) * 2],0) <0) {
fprintf(stderr,"dup2 error\n");
exit(EXIT_FAILURE);
}
}
// write
if (i != 8) {
if (dup2(pipes[i * 2 + 1],1) <0) {
fprintf(stderr,"dup2 error\n");
exit(EXIT_FAILURE);
}
}
for (int j = 0; j <16; j++) {
close(pipes[j]);
}
execvp(*cmds[i],cmds[i]);
} else {
fork_counter += 1;
fprintf(stdout,"counter ++,%d \n",fork_counter);
}
}
for (int j = 0; j <16; j++) {
close(pipes[j]);
}
waitpid(pid,NULL,0); // wait the last one.
std::cout <<"Parent done." <}
第while (fork_limit 行是我限制子编号的对象。
如果删除while块,代码将运行良好,但是如果添加此代码,则它将挂起。
我想以前的孩子会死,所以fork_counter -= 1
和新的孩子可以分叉,但是行为却并非如此,我无法弄清原因。
没有while
的结果。
counter ++,1
counter ++,2
fork p0
fork p1
counter ++,3
fork p2
counter ++,4
counter ++,5
fork p3
fork p4
counter ++,6
fork p5
counter ++,7
counter ++,8
fork p6
fork p7
counter ++,9
fork p8
received SIGCHLD from child process 13316
counter --,8
Applications
Desktop
Documents
Downloads
library
Movies
Music
Pictures
received SIGCHLD from child process 13319
counter --,7
received SIGCHLD from child process 13318
counter --,6
received SIGCHLD from child process 13317
counter --,5
received SIGCHLD from child process 13320
counter --,4
received SIGCHLD from child process 13322
counter --,3
received SIGCHLD from child process 13321
counter --,2
received SIGCHLD from child process 13323
counter --,1
received SIGCHLD from child process 13324
counter --,0
Parent done.
使用while
的结果,这意味着我限制了分叉号。
counter ++,3
counter ++,4
fork p2
fork p3
received SIGCHLD from child process 13291
counter --,3
counter ++,4
fork p4
(hang)
main
程序(依次)执行以下操作:
- 预先创建所有管道
- 叉孩子使用管道(每个孩子关闭所有继承的管道)
- 关闭所有管道
问题在于“关闭所有管道”的时间。因为main
在等待第一个孩子完成(while (fork_limit ),才能完成步骤#2。
但是,cat
个子项(例如第一个cat
)在所有进程(包括他的main
)的输入管道被所有进程关闭之前一直无法完成,完。实际上是一个僵局。
请考虑对main
流程进行小的修改,以在分叉每个孩子后立即关闭每个孩子的管道:
if ( fork() ) {
// Children
...
} else {
// Main - close pipes ASAP.
close(pipes[(i-1)*2]) ;
close(pipes[(i-1)*2+1]);
fork_counter += 1;
fprintf(stdout,"counter ++,%d \n",fork_counter);
}
可能还需要对子级中的管道关闭进行一些修改。
,
感谢@ dash-o的回答
它的工作方式是:
#include
#include
#include
#include
#include
#include
const int fork_limit = 4;
int fork_counter = 0;
static void sig_chld_handler(int signo) {
int status;
pid_t pid;
while ((pid = waitpid(-1,&status,WNOHANG)) > 0) {
printf("received SIGCHLD from child process %d\n",pid);
fork_counter -= 1;
fprintf(stdout,"counter --,%d\n",fork_counter);
}
}
int main(int argc,char **argv) {
signal(SIGCHLD,sig_chld_handler);
char **cmds[9];
char *p1_args[] = {"ls",NULL};
char *p2_args[] = {"cat",NULL};
cmds[0] = p1_args;
cmds[1] = p2_args;
cmds[2] = p2_args;
cmds[3] = p2_args;
cmds[4] = p2_args;
cmds[5] = p2_args;
cmds[6] = p2_args;
cmds[7] = p2_args;
cmds[8] = p2_args;
int pipes[16];
pipe(pipes); // sets up 1st pipe
pipe(pipes + 2); // sets up 2nd pipe
pipe(pipes + 4);
pipe(pipes + 6);
pipe(pipes + 8);
pipe(pipes + 10);
pipe(pipes + 12);
pipe(pipes + 14);
pid_t pid;
for (int i = 0; i <9; i++) {
while (fork_limit usleep(10000);
}
pid = fork();
if (pid == 0) {
fprintf(stdout,"fork p%d\n",i);
// read
if (i != 0) {
if (dup2(pipes[(i - 1) * 2],0) <0) {
fprintf(stderr,"dup2 error\n");
exit(EXIT_FAILURE);
}
}
// write
if (i != 8) {
if (dup2(pipes[i * 2 + 1],1) <0) {
fprintf(stderr,"dup2 error\n");
exit(EXIT_FAILURE);
}
}
for (int j = 0; j <16; j++) {
close(pipes[j]);
}
execvp(*cmds[i],cmds[i]);
} else {
if (i != 0) {
close(pipes[(i - 1) * 2]);
close(pipes[(i - 1) * 2 + 1]);
}
fork_counter += 1;
fprintf(stdout,fork_counter);
}
}
for (int j = 0; j <16; j++) {
close(pipes[j]);
}
waitpid(pid,NULL,0); // wait the last one.
std::cout <<"Parent done." <}