16.1 什么是程序(process)
系统触发任何一个事件时,都会将它定义为一个程序(process),并给予一个 PID,同时依据触发者的 UID/GID 赋予该 PID 一组有效的权限设置。此后这个 PID 在系统上能做的动作,就与其权限有关。
16.1.1 程序与程序(process & program)
| 概念 | 说明 |
|---|---|
| 程序(program) | 硬盘上的二进制文件(binary file),静态的实体文件 |
| 程序(process) | 程序被触发执行后,载入内存的单元,包含权限/属性/程序码/数据,操作系统给予 PID,是运行中的程序 |
- 同一个 program 被不同用户执行时,产生的 process 拥有不同的权限(因为 UID/GID 不同)
- 例如:每个登录者都执行
/bin/bash,但各自取得不同的 PID 和权限
子程序与父程序:
- 登录后取得 bash(父程序),在 bash 中执行指令产生子程序
- 通过 PPID(Parent PID) 判断父程序
- 子程序继承父程序的环境变量
💡 杀掉子程序后父程序可能重新生出子程序——擒贼先擒王,找出父程序才能彻底解决。
fork and exec:程序调用的流程
Linux 程序调用采用 fork-and-exec 机制:
- 父程序以
fork复制一个一模一样的暂存程序(仅 PID 不同,多一个 PPID) - 暂存程序以
exec载入实际要执行的程序,最终变成子程序
常驻在内存的程序——服务(daemon):
- 一直在背景运行的程序称为 daemon(服务)
- 惯例:daemon 类型的程序名称末尾加
d,如 crond, atd, httpd, vsftpd - 系统服务(crond, atd, rsyslogd)负责系统功能
- 网络服务(Apache, named, vsftpd)监听端口(port),提供客户端连线
16.1.2 Linux 的多用户多任务环境
多人环境:每个帐号登录后取得独立 PID,各有各的环境设置(~/.bashrc)。
多任务行为:CPU 每秒数 GHz,在各程序间快速切换,看起来像多个程序同时运行。Linux 的多任务切换机制几乎可以压榨出 PC 的全部性能。
多重登陆环境:默认 6 个文字终端 + 1 个图形界面([Alt]+[F1]~[F7] 切换)。
特殊的程序管理行为:即使某个程序在某个终端卡死,可切换到其他终端用 ps -aux 找到它,kill 掉即可恢复——Linux 几乎不会死机。
bash 环境下的工作管理:在单一 bash 中可以同时进行多个工作(用 & 丢到背景)。
16.2 工作管理(job control)
在单一 bash 终端机接口下,同时进行多个工作的行为管理。这些工作都是当前 bash 的子程序。
16.2.1 什么是工作管理?
工作管理的限制:
- 只能管理自己 bash 的子程序(不能管理其他 bash 的)
- 前景(foreground):可以控制与下达指令的环境
- 背景(background):自行运行的工作,不能用
[Ctrl]+c终止,必须用bg/fg调用 - 背景中”执行”的程序不能等待 terminal/shell 的输入(所以 vim 不能在背景中执行)
16.2.2 job control 的管理
① 将指令丢到背景中”执行”:&
tar -zpcf /tmp/etc.tar.gz /etc &
# [1] 14432 ← [job number] PID
# 完成后显示:[1]+ Done tar -zpcf /tmp/etc.tar.gz /etc⚠️ 背景工作的 stdout/stderr 仍然会输出到屏幕,干扰前景操作。建议加重导向:
tar ... > /tmp/log.txt 2>&1 &
② 将当前工作丢到背景”暂停”:[Ctrl]+z
在程序运行中(如 vim)按 [Ctrl]+z,该工作被暂停并放入背景。
[1]+ Stopped vim ~/.bashrc
+代表最近被放入背景的工作(默认fg取出的对象),-代表倒数第二个。
③ 观察背景工作:jobs
jobs -l # 列出 job number + PID
jobs -r # 仅显示 running 的工作
jobs -s # 仅显示 stopped 的工作④ 将背景工作拿到前景:fg
fg # 取出默认工作(+ 号的那个)
fg %1 # 取出 1 号工作
fg - # 取出 - 号的工作⑤ 让背景工作从暂停变运行:bg
bg %3 # 让 3 号工作在背景继续运行⑥ 管理背景工作:kill
kill -signal %jobnumber
kill -l # 列出所有 signal| 信号 | 名称 | 说明 |
|---|---|---|
-1 | SIGHUP | 重新读取配置文件(类似 reload),运维最常用 |
-2 | SIGINT | 等同于 [Ctrl]+c 中断 |
-9 | SIGKILL | 强制删除,程序来不及清理(如 vim 会残留 .swp) |
-15 | SIGTERM | 正常终止(默认值),程序有机会做完清理动作 |
-19 | SIGSTOP | 等同于 [Ctrl]+z 暂停 |
⚠️
kill后面直接加数字 = PID,加%数字= job number!kill -9 %2≠kill -9 2(后者会杀 PID 2 = kthreadd)。
16.2.3 离线管理问题
以 & 放到背景的工作,在登出终端后会被中断——因为工作管理的背景仍与终端机有关。
解决方案:使用 nohup,让程序在登出后继续运行。
nohup ./script.sh & # 登出后程序继续执行
# 输出自动导向 ~/nohup.out
nohup不支持 bash 内置指令,必须是外部指令。也可用at(工作调度,与终端机无关)。
16.3 程序管理
16.3.1 程序的观察
ps:静态撷取某一时刻的程序状态
| 常用组合 | 说明 |
|---|---|
ps -l | 只查看自己 bash 相关的程序 |
ps aux | 查看系统所有程序(注意:没有 -) |
ps -lA | 查看所有程序(-l 格式) |
ps axjf | 查看程序树关系(含 PPID/PID/PGID/SID) |
ps -l 输出字段:
| 字段 | 说明 |
|---|---|
| F | 程序旗标:4=root 权限,1=仅 fork 未 exec |
| S | 状态:R=Running, S=Sleep(可唤醒), D=不可唤醒睡眠(等待 I/O), T=停止, Z=Zombie |
| UID/PID/PPID | 拥有者 / 程序 ID / 父程序 ID |
| C | CPU 使用率(百分比) |
| PRI/NI | 优先执行顺序(越小越优先) |
| ADDR/SZ/WCHAN | 内存相关(- 表示 running) |
| TTY | 终端机位置(tty1~tty6 本机,pts/n 远端) |
| TIME | 实际使用 CPU 的时间(不是系统时间) |
| CMD | 触发程序的指令 |
ps aux 输出字段:
| 字段 | 说明 |
|---|---|
| USER / PID | 用户 / 程序 ID |
| %CPU / %MEM | CPU 资源百分比 / 内存百分比 |
| VSZ / RSS | 虚拟内存量(KB) / 固定内存量(KB) |
| TTY | 终端机(? = 与终端机无关) |
| STAT | 程序状态(同 S 旗标) |
| START | 启动时间 |
| TIME | 实际使用 CPU 时间 |
| COMMAND | 实际指令 |
僵尸程序(Zombie):程序应终止但父程序无法完整结束它,CMD 后显示 <defunct>。应找出父程序并追踪改善,不能只是 kill 掉。僵尸程序最终由 systemd(PID=1)接管处理。
top:动态持续侦测程序状态
top -d 2 # 每 2 秒更新
top -b -n 2 > /tmp/top.txt # 批次输出到文件
top -d 2 -p PID # 只观察指定 PIDtop 上半部(系统整体资源):
| 行 | 内容 |
|---|---|
| 第 1 行 | 当前时间、开机时长、登录人数、1/5/15 分钟平均负载 |
| 第 2 行 | Tasks:total, running, sleeping, stopped, zombie(不应 >0) |
| 第 3 行 | %Cpu:us, sy, ni, id, wa(I/O wait,系统变慢常见原因), hi, si, st |
| 第 4/5 行 | 实体内存(Mem)/ 虚拟内存(Swap)使用量 |
top 交互按键:
| 按键 | 功能 |
|---|---|
P | 按 CPU 使用率排序 |
M | 按内存使用率排序 |
N | 按 PID 排序 |
T | 按 CPU 累积时间排序 |
k | 给某个 PID 发送 signal |
r | 给某个 PID 修改 nice 值 |
1 | 切换显示每个 CPU 核心 |
q | 退出 |
💡 swap 使用量应尽量少——如果 swap 被大量使用,表示实体内存不足。
pstree:程序树
pstree -A # 用 ASCII 字符画树(避免 Unicode 乱码)
pstree -Aup # 同时显示 PID 和所属用户所有程序的祖先是 systemd(PID=1),由 Linux 核心主动调用的第一支程序。
16.3.2 程序的管理
通过传送 signal 给程序来控制其行为。
kill:给指定 PID 发送信号
kill -1 PID # SIGHUP:重新读取配置文件
kill -15 PID # SIGTERM:正常终止(默认)
kill -9 PID # SIGKILL:强制终止(不给清理机会)
kill -SIGHUP $(ps aux | grep 'rsyslogd' | grep -v 'grep'| awk '{print $2}')killall:按指令名称发送信号
killall -1 rsyslogd # 给所有 rsyslogd 进程发送 SIGHUP
killall -9 httpd # 强制终止所有 httpd 进程
killall -i -9 bash # 交互式询问是否终止每个 bash| 选项 | 说明 |
|---|---|
-i | 交互式,删除前询问 |
-e | 精确匹配指令名(不超过 15 字符) |
-I | 忽略大小写 |
16.3.3 关于程序的执行顺序
Priority 与 Nice 值
PRI(Priority) 值越低越优先被执行,但 PRI 由核心动态调整,使用者无法直接修改。使用者只能通过 Nice(NI) 值来影响 PRI:
PRI(new) = PRI(old) + nice
| 规则 | 说明 |
|---|---|
| nice 可调整范围 | -20 ~ 19 |
| root | 可调整自己/他人程序的 nice,范围 -20 ~ 19 |
| 一般使用者 | 只能调整自己的程序,范围 0 ~ 19,且只能越调越高 |
nice:启动时指定 nice 值
nice -n -5 vim & # 以 nice=-5 启动 vimrenice:调整已存在程序的 nice 值
renice -5 14836 # 将 PID 14836 的 nice 调为 -5nice 值会在父程序 → 子程序之间继承传递。
💡 备份等耗时但不紧急的任务,可以把 nice 值调大(如 19),让系统资源分配更均衡。
16.3.4 系统资源的观察
free:内存使用情况
free -h # 人类可读单位
free -m # MB 单位
free -s 5 -c 3 # 每 5 秒一次,共 3 次| 字段 | 说明 |
|---|---|
| total / used / free | 总量 / 已用 / 未用 |
| shared / buff/cache | 共享 / 缓冲与快取 |
| available | 实际可用内存(buff/cache 可释放) |
💡 Linux 会将常用文件数据快取(cache)到内存中加速性能——内存被用光是正常的!真正需要关注的是 swap 使用量,如果 swap 超过 20%,建议加装实体内存。
uname:系统与核心信息
uname -a # 全部信息
uname -r # 核心版本
uname -m # 硬件平台(i686/x86_64)uptime:系统启动时间与工作负载
uptime # 显示 up 时间、登录人数、1/5/15 分钟平均负载
# 02:35:27 up 7:48, 3 users, load average: 0.00, 0.01, 0.05netstat:网络连线与 socket 文件
netstat -tunlp # 查看所有监听的网络服务及对应 PID/程序名
netstat -a # 所有连线、监听、socket 数据输出分两部分:网络连线(TCP/UDP,含 Local/Remote Address 和 State)和 Unix socket(本机程序间通信的插槽文件)。
# 关闭某网络服务的端口:直接 kill 对应 PID
kill -9 743
killall -9 avahi-daemon⚠️ 这只是临时关闭,正规做法见第 17 章(服务管理)。
dmesg:核心产生的讯息
dmesg | more # 核心开机侦测信息
dmesg | grep -i vda # 搜寻硬盘相关信息vmstat:系统资源变化动态侦测
vmstat 1 3 # 每秒一次,共三次
vmstat -d # 磁盘读写统计| 类别 | 字段 | 说明 |
|---|---|---|
| procs | r / b | 等待运行中 / 不可唤醒的程序数 |
| memory | swpd / free / buff / cache | 虚拟内存 / 未用 / 缓冲 / 快取 |
| swap | si / so | 从磁盘读入 / 写入到 swap(数值大→性能差) |
| io | bi / bo | 从磁盘读入 / 写入到磁盘的区块数 |
| system | in / cs | 每秒中断次数 / 每秒事件切换次数 |
| cpu | us / sy / id / wa / st | 用户 / 核心 / 闲置 / 等待 I/O / 虚拟机盗用 |
💡
wa(I/O wait)是系统变慢时最关键要看的 CPU 指标。
16.4 特殊文件与程序
16.4.1 具有 SUID/SGID 权限的指令执行状态
SUID 的权限会在程序运行时(run-time)生效——执行者取得的是程序拥有者的权限。比如 passwd:
# dmtsai 执行 passwd 后用 pstree 观察:
# sshd(dmtsai)---bash---passwd(root) ← passwd 的权限是 root这是因为 passwd 可执行文件的 SUID 让触发后的 process 取得了 root 的权限。
查询全系统 SUID/SGID 文件:
find / -perm /6000
16.4.2 /proc/* 代表的意义
内存中的程序数据都写入 /proc/* 目录,每个 PID 都有一个对应目录。
PID 目录中的关键文件:
| 文件 | 内容 |
|---|---|
/proc/PID/cmdline | 启动该程序的指令串 |
/proc/PID/environ | 该程序的环境变量 |
/proc 下的系统级信息文件:
| 文件 | 对应的指令/内容 |
|---|---|
/proc/cpuinfo | CPU 信息(频率、类型、功能) |
/proc/meminfo | 内存信息(free 的数据来源) |
/proc/loadavg | 平均负载(top/uptime 的数据来源) |
/proc/version | 核心版本(uname -a 的数据来源) |
/proc/uptime | 开机时间 |
/proc/mounts | 已挂载的文件系统 |
/proc/filesystems | 系统已载入的文件系统 |
/proc/partitions | 磁盘分区信息 |
/proc/swaps | swap 分区信息 |
/proc/cmdline | 开机时传给 kernel 的指令与参数 |
/proc/interrupts | IRQ 分配状态 |
/proc/ioports | 设备 I/O 位址 |
/proc/kcore | 内存大小(不要读它!) |
16.4.3 查询已打开文件或已执行程序打开之文件
fuser:由文件找出正在使用它的程序
fuser -uv . # 查看当前目录被哪些程序使用
fuser -mvu /proc # 查看某文件系统被哪些程序使用
fuser -mki /home # 找出使用 /home 的 PID 并交互式删除
umount遇到 “device is busy” 时,用fuser -mvu /挂载点找出占用程序的 PID。
ACCESS 标记:c = 当前目录,e = 可执行,f = 打开的文件,r = 顶层目录,F = 等待回应,m = 动态函数库。
lsof:由程序找出它打开了哪些文件
lsof # 系统所有被打开的文件(数据量巨大)
lsof -u root -a -U # root 所打开的所有 socket 文件
lsof +d /dev # /dev 下被打开的文件
lsof -u root | grep bash # root 的 bash 打开的文件pidof:找出程序的 PID
pidof systemd rsyslogd
# 1 74216.5 SELinux 初探
16.5.1 什么是 SELinux
SELinux = Security Enhanced Linux,NSA 开发,整合到 Linux 核心的安全模块。目的是避免内部员工的资源误用。
**DAC(自主式存取控制)**的问题:
- 传统的文件权限模式就是 DAC——依据程序拥有者与文件的 rwx 权限决定能否存取
- root 拥有最高权限,不小心被取得 root 程序就可以存取任何资源
- 目录设为 777 就可能被任何人任意写入
**MAC(委任式存取控制)**的做法:
- SELinux 控制的主体是程序而不是使用者
- 即使你是 root,使用不同程序时能取得的权限也不同
- 每个程序只能访问由 Policy 规定的特定类型文件
- 举例:httpd 只能访问
/var/www/,即使被 cracker 取得控制权也无法读取/etc/shadow
16.5.2 SELinux 的运行模式
SELinux 通过 MAC 控管程序(Subject)对文件资源(Object)的存取。核心概念:
三大要素:
| 要素 | 说明 |
|---|---|
| 主体(Subject) | 程序(process) |
| 目标(Object) | 文件资源(file system) |
| 政策(Policy) | 存取安全性政策,内含规则(rule) |
CentOS 7 的三种 Policy:
| Policy | 说明 |
|---|---|
| targeted | 默认政策,对网络服务限制多,对本机限制少 |
| minimum | 由 targeted 修订,仅针对选择的程序保护 |
| mls | 完整的 SELinux 限制,最严格 |
安全性本文(Security Context)
主体与目标之间的存取需要安全性本文匹配。格式为:Identify:Role:Type
| 字段 | 说明 | 文件上的常见值 | 程序上的常见值 |
|---|---|---|---|
| Identify(身份) | 相当于帐号身份 | system_u(系统产生)/ unconfined_u(用户产生) | 同左 |
| Role(角色) | 数据类型 | object_r(文件资源) | system_r(程序) |
| Type(类型) | 最重要的字段 | Type(文件类型) | domain(程序领域) |
💡 在 targeted 政策中,Identity 和 Role 不重要,关键是 Type(类型)字段。domain 必须与 type 匹配,程序才能存取文件资源。
范例:crond 的 domain 与 type 关系:
/usr/sbin/crond → 执行后程序获得 domain = crond_t
crond_t 可以读取 type = system_cron_spool_t 的文件
→ /etc/crontab, /etc/cron.d/ 就是这个 type → 可以被 crond 读取
⚠️ 即使 rwx 设为 777,只要 SELinux type 不对,程序也无法读取目标文件。
实际测试:在 root 主文件夹创建 cron 配置(type 为 admin_home_t),然后 mv 到 /etc/cron.d/(文件 type 不变),crond 无法读取——因为需要的 type 是 system_cron_spool_t,但文件仍是 admin_home_t。
16.5.3 SELinux 三种模式的启动、关闭与观察
| 模式 | 说明 |
|---|---|
| enforcing | 强制模式:SELinux 运行中,直接挡掉不符合规则的存取并记录 |
| permissive | 宽容模式:SELinux 运行中但不实际阻挡,仅记录警告(debug 用) |
| disabled | 关闭:SELinux 不运行 |
三种模式下的程序存取流程:
- disabled:不受限程序(如 bash)和受限程序(如 httpd)都只经过 rwx 检查
- permissive:受限程序经过 SELinux 规则检查但不阻挡,失败也记录下来
- enforcing:受限程序完整经过规则 → 安全本文比对 → rwx 检查,失败就挡住并记录
观察与切换:
getenforce # 查看当前模式
sestatus # 查看完整 SELinux 状态(含 Policy、模式)
sestatus -b # 列出所有规则布林值
setenforce 0 # 切换为 permissive
setenforce 1 # 切换为 enforcing
# 注意:setenforce 无法在 disabled 模式下使用!配置文件:/etc/selinux/config
SELINUX=enforcing # enforcing|permissive|disabled
SELINUXTYPE=targeted # targeted|mls|minimum
⚠️ 改变 Policy 或 disabled ↔ enforcing/permissive 都必须重新开机。从 disabled 切换到启动模式时,系统需要重写所有文件的 SELinux Label,耗时较长,且有概率需要重启两次。如果启动后大量服务报权限错误,在 permissive 下执行
restorecon -Rv /修复。
程序是否受 SELinux 限制的判断:
ps -eZ | grep -E 'cron|bash'
# system_u:system_r:crond_t:s0-s0:c0.c1023 1340 ? 00:00:00 crond ← 受限
# unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 13888 bash ← 不受限Type 字段包含 unconfined_t 的程序不受 SELinux 管制(如本机执行的 bash)。
16.5.4 SELinux 政策内的规则管理
getsebool / setsebool:规则布林值的管理
getsebool -a # 列出所有 SELinux 规则的开关状态
getsebool httpd_enable_homedirs # 查询特定规则
setsebool -P httpd_enable_homedirs 1 # 开启规则(-P 写入配置文件,必加!)⚠️
setsebool一定要加-P,否则只对当前运行生效,重启后丢失。
seinfo:查看 SELinux 政策统计
seinfo # 政策统计:Types 数量、Booleans 数量等
seinfo -u # 所有身份识别
seinfo -r # 所有角色
seinfo -t # 所有类别(type)需要安装
setools-console包。
sesearch:查询主体程序能读取的文件 type
sesearch -A -s crond_t | grep spool # crond_t 能读取哪些 type
sesearch -A -s crond_t | grep admin_home_t
sesearch -A -b httpd_enable_homedirs # 某条规则的详细放行内容16.5.5 SELinux 安全本文的修改
chcon:手动修改文件的 SELinux type
chcon -t net_conf_t /etc/cron.d/checktime # 修改 type
chcon -v --reference=/etc/shadow /etc/cron.d/checktime # 参照范例文件鸟哥几乎不用 chcon,用 restorecon 更方便。
restorecon:恢复文件为默认的 SELinux type
restorecon -Rv /etc/cron.d # 恢复整个目录下文件的默认 SELinux type💡
restorecon -Rv /目录/是解决 SELinux type 问题的首选命令。
semanage fcontext:管理默认的 SELinux type
semanage fcontext -l | grep '/etc/cron' # 查询默认 type
semanage fcontext -a -t system_cron_spool_t "/srv/mycron(/.*)?" # 添加默认值
restorecon -Rv /srv/mycron # 应用新的默认值模式匹配语法:/srv/mycron(/.*)? 表示匹配目录本身及所有子目录和文件。
16.5.6 一个网络服务案例及登录文件协助
setroubleshoot 服务记录了 SELinux 的错误和克服方法,写入 /var/log/messages 和 /var/log/setroubleshoot/*。它由 auditd 间接调用。需要安装 setroubleshoot 和 setroubleshoot-server。
rpm -qa | grep setroubleshoot # 确认已安装SELinux 排错三步法:
1. setenforce 0 → 临时关 SELinux,确认问题是否是 SELinux 引起的
2. 查看 /var/log/messages → 找到 sealert 的错误 ID
3. sealert -l <ID> → 获得详细分析和解决方案
案例一:vsftpd 匿名者无法下载文件
可能是文件权限问题(没有 r),不一定是 SELinux。先检查 rwx 权限:
chmod a+r /var/ftp/pub/securetty # 加读权限
restorecon -Rv /var/ftp # 修复 SELinux type(非必备)案例二:一般帐号无法从主文件夹下载(ftp_home_dir)
现象:权限正确但无法下载,setenforce 0 后正常 → SELinux 问题。
通过 sealert -l <ID> 分析,解决方案是开启 ftp_home_dir 规则:
setsebool -P ftp_home_dir 1案例三:非正规目录通过 FTP 访问
用户 ftptest 需要访问 /srv/gogogo/,但该目录 type 不对。sealert 建议用 semanage 添加默认 type:
semanage fcontext -a -t public_content_t "/srv/gogogo(/.*)?"
restorecon -Rv /srv/gogogo案例四:FTP 改用非标准端口 555
修改 vsftpd 配置 listen_port=555 后启动失败,sealert 显示端口 type 不对:
semanage port -a -t ftp_port_t -p tcp 555
systemctl restart vsftpdSELinux 问题解决策略总结:
| 问题类型 | 解决方式 | 命令 |
|---|---|---|
| SELinux 规则未开启 | 开启布林值 | setsebool -P <规则> 1 |
| 文件 type 错误 | 恢复默认值 | restorecon -Rv <目录> |
| 新目录无默认 type | 添加默认值 | semanage fcontext -a -t <type> "<路径>" |
| 非标准端口 | 添加端口 type | semanage port -a -t <port_type> -p tcp <端口> |