热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何以有限的fork数量运行类似shell的管道任务?

我有一个简单的程序,我想模拟没有足够的分叉容量的情况,因此在执行管道

我有一个简单的程序,我想模拟没有足够的分叉容量的情况,因此在执行管道任务时会限制分叉数。

让用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程序(依次)执行以下操作:


  1. 预先创建所有管道

  2. 叉孩子使用管道(每个孩子关闭所有继承的管道)

  3. 关闭所有管道

问题在于“关闭所有管道”的时间。因为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." <}

推荐阅读
author-avatar
我有岁月619
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有