在 Windows 上模拟进程替换的理想方法是什么?

Isi*_*ows 6 windows optimization winapi process

因此,在我针对 Node.js 提交的功能请求中,我正在寻找一种用另一个节点替换当前 Node 进程的方法。在Linux 和朋友(实际上是任何兼容POSIX 的系统)中,这很简单:使用execve和朋友,就到此为止。但显然,这在 Windows 上不起作用,因为它只有CreateProcess(它execve和朋友委托给,完成异步行为)。人们 并不是不想 类似的事情导致网站上出现大量重复的问题。(这不是重复的,因为它在给定某些限制的情况下明确寻求解决方法,而不仅仅是要求直接替换。)

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

  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. 释放每个打开的手柄
    6. 如果可能(即不在 UWP 程序中),对于每个堆,如果它不是默认堆或临时堆(如果存在),请将其销毁。
    7. 如果可能,请关闭每个打开的手柄
    8. 如果可能,请遍历默认堆并释放与其关联的每个段。
    9. 使用静态分配的文件/参数/环境/等创建一个新进程。没有创建新窗口。
    10. 代理所有未来接收到的信号、异常等,而无需以某种方式修改此过程。标准信号很简单,但除了例外情况之外就不那么容易了。
    11. 等待该过程结束。
    12. 返回进程的退出代码

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

但由于我对Windows不太熟悉,所以这里可能犯了很多错误。另外,上面的方法似乎效率极低,并且在某种程度上,对于内核可以释放一些内存页、释放一堆内存句柄并为下一个进程移动一些内存的事情来说,它感觉非常错误。

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

Chr*_*cke -8

鉴于我不明白实际要求的是什么,而且我当然会带着“谁到底会这么称呼它,除了疯狂之外什么也不会产生”的情绪来看待像“execve”这样的东西,尽管如此,我还是通过以下方式看待这个问题:问自己:

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

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

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

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

除此之外,其他所有内容似乎都是 Node.js 应用程序的内部细节,因此不应影响任何第三方应用程序通过其 stdin/stdout 流与其认为是单节点应用程序进行通信。