Linux内核分析之进程管理-11

韩乔落

第11章 进程资源限制与统计

基于 Linux 6.12.38 源码分析


11.1 资源限制 (rlimit)

11.1.1 rlimit 结构

位置: include/linux/sched.h

1
2
3
4
5
6
7
8
9
struct task_struct {
struct rlimit rlim[RLIM_NLIMITS];
};

// include/linux/resource.h
struct rlimit {
__kernel_ulong_t rlim_cur; /* 软限制 */
__kernel_ulong_t rlim_max; /* 硬限制 */
};

11.1.2 资源限制类型

位置: include/uapi/asm-generic/resource.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
* 资源限制类型
*/
enum __rlimit_resource {
RLIMIT_CPU, /* CPU 时间 (秒) */
RLIMIT_FSIZE, /* 文件大小 (字节) */
RLIMIT_DATA, /* 数据段大小 (字节) */
RLIMIT_STACK, /* 栈大小 (字节) */
RLIMIT_CORE, /* Core 文件大小 (字节) */
RLIMIT_RSS, /* 驻留集大小 (字节) */
RLIMIT_NPROC, /* 进程数 */
RLIMIT_NOFILE, /* 打开文件数 */
RLIMIT_MEMLOCK, /* 锁定内存 (字节) */
RLIMIT_AS, /* 地址空间大小 (字节) */
RLIMIT_LOCKS, /* 文件锁数 */
RLIMIT_SIGPENDING, /* 挂起信号数 */
RLIMIT_MSGQUEUE, /* POSIX 消息队列 (字节) */
RLIMIT_NICE, /* Nice 值 */
RLIMIT_RTPRIO, /* 实时优先级 */
RLIMIT_RTTIME, /* 实时 CPU 时间 (微秒) */
RLIMIT_NLIMITS, /* 限制数量 */
};

11.1.3 软限制与硬限制

1
2
3
4
5
6
7
8
9
10
11
12
/*
* 软限制 (rlim_cur):
* - 进程可以修改 (不能超过硬限制)
* - 内核强制执行
*
* 硬限制 (rlim_max):
* - 只有 privileged 进程可以提高
* - 软限制的上限
*
* RLIM_INFINITY 表示无限制
*/
#define RLIM_INFINITY (~0UL)

11.1.4 资源限制系统调用

位置: kernel/sys.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 获取资源限制
SYSCALL_DEFINE2(getrlimit, unsigned int, resource,
struct rlimit __user *, rlim)
{
struct rlimit value;

if (resource >= RLIM_NLIMITS)
return -EINVAL;

// 复制当前限制
task_lock(current->group_leader);
value = current->signal->rlim[resource];
task_unlock(current->group_leader);

// 复制到用户空间
if (copy_to_user(rlim, &value, sizeof(*rlim)))
return -EFAULT;

return 0;
}

// 设置资源限制
SYSCALL_DEFINE2(setrlimit, unsigned int, resource,
struct rlimit __user *, rlim)
{
struct rlimit new_rlim, *old_rlim;
int retval;

if (resource >= RLIM_NLIMITS)
return -EINVAL;

// 复制用户参数
if (copy_from_user(&new_rlim, rlim, sizeof(*rlim)))
return -EFAULT;

// 检查权限
if (new_rlim.rlim_cur > new_rlim.rlim_max)
return -EINVAL;

old_rlim = current->signal->rlim + resource;

// 检查是否需要 CAP_SYS_RESOURCE
if ((new_rlim.rlim_max > old_rlim->rlim_max) &&
!capable(CAP_SYS_RESOURCE))
return -EPERM;

// 更新限制
*old_rlim = new_rlim;

return 0;
}

11.1.5 prlimit 系统调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
SYSCALL_DEFINE4(prlimit64, pid_t, pid,
unsigned int, resource,
const struct rlimit64 __user *, new_rlim,
struct rlimit64 __user *, old_rlim)
{
struct task_struct *tsk;
struct rlimit64 old, new;
int ret;

// 查找目标进程
tsk = find_task_by_vpid(pid);
if (!tsk)
return -ESRCH;

// 获取旧限制
if (old_rlim) {
old.rlim_cur = tsk->signal->rlim[resource].rlim_cur;
old.rlim_max = tsk->signal->rlim[resource].rlim_max;

if (copy_to_user(old_rlim, &old, sizeof(old)))
return -EFAULT;
}

// 设置新限制
if (new_rlim) {
if (copy_from_user(&new, new_rlim, sizeof(new)))
return -EFAULT;

// 设置限制
ret = do_prlimit(tsk, resource, &new, NULL);
if (ret)
return ret;
}

return 0;
}

