拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux系统管理实战:从用户权限到运维脚本的完整进阶指南

Linux系统管理实战:从用户权限到运维脚本的完整进阶指南

开门见山说个事:如果你只会cd、ls、cp这些命令,那还停留在“会用 Linux”的阶段;而真正让 Linux 发挥价值、也是工作中拉开差距的,是“把 Linux 管好”的能力——用户权限怎么控、磁盘存储怎么挂、进程之间怎么协作、多台机器怎么共享网络、以及怎么把日常重复操作固化成脚本。这篇《Linux基础:下》就是围绕这五块展开的。适合刚学完基础命令、准备进入系统管理和运维方向的人看,也适合那些面试前想系统梳理一遍 Linux 核心知识点的同学。内容会有不少实操细节,都是我在日常维护服务器时踩过坑之后总结出来的。

1. 从“会敲命令”到“管好系统”:用户、权限与sudo机制

1.1 权限模型:读懂了它,你就读懂了半个Linux

Linux 是个多用户操作系统,这个“多用户”不是概念上的,而是设计上的一切资源都围绕“用户”来分配。文件有属主、属组,进程有运行身份,服务有启动账号。理解权限,本质上是理解三个问题:我是谁(uid/gid)、我能访问什么(rwx)、我能让谁替我执行什么(sudo/setuid)。

一条文件权限信息长这样:

-rw-r--r-- 1 root root 1234 Mar 8 10:00 app.conf

从左往右拆:第一个字符是文件类型(-普通文件、d目录、l软链接、b/c设备文件),后面九位分成三组,分别是属主(u)、属组(g)、其他人(o)的权限,每组三位是r读、w写、x执行。目录的x比较特殊,它表示“能否进入这个目录”,没有x权限的话即使有r也进不去。

我见过不少新手把权限理解为“按用户名精确匹配”,其实 Linux 的权限检查是有固定顺序的:先看你是不是文件的属主,是就直接套用属主权限;不是再看你是不是文件属组的成员,是就套用属组权限;都不是才走“其他”权限。这个顺序意味着,如果你是属主但属主权限是rw-,哪怕你的用户也在属组里、属组有r-x,你也不能执行,因为第一条匹配就结束了。

除了基础的 rwx,还有三个特殊位:setuid(数字表示 4)、setgid(2)、sticky(1)。setuid最典型的就是/usr/bin/passwd,普通人能改密码,靠的就是这条文件带上了 setuid 位,执行时临时以 root 身份运行。sticky位体现在/tmp目录上,drwxrwxrwt里的t表示:在这个目录下,只有文件属主或 root 才能删除自己的文件,大家都能建文件,但不能互删。

1.2 sudo的配置与坑:不是所有root都是root

su是切到另一个用户,代价是你得知道那个用户的密码;sudo是“用其他身份执行命令”,通常是 root,但只需要输入自己密码就行。生产环境我强烈不建议用su - root长期切到 root 操作,一个手误可能就是删库跑路,真心想用 root 应该通过 sudo 按需提权。

sudo 的配置在/etc/sudoers,一定用visudo改,它会在保存前做语法检查,避免写错导致 sudo 整个不可用。常用写法就几种:

# 给用户 wang 所有命令的 sudo 权限 wang ALL=(ALL:ALL) ALL # 给 wheel 组所有成员 sudo 权限,且不需要密码 %wheel ALL=(ALL:ALL) NOPASSWD:ALL # 只允许指定命令 ops ALL=(root) /usr/bin/systemctl, /usr/bin/journalctl

第三个写法在运维里很有用:让普通用户能重启某个服务,但给不了 shell,权限边界控制得干净。

这里有个实际坑:很多人配置了 NOPASSWD 之后,发现脚本里sudo还是提示要密码,十有八九是 sudoers 里前面有一条Defaults requiretty,或者规则顺序覆盖问题。sudoers 是按从上到下匹配的,后配置的规则可能被前面更宽的规则盖住。记住一个原则:更具体的规则放在前面,更宽松的规则放后面;同级别规则,后面的生效。

