2.2 配置文件与系统设置

预计阅读时间:28 分钟

📖 目录

学习目标

学完本章后,你将能够:

  • 理解 Linux 配置文件体系的组织方式(/etc 结构)
  • 查看和修改系统配置(主机名、时区、语言、网络)
  • 使用 sysctl 查看和修改内核运行时参数
  • 理解 sysctl.conf/etc/sysctl.d/ 的配置机制
  • 通过 hostnamectltimedatectllocalectl 管理系统设置
  • 理解用户级配置(~/.bashrc~/.profile)与系统级配置的分工
  • 查看、设置、导出环境变量,理解 $PATH 的工作原理并自定义 PATH
  • 理解配置文件修改的最佳实践(备份、语法检查、原子写入)

核心知识

  • /etc——系统的配置目录,几乎所有系统级配置都在这里
  • sysctl——运行时修改内核参数的接口,部分参数可在 /etc/sysctl.conf 中持久化
  • hostnamectl——查看和修改系统主机名的 systemd 工具
  • timedatectl——查看和设置系统时间、时区、NTP 同步的 systemd 工具
  • localectl——查看和设置系统语言、键盘布局的 systemd 工具
  • 内核参数(Kernel Parameters)——控制内核行为的可调变量,分布在 /proc/sys/ 目录树中
  • /etc/default/grub——GRUB 引导加载程序的配置文件
  • ~/.bashrc——用户级 Shell 配置文件(non-login shell 加载),定义别名、提示符和变量
  • 环境变量——影响程序行为的全局键值对配置,通过 export 提升为子进程可继承
  • $PATH——环境变量中最重要的一员,Shell 按它列出的目录顺序搜索可执行文件

知识关联

原理讲解

📝 为什么 Linux 把所有配置都放在 /etc 的纯文本文件中?

这是 Unix/Linux 哲学的核心设计之一:"配置即文本"。原因有四:① 可移植性——任何文本编辑器(nano、vim、甚至 echo)都能修改配置,不依赖特定 GUI 工具;② 可审计——用 diff 对比修改前后的配置文件,一眼看出改了什么;③ 可恢复——配置改坏了,备份文件拷回来就行,不需要"卸载重装";④ 可自动化——Shell 脚本可以 sed 批量修改配置,Ansible 等工具可以远程推送配置文件。Windows 的注册表(二进制数据库)需要专用工具才能编辑和恢复,而 Linux 的 /etc/ssh/sshd_config 就是一个普通文本文件——这使得自动化运维成为可能。

📝 sysctl 是如何与内核通信的?

sysctl -w net.ipv4.ip_forward=1 的底层过程:① sysctl 读取参数名 net.ipv4.ip_forward,将其转换为路径 /proc/sys/net/ipv4/ip_forward(点变斜杠);② 用 open() 系统调用打开该文件;③ 用 write() 写入值 1;④ 内核中注册了该虚拟文件的 proc_handler,在 write 时触发回调函数,将值更新到内核数据结构中。所以 sysctl -wecho 1 > /proc/sys/xxx 本质完全相同——都是通过虚拟文件系统的 write 操作触发内核回调。这也解释了为什么 /proc/sys/ 下的文件大小显示为 0——它们不占用磁盘,是内核动态生成的"窗口"。

💡 hostnamectl / timedatectl vs 直接编辑文件:为什么推荐用 systemd 工具?

hostnamectl set-hostname new-nameecho "new-name" > /etc/hostname 多做了三件事:① 同步更新内核的 hostname(通过 sethostname() 系统调用,立即生效无需重启);② 更新 /etc/hosts 中对应的 127.0.1.1 映射;③ 触发 systemd 的 D-Bus 通知,让相关服务感知变化。timedatectl set-timezone 同理——它不仅改 /etc/timezone,还更新 /etc/localtime 符号链接并通知正在运行的服务。直接编辑文件只能做到一半的事,systemd 工具保证了"改了就生效"。

1. /etc——Linux 配置文件的"神经中枢"

/etc(et cetera 的缩写)是 Linux 系统配置的集中存放地。几乎所有系统级软件都将配置文件放在这个目录或其子目录下。

$ ls /etc
# 核心系统配置
/etc/passwd              # 用户账户
/etc/shadow              # 密码哈希
/etc/group               # 用户组
/etc/sudoers             # sudo 权限
/etc/fstab               # 文件系统挂载表

# 网络配置
/etc/hostname            # 主机名
/etc/hosts               # 本地 DNS 映射
/etc/resolv.conf         # DNS 服务器
/etc/network/            # 网络接口配置(传统,已废弃,推荐 Netplan 或 systemd-networkd)
/etc/netplan/            # Ubuntu 网络配置(新版)

# 软件源
/etc/apt/sources.list    # APT 软件源
/etc/apt/sources.list.d/ # 额外软件源

# SSH
/etc/ssh/sshd_config     # SSH 服务端配置
/etc/ssh/ssh_config      # SSH 客户端配置

# 安全
/etc/security/limits.conf  # 资源限制
/etc/login.defs            # 登录策略

# 时间
/etc/chrony/chrony.conf    # NTP 客户端配置
/etc/timezone              # 时区
/etc/localtime             # 时区文件(符号链接)

# 内核
/etc/sysctl.conf           # 内核参数持久化
/etc/sysctl.d/             # 内核参数分片
💡 /etc 下的文件应该都是纯文本 Linux 的哲学是"配置 = 可编辑的文本文件"。你可以用任何文本编辑器(Vim、Nano)修改 /etc 下的配置文件。但许多修改需要 root 权限,且修改后可能需要重启服务。
⚠️ 废弃命令提醒 管理服务请使用 systemctl(如 sudo systemctl restart nginx),不要使用已废弃的 service 命令(如 sudo service nginx restart)。同样,查看网络配置请使用 ip addr,不要使用已废弃的 ifconfig

2. hostname——系统的主机名

# 查看主机名
hostname
# 输出: ubuntu-pc
hostnamectl               # 更详细的信息
# 输出: Static hostname: ubuntu-pc
# 输出:       Icon name: computer-vm
# 输出: Operating System: Ubuntu 24.04 LTS

# 修改主机名(推荐使用 hostnamectl)
sudo hostnamectl set-hostname my-server

# 修改主机名的三种方式:
# 1. hostnamectl(推荐,systemd 工具)
sudo hostnamectl set-hostname new-hostname

# 2. 直接编辑文件
echo "new-hostname" | sudo tee /etc/hostname
# 输出: new-hostname

# 3. 临时修改(重启后失效)
sudo hostname new-hostname

# 主机名也需同步到 /etc/hosts
# 确保 127.0.1.1 指向你的新主机名
sudo sed -i 's/old-hostname/new-hostname/g' /etc/hosts

3. timedatectl——时间和时区

# 查看当前时间、时区、NTP 状态
timedatectl
# 输出: Local time: Thu 2026-07-30 10:30:00 CST
# 输出: Universal time: Thu 2026-07-30 02:30:00 UTC
# 输出: Time zone: Asia/Shanghai (CST, +0800)
# 输出: NTP enabled: yes

# 列出所有可用时区
timedatectl list-timezones | grep Shanghai
# 输出: Asia/Shanghai
timedatectl list-timezones | grep -E "Europe/(Amsterdam|Berlin)"
# 输出: Europe/Amsterdam
# 输出: Europe/Berlin

# 设置时区
sudo timedatectl set-timezone Asia/Shanghai
sudo timedatectl set-timezone Europe/Amsterdam

# 启用 NTP 时间同步
sudo timedatectl set-ntp true

# 手动设置时间(需要先关闭 NTP)
sudo timedatectl set-ntp false
sudo timedatectl set-time "2026-12-25 08:30:00"
sudo timedatectl set-ntp true    # 再启用 NTP

4. localectl——语言和键盘布局

# 查看当前语言和键盘设置
localectl
# 输出: System Locale: LANG=en_US.UTF-8
# 输出: VC Keymap: us

# 列出所有可用的语言环境
locale -a
# 输出: C
# 输出: en_US.utf8
# 输出: zh_CN.utf8

# 设置系统语言
sudo localectl set-locale LANG=zh_CN.UTF-8
sudo localectl set-locale LANG=en_US.UTF-8

# 设置键盘布局
sudo localectl set-keymap us
sudo localectl set-keymap fr

# 生成语言环境(如果某个 locale 不可用)
sudo locale-gen zh_CN.UTF-8
# 输出: Generating locales... zh_CN.UTF-8... done

5. sysctl——内核运行时参数

Linux 内核有数百个运行时参数,它们在 /proc/sys/ 目录树中以文件形式呈现。你可以通过 sysctl 命令或直接写这些文件来修改内核行为。

# 查看所有可调内核参数
sysctl -a | head -20
# 输出: dev.cdrom.autoclose = 1
# 输出: fs.file-max = 100000
# 输出: kernel.hostname = ubuntu-pc

# 查看特定参数
sysctl net.ipv4.ip_forward           # IP 转发
# 输出: net.ipv4.ip_forward = 0
sysctl kernel.hostname               # 主机名(内核视角)
# 输出: kernel.hostname = ubuntu-pc
sysctl vm.swappiness                 # 内存交换倾向(0-100)
# 输出: vm.swappiness = 60
sysctl fs.file-max                   # 系统最大文件句柄数
# 输出: fs.file-max = 100000

# 临时修改参数
sudo sysctl -w net.ipv4.ip_forward=1
# 输出: net.ipv4.ip_forward = 1
sudo sysctl -w vm.swappiness=10
# 输出: vm.swappiness = 10
sudo sysctl -w fs.file-max=100000
# 输出: fs.file-max = 100000

# 等价于直接写 /proc/sys/
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
# 输出: 1
# 注意 /proc/sys/ 中的路径用点代替斜杠:
# net/ipv4/ip_forward = net.ipv4.ip_forward

# 持久化修改(写入配置文件)
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf
# 输出: net.ipv4.ip_forward = 1
# 或放在分片目录中
echo 'vm.swappiness = 10' | sudo tee /etc/sysctl.d/99-swap.conf
# 输出: vm.swappiness = 10

# 重新加载所有 sysctl 配置
sudo sysctl --system
# 输出: * Applying /etc/sysctl.d/99-swap.conf ...
# 输出: * Applying /etc/sysctl.conf ...
# 或 sysctl -p(仅加载 /etc/sysctl.conf)
常用 sysctl 参数含义推荐值
net.ipv4.ip_forwardIP 转发(路由/网关功能)0(默认),1(如果作为路由器)
net.ipv4.tcp_syncookiesSYN Flood 防护1(默认)
vm.swappiness系统使用 swap 的倾向(越低越少用)10(服务器),60(桌面默认)
fs.file-max系统级最大打开文件数取决于内存,通常 100000+
net.core.somaxconn最大 TCP 连接等待队列1024-65535(高并发服务器)
kernel.pid_max最大 PID 数4194304(64 位默认),大服务器可调高

网络参数实战——高并发 Web 服务器的典型调优:当一台服务器要支撑大量短连接(如 Nginx 反代、API 网关),默认参数往往不够用。下面是一组经过验证的典型配置:

# 高并发短连接场景的 TCP 调优(写入 /etc/sysctl.d/98-tcp.conf)
# 1. 允许 TIME_WAIT 状态下的端口快速复用
net.ipv4.tcp_tw_reuse = 1

# 2. 主动关闭方 FIN_WAIT_2 超时(默认 60 秒;注意这与 TIME_WAIT 无关)
net.ipv4.tcp_fin_timeout = 15

# 3. 增大 SYN 半连接队列(防 SYN Flood 丢连接)
net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 65535

# 4. 增大 TCP 读写缓冲区(高带宽长连接场景)
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

# 5. 允许本地端口范围(默认 32768-60999,放开后可支持更多并发连接)
net.ipv4.ip_local_port_range = 1024 65535

# 6. tcp_tw_recycle 已在 4.12 内核移除(写了无效)
# net.ipv4.tcp_tw_recycle = 1   ← 不要写!4.12 已移除

# 应用后验证
sudo sysctl --system
sysctl net.ipv4.tcp_fin_timeout
# 输出: net.ipv4.tcp_fin_timeout = 15
ss -s | head -5
# 输出: Total: 1234 (kernel 4567)
# 输出: TCP:   789 (estab 321, closed 456, orphaned 12, synrecv 0, timewait 234)
# timewait 明显下降说明 tcp_tw_reuse 生效
⚠️ 调优参数的教训 每个内核参数都有版本演化和适用场景:tcp_tw_recycle 在 4.12 内核已被移除(它会导致 NAT 环境丢包),很多老教程还在推荐它。修改内核参数前先 man 7 tcp 或查询内核文档确认参数仍存在,并在测试环境验证。

6. 配置文件的编辑和安全管理

编辑配置文件的核心规范

# 1. 备份原文件
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%Y%m%d)

# 2. 编辑
sudo vim /etc/ssh/sshd_config

# 3. 语法检查(如果工具支持)
sudo sshd -t                          # 检查 sshd 配置文件语法
sudo nginx -t                         # 检查 nginx 配置语法
# 输出: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# 输出: nginx: configuration file /etc/nginx/nginx.conf test is successful
sudo apachectl configtest              # 检查 Apache 配置语法
# 输出: Syntax OK

# 4. 重启服务使配置生效
sudo systemctl restart sshd

# 5. 如果修改导致无法连接,保留当前终端(别关!),
# 用另一个终端测试。修复或恢复备份
sudo cp /etc/ssh/sshd_config.bak.20260730 /etc/ssh/sshd_config
sudo systemctl restart sshd
⚠️ 远程配置的安全黄金法则 当你通过 SSH 修改网络服务配置(SSH、防火墙、网络)时:① 永远不要关闭当前 SSH 会话 ② 在另一个终端中测试新配置 ③ 如果连接断开,用原会话恢复。这也是为什么生产服务器通常有"带外管理"(iDRAC、IPMI)。

7. 分片配置目录——/etc/xxx.d/ 机制

现代 Linux 发行版很少让你直接修改某个大配置文件,而是鼓励在 /etc/xxx.d/ 分片目录中放自己的小配置文件。这样做的好处:软件包升级时不会覆盖你的自定义配置,每个软件包可以各自管理自己的片段,删除一个文件就等于"删除一项配置"。

# 常见的分片配置目录
ls /etc/*.d/ | head -20
# 输出: /etc/apt/sources.list.d/   软件源片段
# 输出: /etc/sysctl.d/             内核参数片段
# 输出: /etc/rsyslog.d/            日志规则片段
# 输出: /etc/logrotate.d/          日志轮转规则片段
# 输出: /etc/profile.d/            登录环境变量片段
# 输出: /etc/network/if-up.d/      网络接口启用时执行的脚本
# 输出: /etc/systemd/system/       systemd 单元覆盖片段

# 加载顺序规则:按文件名字母序,后加载的覆盖先加载的
ls -1 /etc/sysctl.d/
# 输出: 10-network-security.conf   # 数字前缀控制顺序
# 输出: 50-default.conf
# 输出: 99-swap.conf               # 99 = 最后加载,优先级最高

# 实例:在分片目录中添加内核参数(比改 sysctl.conf 更规范)
echo 'net.core.somaxconn = 65535' | sudo tee /etc/sysctl.d/98-tcp.conf
sudo sysctl --system
分片目录控制什么你的配置文件放哪
/etc/sysctl.d/内核运行时参数/etc/sysctl.d/99-my.conf(99 保证最后加载)
/etc/apt/sources.list.d/APT 软件源/etc/apt/sources.list.d/mirror.list(需以 .list 结尾)
/etc/profile.d/登录 Shell 的环境变量/etc/profile.d/mylang.sh(需以 .sh 结尾且可执行)
/etc/logrotate.d/日志轮转策略/etc/logrotate.d/myapp(无需扩展名)
/etc/systemd/system/systemd 单元覆盖与自定义服务/etc/systemd/system/myapp.service
💡 优先使用分片目录而不是修改主文件 主配置文件(如 /etc/sysctl.conf)通常由发行版维护,软件包升级可能覆盖。把自定义内容放到 /etc/xxx.d/ 中,升级无忧,且 ls /etc/xxx.d/ 一眼就能看到你改了什么。

8. 配置审计——改了什么、什么时候改的

生产环境最常见的追责问题是"这个配置文件谁改的、为什么改"。工具 etckeeper 用 Git 跟踪 /etc 下的所有变化(需 sudo apt install etckeeper),每次 apt 安装软件包后自动提交一次快照。

# 初始化 etckeeper(安装后通常已自动初始化)
sudo etckeeper init
sudo etckeeper commit "initial commit"

# 每次 apt 操作后自动 commit(etckeeper 集成在 apt 钩子中)

# 手动提交配置修改
sudo etckeeper commit "tune sysctl for high concurrency"

# 查看配置历史
sudo etckeeper vcs log --oneline | head -10
# 输出: abc1234 tune sysctl for high concurrency
# 输出: 9f8e7d6 update package lists
# 输出: ...

# 查看某个文件的历史版本
sudo etckeeper vcs log -p -- /etc/sysctl.conf | head -40

# 回滚到上一个版本
sudo etckeeper vcs checkout HEAD~1 -- /etc/sysctl.conf
sudo sysctl --system   # 别忘了重新加载

# 没有 etckeeper 时的朴素做法:定期快照
sudo tar czf /root/etc-backup-$(date +%Y%m%d).tar.gz /etc

审查配置修改的通用流程:diff 对比备份、etckeeper vcs log 查看历史、systemctl status 确认服务状态。这三个动作组合起来,90% 的"配置怎么突然不生效了"问题都能快速定位。

9. update-alternatives——系统默认工具管理

当系统同时安装了多个功能类似的软件(如 Vim 和 Nano、Python 3.8 和 3.11),update-alternatives 通过符号链接统一管理"谁是指定默认工具"。

# 查看当前默认编辑器
update-alternatives --list editor
# 输出: /bin/ed
# 输出: /bin/nano
# 输出: /usr/bin/vim.basic
update-alternatives --display editor | head -5
# 输出: editor - auto mode
# 输出:   link currently points to /usr/bin/vim.basic

# 交互式切换默认编辑器
sudo update-alternatives --config editor
# 输出: There are 3 choices for the alternative editor (providing /usr/bin/editor).
# 输出:   0            /usr/bin/vim.basic        30    auto mode
# 输出:   1            /bin/ed                    -100  manual mode
# 输出:   2            /bin/nano                  40    manual mode
# 输出:   3            /usr/bin/vim.basic         30    manual mode
# 输入数字后回车,/usr/bin/editor 符号链接即切换

# 非交互式设置
sudo update-alternatives --set editor /bin/nano

# 注册新的 alternative(--install 链接 名称 路径 优先级)
sudo update-alternatives --install /usr/bin/editor editor /usr/bin/micro 50

# 其他常见 alternative:python、java、awk、x-terminal-emulator
update-alternatives --list awk
# 输出: /usr/bin/mawk
# 输出: /usr/bin/gawk

10. 用户级配置——~/.bashrc 与 ~/.profile

前面讲的都是系统级配置(/etc 下,影响所有用户)。作为个人用户,你还有一份"私有配置":~/.bashrc~/.profile。Shell 在启动时按类型读取不同的配置文件:

Login Shell vs Non-login Shell

  • Login Shell:通过 ssh 登录、su -、或 tty 登录时启动。读取:/etc/profile~/.profile(或 ~/.bash_profile / ~/.bash_login,按顺序第一个存在的)
  • Non-login Shell:在图形界面中打开终端时启动。读取:~/.bashrc
# 判断当前 Shell 类型
echo $0         # -bash(login shell),bash(non-login)
shopt -q login_shell && echo "login shell" || echo "non-login"

# 推荐的最佳实践:在 ~/.bashrc 中写所有配置,
# 然后在 ~/.profile 中 source ~/.bashrc
# 这样无论什么 Shell 类型,配置都会加载

加载顺序(Bash)

# Login Shell(SSH / tty / su -)
/etc/profile          # 系统级,对所有用户生效
├── /etc/bash.bashrc  # 系统级 bash 配置
└── ~/.bash_profile   # 用户级(只读第一个存在的)
    ├── ~/.bash_login  
    └── ~/.profile     # 推荐使用

# Non-login Shell(图形终端)
~/.bashrc             # 用户级 bash 配置

~/.bashrc 是你最常编辑的文件——别名、自定义 PATH、默认编辑器、提示符(PS1)都写在这里:

nano ~/.bashrc

# 典型的 ~/.bashrc 内容

# 别名——代替长命令
alias ll='ls -lah'
alias la='ls -A'
alias ..='cd ..'
alias ...='cd ../..'
alias gs='git status'

# PATH 自定义
export PATH=$PATH:$HOME/.local/bin

# 默认编辑器
export EDITOR=vim

# 自定义提示符(PS1)
# \u = 用户名,\h = 主机名,\w = 完整路径,\$ = $ 或 #
export PS1='\u@\h:\w\$ '

# 历史命令设置
export HISTSIZE=10000          # 内存中保留 10000 条
export HISTFILESIZE=20000      # 文件中保留 20000 条
export HISTCONTROL=ignoredups  # 忽略连续重复的命令
# 让修改生效
source ~/.bashrc      # 或 . ~/.bashrc
# 或者关闭终端重新打开
💡 系统级还是用户级? 个人配置永远写在 ~/.bashrc,不要改 /etc/profile(影响所有用户,且会被软件包升级覆盖)。如果需要给所有用户加环境变量,用 /etc/profile.d/ 里的 .sh 文件(第 7 节分片机制的又一例)。

11. 环境变量——查看、设置与持久化

环境变量是操作系统为每个进程维护的一组键值对,程序运行时通过查询它们来决策。查看方式:

# 查看所有环境变量
printenv
env

# 查看单个变量
echo $HOME        # /home/alice
echo $USER        # alice
echo $SHELL       # /bin/bash
echo $LANG        # zh_CN.UTF-8(语言和编码)

变量的作用范围——Shell 变量只在当前 Shell 有效,用 export 提升为环境变量后子进程才能继承:

# Shell 变量 vs 环境变量
MYVAR="hello"          # Shell 变量(子进程看不到)
export MYENV="world"   # 环境变量(子进程继承)
bash -c 'echo $MYVAR'  # 空输出(子进程看不到 Shell 变量)
bash -c 'echo $MYENV'  # 输出 "world"(子进程继承环境变量)

常用环境变量

变量名含义典型值
PATH可执行文件搜索路径/usr/local/bin:/usr/bin:/bin
HOME当前用户的家目录/home/alice
USER / LOGNAME当前用户名alice
SHELL当前 Shell 的路径/bin/bash
LANG系统语言和编码zh_CN.UTF-8en_US.UTF-8
PWD当前工作目录/home/alice/projects
OLDPWD上一个工作目录(cd - 使用)/tmp
EDITOR默认文本编辑器vimnano
TERM终端类型xterm-256color
PS1主命令提示符格式\[\e[32m\]\u@\h:\w\$\[\e[0m\]
DISPLAYX11 显示服务器地址:0(本地)或 :10.0(远程)

$PATH——最重要的环境变量:当你输入 ls 并回车,Shell 在 $PATH 列出的目录中依次搜索名为 ls 的可执行文件:

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# 自定义 PATH(临时——当前 Shell 有效)
export PATH=$PATH:/home/alice/mybin

# 永久添加(写入 ~/.bashrc)
echo 'export PATH=$PATH:$HOME/mybin' >> ~/.bashrc
source ~/.bashrc
⚠️ PATH 顺序很重要 如果你把 .(当前目录)放在 PATH 前面,攻击者可以在某个目录下放一个名为 ls 的恶意脚本,当你 cd 进去执行 ls 时就会中招。所以安全规范永远不要把 . 放在 PATH 中。

设置与持久化方法汇总

方法作用范围语法
export VAR="value"当前 Shell 及子进程最常用的设置方式
VAR="value" command仅该命令临时修改变量执行一次命令
unset VAR删除变量移除变量
export VAR=$VAR:/new/path追加到已有变量PATH 追加常用
# 不同作用域的演示
# 方法 1:临时,仅单个命令
LANG=en_US.UTF-8 date

# 方法 2:当前 Shell
export MY_APP_MODE=development
python3 myapp.py          # 可以通过 os.environ['MY_APP_MODE'] 读取

# 方法 3:持久化(写入配置文件)
echo 'export MY_APP_MODE=production' >> ~/.bashrc
source ~/.bashrc

# 方法 4:系统级(/etc/environment 或 /etc/profile.d/)
sudo nano /etc/environment
# MY_APP_MODE=staging
# 格式:每行 KEY=VALUE,不需要 export

12-factor 应用与 .env:现代应用开发(尤其是 Docker 和云原生)遵循"配置从环境变量注入"的原则(12-factor 的第 3 条)。本地开发通常用一个 .env 文件管理变量,运行时再加载:

# .env 文件内容(每行 KEY=VALUE,不加 export)
DATABASE_URL=postgres://app:pass@localhost:5432/app
REDIS_URL=redis://localhost:6379/0
LOG_LEVEL=info

# 手动加载 .env(不依赖额外工具)
set -a; source .env; set +a
# set -a 使后续赋值自动 export,set +a 关闭

# 验证
echo $DATABASE_URL
# 输出: postgres://app:pass@localhost:5432/app
⚠️ .env 文件的安全边界 .env 必须加入 .gitignore——它通常包含数据库密码和 API Key。工程上一般用 .env.example(只含键名和占位值)提交到仓库,真实 .env 由开发者各自创建。服务端环境(如 K8s)则用 Secret 注入,而不是把密钥写进环境变量文件提交。同理,API Key、数据库密码等也不应写在 ~/.bashrc(会出现在历史记录和进程列表中)。

调试技巧:当程序行为诡异时,用 envdeclare -p 检查实际生效的变量:

env | sort              # 查看全部环境变量(排序便于阅读)
declare -p LANG PATH    # 查看变量的类型和值
env | grep -i proxy     # 排查代理相关配置
env -i bash -c 'echo $PATH'   # 干净环境测试:脚本是否依赖外部变量

示例代码

# 练习 1:探索 /etc
ls /etc | head -20                     # 前 20 个配置文件
# 输出: adduser.conf
# 输出: apt
# 输出: bash.bashrc
ls -d /etc/*.conf                      # 所有 .conf 文件
# 输出: /etc/adduser.conf  /etc/debconf.conf  /etc/resolv.conf
wc -l /etc/passwd /etc/group /etc/fstab  # 这些文件的行数
# 输出: 42 /etc/passwd
# 输出: 58 /etc/group
# 输出: 13 /etc/fstab

# 练习 2:查看和修改主机名
hostnamectl
# 输出: Static hostname: ubuntu-pc
echo "我的主机名是: $(hostname)"
# 输出: 我的主机名是: ubuntu-pc

# 练习 3:检查和设置时区
timedatectl
# 输出: Local time: Thu 2026-07-30 10:30:00 CST
timedatectl list-timezones | grep -E "Shanghai|Tokyo|Amsterdam|London"
# 输出: Asia/Shanghai
# 输出: Asia/Tokyo
# 输出: Europe/Amsterdam
# 输出: Europe/London

# 练习 4:查看 sysctl 参数
sysctl vm.swappiness
# 输出: vm.swappiness = 60
sysctl net.ipv4.tcp_syncookies
# 输出: net.ipv4.tcp_syncookies = 1
sysctl fs.file-max
# 输出: fs.file-max = 100000

# 练习 5:临时修改内核参数(不需要重启)
# 注意:这些修改重启后消失
sudo sysctl -w vm.swappiness=30
# 输出: vm.swappiness = 30
sysctl vm.swappiness                   # 验证修改
# 输出: vm.swappiness = 30

# 练习 6:查看 /etc/sysctl.conf
cat /etc/sysctl.conf | grep -v "^#" | grep -v "^$"  # 看非注释行
# 输出: net.ipv4.ip_forward=1

# 练习 7:环境变量基础
printenv | head -10                  # 查看前 10 条环境变量
echo "家目录: $HOME,用户名: $USER"   # 引用变量
TESTVAR="hello"
bash -c 'echo $TESTVAR'              # 空!子进程看不到 Shell 变量
export TESTVAR
bash -c 'echo $TESTVAR'              # 输出!export 后子进程继承

# 练习 8:PATH 自定义
echo $PATH                           # 当前 PATH
which myapp                          # command not found
export PATH=$PATH:$HOME/bin          # 临时加入家目录 bin
# 永久生效:把 export 行写入 ~/.bashrc 后 source ~/.bashrc

# 练习 9:判断 Shell 类型与配置文件加载
echo $0                              # -bash = login shell
shopt -q login_shell && echo "login shell" || echo "non-login"
ls -la ~/.bashrc ~/.profile          # 两个用户配置文件是否存在

常见错误

错误/误区解决方案
"直接修改 /etc/resolv.conf 后重启被还原"现代 Ubuntu 使用 systemd-resolved 或 Netplan 管理 DNS。/etc/resolv.conf 是符号链接。应修改 /etc/systemd/resolved.conf 或使用 netplan 配置。
"修改 /etc/sysctl.conf 后没生效"需要运行 sudo sysctl --systemsudo sysctl -p。纯文本修改不会自动触发内核重新加载。
"编辑了 /etc/ssh/sshd_config 但忘了 restart"配置文件的修改必须重启对应的服务才能生效。sudo systemctl restart sshd
"语法错误导致服务无法启动"编辑前备份(cp file file.bak),编辑后测试语法(sshd -t),确认无误再重启。
"用 sudo 误删了 /etc 下的文件"恢复策略:从另一台同版本系统的机器复制,或用 apt 重新安装包含该文件的软件包。例如 apt list --installed | grep passwd 找到包,apt reinstall passwd 恢复 /etc/passwd
"修改了 ~/.bashrc 但没效果"修改后需要 source ~/.bashrc 或重新打开终端。~/.bashrc 只在 Shell 启动时读取一次。
"export VAR 写在了脚本里,但运行完脚本变量没生效"通过 ./script.sh 执行脚本会启动子 Shell。子 Shell 中 export 的变量不影响父 Shell。用 source script.sh 让变量在当前 Shell 生效。
"在 ~/.bashrc 中写错了导致终端打不开"用完整路径调用编辑器:/usr/bin/nano ~/.bashrc(因为 PATH 可能也被破坏了)。或者 /bin/bash --no-rc 启动不加载配置的干净 Shell,修复后退出。
"混淆了 /etc/profile 和 ~/.bashrc 的作用"/etc/profile 对所有用户生效(需要 root 修改)。~/.bashrc 只对当前用户生效。你的个人配置始终放在 ~/.bashrc

最佳实践

#建议说明
1编辑配置文件前先备份cp file file.bak.$(date +%Y%m%d)。这个习惯在事故排障中往往直接决定恢复速度。
2修改后先检查语法再重启服务sshd -tnginx -tapachectl configtest。大部分服务提供语法检查命令。
3用 /etc/xxx.d/ 分片目录组织配置相比修改单个大文件,在 /etc/sysctl.d//etc/apt/sources.list.d/ 中添加 .conf 文件更清晰,升级时不会被覆盖。
4始终用 systemd 工具(timedatectl/localectl/hostnamectl)这些工具比直接编辑文件更安全——它们会同步相关配置、触发必要的通知。
5用 diff 审查配置修改diff /etc/ssh/sshd_config.bak /etc/ssh/sshd_config 可以清清楚楚看到你改了什么。
6个人配置写 ~/.bashrc,不写 /etc/profile避免影响系统上其他用户。全局配置使用 /etc/profile.d/ 中的 .sh 文件。
7PATH 用冒号追加,不要覆盖export PATH=$PATH:/new/path 而非 export PATH=/new/path。后者会覆盖系统标准路径导致大量命令消失。
8敏感变量不要写在 ~/.bashrcAPI Key、数据库密码等不应放在 ~/.bashrc 或提交到仓库的 .env 中。使用专门的配置管理或 Secret 机制。

练习题

  1. (概念)/etc 目录中找到至少 5 个你在之前章节中实际使用过的配置文件。
  2. (概念)sysctl 修改(sysctl -w)和直接写 /proc/sys/ 文件有什么区别?
  3. (实操)使用 timedatectl 查看你的系统当前时区和 NTP 状态。如果你不在上海时区,修改为你的本地时区。
  4. (实操)使用 hostnamectl 查看系统主机名。临时修改一个不同的主机名(重启后会恢复),然后用 hostnamectl set-hostname 改回原值。
  5. (实操)sysctl 查看系统当前 vm.swappiness 的值。理解这个值的含义——高值意味着什么?低值呢?
  6. (探究 🔍)查看 /etc/sysctl.d//usr/lib/sysctl.d/ 下有哪些文件。这些分片配置按什么顺序加载?找到 sysctl --system 的实际加载顺序文档。
  7. (探究 🔍)研究 /etc/security/limits.conf 文件。了解如何为特定用户或组设置最大进程数、最大打开文件数等资源限制。
  8. (实操)export 创建一个名为 MY_NAME 的环境变量,值设为你自己的名字。然后启动一个子 Shell(输入 bash),用 echo 确认子 Shell 中也能访问该变量。输入 exit 返回。
  9. (实操)~/.bashrc 中添加两个有用的别名(如 alias myip='curl -s ifconfig.me'),然后 source 使其生效。测试你的别名是否工作。
点击查看答案
  1. (概念)`/etc` 是 Linux 配置文件的集中存放目录。本章之前实际使用过的配置文件举例:`/etc/passwd`(用户账户)、`/etc/shadow`(密码哈希)、`/etc/hostname`(主机名)、`/etc/hosts`(主机名解析)、`/etc/apt/sources.list`(软件源)、`/etc/fstab`(文件系统挂载表)。
  2. (概念)两者效果相同——都立即修改内核运行时参数。区别:`sysctl -w` 记录到系统日志且语法更友好。两种方式修改的参数重启后均失效,需写入 `/etc/sysctl.conf` 或 `/etc/sysctl.d/` 文件持久化。
  3. (实操)`timedatectl` 查看时区、本地时间、UTC 时间、RTC 时间、NTP 启用状态。如需改时区:`sudo timedatectl set-timezone Asia/Shanghai`(改为上海时区)。确保 NTP 开启:`sudo timedatectl set-ntp true`。
  4. (实操)`hostnamectl` 查看当前主机名和操作系统信息。临时修改:`sudo hostnamectl set-hostname temp-name`(重启后恢复)。改回原值:`sudo hostnamectl set-hostname 原主机名`。注意主机名不应包含空格。
  5. (实操)`sysctl vm.swappiness` 查看值(默认 60)。含义:控制系统使用 swap 的倾向程度。高值(100)更积极使用 swap,低值(10)尽可能使用物理内存。推荐:桌面 10-20,SSD 服务器 0-10,数据库服务器 0。
  6. (探究 🔍)`ls /etc/sysctl.d/ /usr/lib/sysctl.d/` 查看分片文件。加载顺序:`/usr/lib/sysctl.d/` → `/run/sysctl.d/` → `/etc/sysctl.d/`(后者覆盖前者)。字母序决定同目录内加载顺序。`sysctl --system` 手动重新加载所有配置。
  7. (探究 🔍)`/etc/security/limits.conf` 格式:` `。domain:用户名或 @group。type:soft(软限制可超)或 hard(硬限制不可超)。item:nofile(文件句柄数)、nproc(进程数)、memlock(锁内存)。例:`@devops hard nofile 65536`。
  8. (实操)`export MY_NAME="你的名字"` → `bash`(子 Shell)→ `echo $MY_NAME`(确认可见)→ `exit`。注意 export 在当前 Shell 和子 Shell 有效,退出子 Shell 回到父 Shell 后变量仍在。
  9. (实操)编辑 `~/.bashrc` 添加 alias,例如:`alias myip='curl -s ifconfig.me'`。`source ~/.bashrc` 生效。新终端窗口自动加载。测试别名:执行 myip 应显示公网 IP。注意别名只在交互式 Shell 中生效。

学习检查点

学完本章后,请检验自己是否掌握以下内容:

检查项自测问题验证方法
概念理解能用自己的话解释 /etc 目录下主要配置文件的作用尝试向他人讲解
命令操作能不查文档使用 hostnamectl、timedatectl、sysctl 管理系统配置在终端实际执行
原理掌握能说出 NTP 时间同步的工作原理和 chrony 的角色画出同步流程图
故障排查能独立排查系统时间和时区配置错误的问题模拟故障并修复
最佳实践能说明为什么修改系统配置应该优先使用 systemctl 而不是直接编辑文件对比不同配置方式
用户级配置能区分 login/non-login shell 的配置加载差异,并正确修改 ~/.bashrc运行 bash --login -x 观察

本章总结

Linux 的配置文化是"文本文件为王"。几乎所有系统行为都可以通过编辑 /etc 下的纯文本文件来改变。本章介绍了最重要的配置管理工具——hostnamectl(主机名)、timedatectl(时区)、localectl(语言)、sysctl(内核参数),以及从 /etc 延伸到用户级的配置体系:~/.bashrc~/.profile 定义了你的命令行工作台,环境变量(尤其是 $PATH)是"配置从文本流向进程"的最后一公里。这些工具将你的"配置意图"转化为系统为之生效的持久化设置。

速查表

概念一句话定义
/etc系统配置文件的集中存放目录
hostnamectl查看和修改系统主机名的 systemd 工具
timedatectl查看和设置系统时间、时区、NTP 的 systemd 工具
sysctl读取和修改内核运行时参数的接口
sysctl.conf内核参数的持久化配置文件
*.d/ 目录分片配置目录(如 sysctl.d/、sources.list.d/)
~/.bashrc用户级 bash 配置(non-login shell 加载)
~/.profile用户级登录配置(login shell 加载)
/etc/profile系统级登录配置(对所有用户生效)
环境变量影响程序行为的全局键值对;export 提升为子进程可见
$PATHShell 搜索可执行文件的目录列表(冒号分隔)

下一步:进入 2.3:系统信息与性能查看「系统信息与性能查看」,学习如何监控系统状态。

延伸阅读

  • Linux Kernel Sysctl 文档——所有内核参数的官方说明
  • 命令sysctl -a --pattern 'net.ipv4'——只查看网络相关的内核参数
  • 配置文件黑客技巧batsudo apt install bat)——带语法高亮的 cat,浏览配置文件时非常有用
  • Bash Startup Files 官方文档——各种 Shell 启动文件的完整执行顺序
  • Bash 提示符自定义指南——Arch Wiki 上详尽的 PS1 教程
  • 命令declare -p——查看所有变量的类型和值(包括环境变量、Shell 变量、函数)
  • 命令env -i command——在"干净"的环境(没有任何环境变量)中执行命令,用于测试脚本的健壮性

常见问题

kill -9 杀不掉的进程怎么办?
kill -9 (SIGKILL) 是最后手段,但某些进程(如 D 状态的内核线程、等待 NFS 响应的进程)连 SIGKILL 也无法终止。这种情况通常需要重启系统,或者修复底层问题(如恢复 NFS 连接)。首先尝试 kill -15 (SIGTERM) 让进程优雅退出,无效再试 kill -9。
僵尸进程(Zombie)怎么清理?
僵尸进程已终止但父进程未调用 wait() 读取其退出状态。僵尸只消耗 PID 和进程表条目,不占 CPU/内存。解决方法:kill 父进程(僵尸的 parent),init 会自动收养并清理僵尸。如果父进程是 init/systemd,僵尸会持续存在,只能重启。
top 和 htop 推荐用哪个?
htop 更直观:彩色显示、可用鼠标操作、树形视图、直接按 F9 发送信号。但 top 是大多数服务器的默认安装,且脚本化操作更友好(如 top -bn1 批处理模式)。建议交互式排错用 htop,脚本监控用 top 的批处理模式。
↑ 回到顶部