11.2 CPU 时间限制

11.2.1 RLIMIT_CPU

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 检查 CPU 时间限制
static inline int check_rlimit_cpu(struct task_struct *p, u64 runtime)
{
unsigned long rlim = p->signal->rlim[RLIMIT_CPU].rlim_cur;

if (rlim == RLIM_INFINITY)
return 0;

// 转换为时钟 tick
if (runtime / HZ >= rlim) {
// 发送 SIGXCPU
send_sig(SIGXCPU, p, 0);

// 如果超出硬限制,发送 SIGKILL
if (runtime / HZ >= p->signal->rlim[RLIMIT_CPU].rlim_max)
send_sig(SIGKILL, p, 0);

return 1;
}

return 0;
}

11.2.2 实时 CPU 时间限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// RLIMIT_RTTIME - 实时任务 CPU 时间限制
static int check_rlimit_rttime(struct task_struct *p, u64 runtime)
{
unsigned long rlim = p->signal->rlim[RLIMIT_RTTIME].rlim_cur;

if (rlim == RLIM_INFINITY)
return 0;

if (runtime >= rlim) {
// 发送 SIGKILL
send_sig(SIGKILL, p, 0);
return 1;
}

return 0;
}

11.3 文件描述符限制

11.3.1 RLIMIT_NOFILE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// kernel/sys.c
// 检查文件描述符限制
int get_fd_limit(struct task_struct *tsk)
{
struct rlimit *rlim = &tsk->signal->rlim[RLIMIT_NOFILE];
return rlim->rlim_cur;
}

// fs/file.c
// 分配文件描述符时检查限制
static int alloc_fd(unsigned start, unsigned flags)
{
struct files_struct *files = current->files;
unsigned int max_files = get_fd_limit(current);

// 检查是否超出限制
if (fd >= max_files)
return -EMFILE;

// 分配 fd
// ...
}

11.3.2 系统限制

1
2
3
4
5
6
7
8
9
10
# 系统级文件描述符限制
/proc/sys/fs/file-max # 系统最大打开文件数
/proc/sys/fs/file-nr # 当前分配的文件描述符

# 查看进程限制
$ ulimit -n
1024

# 设置限制
$ ulimit -n 4096

11.4 内存限制

11.4.1 RLIMIT_AS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// RLIMIT_AS - 地址空间大小限制
// mm/mmap.c
unsigned long task_rlimit(const struct task_struct *tsk,
unsigned int resource)
{
struct rlimit *rlim = tsk->signal->rlim + resource;
return READ_ONCE(rlim->rlim_cur);
}

// 检查地址空间限制
bool check_rlimit_address(struct mm_struct *mm, unsigned long len)
{
unsigned long rlim, new;

rlim = task_rlimit(current, RLIMIT_AS);
if (rlim == RLIM_INFINITY)
return true;

new = mm->total_vm << PAGE_SHIFT;
if (new + len > rlim)
return false;

return true;
}

11.4.2 RLIMIT_DATA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// RLIMIT_DATA - 数据段大小限制
static int check_data_rlimit(unsigned long addr, unsigned long len)
{
unsigned long rlim = task_rlimit(current, RLIMIT_DATA);
unsigned long size = current->mm->total_vm << PAGE_SHIFT;

if (rlim == RLIM_INFINITY)
return 0;

if (size + len > rlim)
return -ENOMEM;

return 0;
}

11.4.3 RLIMIT_STACK

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// RLIMIT_STACK - 栈大小限制
// mm/mmap.c
unsigned long rlimit_stack(void)
{
struct rlimit *rlim = current->signal->rlim + RLIMIT_STACK;

/* 最小栈大小 */
if (rlim->rlim_cur == RLIM_INFINITY)
return STACK_MAX;

return max(rlim->rlim_cur, (unsigned long)rlim->rlim_max);
}

// 扩展栈时检查
int expand_stack(struct vm_area_struct *vma, unsigned long addr)
{
unsigned long rlim = rlimit_stack();

if (addr > vma->vm_start + rlim)
return -ENOMEM;

// 扩展栈
// ...
}

11.5 进程数限制

11.5.1 RLIMIT_NPROC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// kernel/fork.c
// 检查进程数限制
static int check_nproc_limit(struct task_struct *p)
{
struct rlimit *rlim = &p->signal->rlim[RLIMIT_NPROC];
long count;

// 统计当前用户的进程数
count = atomic_read(&p->user->processes);

if (count >= rlim->rlim_cur) {
// 检查是否有特权
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE))
return -EAGAIN;
}