1.3 新建用户背后的完整流程

我之前看人创建用户,就一个useradd testuser,然后用不了,因为连家目录都没有。完整地建一个能登录、能管理、环境干净的用户,建议这样:

useradd -m -G wheel -s /bin/bash testuser passwd testuser

-m创建家目录并复制/etc/skel下的模板文件,-G wheel把用户加入管理组,-s指定登录 shell。创建命令背后的文件变化关系到你对系统的理解:

  • /etc/passwd:用户基本信息,包括 uid、gid、家目录、shell
  • /etc/shadow:密码哈希和过期策略,只有 root 能读
  • /etc/group:组信息和组内成员

我看过很多面试题让人解释/etc/passwd里密码字段为啥是x,其实就是因为真实密码在 shadow 里,passwd 文件是全局可读的,不能放哈希。

日常管理用户很少需要用usermod改大项,但有个细节容易被忽略:用户 A 要加入用户 B 的附属组来协作文档时,A 必须重新登录一次,组权限才会生效。偶尔遇到画面:明明刚把用户加入组,登录进去组还是进不去,于是怀疑配置写错。其实 Linux 的组缓存目前只生效在会话创建的时刻。

2. 存储实战:分区、文件系统与NAS挂载

2.1 Linux如何“看到”一块新磁盘

Windows 插个 U 盘自动分配盘符,Linux 则完全不一样——它把一切都抽象成文件。磁盘设备在/dev/下,比如sda、sdb、nvme0n1,需要用lsblk查看块设备关系,这是排查磁盘问题的第一工具。

lsblk # NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT # sda 8:0 0 100G 0 disk # ├─sda1 8:1 0 1G 0 part /boot # └─sda2 8:2 0 99G 0 part /

新磁盘拿到手,步骤是分区或直接建文件系统、挂载。如果只是当数据盘整块用,可以省掉分区直接格式化:

mkfs.xfs /dev/sdb mkdir -p /data mount /dev/sdb /data

这里选 xfs 还是 ext4,取决于场景:xfs 擅长大文件和高吞吐,是 RHEL/CentOS/Rocky 系的默认;ext4 老牌稳妥,小文件场景和旧工具链兼容性更好。SSD 上两者差异在新内核里已经没那么明显,别为了“性能极致”陷入玄学,稳定大于一切。

分区工具我用得最多的是fdisk和parted。超过 2T 的盘必须用parted+ GPT 分区表,fdisk默认 MBR 到 2T 就到顶了。这块面试也常考。

2.2 挂载NAS存储:从手动 mount 到开机自动挂载

处理 Linux 服务器时,很大概率要挂网络存储,最常见的两种协议是 NFS(Linux 之间)和 CIFS/SMB(和 Windows 或 NAS 设备对接)。命令并不难,真正的坑在挂载选项上。

# NFS 方式,假设 NAS 导出的目录是 /volume/share apt install -y nfs-common # Debian/Ubuntu yum install -y nfs-utils # RHEL/Rocky mount -t nfs4 -o rw,vers=4.1 192.168.1.10:/volume/share /mnt/nas # CIFS 方式,NAS 通常需要账号密码 apt install -y cifs-utils mkdir -p /mnt/nas mount -t cifs //192.168.1.10/share /mnt/nas -o username=nasuser,password=xxx,uid=1000,gid=1000

几个实际经验:

  • NFS 默认用root_squash,root 在 NFS 服务端会被压制成 nobody,所以客户端用 root 写的文件,服务端打开一看属主是 nobody,这是特性不是故障。
  • CIFS 挂载时一定要指定uid/gid或/etc/fstab里的uid=选项,否则文件全归 root,普通用户只能干瞪眼。
  • 挂载 NAS 的_netdev选项很重要:它告诉系统“这是网络设备,等网络起来后再挂”,不加的话开机时网络没准备好,挂载会失败,系统甚至可能卡在等待。

开机自动挂载配置在/etc/fstab里,典型一行:

192.168.1.10:/volume/share /mnt/nas nfs4 defaults,_netdev,noatime 0 0

