第 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 等;hostnamedomainname 单独获取。

      本章主旨

      本章介绍 Linux 系统与进程信息的两个主要查询接口:/proc 虚拟文件系统和 uname() 系统调用。理解 /proc 是理解「为什么 lsof 能列出进程打开的文件」「为什么 /proc/sys/…​ 可调内核参数」「怎么写监控/调试工具」的前提。本章不展开具体的 /proc 文件细节(后续章节按主题展开),也不展开 sysfs(/sys,第 14 章简单提及)。

      一、核心概念

      本章围绕 5 个核心概念展开:从「/proc 是什么」到「/proc 目录结构」再到「系统标识」。

      概念 定义 + 重要性 实现提示

      /proc 文件系统

      Linux 提供的虚拟文件系统;不占磁盘;内核按需生成文件内容;用 man proc 查看详细文档。

      §12.1;`mount

      grep proc` 查看挂载点;Linux 特有。

      /proc/PID 目录

      每个运行进程的目录;进程终止时自动消失;包含进程的所有信息文件(status、maps、fd/)。

      §12.1.1;用 ls /proc/<pid> 查看;/proc/self 链接到当前进程。

      /proc/PID/status

      人类可读的进程状态汇总;字段稳定(按行 <key>:<value> 格式);shell 脚本友好。

      §12.1.1 表 12-1;用 cat /proc/self/status 看当前进程。

      /proc/PID/fd 目录

      符号链接到进程打开的文件;/proc/<pid>/fd/<n> 是 fd n 指向的文件;lsof 工具的底层。

      §12.1.1;权限通常限制为 owner 可见。

      uname()

      获取系统标识(内核名、版本、架构、hostname);gethostname/getdomainname 单独获取。

      二、详细笔记

      12.1 /proc 文件系统概述

      What/proc 是内核导出的虚拟文件系统;「文件」由内核按需生成;提供进程和系统信息的访问接口。

      Why:理解 /proc 是理解「Linux 如何暴露内核信息」「为什么 lsof 能列出进程打开的文件」「怎么调试生产环境进程」的前提。

      How

      类别 路径示例

      内容

      系统信息

      /proc/cpuinfo/proc/meminfo/proc/version/proc/uptime/proc/loadavg

      CPU、内存、内核版本、运行时间、负载

      文件系统

      /proc/filesystems/proc/mounts/proc/swaps

      支持的文件系统、挂载点、swap

      网络

      /proc/net/tcp/proc/net/udp/proc/net/unix

      TCP 连接、UDP socket、UNIX socket

      内核参数

      /proc/sys/kernel//proc/sys/fs//proc/sys/net/*

      pid_max、文件句柄上限、网络参数

      系统 V IPC

      /proc/sysvipc/{msg,sem,shm}

      消息队列、信号量、共享内存

      进程特定

      /proc/<PID>/ 及其子目录

      特性

      • 虚拟——不占磁盘;mount 显示 proc on /proc type proc (rw,noexec,nosuid,nodev)

      • 权限敏感——大多数文件所有人是 root;/proc/<PID>/ 是进程 owner。

      • 实时——每次读取都重新生成;进程终止后目录消失。

      When

      • 调试生产环境——cat /proc/<PID>/statusgdb 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):

      文件 内容

      cmdline

      命令行参数(\0 分隔)

      cwd

      符号链接 → 当前工作目录

      environ

      环境变量(NAME=value\0 形式)

      exe

      符号链接 → 正在执行的可执行文件

      fd/

      目录:每个 fd 一个符号链接

      maps

      内存映射(heap、stack、libc、文件等)

      mem

      进程虚拟内存(需 lseek 到合法 offset 才能读)

      mounts

      进程可见的挂载点

      root

      符号链接 → 根目录(chroot 后的)

      status

      人类可读的状态汇总

      task/

      目录:每线程一个子目录

      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。

      • lsoffuser 等工具基于 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 下有许多系统级信息文件;常用如 cpuinfomeminfoversionuptimeloadavg

      Why:监控系统资源、收集主机信息、调整内核参数都依赖这些文件。

      How:常用系统信息文件:

      文件 内容

      /proc/cpuinfo

      CPU 型号、核心数、特性、缓存

      /proc/meminfo

      内存使用汇总(total、free、buffers、cached、available)

      /proc/version

      内核版本字符串

      /proc/uptime

      系统启动时间(秒)、空闲时间

      /proc/loadavg

      1/5/15 分钟负载平均

      /proc/stat

      内核统计(启动时间、用户态/内核态 ticks、中断次数、上下文切换)

      /proc/filesystems

      内核支持的文件系统类型

      /proc/mounts

      当前挂载的文件系统

      /proc/devices

      已注册的字符/块设备

      /proc/partitions

      磁盘分区

      /proc/swaps

      启用的 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() 系统标识

      Whatuname() 返回 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 与系统信息速查)
      项目 描述

      /proc 挂载

      `mount

      grep proc` 显示 proc on /proc type proc

      /proc/PID/status

      人类可读的状态;字段按 key:value 格式

      /proc/PID/fd

      符号链接目录;每个 fd 一个链接

      /proc/self

      链接到当前进程

      /proc/cpuinfo

      CPU 信息

      /proc/meminfo

      内存使用(total/free/available/buffers/cached)

      /proc/version

      内核版本字符串

      /proc/loadavg

      1/5/15 分钟负载平均

      /proc/sys/

      内核参数;可写(修改)

      uname()

      sysname/release/version/machine/nodename/domainname

      gethostname()

      四、思维导图

      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
            监控系统
            调试工具
            配置脚本

      五、重点与易错点

      1. /proc 是虚拟文件系统:不占磁盘;不能假设文件大小;用 lsof 看真实属性。

      2. /proc/PID 目录是易失的:进程终止立即消失——写工具时要处理 race condition。

      3. /proc/self 是软链接:每次访问解析为当前进程;适合「自省」场景。

      4. /proc/PID/fd/0 1 2 是符号链接readlink 获取真实文件名;lsof 用此实现。

      5. 多数 /proc 文件的所有者是 root/proc/PID/* 属于进程 owner;其他进程可能无权限读。

      6. /proc/PID/environ 内容用 \0 分隔tr '\0' '\n' 转换为易读格式。

      7. /proc/PID/status 字段随时可能增减:解析时按 key 匹配,不要按行号。

      8. /proc/sys/ 下的文件可写:需要 root;改动影响整个系统——谨慎。

      9. uname 不是 POSIX 完整标准化:Linux 有 domainname(GNU 扩展);需 _GNU_SOURCE

      10. /proc 不是唯一接口:很多信息也可通过 sysfs(/sys)访问(设备相关)。

      11. 跨章衔接:第 6 章进程基础;第 8 章用户/组;第 9 章进程凭证;本章是信息查询;后续章节会按需展开具体 /proc 文件。