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 机制:

  1. 父程序以 fork 复制一个一模一样的暂存程序(仅 PID 不同,多一个 PPID)
  2. 暂存程序以 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
信号名称说明
-1SIGHUP重新读取配置文件(类似 reload),运维最常用
-2SIGINT等同于 [Ctrl]+c 中断
-9SIGKILL强制删除,程序来不及清理(如 vim 会残留 .swp)
-15SIGTERM正常终止(默认值),程序有机会做完清理动作
-19SIGSTOP等同于 [Ctrl]+z 暂停

⚠️ kill 后面直接加数字 = PID,加 %数字 = job number!kill -9 %2kill -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
CCPU 使用率(百分比)
PRI/NI优先执行顺序(越小越优先)
ADDR/SZ/WCHAN内存相关(- 表示 running)
TTY终端机位置(tty1~tty6 本机,pts/n 远端)
TIME实际使用 CPU 的时间(不是系统时间)
CMD触发程序的指令

ps aux 输出字段

字段说明
USER / PID用户 / 程序 ID
%CPU / %MEMCPU 资源百分比 / 内存百分比
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             # 只观察指定 PID

top 上半部(系统整体资源)

内容
第 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 启动 vim

renice:调整已存在程序的 nice 值

renice -5 14836              # 将 PID 14836 的 nice 调为 -5

nice 值会在父程序 → 子程序之间继承传递

💡 备份等耗时但不紧急的任务,可以把 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.05

netstat:网络连线与 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                 # 磁盘读写统计
类别字段说明
procsr / b等待运行中 / 不可唤醒的程序数
memoryswpd / free / buff / cache虚拟内存 / 未用 / 缓冲 / 快取
swapsi / so从磁盘读入 / 写入到 swap(数值大→性能差)
iobi / bo从磁盘读入 / 写入到磁盘的区块数
systemin / cs每秒中断次数 / 每秒事件切换次数
cpuus / 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/cpuinfoCPU 信息(频率、类型、功能)
/proc/meminfo内存信息(free 的数据来源)
/proc/loadavg平均负载(top/uptime 的数据来源)
/proc/version核心版本(uname -a 的数据来源)
/proc/uptime开机时间
/proc/mounts已挂载的文件系统
/proc/filesystems系统已载入的文件系统
/proc/partitions磁盘分区信息
/proc/swapsswap 分区信息
/proc/cmdline开机时传给 kernel 的指令与参数
/proc/interruptsIRQ 分配状态
/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 742

16.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 间接调用。需要安装 setroubleshootsetroubleshoot-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 vsftpd

SELinux 问题解决策略总结

问题类型解决方式命令
SELinux 规则未开启开启布林值setsebool -P <规则> 1
文件 type 错误恢复默认值restorecon -Rv <目录>
新目录无默认 type添加默认值semanage fcontext -a -t <type> "<路径>"
非标准端口添加端口 typesemanage port -a -t <port_type> -p tcp <端口>