写完 fstab 建议先执行mount -a验证语法,否则配置写错,重启后系统会进入 emergency mode,那种局面在机房远程操作时特别痛苦。千万别把所有鸡蛋放进 fstab,能自动挂载的盘自己测过再往里写。

2.3 磁盘空间排查三板斧

“服务器磁盘满了”是我处理过最多的告警之一。排查三板斧:df -h看文件系统整体使用率,du -sh *看当前目录下谁吃空间,lsof +L1看已删除但还被进程占用的文件。第三个命令很多人不知道:某个进程删了日志文件但没释放句柄,df看到空间没回来,du却找不到大文件。这时候lsof +L1 | grep deleted能找到那个还握着文件句柄的进程,重启它或清空句柄就释放空间了。

再提供一个冷知识点:df -h和df -i的区别。-h看数据块,-i看 inode。当磁盘“没满”但建不了新文件时,多半是 inode 耗尽——小文件太多把 inode 表占满了。碰到这种就往 /tmp 或日志目录查,删一批小文件,df -i的数字马上降下来。

3. 进程管理:看穿系统里正在发生什么

3.1 ps、top与信号机制

进程管理是 Linux 系统管理的核心地盘。日常查看进程,两条命令足矣:

ps aux # 所有进程,CPU/内存排序看 ps -ef # 进程树父子关系更清晰 top -c # 动态刷新,按 P 按 CPU 排、按 M 按内存排

但“看”只是第一步,真正控制进程靠的是信号。kill不只是“杀死”的意思,它是发信号。常用信号有:SIGTERM(15,默认,请求进程正常退出)、SIGKILL(9,内核直接强制终结,进程没有任何机会清理资源)、SIGHUP(1,终端挂断,nginx 用它重载配置收得更平滑)、SIGUSR1/2(自定义业务信号)。

实操经验:能用kill -15就不要用kill -9。原因很简单,数据库或应用在收到 SIGTERM 时能做收尾:刷缓存、关连接、写日志。直接 SIGKILL 的话,进程状态可能残留在共享内存或文件锁里,服务能拉起来但丢数据、锁卡死是常有的事。先说一句:kill -9像搬家时直接把房子炸了,东西全砸在里面,kill -15才是正常退租。

批量操作时用pkill或killall,但得小心后者是按名字匹配的,比如killall java,会把机器上所有叫 java 的进程全干掉。所以线上环境我推荐先pgrep(进程查询)配合正则确认目标,再 kill,别拿生产环境练手。

3.2 进程间通信入门:管道、共享内存与Socket

Linux 面试题里有一类必考:进程间通信方式有哪些。理解它的本质,是搞明白“进程之间为什么需要通信”以及“通信双方的亲密程度不同,方案就不同”。

  • 管道(pipe):内存里的一条单向数据流,适合父子进程或同家族进程,比如ps aux | grep nginx抓管道两端就是两个进程在协作。
  • 信号(signal):轻量级异步通知,适合“我告诉你一声”的场景,不适合传大量数据。
  • 共享内存(shared memory):效率最高,多个进程直接读写同一块内存,但需要自己控制并发,一般配合信号量使用。
  • 消息队列(message queue):内核维护的消息链表,比共享内存安全,天然有边界(每条消息有长度),适合应用层通信但会有拷贝开销。
  • Socket(套接字):适用范围最广,既能走本机,也能跨机器。你访问网站、连数据库,底层都是 Socket。

实际业务开发中我接触最多的是 Socket 和消息队列。命令行的 grep/awk 组合依赖最多的则是管道。作为系统管理员,不必急着学所有底层机制,但要清楚:管道是命令行的粘合剂,Socket 是服务间通信的主流,共享内存是追求极致性能时的选择。

3.3 给进程改名字:一种实际可用的做法

“修改进程名称”这个话题网上资料不算多,场景却很真实:你起了一堆 Python 脚本或 Java 进程,全在进程列表里叫python3,没法区分谁是谁。解决办法有两个层面:

高级一点的,用 Python 直接修改进程名称,原理是改写argv[0]和借助PR_SET_NAME:

