第 11 章 系统限制与选项 (System Limits and Options)

      +

      核心结论

      • 三类系统限制:(1) 编译时固定(如 INT_MAX);(2) 运行时不变(runtime invariant,如页大小);(3) 路径相关(pathname variable,如文件名最大长度)。

      • POSIX 最小值:SUSv3 为每个限制定义 _POSIX_XXX_MAX 常量作为「最小保证值」;只要限制 ≥ 这个值就算合规。

      • sysconf():查询运行时不变的限制;参数是 _SC_XXX(如 _SC_PAGESIZE_SC_OPEN_MAX_SC_ARG_MAX)。

      • pathconf()/fpathconf():查询路径相关的限制;参数是 _PC_XXX(如 _PC_NAME_MAX_PC_PATH_MAX);通过 pathname 或 fd 指定文件。

      • Feature Test Macros_POSIX_C_SOURCE_XOPEN_SOURCE_GNU_SOURCE 控制在编译时启用哪些标准的特性;man page 列出所需宏。

      • getconf:命令行工具,对应 sysconf/pathconf;如 getconf NAME_MAX /boot

      本章主旨

      本章是 Linux 系统编程「可移植性」的核心。理解系统限制的分类、SUSv3 最小值约定、sysconf/pathconf 的查询机制;理解 feature test macros 的作用。本章不展开资源限制(详见第 36 章),那里讲的是「进程可调整的软/硬限制」。

      一、核心概念

      本章围绕 5 个核心概念展开:从「限制分类」到「查询 API」再到「feature test macros」。

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

      三类限制

      编译时固定(<limits.h>)、运行时不变(sysconf)、路径相关(pathconf);不同类型查询 API 不同。

      §11.1;<limits.h> 是静态的;sysconf/pathconf 是动态的。

      POSIX 最小值约定

      SUSv3 为每个限制定义 _POSIX_XXX_MAX 最小保证值;只要实际限制 ≥ 此值就合规;通常 <limits.h> 定义。

      §11.1;硬编码最小值保证可移植,但无法利用更大的限制。

      sysconf()

      查询运行时不变的限制;参数 _SC_XXX;返回 long;-1 可能表示「不确定」或「错误」(需先 errno=0)。

      §11.2;返回值含义:>0 = 值,-1 且 errno=0 = 不确定,-1 且 errno≠0 = 错误。

      pathconf()/fpathconf()

      查询路径相关的限制(如文件名长度);参数 _PC_XXX;通过 pathname 或 fd 指定文件。

      §11.3;同名文件可能在不同文件系统上有不同限制(如 /boot vs /tmp)。

      Feature Test Macros

      _POSIX_C_SOURCE_XOPEN_SOURCE_GNU_SOURCE 控制在编译时暴露的声明和定义;man page 列出所需宏。

      §3.6;必须在 #include 之前定义。

      二、详细笔记

      11.1 系统限制的分类

      What:系统限制按「查询时机」和「是否依赖路径」分为三类;不同类型用不同 API 查询。

      Why:理解分类才能正确选择查询 API;硬编码限制值(如 NAME_MAX = 14)会失去可移植性。

      How

      类型 特点 查询 API

      编译时固定

      在编译时确定(如 INT_MAX);记录在 <limits.h>

      直接 #include <limits.h> 或 C 宏

      运行时不变

      系统启动后固定,但可能不定义在 <limits.h>(因依赖运行条件);如页大小

      sysconf(_SC_XXX)

      路径相关

      因文件系统而异;如文件名长度、路径长度

      pathconf(path, _PC_XXX) / fpathconf(fd, _PC_XXX)

      运行时可增加

      可在运行时调高(如 OPEN_MAX);最小值固定,实际值可能更大

      sysconf(_SC_XXX)

      SUSv3 最小值示例(§11.1 表 11-1):

      限制 最小值 sysconf 名字

      ARG_MAX

      4096

      _SC_ARG_MAX

      OPEN_MAX

      20

      _SC_OPEN_MAX

      NGROUPS_MAX

      8

      _SC_NGROUPS_MAX

      _SC_PAGESIZE

      1

      _SC_PAGESIZE(或 _SC_PAGE_SIZE

      RTSIG_MAX

      8

      _SC_RTSIG_MAX

      SIGQUEUE_MAX

      32

      _SC_SIGQUEUE_MAX

      NAME_MAX

      14

      _PC_NAME_MAX

      PATH_MAX

      256

      _PC_PATH_MAX

      PIPE_BUF

      512

      _PC_PIPE_BUF

      命名约定(§11.1):

      • _POSIX_XXX_MAX:SUSv3 规定的最小值(在 <limits.h> 中)。

      • XXX_MAX:实现提供的实际值(如果定义了)。

      • _SC_XXX:sysconf 的参数名。

      • _PC_XXX:pathconf/fpathconf 的参数名。

      When

      • 写可移植代码——使用 sysconf/pathconf 查询实际值,不要硬编码。

      • 性能优化——查询更大限制,利用优势。

      • 调试兼容性——getconf 命令快速检查当前系统值。

      Example

      $ getconf NAME_MAX /boot
      255
      $ getconf NAME_MAX /  # 根文件系统(可能不同)
      255
      $ getconf ARG_MAX
      2097152
      $ getconf OPEN_MAX
      1024

      11.2 sysconf() 详解

      Whatsysconf(name) 查询运行时不变的限制;name_SC_XXX 常量;返回 long 值或 -1。

      Why:唯一能在运行时查询限制的标准化 API;-1 含义需小心区分。

      How

      // 摘自《The Linux Programming Interface》第 11 章
      #include <unistd.h>
      long sysconf(int name);
      // 返回限制值;-1 表示「不确定」或「错误」
      // 通过 errno 区分:调用前 errno=0,调用后
      //   errno 仍为 0 → 限制不确定
      //   errno 非 0 → 错误(如 EINVAL = name 非法)

      When

      • 启动时查询所有限制——记录在配置或日志。

      • 需要 64 位 offset——sysconf(_SC_FILE_OFFSET_BITS) == 64

      • 分配缓冲区——根据限制值动态决定大小(如 PATH_MAX)。

      Example(Listing 11-1):

      // 摘自《The Linux Programming Interface》第 11 章 syslim/t_sysconf.c
      static void sysconfPrint(const char *msg, int name) {
          errno = 0;
          long lim = sysconf(name);
          if (lim != -1) {
              printf("%s %ld\n", msg, lim);
          } else {
              if (errno == 0)
                  printf("%s (indeterminate)\n", msg);
              else
                  errExit("sysconf %s", msg);
          }
      }
      
      int main() {
          sysconfPrint("_SC_ARG_MAX:        ", _SC_ARG_MAX);
          sysconfPrint("_SC_LOGIN_NAME_MAX: ", _SC_LOGIN_NAME_MAX);
          sysconfPrint("_SC_OPEN_MAX:       ", _SC_OPEN_MAX);
          sysconfPrint("_SC_NGROUPS_MAX:    ", _SC_NGROUPS_MAX);
          sysconfPrint("_SC_PAGESIZE:       ", _SC_PAGESIZE);
          sysconfPrint("_SC_RTSIG_MAX:      ", _SC_RTSIG_MAX);
          return 0;
      }

      11.3 pathconf() 与 fpathconf()

      Whatpathconf(path, name)fpathconf(fd, name) 查询路径相关的限制;通过 pathname 或已打开的 fd 指定文件。

      Why:不同文件系统(ext4、NTFS、NFS)可能有不同限制;查询「特定路径」的限制才是准确的。

      How

      // 摘自《The Linux Programming Interface》第 11 章
      #include <unistd.h>
      long pathconf(const char *path, int name);
      // path 必须是该文件系统中存在的路径
      // 返回限制值;-1 含义同 sysconf
      
      long fpathconf(int fd, int name);
      // 通过已打开的 fd 指定文件

      常用 name_PC_XXX):

      • _PC_NAME_MAX:文件名最大长度(不含 null)。

      • _PC_PATH_MAX:路径名最大长度(含 null)。

      • _PC_PIPE_BUF:管道原子写最大字节数。

      • _PC_LINK_MAX:硬链接最大数。

      与 sysconf 的区别:pathconf 路径相关;sysconf 系统范围不变。

      When

      • 创建长文件名——先 pathconf(dir, _PC_NAME_MAX)

      • 分配路径缓冲区——pathconf(dir, _PC_PATH_MAX) + 1

      • 写管道——不能超过 PIPE_BUF,否则可能交错。

      Example

      // 摘自《The Linux Programming Interface》第 11 章
      long name_max = pathconf("/tmp", _PC_NAME_MAX);
      long path_max = pathconf("/tmp", _PC_PATH_MAX);
      printf("/tmp: name max = %ld, path max = %ld\n", name_max, path_max);
      
      // 分配 path 缓冲区
      char *path = malloc(path_max);
      if (path == NULL) errExit("malloc");

      11.4 Feature Test Macros

      What:Feature test macros 是「特性测试宏」;在 #include 之前定义,控制 <unistd.h> 等头文件暴露哪些声明、常量、类型。

      Why:不同 UNIX 标准(POSIX、SUS、XPG)定义了不同的函数子集;通过宏告诉编译器「我要遵循哪个标准」——这是可移植性的基石。

      How:常见宏(§3.6):

      暴露的标准

      典型值

      _POSIX_C_SOURCE

      POSIX

      200809L(POSIX.1-2008)

      _XOPEN_SOURCE

      X/Open + SUS

      500(XPG5)/ 600(SUSv3)/ 700(SUSv4)

      _GNU_SOURCE

      GNU 扩展(Linux 特有)

      不需值;等价于 `_BSD_SOURCE

      _SVID_SOURCE` + 上面的所有

      _DEFAULT_SOURCE

      默认行为(含 BSD/SVID)

      不需值

      _BSD_SOURCE

      BSD 风格(已废弃)

      When

      • 写可移植 UNIX 代码——_POSIX_C_SOURCE=200809L

      • 用 Linux 特有扩展——_GNU_SOURCE

      • man page 第 2/3 节的「Feature Test Macro Requirements」会说明所需宏。

      Example

      // 摘自《The Linux Programming Interface》第 3 章
      // 启用 POSIX.1-2008 和 GNU 扩展
      #define _POSIX_C_SOURCE 200809L
      #define _GNU_SOURCE
      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      #include <fcntl.h>
      // 现在可以使用 O_CLOEXEC、accept4() 等 GNU 扩展
      int fd = open("file", O_RDONLY | O_CLOEXEC);

      11.5 getconf 命令

      Whatgetconf 命令行工具,对应 sysconf/pathconf;快速查询系统限制。

      Why:调试、排查问题时快速知道当前系统限制。

      How

      # 摘自《The Linux Programming Interface》第 11 章
      # 查询系统限制
      $ getconf ARG_MAX
      2097152
      
      # 查询路径相关限制(需指定路径)
      $ getconf NAME_MAX /boot
      255
      
      # 列出所有支持的变量
      $ getconf -a

      When

      • 排查「为什么我的程序失败」——先 getconf 看限制。

      • 写可移植构建脚本——getconf 查询值用于编译选项。

      三、关键图表

      非可视化条目(限制查询速查)
      API / 命令 用途

      <limits.h>

      编译时限制(INT_MAX、PATH_MAX 等)

      sysconf(_SC_XXX)

      运行时不变限制

      pathconf(path, _PC_XXX)

      路径相关限制

      fpathconf(fd, _PC_XXX)

      通过 fd 查询路径限制

      getconf variable [path]

      命令行查询

      _POSIX_XXX_MAX

      SUSv3 最小保证值

      _POSIX_C_SOURCE=200809L

      启用 POSIX.1-2008

      _GNU_SOURCE

      启用 GNU 扩展

      _SC_PAGESIZE

      页大小

      _SC_OPEN_MAX

      进程最大打开 fd 数

      _SC_NGROUPS_MAX

      supplementary group 最大数

      _PC_NAME_MAX

      文件名最大长度

      _PC_PATH_MAX

      路径最大长度

      _PC_PIPE_BUF

      管道原子写大小

      四、思维导图

      mindmap
        root((第 11 章 系统限制))
          三类限制
            编译时
            运行时不变
            路径相关
            运行时可增加
          最小值
            _POSIX XXX MAX
            SUSv3 保证
            可移植基础
          sysconf
            _SC_XXX
            long 返回值
            errno 区分
          pathconf
            _PC_XXX
            路径相关
            fpathconf fd 版
          Feature Macros
            _POSIX_C_SOURCE
            _XOPEN_SOURCE
            _GNU_SOURCE
            include 前定义
          getconf
            命令行
            调试工具
          实际查询
            页大小 4096
            OPEN_MAX 1024
            NAME_MAX 255
            PIPE_BUF 512

      五、重点与易错点

      1. 硬编码 NAME_MAX = 14 危险:SysV 文件系统是 14,但 ext4/NTFS 是 255;用 pathconf 查询。

      2. sysconf 返回 -1 时区分含义:先 errno = 0,再判断;errno 非 0 是错误,等于 0 是「限制不确定」。

      3. pathconf 必须传存在的路径:路径不必存在,但必须位于待查询的文件系统上。

      4. _POSIX_C_SOURCE 必须在 #include 前:否则头文件已展开;用编译器选项 -D_POSIX_C_SOURCE=200809L

      5. Linux 2.6+ 实际值远超 POSIX 最小值:OPEN_MAX 可达 1024+;NGROUPS_MAX 可达 65536;不要受最小值约束。

      6. _SC_PAGESIZE == _SC_PAGE_SIZE:两者是同义词。

      7. 某些限制可动态改变:如 OPEN_MAXRLIMIT_NOFILE 影响;ARG_MAXRLIMIT_STACK 影响(详见第 36 章)。

      8. getconf 不可移植:不是 POSIX 标准;但 Linux/Solaris/AIX 都有;用于调试。

      9. 跨章衔接:第 36 章展开资源限制(getrlimit/setrlimit);第 7 章展开 malloc 实现中的限制;本章是基础限制查询。