ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深入 linux-insides:Linux 进程资源限制 getrlimit / setrlimit / prlimit 系统调用解析

深入 linux-insides:Linux 进程资源限制 getrlimit / setrlimit / prlimit 系统调用解析 文档教程操作系统【免费下载链接】linux-insidesA book-in-progress about the Linux kernel and its insides.项目地址https://gitcode.com/gh_mirrors/li/linux-insides点击查看免费下载本文是 linux-insides 开源项目Linux 内核内核机制逐篇精讲系列书籍中系统调用System call章节的第六部分。系统内每个进程都会消耗文件、CPU 时间、内存等各类资源而这些资源并非无限内核必须提供一套机制来查看和调整每个进程的资源上限。本文将先以用户空间视角介绍管理资源限制的三个系统调用getrlimit、setrlimit与prlimit再深入 Linux 内核源码剖析其核心实现do_prlimit并借助本仓库其他章节的初始化代码验证这些限制在进程生命周期中的落地方式。读完本文你将掌握资源限制的 soft/hard 语义、16 类可管理资源清单、ulimit与prlimit的真实调用关系以及内核侧完整的参数校验与更新流程。为什么进程需要资源限制系统中每个进程都会使用一定量的不同资源打开的文件、CPU 时间、内存、信号队列、消息队列等等。这些资源不是无限的因此每个进程以及系统管理员都需要一种工具来回答两个问题当前某个资源的上限是多少如何增大或减小这个上限Linux 为此提供了专门的管理接口。理解资源限制机制对排查生产环境问题尤为重要例如服务启动时报 Too many open files本质就是RLIMIT_NOFILE资源限制被触达容器、systemd 等系统组件也普遍通过调整资源限制来控制进程行为。本文从用户空间视角出发再逐步下探到内核实现。管理资源限制的三个系统调用在用户空间管理进程资源限制主要依赖三个系统调用它们都定义在 glibc 中并最终进入内核系统调用作用getrlimit读取当前进程某项资源的限制值setrlimit设置当前进程某项资源的限制值prlimit读取 / 设置由 PID 指定的任意进程的资源限制前两者的扩展三者原型如下int getrlimit(int resource, struct rlimit *rlim);int setrlimit(int resource, const struct rlimit *rlim);int prlimit(pid_t pid, int resource, const struct rlimit *new_limit, struct rlimit *old_limit);前两个函数只接收两个参数resource资源类型标识可用的资源类型见下文清单rlim由soft与hard两个限制值组合成的结构体。prlimit则多出两个参数这也是它被称为扩展的原因pid目标进程 IDprlimit会针对该进程执行操作new_limit非NULL时表示要写入的新限制值old_limit非NULL时目标进程当前的soft/hard限制会被填入这里。也就是说prlimit可以在一次调用中同时完成读取旧值和写入新值两件事并且可以作用于其他进程而不仅仅是调用者自身。soft limit 与 hard limit两种层次的限制系统调用涉及的每个资源都有两个限制层次soft软限制进程实际生效的资源上限hard硬限制软限制的天花板即软限制可以增长到的最大值。硬限制只有超级用户root才能调高因此软限制永远不可能超过对应的硬限制。两个值被封装在rlimit结构体中struct rlimit { rlim_t rlim_cur; /* soft limit */ rlim_t rlim_max; /* hard limit */ };其中rlim_t是内核定义的资源量类型通常为__kernel_ulong_t。这种软限制 硬限制的双层设计允许普通用户在自己的硬限制范围内自主调低或适度调高软限制而系统级的安全边界由硬限制守住。用 strace 验证 ulimit 背后的 prlimit 调用prlimit系统调用正是 shell 内置命令ulimit的底层实现。这一点可以借助strace工具直接验证ulimit命令运行时它发起的系统调用会被完整记录。执行~$ strace ulimit -s 21 | grep rl会得到类似下面的输出prlimit64(0, RLIMIT_NPROC, NULL, {rlim_cur63727, rlim_max63727}) 0 prlimit64(0, RLIMIT_NOFILE, NULL, {rlim_cur1024, rlim_max4*1024}) 0 prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur8192*1024, rlim_maxRLIM64_INFINITY}) 0这里出现的是prlimit64而不是prlimit原因在于strace记录的是底层系统调用内核入口而prlimit是 glibc 库函数层面的封装在 64 位系统上内核实际提供的是prlimit64库函数负责做兼容性转换后转发。输出还透露了几个有意思的细节三个调用都以pid 0为目标按 POSIX 语义pid 0表示当前进程new_limit传NULL说明ulimit -s在这里只做读取输出展示当前限制RLIMIT_STACK的硬限制显示为RLIM64_INFINITY64 位无上限而软限制为8192*10248 MiBRLIMIT_NOFILE软限制 1024、硬限制 4096这正是多数发行版默认的打开文件数上限。可管理的资源类型清单resource参数可取的枚举值由内核在include/uapi/linux/resource.h中定义。下表完整列出 Linux 支持的资源类型及其含义ResourceDescription含义RLIMIT_CPUCPU 时间限制单位为秒RLIMIT_FSIZE进程可创建文件的最大大小RLIMIT_DATA进程数据段data segment的最大大小RLIMIT_STACK进程栈的最大大小单位为字节RLIMIT_COREcore 转储文件的最大大小RLIMIT_RSS进程在物理内存RAM中可分配的字节数RLIMIT_NPROC单个用户可创建的进程最大数量RLIMIT_NOFILE进程可打开的文件描述符最大数量RLIMIT_MEMLOCK通过mlock锁定到 RAM 的内存最大字节数RLIMIT_AS虚拟内存的最大字节数RLIMIT_LOCKSflock及锁相关的fcntl调用的最大次数RLIMIT_SIGPENDING调用进程所属用户可排队的最大信号数量RLIMIT_MSGQUEUE可用于 POSIX 消息队列分配的字节数RLIMIT_NICE进程可设置的nice最大值RLIMIT_RTPRIO实时real-time优先级最大值RLIMIT_RTTIME实时调度策略下进程在不发起阻塞系统调用的情况下可被调度执行的微秒数上限借助prlimit或ulimit -a可以一次性查看当前 shell 进程所有资源的软 / 硬限制借助/proc/self/limits文件也能看到进程的资源限制全貌见下文。真实开源项目中的资源限制实践阅读大型开源项目源码时你会发现读取或更新资源限制是极其常见的操作本节以两个经典案例说明其典型用法。systemd放开 coredump 大小限制systemd 主程序在初始化时明确放开核心转储文件的大小限制避免核心转储被截断源码位于其src/core/main.c/* Dont limit the coredump size */ (void) setrlimit(RLIMIT_CORE, RLIMIT_MAKE_CONST(RLIM_INFINITY));其中RLIMIT_MAKE_CONST(RLIM_INFINITY)构造一个软硬限制都为无穷大的struct rlimit随后用setrlimit一次写入。(void)强转表明此处有意忽略返回值即使失败也不影响主流程。HAProxy启动前自检文件描述符上限HAProxy 这类高并发代理对文件描述符数量极其敏感它在启动阶段读取RLIMIT_NOFILE并与配置期望值比对若不足则给出警告源码位于其src/haproxy.cgetrlimit(RLIMIT_NOFILE, limit); if (limit.rlim_cur global.maxsock) { Warning([%s.main()] FD limit (%d) too low for maxconn%d/maxsock%d. Please raise ulimit-n to %d or more to avoid any trouble.\n, argv[0], (int)limit.rlim_cur, global.maxconn, global.maxsock, global.maxsock); }这里的模式很典型先getrlimit读取当前软限制再与业务配置global.maxsock比较提示运维人员通过ulimit -n调高限制。这也印证了资源限制接口在真实服务生命周期管理中的核心地位。内核侧实现getrlimit 与 setrlimit 都汇入 do_prlimit用户空间看到的是库函数内核侧则需要真正实现这三个系统调用。getrlimit与setrlimit的实现代码位于 Linux 内核的 kernel/sys.c下文未给出仓库内相对路径的内核文件均指 Linux 内核源码树中的同名文件本仓库为讲解书籍不包含内核源码本身。两者的实现高度相似都调用do_prlimit这个核心函数并负责把rlimit结构体在用户空间与内核空间之间拷贝。getrlimit的实现SYSCALL_DEFINE2(getrlimit, unsigned int, resource, struct rlimit __user *, rlim) { struct rlimit value; int ret; ret do_prlimit(current, resource, NULL, value); if (!ret) ret copy_to_user(rlim, value, sizeof(*rlim)) ? -EFAULT : 0; return ret; }setrlimit的实现SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim) { struct rlimit new_rlim; if (copy_from_user(new_rlim, rlim, sizeof(*rlim))) return -EFAULT; return do_prlimit(current, resource, new_rlim, NULL); }两个函数都用SYSCALL_DEFINE2宏定义参数个数为 2。注意它们的目标进程都是current——即调用者自身。getrlimit以NULL作为new_rlim传入只读从do_prlimit拿回结果后再通过copy_to_user写回用户空间setrlimit则先用copy_from_user把用户空间的新限制拷入内核再传给do_prlimitold_rlim传NULL只写。__user标注提示这些指针来自用户空间是 sparse 静态检查工具的约定。do_prlimit校验、加锁与更新的完整流程do_prlimit是prlimit系统调用的核心实现承担了所有实际工作。它的执行分为三个关键阶段。第一阶段资源合法性校验if (resource RLIM_NLIMITS) return -EINVAL;RLIM_NLIMITS是资源类型的总个数。任何超出范围的resource值都会立即返回-EINVAL非法参数。第二阶段新限制值语义校验当调用传入非NULL的new_rlim时内核执行两道关键检查if (new_rlim) { if (new_rlim-rlim_cur new_rlim-rlim_max) return -EINVAL; if (resource RLIMIT_NOFILE new_rlim-rlim_max sysctl_nr_open) return -EPERM; }第一道检查落实了软限制不能超过硬限制的语义若rlim_cur rlim_max则拒绝返回-EINVAL第二道检查针对RLIMIT_NOFILE即便调用者是超级用户文件描述符硬限制也不能超过sysctl_nr_open这个系统级上限否则返回-EPERM权限不足。这是内核在进程级上限之上设置的系统级安全网。sysctl_nr_open的值可以在运行时通过 procfs 查看~$ cat /proc/sys/fs/nr_open 1048576即单个进程可打开的文件描述符硬上限被系统钳制在 1048576。从本仓库 SysCall/linux-syscall-5.md 对open系统调用的讲解可知文件描述符分配函数get_unused_fd_flags正是在0与RLIMIT_NOFILE之间为进程寻找空闲描述符——资源限制直接参与日常文件操作的上限判定。第三阶段加锁保护与更新目标由于prlimit允许通过pid修改其他进程的限制内核在真正读写前必须对任务列表加锁防止并发场景下信号signal处理相关结构被破坏read_lock(tasklist_lock); ... ... ... read_unlock(tasklist_lock);任务列表被锁住后内核定位目标进程对应的资源限制实例rlim tsk-signal-rlim resource;这里tsk-signal-rlim是一个struct rlimit数组按资源类型编号索引 resource即定位到该进程第resource项资源的限制。随后的更新逻辑非常简洁if (old_rlim) *old_rlim *rlim;若new_rlim非NULL直接将其写入*rlim更新软 / 硬限制若old_rlim非NULL把更新前的旧值拷贝出来交给调用者。一次调用即可完成读旧值 写新值的原子化操作这正是prlimit相对getrlimit/setrlimit的扩展价值。仓库佐证资源限制数组在内核启动时的初始化资源限制并非凭空而来内核启动阶段就会为 0 号进程init_task初始化默认值。本仓库 Initialization/linux-initialization-10.md初始化章节End of initialization详细记录了fork_init中的这段代码init_task.signal-rlim[RLIMIT_NPROC].rlim_cur max_threads/2; init_task.signal-rlim[RLIMIT_NPROC].rlim_max max_threads/2; init_task.signal-rlim[RLIMIT_SIGPENDING] init_task.signal-rlim[RLIMIT_NPROC];init_task是task_struct的静态实例其signal字段类型为struct signal_struct内含rlim资源限制数组这里把RLIMIT_NPROC用户可拥有的最大进程数的软 / 硬限制都设为max_threads/2max_threads由FUTEX_TID_MASK推导RLIMIT_SIGPENDING最大待处理信号数直接复用RLIMIT_NPROC的值。rlim数组元素类型正是前文介绍的struct rlimit内核侧以__kernel_ulong_t定义字段位于include/uapi/linux/resource.h。继承自init_task的默认限制会随fork传递给子进程之后进程再通过setrlimit/prlimit按需调整。这些初始化值可以通过 procfs 直接观察cat /proc/self/limits Limit Soft Limit Hard Limit Units ... Max processes 63815 63815 processes Max pending signals 63815 63815 signals .../proc/self/limits输出的正是rlim数组中各项资源的软 / 硬限制与内核源码一一对应是快速验证资源限制状态的实用手段。从调用链看资源限制的完整生命周期综合上述各环节可以得到资源限制在内核中的完整工作链路内核启动时fork_init见 Initialization/linux-initialization-10.md为init_task.signal-rlim[]数组设置默认值进程通过fork继承父进程的rlim数组副本运行期进程调用setrlimit/prlimit经copy_from_user进入内核由do_prlimit完成合法性校验RLIM_NLIMITS边界、软限制 ≤ 硬限制、RLIMIT_NOFILE≤sysctl_nr_open、加锁tasklist_lock与数组更新各项资源消耗类操作在分配时依据rlim判定是否超限——如open分配文件描述符时受RLIMIT_NOFILE约束见 SysCall/linux-syscall-5.mdgetrlimit/prlimit则反向读取当前限制值经copy_to_user返回用户空间最终呈现给ulimit、/proc/self/limits等用户态工具。总结资源限制是 Linux 保障进程行为可控的基础机制。从用户空间看getrlimit、setrlimit与prlimit提供了读取、修改、跨进程操作的完整能力从内核看它们统一收敛到do_prlimit先校验资源编号与新值的语义软 ≤ 硬、RLIMIT_NOFILE不超过sysctl_nr_open再在tasklist_lock保护下更新tsk-signal-rlim数组中的对应条目。struct rlimit的软 / 硬双层设计与rlim按资源编号组织的数组结构构成了这一机制最核心的数据模型。若要继续深入系统调用主题可接着阅读本仓库 SysCall/README.md 列出的其他章节如 linux-syscall-1.md 介绍系统调用概念、linux-syscall-2.md 讲解内核处理流程也可借助strace与/proc/self/limits在实际系统上做对照验证。赞分享文档教程操作系统【免费下载链接】linux-insidesA book-in-progress about the Linux kernel and its insides.项目地址https://gitcode.com/gh_mirrors/li/linux-insides点击查看免费下载相关推荐Linux 资源限制系统调用深入解析getrlimit / setrlimit / prlimit 的机制与内核实现Linux 资源限制系统调用深入解析getrlimit / setrlimit / prlimit 的机制与内核实现 本文隶属于本仓库《Linux 内核揭秘》理解NoiseTorch-ng的资源限制setrlimit系统调用应用理解NoiseTorch ng的资源限制setrlimit系统调用应用 你是否曾在Linux系统上使用麦克风时遇到过音频卡顿、延迟或噪音抑制效果不佳的问题这桌面应用音频处理终极指南如何使用Bash中的prlimit命令管理进程资源限制终极指南如何使用Bash中的prlimit命令管理进程资源限制 进程资源限制是Linux系统管理中至关重要的概念而 prlimit 命令则是bash环境中管教程上一篇HyperFrames v0.7.82 发布解析Studio 导出保真分辨率与浏览器启动可靠性修复下一篇Onyx Craft 出口代理如何执行外部应用动作策略从出站请求到 forward / hold / block 的匹配与裁决链创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表