热门标签 | 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." <}

推荐阅读
  • 题目描述:给定n个半开区间[a, b),要求使用两个互不重叠的记录器,求最多可以记录多少个区间。解决方案采用贪心算法,通过排序和遍历实现最优解。 ... [详细]
  • C++: 实现基于类的四面体体积计算
    本文介绍如何使用C++编程语言,通过定义类和方法来计算由四个三维坐标点构成的四面体体积。文中详细解释了四面体体积的数学公式,并提供了两种不同的实现方式。 ... [详细]
  • 题目Link题目学习link1题目学习link2题目学习link3%%%受益匪浅!-----&# ... [详细]
  • 本文详细探讨了KMP算法中next数组的构建及其应用,重点分析了未改良和改良后的next数组在字符串匹配中的作用。通过具体实例和代码实现,帮助读者更好地理解KMP算法的核心原理。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 扫描线三巨头 hdu1928hdu 1255  hdu 1542 [POJ 1151]
    学习链接:http:blog.csdn.netlwt36articledetails48908031学习扫描线主要学习的是一种扫描的思想,后期可以求解很 ... [详细]
  • 文件描述符、文件句柄与打开文件之间的关联解析
    本文详细探讨了文件描述符、文件句柄和打开文件之间的关系,通过具体示例解释了它们在操作系统中的作用及其相互影响。 ... [详细]
  • 本文详细探讨了VxWorks操作系统中双向链表和环形缓冲区的实现原理及使用方法,通过具体示例代码加深理解。 ... [详细]
  • 本题涉及一棵由N个节点组成的树(共有N-1条边),初始时所有节点均为白色。题目要求处理两种操作:一是改变某个节点的颜色(从白变黑或从黑变白);二是查询从根节点到指定节点路径上的第一个黑色节点,若无则输出-1。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 本文介绍如何使用Objective-C结合dispatch库进行并发编程,以提高素数计数任务的效率。通过对比纯C代码与引入并发机制后的代码,展示dispatch库的强大功能。 ... [详细]
  • 本文详细介绍了如何构建一个高效的UI管理系统,集中处理UI页面的打开、关闭、层级管理和页面跳转等问题。通过UIManager统一管理外部切换逻辑,实现功能逻辑分散化和代码复用,支持多人协作开发。 ... [详细]
  • 本文探讨了 Objective-C 中的一些重要语法特性,包括 goto 语句、块(block)的使用、访问修饰符以及属性管理等。通过实例代码和详细解释,帮助开发者更好地理解和应用这些特性。 ... [详细]
  • 本文详细介绍了 Apache Jena 库中的 Txn.executeWrite 方法,通过多个实际代码示例展示了其在不同场景下的应用,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 本文详细介绍了Java中的访问器(getter)和修改器(setter),探讨了它们在保护数据完整性、增强代码可维护性方面的重要作用。通过具体示例,展示了如何正确使用这些方法来控制类属性的访问和更新。 ... [详细]
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社区 版权所有