return 0;
}

// copy_process() 中检查
static struct task_struct *copy_process(...)
{
// ...

retval = check_nproc_limit(p);
if (retval)
goto bad_fork_free;

// ...
}

11.6 进程统计

11.6.1 CPU 时间统计

位置: include/linux/sched.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct task_struct {
u64 utime; /* 用户态 CPU 时间 */
u64 stime; /* 内核态 CPU 时间 */
u64 gtime; /* Guest (虚拟机) CPU 时间 */
unsigned long long utimescaled; /* 缩放的用户时间 */
unsigned long long stimescaled; /* 缩放的内核时间 */
u64 prev_cputime; /* 上一次的 CPU 时间 */
};

// 计算进程 CPU 时间
static inline u64 task_utime(struct task_struct *p)
{
return p->utime;
}

static inline u64 task_stime(struct task_struct *p)
{
return p->stime;
}

11.6.2 缺页统计

1
2
3
4
5
6
7
8
9
10
11
12
struct task_struct {
unsigned long min_flt; /* 次要缺页 */
unsigned long maj_flt; /* 主要缺页 */
};

/*
* 次要缺页 (min_flt):
* - 页在内存中,只需要更新页表
*
* 主要缺页 (maj_flt):
* - 页不在内存中,需要从磁盘读取
*/

11.6.3 上下文切换统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct task_struct {
unsigned long nvcsw; /* 自愿上下文切换 */
unsigned long nivcsw; /* 非自愿上下文切换 */
};

/*
* 自愿切换 (nvcsw):
* - 进程主动调用 schedule()
* - 例如:等待 I/O
*
* 非自愿切换 (nivcsw):
* - 时间片用完
* - 被高优先级任务抢占
*/

11.6.4 I/O 统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// include/linux/task_io_accounting.h
struct task_io_accounting {
u64 rchar; /* 读取字节数 */
u64 wchar; /* 写入字节数 */
u64 syscr; /* 读取系统调用次数 */
u64 syscw; /* 写入系统调用次数 */
u64 read_bytes; /* 实际读取磁盘字节数 */
u64 write_bytes; /* 实际写入磁盘字节数 */
u64 cancelled_write_bytes; /* 取消写入字节数 */
};

struct task_struct {
#ifdef CONFIG_TASK_IO_ACCOUNTING
struct task_io_accounting ioac;
#endif
};

11.7 /proc 进程统计接口

11.7.1 /proc/[pid]/stat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
$ cat /proc/1234/stat
1234 (sleep) S 1000 1000 1000 0 -1 4194304 45 0 0 0 12 8 0 0 20 0 1 0 123456789 123456 18446744073709551615 94763570548736 94763570559329 140736123456789 0 0 0 0 16781312 0 0 0 0 17 5 0 0 0 0 0 94763570564864 94763570566384 94763576612106 140736123459321 140736123459336 140736123459336 140736123462825 0

# 字段说明:
# 1. pid 进程 ID
# 2. comm 进程名
# 3. state 状态 (R/S/D/Z/T)
# 4. ppid 父进程 ID
# 5. pgrp 进程组 ID
# 6. session 会话 ID
# 7. tty_nr 终端号
# 8. tpgid 前台进程组 ID
# 9. flags 标志
# 10. min_flt 次要缺页
# 11. cmin_flt 子进程次要缺页
# 12. maj_flt 主要缺页
# 13. cmaj_flt 子进程主要缺页
# 14. utime 用户态时间 (jiffies)
# 15. stime 内核态时间 (jiffies)
# 16. cutime 子进程用户态时间
# 17. cstime 子进程内核态时间
# 18. priority 优先级
# 19. nice nice 值
# 20. num_threads 线程数
# 21. itrealvalue 下一个 alarm 剩余时间
# 22. starttime 启动时间
# 23. vsize 虚拟内存大小
# 24. rss 驻留集大小
# 25. rsslim RSS 限制
# 26. startcode 代码段起始地址
# 27. endcode 代码段结束地址
# 28. startstack 栈起始地址
# 29. kstkesp 内核栈指针
# 30. kstkeip 内核指令指针
# 31. signal 挂起信号
# 32. blocked 阻塞信号
# 33. sigignore 忽略信号
# 34. sigcatch 捕获信号
# 35. wchan 等待的内核函数
# 36. nswap 交换页面数
# 37. cnswap 子进程交换页面数
# 38. exit_signal 退出信号
# 39. processor 最后执行的 CPU
# 40. rt_priority 实时优先级
# 41. policy 调度策略
# 52. delayacct_blkio_ticks I/O 延迟
# ...