import ctypes import sys def set_proc_name(new_name: str): # 修改 /proc/self/comm,top/ps 里会显示 libc = ctypes.CDLL("libc.so.6") libc.prctl(15, new_name.encode()[:15], 0, 0, 0) # 15 = PR_SET_NAME # 修改 argv[0] 以让 ps -ef 里的命令显示也改变 if len(sys.argv) >= 1: sys.argv[0] = new_name set_proc_name("my_worker_node")

或者用 systemd 启动服务时,直接定义好服务名:

[Unit] Description=My Worker [Service] ExecStart=/usr/bin/python3 /opt/worker/main.py

一个更简单但实用的做法是启动脚本时带一个特征参数或改进程comm字段。我自己的经验是:不要等到进程起来了再改,而是在启动脚本里就设计好标识(比如systemd服务名、shell 脚本的$0),后期排查日志时能立刻定位到是什么进程、属哪个服务。直接用prctl改名字时留意,进程名最长 15 个字符,取太长会被截断,这是我在监控脚本里发现名称不全时才注意到的。

4. 网络配置与共享上网的实用解法

4.1 静态IP配置:以Rocky Linux 10为例

过去的 RHEL 系用ifcfg-*配置文件,路径/etc/sysconfig/network-scripts/ifcfg-ens33。后来 NetworkManager 全面接管,现在更推荐用nmcli,尤其是 Rocky Linux 10 这种新版本,网络配置已经默认走 NetworkManager。

# 查看连接 nmcli connection show # 修改现有连接为静态IP nmcli con mod ens33 ipv4.method manual \ ipv4.addresses 192.168.1.100/24 \ ipv4.gateway 192.168.1.1 \ ipv4.dns "223.5.5.5 8.8.8.8" nmcli con up ens33

一定先nmcli connection show看连接名字,很多系统里网卡叫ens33,但连接名可能叫Wired connection 1。如果用前者去配置,命令会直接报错“找不到连接名”,这是新手第一次用 nmcli 时最容易踩的坑。

静态 IP 配置好后,立即ping网关、ping外部域名验证。我之前遇到过 IP 配好了、网关通、就是解析不了域名,排查发现是 DNS 没生效——systemd-resolved的 stub 文件没关联上,把/etc/resolv.conf手动建好指向systemd-resolved后解决。

4.2 让Linux做NAT共享上网

公司网段不够用,或者实验室内网需要统一出口时,完全可以让一台 Linux 服务器做 NAT 共享上网,其他机器把网关指到它。这个做法在生产环境中也能顶替部分路由器功能。

前提是这台机器有两个网卡:外网口(能上公网)和内网口(连局域网设备),然后开启内核转发并配置 iptables 规则:

# 开启转发,立即生效并持久化 echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf sysctl -p # 防火墙放行转发和 NAT firewall-cmd --permanent --add-masquerade firewall-cmd --reload # 习惯用 iptables 的可以这样: iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT iptables -A FORWARD -i eth0 -o eth1 -m state --state ESTABLISHED,RELATED -j ACCEPT

然后局域网内的其他机器把 IP 设为同一网段、网关指向这台机器的内网 IP,DNS 指向外网 DNS 或配置的本地 DNS 都可。这里最容易犯的错是:只加了 NAT 规则,忘了开 sysctl 的转发,结果内网机器连网关能通、但包到 Linux 上就被丢弃,一条sysctl -w net.ipv4.ip_forward=1就能解决,却经常被忽略大半天。检查时用ping 内网网关 -> ping 网关机器外网IP -> ping 公网IP -> ping 域名逐段定位,是哪一段断了,问题就在哪一层。

4.3 网络排查的常规步骤

网络问题的定位思路,我总结成“从下往上”:链路 -> 地址 -> 路由 -> DNS -> 应用。

