【问题标题】:Executing new task based on sigchld() from previous task基于上一个任务的 sigchld() 执行新任务
【发布时间】:2011-11-13 01:16:23
【问题描述】:

我目前正在用 C++ 构建一个小型 shell。

用户可以在提示符处输入作业,例如exe1 && exe2 &。与 BASH shell 类似,如果exe1 成功退出,我只会执行exe2。此外,整个作业必须在后台执行(由结尾的 & 运算符指定)。

现在,我有一个处理作业执行的jobManager 和一个包含作业的可执行文件及其各自参数/条件的job 结构。通过调用fork() 开始作业,然后使用正确的参数调用execvp()。当作业结束时,我有一个SIGCHLD 的信号处理程序,我在其中执行wait() 以确定哪个进程刚刚结束。当exe1 结束时,我观察它的退出代码并确定是否应该继续启动exe2

我关心的是如何启动exe2。我担心如果我在 SIGCHLD 处理程序的上下文中使用我的 jobManager 启动函数,我最终可能会在堆栈上挂出太多的 SIGCHLD 处理程序函数(例如,如果有 10 个条件执行)。此外,从信号处理程序开始下一次执行似乎不是一个好主意,即使它是间接发生的。 (1.5 年前,当我刚刚学习信号处理时,我尝试做类似的事情——我似乎记得它对我失败了)。

以上所有内容都需要能够在后台发生,我想避免让jobManager 忙着等待,只是等待exe1 返回。我也希望不要有一个单独的线程只是等待开始执行另一个进程。但是,指示我的 jobManagerSIGCHLD 处理程序开始执行下一个进程似乎是糟糕的代码。

如有任何反馈意见。

【问题讨论】:

    标签: c++ shell fork signals process-management


    【解决方案1】:

    我看到了两种方式:
    1)用调用“sigwait”的循环替换你的 sighandler(见 man 3 sigwait)
    然后循环

    2) 在开始创建管道之前,在程序的主循环中使用管道句柄上的“选择”等待 事件。在信号处理程序写入管道和主循环处理情况。

    【讨论】:

    • 啊——你的 select() 解决方案对我来说很有趣。我在套接字编程中使用了 select() / poll() 并希望在这里做类似的事情以避免忙碌的等待。但是,我猜管道需要是“全局的”,因为我无法在运行时向信号处理程序提供任何东西。
    • sigwait 循环很丑陋,在这里没有多大用处,因为它们需要成为后台作业。
    • 您可以在没有全局变量的情况下处理此问题,但在 linux 特定的方式中:signalfd 创建描述符以获取有关信号的信息。
    • 是的——更多地研究了signalfd——你是对的。感谢您提供信息。
    【解决方案2】:

    嗯,这个不错。

    如果分叉两次,每个进程一次呢?第一个运行,第二个停止。在父 SIGCHLD 处理程序中,如果合适,将 SIGCONT 发送给第二个孩子,然后它会关闭并运行作业。当然,如果第一个不应该运行,你会 SIGKILL 第二个,这应该是安全的,因为你不会真正设置任何东西。

    听起来怎么样?您将有一个无所事事的过程,但它不应该持续很长时间。

    【讨论】:

    • 我想避免这种解决方案的唯一原因是因为从我的角度来看,这似乎是一种不幸的资源浪费。我还希望能够处理更高级的情况,例如作业中的 for() 循环。
    • @BSchlinker 这并不是真正的资源浪费,除非您的子进程正在做很多事情。 fork() 是写时复制,因此等待进程可能使用 KB 左右 - 并且没有 CPU 时间,因为它会被阻塞。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多