11.7.2 /proc/[pid]/statm

1
2
3
4
5
6
7
8
9
10
11
$ cat /proc/1234/statm
1234 567 890 123 0 234 0

# 字段说明:
# 1. size 总虚拟页面数
# 2. resident 驻留集大小 (RSS)
# 3. share 共享页面数
# 4. text 代码段页面数
# 5. lib 库页面数
# 6. data 数据/栈页面数
# 7. dt 脏页面数

11.7.3 /proc/[pid]/status

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
$ cat /proc/1234/status
Name: sleep
State: S (sleeping)
Tgid: 1234
Ngid: 0
Pid: 1234
PPid: 1000
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000

# 进程资源使用
FDSize: 64
Groups: 1000 1001
NStgid: 1234
NSpid: 1234
NSpgid: 2000
NSsid: 1000

# 内存使用
VmPeak: 12345 kB
VmSize: 12345 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 5678 kB
VmRSS: 4567 kB
RssAnon: 3456 kB
RssFile: 1111 kB
RssShmem: 0 kB
VmData: 2345 kB
VmStk: 136 kB
VmExe: 120 kB
VmLib: 2345 kB
VmPTE: 48 kB
VmSwap: 0 kB

# CPU 时间
Threads: 1
voluntary_ctxt_switches: 123
nonvoluntary_ctxt_switches: 45

# 缺页
CapEff: 0000000000000000
CapBnd: 0000000000000000
CapAmb: 0000000000000000

11.7.4 /proc/[pid]/io

1
2
3
4
5
6
7
8
$ cat /proc/1234/io
rchar: 1234567
wchar: 2345678
syscr: 1234
syscw: 2345
read_bytes: 1234567
write_bytes: 2345678
cancelled_write_bytes: 0

11.8 cgroup 资源控制

11.8.1 CPU 控制

1
2
3
4
5
6
7
8
9
10
11
12
# 创建 cgroup
mkdir /sys/fs/cgroup/cpu/mygroup

# 设置 CPU 配额 (50% CPU)
echo 50000 > /sys/fs/cgroup/cpu/mygroup/cpu.cfs_quota_us
echo 100000 > /sys/fs/cgroup/cpu/mygroup/cpu.cfs_period_us

# 设置 CPU 份额
echo 512 > /sys/fs/cgroup/cpu/mygroup/cpu.shares

# 将进程添加到组
echo 1234 > /sys/fs/cgroup/cpu/mygroup/cgroup.procs

11.8.2 内存控制

1
2
3
4
5
6
7
8
9
10
11
# 创建内存 cgroup
mkdir /sys/fs/cgroup/memory/mygroup

# 设置内存限制
echo 1G > /sys/fs/cgroup/memory/mygroup/memory.limit_in_bytes

# 设置 OOM 控制
echo 1 > /sys/fs/cgroup/memory/mygroup/memory.oom_control

# 查看使用情况
cat /sys/fs/cgroup/memory/mygroup/memory.usage_in_bytes

11.8.3 进程数控制

1
2
3
4
5
6
7
8
# 创建 pids cgroup
mkdir /sys/fs/cgroup/pids/mygroup

# 设置最大进程数
echo 100 > /sys/fs/cgroup/pids/mygroup/pids.max

# 将进程添加到组
echo 1234 > /sys/fs/cgroup/pids/mygroup/cgroup.procs

11.9 本章小结

本章介绍了 Linux 进程资源限制与统计:

  1. rlimit 机制:软限制和硬限制
  2. 资源类型:CPU、内存、文件、进程数等
  3. 系统调用:getrlimit、setrlimit、prlimit
  4. CPU 统计:utime、stime、上下文切换
  5. 缺页统计:min_flt、maj_flt
  6. I/O 统计:读写字节数、系统调用次数
  7. /proc 接口:stat、statm、status、io
  8. cgroup 控制:CPU、内存、进程数限制

下一章将介绍进程调试与监控。

  • Title: Linux内核分析之进程管理-11
  • Author: 韩乔落
  • Created at : 2026-01-14 19:20:35
  • Updated at : 2026-01-19 13:40:43
  • Link: https://jelasin.github.io/2026/01/14/Linux内核分析之进程管理-11/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments