You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python setrlimit设内存限制后子进程与HTTP请求异常问题

问题描述

在Python程序中使用setrlimit设置RLIMIT_AS内存限制后,出现两类异常:

  1. 子进程调用失败,报错:/bin/sh: error while loading shared libraries: libc.so.6: failed to map segment from shared object
  2. requests.get()发送HTTP请求时出现挂起

代码示例:

def main():        
    # resource.setrlimit(resource.RLIMIT_AS, (1024 *1000, 1024*1000))
    print(requests.get('https://api.github.com').json())
    print(subprocess.Popen(['which', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0])

运行环境:

  • Kubernetes/Docker 基于 python:3.10.5 镜像
  • Linux内核版本 6.1.129-vsh0zfs227
  • Kubernetes已配置cgroup memory.max内存限制,但希望在Python中捕获MemoryError,而非容器被随机Sigkill

原因分析

  • 子进程启动失败:RLIMIT_AS限制的是进程的虚拟内存总大小,包括主进程、子进程的所有虚拟内存空间。子进程启动时需要加载共享库(如libc.so.6),这些库需要映射到虚拟内存中,但你设置的1MB限制过小,完全不足以容纳共享库的映射空间,导致加载失败。
  • HTTP请求挂起:requests底层的socket操作、SSL/TLS握手等流程需要分配虚拟内存(如缓冲区、加密上下文),当虚拟内存被严格限制到1MB时,这些操作会因内存不足陷入阻塞或无法完成,表现为请求挂起。
  • 两类内存限制的差异:cgroup的memory.max限制的是物理内存+swap的实际使用量,而RLIMIT_AS是虚拟内存上限——虚拟内存远大于实际内存(包含磁盘缓存、共享库映射、匿名内存等),过小的RLIMIT_AS会触发和实际内存使用无关的内存不足问题。

解决方法

1. 合理调整RLIMIT_AS阈值

不要设置过小的虚拟内存限制,需预留足够空间给共享库、系统调用所需的虚拟内存区域。可参考程序正常运行时的虚拟内存使用量来设置:

import resource
# 设置虚拟内存限制为2GB(单位:字节)
resource.setrlimit(resource.RLIMIT_AS, (2 * 1024 * 1024 * 1024, 2 * 1024 * 1024 * 1024))

可通过pmap -x <pid>命令查看正常运行时进程的虚拟内存使用情况,以此为参考设置阈值。

2. 改用实际内存限制(RLIMIT_RSS)

如果目标是限制进程的实际物理内存占用,建议使用RLIMIT_RSS(Resident Set Size),它限制的是进程实际占用的物理内存大小,不会影响共享库映射等虚拟内存操作:

import resource
# 设置物理内存限制为1GB
resource.setrlimit(resource.RLIMIT_RSS, (1 * 1024 * 1024 * 1024, 1 * 1024 * 1024 * 1024))

注意:RLIMIT_RSS在部分Linux内核中是建议性限制,内核不会严格强制执行,但结合cgroup的限制可形成互补——当进程接近RLIMIT_RSS时Python会抛出MemoryError,cgroup则作为最后防线防止内存溢出。

3. 隔离子进程的内存限制

如果不需要子进程继承主进程的内存限制,可在启动子进程前临时重置其内存限制:

import resource
import subprocess

def reset_rlimit():
    # 子进程将RLIMIT_AS重置为无限制
    resource.setrlimit(resource.RLIMIT_AS, (resource.RLIM_INFINITY, resource.RLIM_INFINITY))

def main():
    resource.setrlimit(resource.RLIMIT_AS, (2 * 1024 * 1024 * 1024, 2 * 1024 * 1024 * 1024))
    print(requests.get('https://api.github.com').json())
    # 启动子进程时执行重置函数
    print(subprocess.Popen(['which', 'wkhtmltopdf'], stdout=subprocess.PIPE, preexec_fn=reset_rlimit).communicate()[0])

4. 结合cgroup与Python限制的最佳实践

  • 保留Kubernetes的cgroup memory.max作为最终兜底,防止容器因内存溢出被Sigkill。
  • 在Python中设置合理的RLIMIT_AS或RLIMIT_RSS,确保程序在接近内存上限时主动抛出MemoryError,方便捕获并处理,而非直接被内核终止。

内容的提问来源于stack exchange,提问作者Bálint Balina

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 01:57:23