十、进阶 | Linux 内核中的系统调用(6)
Linux中的资源限制
系统中的每个进程都会使用一定数量的不同资源,比如文件、CPU时间、内存等。
这些资源并不是无限的,每个进程都应该有一个工具来管理它们。有时了解某个资源的当前限制或更改其值是有用的。在这篇文章中,我们将考虑这样的工具,它们允许我们获取有关进程限制的信息,并增加或减少这些限制。
我们将从用户空间视图开始,然后看看它在Linux内核中的实现。
有三个主要的基本系统调用来管理进程的资源限制:
getrlimit
setrlimit
prlimit
前两个允许进程读取和设置系统资源的限制。最后一个是对前面函数的扩展。prlimit
允许设置和读取由PID指定的进程的资源限制。这些函数的定义如下:
getrlimit
是:
int getrlimit(int resource, struct rlimit *rlim);
setrlimit
是:
int setrlimit(int resource, const struct rlimit *rlim);
prlimit
的定义是:
int prlimit(pid_t pid, int resource, const struct rlimit *new_limit,
struct rlimit *old_limit);
在前两种情况下,函数接受两个参数:
resource
- 表示资源类型(稍后我们将看到可用类型);rlim
-soft
和hard
限制的组合。
有两种类型的限制:
soft
hard
第一个提供了进程资源的实际限制。第二个是soft
限制的上限值,只能由超级用户设置。因此,soft
限制永远不能超过相关的hard
限制。
这两个值都结合在rlimit
结构中:
struct rlimit {
rlim_t rlim_cur;
rlim_t rlim_max;
};
最后一个函数看起来有点复杂,接受4
个参数。除了resource
参数,它还接受:
pid
- 指定应在其上执行prlimit
的进程ID;new_limit
- 如果不是NULL
,则提供新的限制值;old_limit
- 如果不是NULL
,则当前的soft
和hard
限制将放在这里。
正是prlimit
函数被ulimit实用工具使用。我们可以用strace实用工具来验证这一点。
例如:
~$ strace ulimit -s 2>&1 | grep rl
prlimit64(0, RLIMIT_NPROC, NULL, {rlim_cur=63727, rlim_max=63727}) = 0
prlimit64(0, RLIMIT_NOFILE, NULL, {rlim_cur=1024, rlim_max=4*1024}) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
在这里我们可以看到prlimit64
,而不是prlimit
。事实上,我们在这里看到的是底层系统调用,而不是库调用。
现在让我们来看看可用资源的列表:
RLIMIT_CPU | 以秒为单位给出的CPU时间限制 |
RLIMIT_FSIZE | 进程可能创建的文件的最大大小 |
RLIMIT_DATA | 进程的数据段的最大大小 |
RLIMIT_STACK | 进程堆栈的最大大小(以字节为单位) |
RLIMIT_CORE | 核心 文件的最大大小 |
RLIMIT_RSS | 进程在RAM中可以分配的字节数 |
RLIMIT_NPROC | 用户可以创建的最大进程数 |
RLIMIT_NOFILE | 进程可以打开的最大文件描述符数量 |
RLIMIT_MEMLOCK | 通过mlock锁定到RAM的内存最大字节数 |
RLIMIT_AS | 虚拟内存的最大大小(以字节为单位) |
RLIMIT_LOCKS | 最大flock和锁定相关的fcntl调用次数 |
RLIMIT_SIGPENDING | 可以为调用进程的用户排队的信号的最大数量 |
RLIMIT_MSGQUEUE | 可以为POSIX消息队列分配的字节数 |
RLIMIT_NICE | 进程可以设置的最大nice值 |
RLIMIT_RTPRIO | 最大实时优先级值 |
RLIMIT_RTTIME | 进程可以在实时调度策略下被调度的最大微秒数,而无需执行阻塞系统调用 |
如果你查看开源项目的源代码,你会发现读取或更新资源限制是一个相当广泛使用的操作。
例如:systemd
/* 不限制coredump大小 */
(void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY));
或haproxy:
getrlimit(RLIMIT_NOFILE, &limit);
if (limit.rlim_cur < global.maxsock) {
Warning("[%s.main()] FD限制(%d)太低,maxconn=%d/maxsock=%d。请将'ulimit-n'提高到%d或更高以避免任何问题。\n",
argv[0], (int)limit.rlim_cur, global.maxconn, global.maxsock, global.maxsock);
}
我们刚刚看到了用户空间中与资源限制相关的一些内容,现在让我们看看Linux内核中的相同系统调用。
Linux内核中的资源限制
getrlimit
系统调用和setrlimit
的实现看起来相似。它们都执行do_prlimit
函数,这是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);
}
这些系统调用的实现定义在kernel/sys.c内核源代码文件中。
首先do_prlimit
函数执行一个检查,以确保给定的资源是有效的:
if (resource >= RLIM_NLIMITS)
return -EINVAL;
如果失败,则返回-EINVAL
错误。在此检查成功通过并且新的限制作为非NULL
值传递后,进行以下两个检查:
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;
}
检查给定的soft
限制是否没有超过hard
限制,并且如果给定的资源是最大文件描述符数量,则hard
限制不大于sysctl_nr_open
值。sysctl_nr_open
的值可以通过procfs找到:
~$ cat /proc/sys/fs/nr_open
1048576
在所有这些检查之后,我们锁定tasklist
以确保在更新给定资源的限制时,与[信号](https://en.wikipedia.org/wiki/Signal_(操作系统)处理程序相关的内容不会被破坏:
read_lock(&tasklist_lock);
...
...
...
read_unlock(&tasklist_lock);
我们需要这样做,因为prlimit
系统调用允许我们通过给定的pid更新另一个任务的限制。由于任务列表被锁定,我们获取负责给定进程给定资源限制的rlimit
实例:
rlim = tsk->signal->rlim + resource;
其中tsk->signal->rlim
只是一个表示某些资源的struct rlimit
数组。如果new_rlim
不是NULL
,我们只是更新它的值。如果old_rlim
不是NULL
,我们填充它:
if (old_rlim)
*old_rlim = *rlim;
就是这样。
结论
这是描述Linux内核系统调用实现的第二部分的结尾。如果您有任何问题或建议,请在Twitter上联系我0xAX,给我发一封电子邮件,或者在问题中提出。
请注意,英语不是我的母语,如果有任何不便,我深表歉意。如果您发现任何错误,请向我发送linux-insides的PR。
链接
"《Linux嵌入式必考必会》专栏,专为嵌入式开发者量身打造,聚焦Linux环境下嵌入式系统面试高频考点。涵盖基础架构、内核原理、驱动开发、系统优化等核心技能,实战案例与理论解析并重。助你快速掌握面试关键,提升竞争力。