第 12 章 系统与进程信息 (System and Process Information)
核心结论
-
/proc 文件系统:Linux 提供的虚拟文件系统;不占磁盘;提供内核和进程信息的访问接口;按
/proc/PID形式组织每个进程。 -
/proc/PID 目录:每个进程对应一个目录,包含 status、cmdline、environ、maps、fd/、task/ 等文件;进程终止时目录消失。
-
/proc/PID/status:人类可读的进程信息——PID、PPID、UID/GID、状态、内存使用、信号、能力等;适合 shell 脚本解析。
-
/proc/PID/fd 目录:符号链接到进程打开的文件;
/proc/self链接到当前进程;是 lsof 工具的底层。 -
/proc 系统信息:
/proc/cpuinfo、/proc/meminfo、/proc/version、/proc/filesystems、/proc/sys/…提供系统级信息;/proc/sys/下的文件可写(修改内核参数)。 -
uname():获取系统标识——内核名、版本、架构、hostname 等;
hostname和domainname单独获取。
|
本章主旨
本章介绍 Linux 系统与进程信息的两个主要查询接口:/proc 虚拟文件系统和 |
一、核心概念
本章围绕 5 个核心概念展开:从「/proc 是什么」到「/proc 目录结构」再到「系统标识」。
| 概念 | 定义 + 重要性 | 实现提示 |
|---|---|---|
/proc 文件系统 |
Linux 提供的虚拟文件系统;不占磁盘;内核按需生成文件内容;用 |
§12.1;`mount |
grep proc` 查看挂载点;Linux 特有。 |
/proc/PID 目录 |
每个运行进程的目录;进程终止时自动消失;包含进程的所有信息文件(status、maps、fd/)。 |
§12.1.1;用 |
/proc/PID/status |
人类可读的进程状态汇总;字段稳定(按行 |
§12.1.1 表 12-1;用 |
/proc/PID/fd 目录 |
符号链接到进程打开的文件; |
§12.1.1;权限通常限制为 owner 可见。 |
uname() |
获取系统标识(内核名、版本、架构、hostname); |
二、详细笔记
12.1 /proc 文件系统概述
What:/proc 是内核导出的虚拟文件系统;「文件」由内核按需生成;提供进程和系统信息的访问接口。
Why:理解 /proc 是理解「Linux 如何暴露内核信息」「为什么 lsof 能列出进程打开的文件」「怎么调试生产环境进程」的前提。
How:
| 类别 | 路径示例 |
|---|---|
内容 |
系统信息 |
|
CPU、内存、内核版本、运行时间、负载 |
文件系统 |
|
支持的文件系统、挂载点、swap |
网络 |
|
TCP 连接、UDP socket、UNIX socket |
内核参数 |
|
pid_max、文件句柄上限、网络参数 |
系统 V IPC |
|
消息队列、信号量、共享内存 |
进程特定 |
|
特性:
-
虚拟——不占磁盘;
mount显示proc on /proc type proc (rw,noexec,nosuid,nodev)。 -
权限敏感——大多数文件所有人是 root;
/proc/<PID>/是进程 owner。 -
实时——每次读取都重新生成;进程终止后目录消失。
When:
-
调试生产环境——
cat /proc/<PID>/status比gdb attach安全。 -
写监控工具——解析
/proc/meminfo比解析free(1)输出可靠。 -
调整内核参数——
echo 1 > /proc/sys/net/ipv4/ip_forward(root)。
Example:
# 摘自《The Linux Programming Interface》第 12 章
# 查看 init 进程状态
$ cat /proc/1/status | head -20
Name: systemd
State: S (sleeping)
Tgid: 1
Pid: 1
PPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
...
12.2 /proc/PID 目录详解
What:每个运行进程对应一个 /proc/<PID>/ 目录;进程终止时自动消失;包含进程的所有信息。
Why:理解这个目录是理解「lsof 怎么工作」「怎么调试进程」「怎么找到进程的内存映射」的前提。
How:重要文件(§12.1.1 表 12-1):
| 文件 | 内容 |
|---|---|
|
命令行参数(\0 分隔) |
|
符号链接 → 当前工作目录 |
|
环境变量(NAME=value\0 形式) |
|
符号链接 → 正在执行的可执行文件 |
|
目录:每个 fd 一个符号链接 |
|
内存映射(heap、stack、libc、文件等) |
|
进程虚拟内存(需 lseek 到合法 offset 才能读) |
|
进程可见的挂载点 |
|
符号链接 → 根目录(chroot 后的) |
|
人类可读的状态汇总 |
|
目录:每线程一个子目录 |
status 文件示例(§12.1.1):
$ cat /proc/self/status
Name: bash
State: S (sleeping)
Pid: 12345
PPid: 12340
Uid: 1000 1000 1000 1000 # real/effective/saved/fs
Gid: 1000 1000 1000 1000
VmSize: 12340 kB # 虚拟内存大小
VmRSS: 4520 kB # 常驻集大小
Threads: 1 # 线程数
CapInh: 0000000000000000
CapPrm: 00000000ffffffff
CapEff: 00000000fffffeff
fd 目录示例:
$ ls -la /proc/self/fd
lrwx------ 1 user user 64 ... 0 -> /dev/pts/0
lrwx------ 1 user user 64 ... 1 -> /dev/pts/0
lrwx------ 1 user user 64 ... 2 -> /dev/pts/0
lrwx------ 1 user user 64 ... 3 -> /tmp/some_file
When:
-
调试「程序打开了什么文件」——
lsof -p <PID>或ls -la /proc/<PID>/fd。 -
查看进程命令行——
tr '\0' ' ' < /proc/<PID>/cmdline。 -
查看进程环境——
tr '\0' '\n' < /proc/<PID>/environ。 -
多线程调试——
/proc/<PID>/task/<TID>看每线程信息。
Example:
// 摘自《The Linux Programming Interface》第 12 章 sysinfo/procfs_pidmax.c
// 读写 /proc/sys/kernel/pid_max
int fd = open("/proc/sys/kernel/pid_max", O_RDWR);
char line[MAX_LINE];
ssize_t n = read(fd, line, MAX_LINE);
printf("Old value: %.*s", (int) n, line);
if (argc > 1) {
write(fd, argv[1], strlen(argv[1]));
system("cat /proc/sys/kernel/pid_max");
}
12.3 /proc/PID/fd 与 /proc/self
What:/proc/<PID>/fd/ 是符号链接目录,每项对应进程打开的一个 fd;/proc/self 是链接到当前进程 /proc 目录的快捷方式。
Why:fd 目录是 lsof 工具的底层;/proc/self 让程序自省(读自己的信息)。
How:
// 摘自《The Linux Programming Interface》第 12 章
// 读自己的 status
int fd = open("/proc/self/status", O_RDONLY);
char buf[4096];
ssize_t n = read(fd, buf, sizeof(buf));
// 解析 buf...
close(fd);
// 等价
// cat /proc/self/status
注意:
-
/proc/<PID>/目录在进程终止时立即消失——程序应优雅处理 race。 -
lsof、fuser等工具基于 fd 目录实现。 -
lsof -p <PID>列出进程打开的文件。
When:
-
写调试工具——
/proc/self/status自查。 -
排查「fd 泄漏」——比较
ls /proc/<PID>/fd | wc -l与预期。 -
实现「看进程打开了哪些文件」——遍历
/proc/<PID>/fd/。
Example:
// 摘自《The Linux Programming Interface》第 12 章
// 列出当前进程打开的文件(简化版 lsof)
DIR *dir = opendir("/proc/self/fd");
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_name[0] == '.') continue;
char path[256];
snprintf(path, sizeof(path), "/proc/self/fd/%s", entry->d_name);
char target[256];
ssize_t n = readlink(path, target, sizeof(target) - 1);
if (n > 0) {
target[n] = '\0';
printf("fd %s -> %s\n", entry->d_name, target);
}
}
closedir(dir);
12.4 系统信息:/proc/cpuinfo, /proc/meminfo 等
What:/proc 下有许多系统级信息文件;常用如 cpuinfo、meminfo、version、uptime、loadavg。
Why:监控系统资源、收集主机信息、调整内核参数都依赖这些文件。
How:常用系统信息文件:
| 文件 | 内容 |
|---|---|
|
CPU 型号、核心数、特性、缓存 |
|
内存使用汇总(total、free、buffers、cached、available) |
|
内核版本字符串 |
|
系统启动时间(秒)、空闲时间 |
|
1/5/15 分钟负载平均 |
|
内核统计(启动时间、用户态/内核态 ticks、中断次数、上下文切换) |
|
内核支持的文件系统类型 |
|
当前挂载的文件系统 |
|
已注册的字符/块设备 |
|
磁盘分区 |
|
启用的 swap 区域 |
/proc/sys/ 下的文件可写——修改内核参数:
# 摘自《The Linux Programming Interface》第 12 章
# 查看当前 pid_max
$ cat /proc/sys/kernel/pid_max
32768
# 修改 pid_max(需要 root)
$ echo 100000 > /proc/sys/kernel/pid_max
# 启用 IP 转发
$ echo 1 > /proc/sys/net/ipv4/ip_forward
When:
-
监控脚本——
awk解析/proc/meminfo。 -
配置工具——读取
/proc/cpuinfo决定并行度。 -
调优——写
/proc/sys/…修改运行时参数。
12.5 uname() 系统标识
What:uname() 返回 utsname 结构,包含系统名、内核版本、架构、hostname 等。
Why:程序需要知道「我在哪个系统上运行」(日志、错误报告);uname -a 命令的底层。
How:
// 摘自《The Linux Programming Interface》第 12 章
#include <sys/utsname.h>
int uname(struct utsname *utsbuf);
// 成功返回 0;失败返回 -1
struct utsname {
char sysname[_UTSNAME_LENGTH]; // 实现名(如 "Linux")
char nodename[_UTSNAME_LENGTH]; // 节点名(hostname)
char release[_UTSNAME_LENGTH]; // 内核 release(如 "5.15.0")
char version[_UTSNAME_LENGTH]; // 内核 version(如 "#1 SMP ...")
char machine[_UTSNAME_LENGTH]; // 架构(如 "x86_64")
#ifdef _GNU_SOURCE
char domainname[_UTSNAME_LENGTH]; // NIS 域名(Linux 特有)
#endif
};
// _UTSNAME_LENGTH = 65(Linux)
gethostname() 和 getdomainname():
// 摘自《The Linux Programming Interface》第 12 章
#include <unistd.h>
int gethostname(char *name, size_t namelen);
int getdomainname(char *name, size_t namelen); // BSD;Linux 也有
与 /proc 的关系:
-
sysname/release/version/machine与/proc/version、/proc/sys/kernel/{ostype,osrelease}对应。 -
nodename与/proc/sys/kernel/hostname对应。 -
domainname与/proc/sys/kernel/domainname对应。
When:
-
错误报告——
uname()输出便于诊断。 -
平台特定代码——根据
machine选择实现。 -
日志——记录主机名便于多机日志聚合。
Example:
// 摘自《The Linux Programming Interface》第 12 章
struct utsname uts;
if (uname(&uts) == -1) errExit("uname");
printf("OS: %s %s (%s)\n", uts.sysname, uts.release, uts.machine);
printf("Hostname: %s\n", uts.nodename);
// 输出: "OS: Linux 5.15.0-generic (x86_64)"
// "Hostname: myhost"
三、关键图表
|
非可视化条目(/proc 与系统信息速查)
|
四、思维导图
mindmap
root((第 12 章 系统进程信息))
proc 文件系统
虚拟
内核按需生成
进程终止消失
proc PID
status
cmdline
environ
fd
maps
task 线程
proc 系统
cpuinfo
meminfo
version
loadavg
sys 可写
proc self
自身快捷
自省工具
uname
sysname
release
version
machine
nodename
应用
lsof 基于 fd
监控系统
调试工具
配置脚本
五、重点与易错点
-
/proc 是虚拟文件系统:不占磁盘;不能假设文件大小;用
lsof看真实属性。 -
/proc/PID 目录是易失的:进程终止立即消失——写工具时要处理 race condition。
-
/proc/self 是软链接:每次访问解析为当前进程;适合「自省」场景。
-
/proc/PID/fd/0 1 2 是符号链接:
readlink获取真实文件名;lsof用此实现。 -
多数 /proc 文件的所有者是 root:
/proc/PID/*属于进程 owner;其他进程可能无权限读。 -
/proc/PID/environ 内容用 \0 分隔:
tr '\0' '\n'转换为易读格式。 -
/proc/PID/status 字段随时可能增减:解析时按 key 匹配,不要按行号。
-
/proc/sys/ 下的文件可写:需要 root;改动影响整个系统——谨慎。
-
uname 不是 POSIX 完整标准化:Linux 有
domainname(GNU 扩展);需_GNU_SOURCE。 -
/proc 不是唯一接口:很多信息也可通过 sysfs(/sys)访问(设备相关)。
-
跨章衔接:第 6 章进程基础;第 8 章用户/组;第 9 章进程凭证;本章是信息查询;后续章节会按需展开具体 /proc 文件。