ip addr # 网卡IP是否存在、UP还是DOWN ip route # 默认路由是否正确 ping -c 4 网关 # 二层/三层通不通 ping -c 4 8.8.8.8 # 公网通不通(不要猜,先确认真实IP通不通) ss -lntp # 本机监听端口,列出进行中的 TCP 状态,确认服务是否在监听 traceroute 目标地址 # 找出丢包点在哪一跳 dig +short 域名 # 解析是否正常

排查习惯比命令重要得多。很多人一上来就ping www.xxx.com,不通就认为网络断了,其实 ping 域名失败可能是 DNS 问题,可能是 ICMP 被防火墙丢,也可能是中间链路 MTU 问题。我的做法是先把“纯 IP 连通性”和“DNS 解析”拆开:ping 8.8.8.8通而ping 域名不通,问题就聚焦在解析层,查/etc/resolv.conf和 DNS 服务器状态即可。别混在一起,效率会高很多。

5. 运维脚本与定时任务:把重复劳动交给机器

5.1 Shell脚本的真正价值

Shell 脚本是 Linux 管理员绕不开的“胶水”。它不需要像 Python 那样考虑依赖和库,天生就是命令的组合器。判断一个运维脚本写得好不好的标准很简单:可读、可控、可重跑。

写脚本最底层的习惯:

#!/usr/bin/env bash set -euo pipefail # 遇到错误退出、变量未定义报错、管道中出错即失败

这三行是我所有脚本的固定开头。set -e防止脚本在某些命令失败后继续执行到更危险的地方;set -u防止变量拼错导致莫名其妙的错误值;set -o pipefail防止管道命令“前面失败了前面无伤大雅”的假象。没写这三行之前,我的备份脚本因为某个tar命令静默失败,等到恢复数据时才发现备份是坏的——从那以后这三行成了铁律。

5.2 定时任务cron的写法与排查

定时任务用crontab -e编辑当前用户的任务,格式是五个时间字段加命令:

# 分 时 日 月 周 命令 */5 * * * * /usr/local/bin/check_health.sh >> /var/log/health.log 2>&1 0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

cron 有几个不太友好的点,我吃过亏,老朋友也踩过:

  • cron 环境变量极其精简,脚本里的PATH可能只有/usr/bin:/bin,导致脚本里调用的命令找不到。解决方法是脚本头部显式定义export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin。
  • cron 不会自动把输出发到邮件,一般都要自己重定向到日志文件,并且按日期切分。
  • 想确认任务是否执行,查/var/log/cron即可,很多问题其实是脚本执行了但报错,日志全被冲掉了。

5.3 常用运维命令的组合拳

命令单独学很容易,难的是组合。下面这些是我用得最频繁的组合,敲多了就自然成为肌肉记忆:

# 查找占用8080端口的进程PID并强制结束 fuser -k 8080/tcp # 查看所有监听端口的对应进程 ss -lntp | awk '{print $6}' # 日志里统计接口请求次数与TOP IP awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20 # 找到大于10G的文件 find / -type f -size +10G -exec ls -lh {} \; # 把当前目录所有普通文件按大小排序展示 find . -maxdepth 1 -type f -printf '%s %p\n' | sort -n | tail -10 # 批量重命名备份文件 for f in *.conf; do cp "$f" "$f.bak_$(date +%Y%m%d)"; done

这些都涉及到管道把不同的小工具串起来,数据从一个命令流转到下一个命令。理解了管道思想,awk grep sed xargs sort uniq就会像乐高积木一样在手里自由搭。

我始终觉得,脚本是“把你自己犯过的错固化下来”的工具:流程里有一步容易漏、容易错,就把它写进脚本,每次自动跑,比记在笔记里可靠得多。运维的很多价值不是“操作快”,而是“不用操作”。

最后再分享一个个人心得:想真正掌握 Linux,光看是不行的,去虚拟机里开两台机器,一台做 NAT,一台做客户端,把《Linux基础:下》里的内容完整过一遍,比看十篇博客都有用。遇到问题时优先看man文档和系统自带日志,比如/var/log/messages真能告诉你绝大多数故障的真相。Linux 是越用越熟的,别怕敲错命令——只要别在生产环境不假思索地复制执行,就大胆去试吧。

返回列表