【问题标题】:What is the ideal way to emulate process replacement on Windows?在 Windows 上模拟进程替换的理想方法是什么?
【发布时间】:2018-07-05 07:00:12
【问题描述】:

所以,在feature request I filed against Node.js 中,我正在寻找一种方法来用另一个替换当前的 Node 进程。在 Linux 和朋友(实际上,任何符合 POSIX 的系统)中,这很容易:使用execve 和朋友,就这样结束了。但显然,这在 Windows 上不起作用,因为它只有 CreateProcessexecve 和朋友委托给 complete with async behavior)。而且它不像peoplehaven'twantedtodosimilar,导致numerous duplicate questions on this site。 (这不是重复的,因为它在给定某些限制的情况下明确寻求解决方法,而不仅仅是要求直接替换。)

流程替换有几个方面需要解决:

  1. 所有控制台 I/O 流都必须转发到新进程。
  2. 所有信号都需要透明地转发到新进程。
  3. 必须销毁旧进程中的数据,尽可能多地回收资源。
  4. 应销毁所有预先存在的线程和子进程。
  5. 除了打开的文件描述符和命名管道/等之外,所有预先存在的句柄都应该被销毁。
  6. 理想情况下,旧进程的内存应在创建进程后保持在最低限度。
  7. 对于我的特定用例,保留进程 ID 并不重要。

对于我的特殊情况,有一些限制:

  1. 我可以控制初始进程的启动以及“进程替换”功能的位置。
  2. 我可以通过附加组件在可能的任何堆栈偏移处加载任意本机代码。
    • 含义:我什至无法梦想跟踪 malloc 调用、句柄、线程操作或进程操作来跟踪和释放它们,因为 DLL 重写并不完全实用。
  3. 我无法控制何时我的“进程替换”被调用。它可以通过附加组件调用,该附加组件可以通过 FFI 的解释代码甚至递归的另一个附加组件调用。它甚至可以在附加初始化期间调用。
    • 含义:我无法知道堆栈中的内容,即使我完美地检测了我的一面。并且重写他们所有的calls 和pushes 远非实际,而且由于显而易见的原因,这将是全方位的缓慢。

所以,这是我的想法的要点:使用类似于伪蹦床的东西。

  1. 静态分配以下内容:
    1. 堆栈指针的单个指针。
    2. MAX_PATH + 1 应用程序路径的字符 + '\0'
    3. MAX_PATH + 1 当前工作目录路径的字符 + '\0'
    4. 32768 个字符用于参数 + '\0'
    5. 32768 个环境字符 + '\0'
  2. 进入时,将全局堆栈指针引用设置为堆栈指针。
  3. 关于“替换”:
    1. 进行相关的进程清理并尽可能锁定/释放。
    2. 将堆栈指针设置为存储的原始全局指针。
    3. 终止每个子线程。
    4. 杀死每个子进程。
    5. 免费each open handle
    6. 如果可能(即不在 UWP 程序中),For each heapdestroy it 如果不是 default heap 或临时堆(如果存在)。
    7. 如果可能,关闭each open handle
    8. 如果可能,walk 默认堆和free 与其关联的每个段。
    9. 使用静态分配的文件/参数/环境/等创建一个新进程。没有创建新窗口。
    10. 代理所有未来接收到的信号、异常等,而无需以某种方式修改此过程。 The standard signals are easy,但除了例外情况不多。
    11. 等待进程结束。
    12. 返回the process's exit code

这里的想法是使用基于进程的蹦床,并在启动新创建的进程时将当前进程大小降至绝对最小值。

但是在我对 Windows 不是很熟悉的地方,我可能在这里犯了很多错误。此外,上述方法似乎非常效率低下,并且在某种程度上它只是感觉非常错误,因为内核可以释放一些内存页面,释放一堆内存句柄,并为下一个移动一些内存过程。

所以,总而言之,在 Windows 上以最少的限制模拟进程替换的理想方法是什么?

【问题讨论】:

  • windows 根本不支持进程“替换”。 windows中没有这样的概念。您可以创建新流程。你可以退出\终止现有的
  • 这就是我说“模仿”的原因。我只需要它模拟足够它大部分在表面上似乎以这种方式工作,即使它只是一个复杂的,hacky shim。
  • “为了什么”是this Node feature request。而且我不需要精确模拟它——我只需要它模拟得足够好,这样人们就不必在 Windows 上回退到他们一直在所有平台上做的相同的解决方法。至于“替换”,this Wikipedia article sectionthis Linux manpage of exec 应该会有所帮助。请记住,在 Linux/Unix 上,它们的字面意思是“进程替换”。
  • 在那些平台上,exec 和类似的有效地杀死旧进程(好像通过不可恢复的SIGKILL),释放该进程分配的所有内存,在其位置创建一个新进程,并启动该进程,就好像它是原始进程本身一样。
  • 是的 - Windows 上的大多数“信号”都是模拟的,但它们的模拟范围非常广泛,我很容易忘记。 :-)

标签: windows winapi optimization process


【解决方案1】:

鉴于我不了解实际要求的是什么,并且我肯定会带着“无论如何都不会这么称呼它,只会导致疯狂”的情绪来看待诸如“执行”之类的事情,但我还是会看这个问题通过问自己:

如果进程-a 被杀死并被一个几乎相同的进程-b 取代 - 谁或什么会注意到?

任何拥有进程 ID 或进程句柄的东西肯定会注意到。这可以通过编写一个包装应用程序来处理,该应用程序加载第一个节点进程,当被触发时,将其杀死并加载下一个。外部观察者看到包装过程句柄和 id 没有改变。

显然,这会切断输入节点应用程序的标准输入和标准输出流。但同样,包装进程可以通过将相同的可继承句柄集传递给通过正确填写传递给CreateProcessSTARTUPINFO 结构启动的每个节点进程来解决此问题。

Windows 不支持信号,MS C 运行时伪造的所有信号都处理内部错误,除了一个处理通过 ctrl-C 关闭的交互式控制台窗口,活动的 Node.js 应用程序确定无论如何都要得到 - 或者可以从包装器传递,因为节点应用程序实际上不会在交互式控制台上运行这种方法。

除此之外,其他一切似乎都是 Node.js 应用程序的内部细节,因此不应影响任何第 3 方应用程序通过其标准输入/标准输出流与它认为是单节点应用程序的通信。

【讨论】:

    猜你喜欢
    • 2014-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-26
    • 1970-01-01
    • 2012-08-18
    • 2023-02-15
    • 1970-01-01
    相关资源
    最近更新 更多