如何减少分叉进程以运行可执行文件的 Web 服务器的内存使用量?

Posted

技术标签:

【中文标题】如何减少分叉进程以运行可执行文件的 Web 服务器的内存使用量?【英文标题】:How can I decrease the memory usage of my web server which forks processes to run an executable? 【发布时间】:2019-02-16 16:48:01 【问题描述】:

我有一个运行在具有 1GB RAM 的 AWS EC2 实例上的 Django Web 服务器。当向 Web 服务器发出某个请求时,我需要使用 subprocess.call('./executable') 运行可执行文件。可执行文件运行一个 Perl 脚本,该脚本执行一些文件 I/O,然后对从文件解析的数据进行一些计算,没什么太疯狂的。

我开始遇到内存分配问题,导致我的 Web 服务器崩溃,所以我使用 ulimit -v some_value 对分配给每个子进程的虚拟内存设置硬限制。我发现每个子进程需要大约 100MB 才能运行而不会出错,所以我遇到内存问题也就不足为奇了,只有 1GB 的 RAM。

不过,我想知道为什么这个内存使用率如此之高。是否因为我从运行内存密集型 Web 服务器的进程调用 subprocess.call 而分配了大量额外内存?运行一个运行 Perl 脚本的可执行文件是否必然会占用大量内存,因为 Perl 有一些开销之类的?如果 Perl 脚本用 Python 重新编写并直接在 Django Web 服务器中运行,它会使用更少的内存吗?

非常感谢您对此提供的任何帮助。谢谢!

【问题讨论】:

【参考方案1】:

有一些很棒的 cmets 来自比我更了解内核、进程和内存的细节的人!看看他们。

我真的没有给你一个明确的答案,但我希望在这里能有所启发:

此 SO anwer 解释了内存使用的原因以及内存不足异常:Python memory allocation error using subprocess.Popen。

这是 Unix 中很常见的问题,实际上与 python 或生物信息学无关。调用 os.fork() 会暂时将父进程的内存加倍(父进程的内存必须对子进程可用),然后将其全部丢弃以执行 exec()。虽然实际上并不总是复制此内存,但系统必须有足够的内存来允许它被复制,因此如果您的父进程正在使用超过一半的系统内存并且您的子进程甚至"wc -l ",你会遇到内存错误。

您应该考虑在 Python 中实现此 perl 脚本并在您的视图中使用该模块/包,避免在您的请求-响应周期中使用另一个线程/进程来处理此问题。

另一件事可能相关,也可能不相关,如果这是一项长时间运行的作业或 CPU 密集型任务,您应该考虑使用 Celery 或 Python RQ 之类的东西在后台作业中处理它。这可以让您的服务器快速响应请求并避免请求积压,从而避免出现 20 个请求仍在处理中的情况,因为这个长时间运行的任务正在完成它的工作,因此没有其他人可以访问服务器。使用工人的选择取决于您的需求、截止日期等。

【讨论】:

引用的那段话很有误导性。 “对 os.fork() 的调用暂时使父进程的内存加倍”意味着“由 os.fork() 创建的子进程将暂时使用与父进程一样多的内存”,但即使这样也具有误导性,因为它没有'实际上根本没有用完任何内存;全部与父进程共享。 我认为“对 os.fork() 的调用暂时使父进程的内存加倍(父进程的内存必须对子进程可用)”准确描述了您的身份在“它实际上并没有占用任何内存;它都与父进程共享。” 不,父进程的内存大小根本没有变化,但那段话听起来像。如果在分叉之前父级的大小是 50 MiB,那么您最终会得到一个 50 MiB 的父级和一个 50 MiB 的子级,它们总共使用 50 MiB。在 exec 之后,孩子的使用量可能会下降(比如 10 MiB),但总量会增加(在我们的示例中为 60 MiB)。 fork 根本不分配任何内存。 不可能没有足够的可用内存,因为分叉不占用内存。

以上是关于如何减少分叉进程以运行可执行文件的 Web 服务器的内存使用量?的主要内容,如果未能解决你的问题,请参考以下文章

Python:在分叉的孩子和父母之间共享变量

分叉和传递套接字c ++

如何以可编程方式等待进程启动端点

ubuntu12.04下创建了一个守护进程,生成了一个可执行文件,如何让这个可执行文件开机自动运行?

`read` 命令导致分叉进程在前台发生

如何运行资源内的exe 以线程运行exe