POSIX 标准详解教程 / 第十一章:环境与配置
第十一章:环境与配置
掌握 POSIX 环境变量、locale 国际化、sysconf 系统配置、路径配置。
11.1 环境变量
11.1.1 标准环境变量
| 变量 | 说明 | 典型值 |
|---|---|---|
HOME | 用户主目录 | /home/user |
PATH | 可执行文件搜索路径 | /usr/bin:/bin |
USER / LOGNAME | 当前用户名 | root |
SHELL | 用户默认 Shell | /bin/bash |
LANG | 默认 locale | en_US.UTF-8 |
TERM | 终端类型 | xterm-256color |
PWD | 当前工作目录 | /home/user/project |
LD_LIBRARY_PATH | 动态链接库搜索路径 | /usr/local/lib |
TMPDIR | 临时文件目录 | /tmp |
TZ | 时区 | Asia/Shanghai |
11.1.2 环境变量操作
/*
* env_advanced.c - 环境变量高级操作
* 编译: gcc -Wall -o env_advanced env_advanced.c
*/
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **environ;
/* 安全获取环境变量,带默认值 */
static const char *env_get(const char *key, const char *def)
{
const char *val = getenv(key);
return val ? val : def;
}
/* 打印所有环境变量(带过滤) */
static void print_env(const char *prefix)
{
printf("以 '%s' 开头的环境变量:\n", prefix ? prefix : "");
for (char **ep = environ; *ep; ep++) {
if (!prefix || strncmp(*ep, prefix, strlen(prefix)) == 0)
printf(" %s\n", *ep);
}
}
int main(void)
{
/* 1. 带默认值的安全获取 */
printf("HOME: %s\n", env_get("HOME", "/tmp"));
printf("SHELL: %s\n", env_get("SHELL", "/bin/sh"));
printf("MY_MISSING: %s\n", env_get("MY_MISSING", "(未设置)"));
/* 2. putenv vs setenv 区别 */
/* putenv: 直接设置 "KEY=VALUE" 字符串(不复制 key) */
/* setenv: 分别提供 key 和 value,可控制覆盖 */
setenv("DEMO_KEY", "demo_value", 1);
printf("DEMO_KEY: %s\n", getenv("DEMO_KEY"));
/* 3. 遍历环境变量(前 10 个) */
printf("\n前 10 个环境变量:\n");
int count = 0;
for (char **ep = environ; *ep && count < 10; ep++, count++)
printf(" [%d] %s\n", count, *ep);
/* 4. clearenv() 清除所有环境变量(危险,谨慎使用) */
/* clearenv(); */
return 0;
}
11.2 POSIX Locale(区域设置)
11.2.1 Locale 类别
| 类别 | 宏 | 影响范围 |
|---|---|---|
LC_CTYPE | 字符分类 | 字母/数字判断、大小写转换 |
LC_COLLATE | 排序规则 | 字符串比较和排序 |
LC_TIME | 日期/时间格式 | strftime() 输出格式 |
LC_NUMERIC | 数字格式 | 小数点符号(1,000.00 vs 1.000,00) |
LC_MONETARY | 货币格式 | 货币符号、分隔符 |
LC_MESSAGES | 消息语言 | 系统提示信息语言 |
LC_ALL | 设置所有类别 | 覆盖所有单独设置 |
11.2.2 Locale 操作
/*
* locale_demo.c - POSIX locale 操作演示
* 编译: gcc -Wall -o locale_demo locale_demo.c
*/
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <string.h>
#include <ctype.h>
int main(void)
{
/* 1. 设置 locale 为环境变量指定的值 */
char *old = setlocale(LC_ALL, "");
printf("默认 locale: %s\n", old ? old : "(C)");
/* 2. 查询当前 locale */
printf("当前 LC_CTYPE: %s\n", setlocale(LC_CTYPE, NULL));
printf("当前 LC_TIME: %s\n", setlocale(LC_TIME, NULL));
/* 3. 字符分类与 locale */
printf("\n字符分类 (LC_CTYPE):\n");
const char *test = "Hello 你好 123!@#";
for (const char *p = test; *p; p++) {
printf(" '%c' isalpha=%d isdigit=%d isprint=%d\n",
*p, isalpha((unsigned char)*p),
isdigit((unsigned char)*p),
isprint((unsigned char)*p));
}
/* 4. 时间格式与 locale */
printf("\n不同 locale 的日期格式:\n");
time_t now = time(NULL);
struct tm tm;
localtime_r(&now, &tm);
char buf[128];
/* C locale */
setlocale(LC_TIME, "C");
strftime(buf, sizeof(buf), "%A, %B %d, %Y", &tm);
printf(" C: %s\n", buf);
/* 中文 locale (如果可用) */
if (setlocale(LC_TIME, "zh_CN.UTF-8")) {
strftime(buf, sizeof(buf), "%Y年%m月%d日 %A", &tm);
printf(" zh_CN: %s\n", buf);
}
/* 英文 locale */
if (setlocale(LC_TIME, "en_US.UTF-8")) {
strftime(buf, sizeof(buf), "%A, %B %d, %Y", &tm);
printf(" en_US: %s\n", buf);
}
/* 5. 数字格式 */
struct lconv *lc;
setlocale(LC_ALL, "C");
lc = localeconv();
printf("\nC locale 数字格式:\n");
printf(" 小数点: '%s'\n", lc->decimal_point);
printf(" 千位分隔: '%s'\n", lc->thousands_sep);
return 0;
}
11.3 sysconf():系统配置查询
11.3.1 常用配置项
/*
* sysconf_demo.c - 查询系统配置限制
* 编译: gcc -Wall -o sysconf_demo sysconf_demo.c
*/
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
static void query_conf(const char *name, int sc_name)
{
long val = sysconf(sc_name);
if (val == -1) {
printf(" %-35s: 不支持或无限制\n", name);
} else {
printf(" %-35s: %ld\n", name, val);
}
}
int main(void)
{
printf("=== 进程相关 ===\n");
query_conf("最大子进程数", _SC_CHILD_MAX);
query_conf("每用户最大进程数", _SC_NPROCESSORS_CONF);
query_conf("进程组最大数量", _SC_CHILD_MAX);
printf("\n=== 文件相关 ===\n");
query_conf("最大打开文件数", _SC_OPEN_MAX);
query_conf("文件名最大长度", _SC_NAME_MAX);
query_conf("路径名最大长度", _SC_PATH_MAX);
query_conf("管道缓冲区大小", _SC_PIPE_BUF);
printf("\n=== 内存相关 ===\n");
query_conf("物理页面大小", _SC_PAGESIZE);
query_conf("物理页面数", _SC_PHYS_PAGES);
query_conf("可用页面数", _SC_AVPHYS_PAGES);
printf("\n=== 系统相关 ===\n");
query_conf("处理器数量(在线)", _SC_NPROCESSORS_ONLN);
query_conf("处理器数量(配置)", _SC_NPROCESSORS_CONF);
query_conf("时钟滴答频率(Hz)", _SC_CLK_TCK);
query_conf("主机名最大长度", _SC_HOST_NAME_MAX);
query_conf("登录名最大长度", _SC_LOGIN_NAME_MAX);
printf("\n=== POSIX 版本 ===\n");
query_conf("POSIX 版本", _SC_VERSION);
query_conf("POSIX.2 版本", _SC_2_VERSION);
printf("\n=== 路径配置 ===\n");
char *path;
path = confstr(_CS_PATH, NULL, 0);
if (path > 0) {
char *buf = (char *)path;
buf = __builtin_alloca((size_t)path);
confstr(_CS_PATH, buf, (size_t)path);
printf(" 默认 PATH: %s\n", buf);
}
return 0;
}
11.4 路径配置:confstr()
/*
* confstr_demo.c - 查询系统路径配置
* 编译: gcc -Wall -o confstr_demo confstr_demo.c
*/
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>
#include <string.h>
static void query_path(const char *name, int cs_name)
{
size_t len = confstr(cs_name, NULL, 0);
if (len == 0) {
printf(" %-20s: 不支持\n", name);
return;
}
char *buf = malloc(len);
if (!buf) return;
confstr(cs_name, buf, len);
printf(" %-20s: %s\n", name, buf);
free(buf);
}
int main(void)
{
printf("=== 系统路径配置 (confstr) ===\n");
query_path("PATH", _CS_PATH);
/* 平台相关路径 */
#ifdef _CS_GNU_LIBC_VERSION
query_path("glibc 版本", _CS_GNU_LIBC_VERSION);
#endif
#ifdef _CS_GNU_LIBPTHREAD_VERSION
query_path("pthread 版本", _CS_GNU_LIBPTHREAD_VERSION);
#endif
/* 编译路径 */
#ifdef _CS_POSIX_V7_ILP32_OFF32_CFLAGS
query_path("ILP32 CFLAGS", _CS_POSIX_V7_ILP32_OFF32_CFLAGS);
#endif
return 0;
}
11.5 用户与组信息
/*
* user_info.c - 获取用户和组信息
* 编译: gcc -Wall -o user_info user_info.c
*/
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <string.h>
int main(void)
{
/* 当前用户信息 */
uid_t uid = getuid();
uid_t euid = geteuid();
gid_t gid = getgid();
gid_t egid = getegid();
printf("=== 当前进程身份 ===\n");
printf(" 实际 UID: %d\n", uid);
printf(" 有效 UID: %d\n", euid);
printf(" 实际 GID: %d\n", gid);
printf(" 有效 GID: %d\n", egid);
/* 用户信息查询 */
struct passwd *pw = getpwuid(uid);
if (pw) {
printf("\n=== 用户信息 ===\n");
printf(" 用户名: %s\n", pw->pw_name);
printf(" 全名: %s\n", pw->pw_gecos);
printf(" 主目录: %s\n", pw->pw_dir);
printf(" Shell: %s\n", pw->pw_shell);
printf(" UID: %d, GID: %d\n", pw->pw_uid, pw->pw_gid);
}
/* 组信息查询 */
struct group *gr = getgrgid(gid);
if (gr) {
printf("\n=== 主组信息 ===\n");
printf(" 组名: %s\n", gr->gr_name);
printf(" GID: %d\n", gr->gr_gid);
printf(" 组成员: ");
for (char **members = gr->gr_mem; *members; members++)
printf("%s ", *members);
printf("\n");
}
/* 获取所有所属组 */
int ngroups = 0;
getgroups(0, NULL); /* 查询数量 */
gid_t *groups = malloc(sizeof(gid_t) * 64);
ngroups = getgroups(64, groups);
printf("\n=== 所属组 (%d 个) ===\n", ngroups);
for (int i = 0; i < ngroups; i++) {
gr = getgrgid(groups[i]);
printf(" %d (%s)\n", groups[i], gr ? gr->gr_name : "?");
}
free(groups);
/* 登录名 */
printf("\n登录名: %s\n", getlogin());
return 0;
}
11.6 资源限制:getrlimit/setrlimit
/*
* resource_limits.c - POSIX 资源限制
* 编译: gcc -Wall -o resource_limits resource_limits.c
*/
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <sys/resource.h>
#include <string.h>
static void show_limit(const char *name, int resource)
{
struct rlimit rl;
if (getrlimit(resource, &rl) == -1) {
printf(" %-30s: 获取失败\n", name);
return;
}
const char *soft = (rl.rlim_cur == RLIM_INFINITY) ? "unlimited" : "";
const char *hard = (rl.rlim_max == RLIM_INFINITY) ? "unlimited" : "";
printf(" %-30s: soft=", name);
if (rl.rlim_cur == RLIM_INFINITY)
printf("unlimited, hard=");
else
printf("%lu, hard=", (unsigned long)rl.rlim_cur);
if (rl.rlim_max == RLIM_INFINITY)
printf("unlimited\n");
else
printf("%lu\n", (unsigned long)rl.rlim_max);
(void)soft;
(void)hard;
}
int main(void)
{
printf("=== 资源限制 ===\n");
show_limit("核心文件大小", RLIMIT_CORE);
show_limit("CPU 时间(秒)", RLIMIT_CPU);
show_limit("数据段大小", RLIMIT_DATA);
show_limit("文件大小", RLIMIT_FSIZE);
show_limit("进程优先级", RLIMIT_NICE);
show_limit("打开文件数", RLIMIT_NOFILE);
show_limit("栈大小", RLIMIT_STACK);
show_limit("地址空间", RLIMIT_AS);
/* 增加打开文件数限制 */
struct rlimit rl;
getrlimit(RLIMIT_NOFILE, &rl);
printf("\n当前打开文件限制: %lu (软) / %lu (硬)\n",
(unsigned long)rl.rlim_cur, (unsigned long)rl.rlim_max);
return 0;
}
11.7 系统信息:uname()
/*
* uname_info.c - 获取系统信息
* 编译: gcc -Wall -o uname_info uname_info.c
*/
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <sys/utsname.h>
int main(void)
{
struct utsname info;
if (uname(&info) == -1) {
perror("uname");
return 1;
}
printf("=== 系统信息 (uname) ===\n");
printf(" 系统名称: %s\n", info.sysname);
printf(" 主机名: %s\n", info.nodename);
printf(" 内核版本: %s\n", info.release);
printf(" 内核完整: %s\n", info.version);
printf(" 硬件架构: %s\n", info.machine);
return 0;
}
$ ./uname_info
=== 系统信息 (uname) ===
系统名称: Linux
主机名: workstation
内核版本: 6.1.0-generic
内核完整: #1 SMP PREEMPT_DYNAMIC
硬件架构: x86_64
11.8 业务场景:程序启动配置探测
/*
* system_probe.c - 程序启动时探测系统能力
* 编译: gcc -Wall -o system_probe system_probe.c
*/
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/utsname.h>
typedef struct {
long pagesize;
long ncpu;
long max_open_files;
long max_path;
long max_name;
long pipe_buf;
long phys_mem_pages;
} system_info_t;
static int probe_system(system_info_t *info)
{
info->pagesize = sysconf(_SC_PAGESIZE);
info->ncpu = sysconf(_SC_NPROCESSORS_ONLN);
info->max_open_files = sysconf(_SC_OPEN_MAX);
info->max_path = pathconf("/", _PC_PATH_MAX);
info->max_name = pathconf("/", _PC_NAME_MAX);
info->pipe_buf = pathconf("/", _PC_PIPE_BUF);
info->phys_mem_pages = sysconf(_SC_PHYS_PAGES);
if (info->pagesize < 0 || info->ncpu < 0)
return -1;
return 0;
}
int main(void)
{
system_info_t info;
struct utsname uts;
if (probe_system(&info) == -1) {
perror("probe_system");
return 1;
}
uname(&uts);
printf("=== 系统探测报告 ===\n");
printf("系统: %s %s (%s)\n", uts.sysname, uts.release, uts.machine);
printf("CPU 核心数: %ld\n", info.ncpu);
printf("页面大小: %ld 字节\n", info.pagesize);
printf("物理内存: %.1f GB\n",
(double)info.phys_mem_pages * info.pagesize / (1024*1024*1024));
printf("最大打开文件: %ld\n", info.max_open_files);
printf("路径最大长度: %ld\n", info.max_path);
printf("文件名最大长度: %ld\n", info.max_name);
printf("管道缓冲区: %ld 字节\n", info.pipe_buf);
/* 根据系统能力调整工作参数 */
int worker_threads = (info.ncpu > 4) ? info.ncpu - 1 : info.ncpu;
int io_threads = (info.max_open_files > 1024) ? 4 : 2;
printf("\n建议配置:\n");
printf(" 工作线程: %d\n", worker_threads);
printf(" I/O 线程: %d\n", io_threads);
return 0;
}
11.9 注意事项
⚠️ setenv() 线程安全:
setenv()修改全局环境变量,在多线程环境中不安全。如果需要线程安全的环境变量管理,自行加锁。
⚠️ locale 与性能:设置 locale 会影响
ctype函数(如toupper())的性能。在性能敏感的代码中,临时使用setlocale(LC_ALL, "C")。
⚠️ sysconf 返回值:返回 -1 且
errno == 0表示该限制无限制(unlimited)。返回 -1 且errno != 0表示错误。
⚠️ 环境变量注入:不应信任环境变量(特别是
LD_LIBRARY_PATH、PATH)。SUID 程序中必须清理环境。
⚠️ /proc 文件系统:Linux 提供了
/proc/self/目录获取进程信息。这是 Linux 特有的,非 POSIX 标准。
11.10 扩展阅读
man 3 getenv、man 3 setlocale、man 3 sysconf、man 3 confstrman 7 environ— 环境变量概述man 7 locale— locale 概述man 2 getrlimit— 资源限制- APUE 第 2, 6 章:Unix Standard, System Information
11.11 本章小结
| 要点 | 说明 |
|---|---|
| 环境变量 | getenv/setenv/unsetenv,注意线程安全 |
| locale | 国际化设置,影响字符分类、排序、时间格式 |
| sysconf() | 查询系统运行时限制(页面大小、CPU数等) |
| confstr() | 查询系统路径配置 |
| getrlimit/setrlimit | 进程资源限制(文件数、栈大小等) |
| uname() | 获取系统信息(内核版本、架构等) |