【发布时间】:2018-07-05 07:00:12
【问题描述】:
所以,在feature request I filed against Node.js 中,我正在寻找一种方法来用另一个替换当前的 Node 进程。在 Linux 和朋友(实际上,任何符合 POSIX 的系统)中,这很容易:使用execve 和朋友,就这样结束了。但显然,这在 Windows 上不起作用,因为它只有 CreateProcess(execve 和朋友委托给 complete with async behavior)。而且它不像peoplehaven'twantedtodosimilar,导致numerous duplicate questions on this site。 (这不是重复的,因为它在给定某些限制的情况下明确寻求解决方法,而不仅仅是要求直接替换。)
流程替换有几个方面需要解决:
- 所有控制台 I/O 流都必须转发到新进程。
- 所有信号都需要透明地转发到新进程。
- 必须销毁旧进程中的数据,尽可能多地回收资源。
- 应销毁所有预先存在的线程和子进程。
- 除了打开的文件描述符和命名管道/等之外,所有预先存在的句柄都应该被销毁。
- 理想情况下,旧进程的内存应在创建进程后保持在最低限度。
- 对于我的特定用例,保留进程 ID 并不重要。
对于我的特殊情况,有一些限制:
- 我可以控制初始进程的启动以及“进程替换”功能的位置。
- 我可以通过附加组件在可能的任何堆栈偏移处加载任意本机代码。
- 含义:我什至无法梦想跟踪
malloc调用、句柄、线程操作或进程操作来跟踪和释放它们,因为 DLL 重写并不完全实用。
- 含义:我什至无法梦想跟踪
- 我无法控制何时我的“进程替换”被调用。它可以通过附加组件调用,该附加组件可以通过 FFI 的解释代码甚至递归的另一个附加组件调用。它甚至可以在附加初始化期间调用。
- 含义:我无法知道堆栈中的内容,即使我完美地检测了我的一面。并且重写他们所有的
calls 和pushes 远非实际,而且由于显而易见的原因,这将是全方位的缓慢。
- 含义:我无法知道堆栈中的内容,即使我完美地检测了我的一面。并且重写他们所有的
所以,这是我的想法的要点:使用类似于伪蹦床的东西。
- 静态分配以下内容:
- 堆栈指针的单个指针。
-
MAX_PATH + 1应用程序路径的字符 +'\0'。 -
MAX_PATH + 1当前工作目录路径的字符 +'\0'。 - 32768 个字符用于参数 +
'\0'。 - 32768 个环境字符 +
'\0'。
- 进入时,将全局堆栈指针引用设置为堆栈指针。
- 关于“替换”:
- 进行相关的进程清理并尽可能锁定/释放。
- 将堆栈指针设置为存储的原始全局指针。
- 终止每个子线程。
- 杀死每个子进程。
- 免费each open handle。
- 如果可能(即不在 UWP 程序中),For each heap、destroy it 如果不是 default heap 或临时堆(如果存在)。
- 如果可能,关闭each open handle。
- 如果可能,walk 默认堆和free 与其关联的每个段。
- 使用静态分配的文件/参数/环境/等创建一个新进程。没有创建新窗口。
- 代理所有未来接收到的信号、异常等,而无需以某种方式修改此过程。 The standard signals are easy,但除了例外情况不多。
- 等待进程结束。
- 返回the process's exit code。
这里的想法是使用基于进程的蹦床,并在启动新创建的进程时将当前进程大小降至绝对最小值。
但是在我对 Windows 不是很熟悉的地方,我可能在这里犯了很多错误。此外,上述方法似乎非常效率低下,并且在某种程度上它只是感觉非常错误,因为内核可以释放一些内存页面,释放一堆内存句柄,并为下一个移动一些内存过程。
所以,总而言之,在 Windows 上以最少的限制模拟进程替换的理想方法是什么?
【问题讨论】:
-
windows 根本不支持进程“替换”。 windows中没有这样的概念。您可以创建新流程。你可以退出\终止现有的
-
这就是我说“模仿”的原因。我只需要它模拟足够它大部分在表面上似乎以这种方式工作,即使它只是一个复杂的,hacky shim。
-
“为了什么”是this Node feature request。而且我不需要精确模拟它——我只需要它模拟得足够好,这样人们就不必在 Windows 上回退到他们一直在所有平台上做的相同的解决方法。至于“替换”,this Wikipedia article section 和 this Linux manpage of
exec应该会有所帮助。请记住,在 Linux/Unix 上,它们的字面意思是“进程替换”。 -
在那些平台上,
exec和类似的有效地杀死旧进程(好像通过不可恢复的SIGKILL),释放该进程分配的所有内存,在其位置创建一个新进程,并启动该进程,就好像它是原始进程本身一样。 -
是的 - Windows 上的大多数“信号”都是模拟的,但它们的模拟范围非常广泛,我很容易忘记。 :-)
标签: windows winapi optimization process