☰
Linux权限、用户与进程管理指令实战
2026/10/14 4:21:02 网站建设 项目流程

这是Linux基本指令系列第三篇。前两篇把文件与目录操作、文本处理这两条主线捋了一遍,这次聊聊权限、用户和进程管理这几块。为什么把这三类放到一起?因为它们在日常运维和开发中出现的频率最高,而且彼此纠缠:权限决定你能对文件做什么,进程跑在某个用户身份下,用户管理又直接决定权限的归属。很多初学者学到这一块开始犯晕,我觉得不是因为命令难背,而是没搞明白这些指令背后的系统设计逻辑。

这篇文章适合两类人:一类是刚学完基础指令、正打算往运维或后端方向走的读者;另一类是平时用图形界面居多,但想补上命令行短板、遇到权限报错能自己解决的人。我会把常用指令的使用方式、适用场景和容易踩的坑一起讲,不会只罗列参数。

1. 权限到底在保护什么:先看明白 rwx 对文件和目录的差别

1.1 身份三元组:为什么每个文件都要绑定一个用户和一个组

在Linux里,每个文件都带两个归属属性:所属用户(owner)和所属组(group)。系统判断一个人能不能操作某个文件,看的不是你"是谁",而是你的身份落在哪个区间里。一个文件面向三种身份开放权限:文件属主(User)、属组内的成员(Group)、以及其余所有人(Other)。这就是常说的 UGO 模型。

可以用门禁卡来理解。文件是一间办公室,门禁规则分三层:业主本人拿万能卡(U),同一团队的同事拿团队卡(G),外部访客拿临时卡(O)。系统检查权限时是有顺序的:先看你是不是业主,是就直接按业主权限处理,不再看后面的;不是再看你是不是团队成员,是就按团队权限;都不满足,最后才看访客权限。

查看自己当前的身份用id,查看当前登录名用whoami,查看自己属于哪些组用groups。这三个命令在任何系统上都能跑,也是最基础的权限诊断工具。

$ id uid=1000(frank) gid=1000(frank) groups=1000(frank),27(sudo),998(docker) $ whoami frank $ groups frank sudo docker

这里的 uid 是用户ID,gid 是主组ID。一个用户可以同时属于多个附加组,比如上面的 frank 就同时属于 sudo 和 docker 两个附加组,这就是为什么他能执行某些管理员操作。/etc/passwd文件保存了用户的基本信息,/etc/shadow保存密码散列,普通用户对后者默认没有任何读取权限,这也是后面讲 SUID 时的重要伏笔。

1.2 rwx 在文件和目录上含义完全不同,这是我见过最多误解的地方

一条ls -l的长格式输出里,第一列是十位字符,第一位表示文件类型,后面九位分成三组,分别对应 U/G/O 权限。比如:

-rw-r--r-- 1 frank dev 1234 Jan 1 10:00 notes.txt drwxr-xr-x 2 frank dev 4096 Jan 1 10:01 data

第一列第一位是-代表普通文件,d代表目录,l代表符号链接。后面rw-、r--、r--就是三种身份各自的权限位了。

关键问题是:r、w、x 在文件上和目录上的含义不一样。文件这里的 r 是读取内容,w 是修改内容,x 是执行;但目录上的这三个位,含义完全不同,很多新手在这里栽过跟头。

权限位作用于文件作用于目录
r读取文件内容(cat、less、tail)列出目录项(ls),但能否进入目录还要看 x
w修改文件内容(vi、echo 重定向)在目录内新建、删除、重命名文件或子目录
x执行文件(脚本、二进制程序)进入目录(cd),并通过路径访问目录内文件

目录没有 x 权限会怎样?你能ls看到文件名,但cd进不去,也没有办法访问目录里任何文件。目录没有 w 权限会怎样?你可以在里面读写已有文件(前提是文件本身权限允许),但不能新建或删除文件。这是很容易混淆的一点:删除一个文件的权限,取决于文件所在目录的 w 权限,而不是文件本身的 w 权限。

在实操中这个逻辑有很多直接应用。比如一个 Web 项目目录,通常做法是目录给 755,文件给 644,属主是运行 Web 服务的用户。这样属主能写,其他人只能读和执行,防止别人通过 Web 漏洞往目录里塞文件。反过来,如果一个目录被设成 777,任何用户都能在里面创建和删除文件,那整个目录的安全性基本等于零。我见过有人在项目根目录直接chmod -R 777图省事,结果权限系统形同虚设,这是最典型的反面教材。

2. chmod 与 umask:数字法、符号法和默认权限的隐性坑

2.1 数字法为什么是 4、2、1,以及递归改权限的正确姿势

chmod 是修改权限的核心指令,最常见的是数字法,也就是chmod 755 file这种写法。很多初学者一开始不理解为什么是 4、2、1 这三个数,其实如果你把 rwx 看成三个二进制位,一下就通了。

r 对应 100(二进制)就是 4,w 对应 010 就是 2,x 对应 001 就是 1。把三个位的数值加起来,就能表达任意组合。比如 rwxr-xr-x,分成三组算:rwx = 4+2+1 = 7,r-x = 4+0+1 = 5,r-x = 4+0+1 = 5,合起来就是 755。同样 rw-r--r-- 就是 644。

chmod 755 script.sh # 属主可读写执行,组和其他人只读+执行 chmod 644 notes.txt # 属主可读写,组和其他人只读 chmod 600 .ssh/id_rsa # 私钥只允许属主读写,这是安全基线 chmod 700 ~/.ssh # 目录只允许属主进入

数字法最实用的一点是,看到ls -l的输出你能反推权限值,反过来也一样。排查权限问题的时候,我经常直接在脑子里算一遍十进制。

递归修改是一个常见坑点。chmod -R 755 dir会把目录下所有文件和目录统一改成 755,但目录和文件的语义不一样,通常我们想要的是:目录 755(能进入能列出),普通文件 644(只读即可)。如果用了chmod -R 777或者chmod -R 755,要么太松,要么给所有脚本文件都加了执行位,属于不该有的权限外溢。

更稳妥的做法是分开处理,用 find 把目录和文件筛出来分别改:

find /path/to/project -type d -exec chmod 755 {} + find /path/to/project -type f -exec chmod 644 {} +

-type d只匹配目录,-type f只匹配普通文件,{} +是把结果一次性传给 chmod,执行效率比-exec chmod 755 {} \;高不少。这个方法在批量修正权限时特别可靠,我处理线上项目的目录权限时都会用这一套,而不是无脑 -R。

2.2 符号法适合微调,umask 则决定了新建文件和目录的出生权限

符号法用u、g、o、a表示身份,+、-、=表示增加、移除、设为指定权限,后面跟上 rwx 中的若干位。它适合做局部调整,比如给属主加执行权限、给组去掉写权限:

chmod u+x run.sh # 给属主加执行位 chmod g-w shared.txt # 去掉组写权限 chmod o=r secret.log # 其他人只读,不读不执行 chmod a+x deploy.sh # 所有身份都加上执行位

和数字法相比,符号法不用记住最终形态,先ls -l看一眼,然后只改要改的那一位,更安全。我的习惯是:新建文件时知道要什么结果,直接用数字法;在已有文件基础上微调,用符号法,避免误伤其他权限位。

umask 是另一个常被忽略但直接影响日常操作的东西。你可以把它理解为"默认权限的遮罩"。创建文件时,系统默认文件权限是 666(rw-rw-rw-),目录是 777(rwxrwxrwx),但会跟 umask 做一个"按位取反再与"的运算,最终结果就是文件真实的初始权限。

用最常见的 umask 022 来举例:文件是 666 减去 022 得到 644,目录是 777 减去 022 得到 755。也就是说,你每次创建新文件默认就是 rw-r--r--,新目录默认是 rwxr-xr-x。如果 umask 是 077,那么新文件是 600,新目录是 700,只有你自己能碰。

$ umask 0022 $ touch newfile $ mkdir newdir $ ls -ld newdir newfile drwxr-xr-x 2 frank dev 4096 Jan 1 10:05 newdir -rw-r--r-- 1 frank dev 0 Jan 1 10:05 newfile

需要注意两点。第一,为什么新建文件默认没有执行位?这是安全设计,防止你一创建文件就得到一个可执行脚本,降低误执行风险;需要执行权限时再手动 chmod。第二,umask 是 shell 的内建状态,不是系统级的,重启或新开终端后需要重新加载。想长期生效就写进 shell 的配置文件,比如~/.bashrc或~/.profile里加一行umask 027。不同团队对 umask 的偏好差异很大,有的选 022 方便共享,有的选 027 更保守,有的选 077 强调隔离,取决于目录的共享需求。

3. chown、特殊权限与属主陷阱:谁拥有文件比权限本身更关键

3.1 chown 与 chgrp:数据目录的属主设置是运维翻车高发区

权限位只是规则,真正决定"谁是主人"的是属主和属组,这也是 chown 和 chgrp 存在的意义。chown 修改属主,chgrp 修改属组。最常见的用法是同步修改属主和属组:

chown frank:dev notes.txt # 属主改为 frank,属组改为 dev chown frank notes.txt # 只改属主 chown :dev notes.txt # 只改属组,等价于 chgrp dev notes.txt chgrp dev notes.txt # 同样是改属组 chown -R frank:dev /data/app # 递归修改整个目录树的归属

这里有一个容易被历史命令误导的点。旧版本的 chown 支持用chown frank.dev notes.txt这种点分写法,现在虽然某些系统还兼容,但新代码一律建议用冒号user:group,因为点号可能与主机名场景冲突。凡是新写的脚本,统一用冒号就好。

为什么属主比权限位更关键?因为权限位是"规则",而属主决定规则怎么套。一个典型的翻车场景是:把某个应用的数据目录 chown 成了 root,但应用本身以普通用户运行,之后应用发现自己写不了自己的数据,只能临时去改权限位或者重新 chown。真正好的习惯是:应用目录的属主必须是运行该应用的那个用户,而不是 root。用 root 启动 Web 服务然后让应用去写文件,是非常不推荐的姿势,因为一旦应用被攻击,攻击者拿到的就是 root 权限。

更灾难的操作是chown -R用错目标。比如手一抖把chown -R frank:dev /这种命令敲下去,系统上所有文件属主都变了,后果是系统直接半瘫痪。我自己处理过一次类似事故的善后,那种场景下几乎没有快捷恢复手段,只能从备份或快照回滚。所以凡是带-R的 chown 或 chmod,执行前先pwd确认路径,再ls -ld看一眼目标,多花五秒钟,省掉的是一整天的恢复时间。

3.2 SUID、SGID 和 Sticky Bit:ls -l 里那些 s 和 t 到底代表什么

除了 rwx 之外,Linux 还有三个特殊权限位:SUID、SGID 和 Sticky Bit。它们平时不容易见到,但一旦出现,往往对应着系统安全中需要重点关注的逻辑。

先看 SUID(Set User ID)。普通用户修改密码用的passwd命令有权限这么干,靠的就是 SUID。你可以用ls -l /usr/bin/passwd看一下,它的权限位是-rwsr-xr-x,注意属主那一组的 x 位变成了 s,这说明这个文件带有 SUID。普通用户执行它时,进程的有效身份会临时变成文件属主,也就是 root,从而能写/etc/shadow。如果没有 SUID,普通用户根本不可能修改自己的密码。

$ ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 59976 Nov 12 2024 /usr/bin/passwd

用chmod u+s file可以给文件设置 SUID,对应的数字法是 4 开头,比如chmod 4755 file。但这里有很多禁忌。第一,不要给 vim、bash 这类能够改文件的程序乱加 SUID,否则任何执行它的用户都能以 root 身份写文件,等于把系统裸奔。第二,SUID 对 shell 脚本无效,只有二进制可执行文件能可靠地使用 SUID,脚本的 SUID 位在很多系统上会被忽略,还会带来安全隐患。如果你需要让某个普通用户以特定身份执行一个固定操作,正确做法不是加 SUID,而是用 sudo 配置授权。

SGID(Set Group ID)分两种情况。对二进制文件,类似 SUID,但改变的是有效组身份;对目录,SGID 有更常见的应用:目录设置了 SGID 后,任何人在里面新建的文件,属组自动继承目录的属组,而不是创建者自己的主组。这对于团队共享目录特别有用,配合相关权限位可以实现"谁都能写,但文件始终归项目组所有"的效果。命令是chmod g+s dir,数字法是 2 开头,比如chmod 2770 shared_dir。

Sticky Bit 则主要用在/tmp这类公共目录。/tmp的权限是drwxrwxrwt,最后的 t 就是 Sticky Bit。它的作用是:目录对所有人开放写权限,但用户只能删除属于自己或者自己拥有的文件,不能随手删别人的。如果去掉这个位,一个多用户系统的 /tmp 会变成一个谁都可以互相删文件的混乱现场。设置 Sticky Bit 用chmod +t dir,数字法是 1 开头,比如chmod 1777 /tmp。

想审计系统里哪些文件开了 SUID,可以执行:

find / -perm -4000 -type f 2>/dev/null

-perm -4000表示只要有 SUID 位就匹配。这个命令建议在全新安装的系统上跑一次留个底,以后定期对比。如果哪天多出了一个不该有的带 SUID 的诡异文件,那就要警惕了,这通常是系统被入侵的典型信号之一。

4. 进程与用户管理的日常高频指令:从 ps 看到 kill,从 useradd 到 sudo

4.1 ps 与 top:先看清进程在干什么,再决定怎么处理

进程管理的第一步不是 kill,而是看。ps是最基础的进程查看命令,两个最常见的派系是ps aux和ps -ef。它们想表达的内容差不多,都是"列出所有进程的详细快照",区别只是输出字段格式略有不同。我自己的使用习惯是ps aux,因为字段更直观:USER、PID、CPU 占用、内存占用、VSZ、RSS、TTY、STAT、START、TIME、COMMAND 一列排开。

$ ps aux | head -5 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.2 102204 14836 ? Ss Jan10 0:41 /sbin/init frank 1204 0.0 0.5 146532 30012 ? S 11:00 0:15 /usr/bin/gnome-shell frank 1450 0.3 0.1 204532 8764 ? Sl 11:01 1:09 /usr/lib/firefox/firefox

STAT 这一列代表了进程状态,非常重要:R 表示正在运行,S 表示可中断睡眠(正常等待状态),D 表示不可中断睡眠(通常在等磁盘 I/O),T 表示已停止,Z 表示僵尸状态。如果你的系统里出现大量 D 状态进程,先检查磁盘;如果出现 Z 状态进程,那说明有进程还没被父进程回收,需要关注。

僵尸进程是很多新手问过的问题。它其实已经不是活跃进程了,只是进程表里还保留着一条代表"我已退出,等父进程来收尸"的记录。正常情况下父进程会调用系统接口回收它,但如果父进程自己写得有问题或者卡住了,僵尸进程就会一直留着。单个僵尸不用太紧张,关键看数量:如果越来越多,通常要处理的是它的父进程,kill父进程或让父进程重启,僵尸才能被系统回收。用pstree -p可以直观看到进程父子关系,定位到谁生出了这个僵尸。

top是动态视图,刷新进程状态用的。启动后按P按 CPU 排序,按M按内存排序,按k可以输入 PID 杀进程,按q退出。如果觉得安装替代品没问题,htop体验更好,但生产环境不保证有,top是底线技能。排查 CPU 飙升问题时,我的固定流程是:先top按 P 排序确定可疑进程,再用ps -fp PID查看它的完整命令行,判断是不是合法进程,最后再决定处理手段。只看进程名是不太够的,因为同名进程可能很多,且恶意程序常把进程名伪装成正常名字,结合ls -l /proc/PID/exe看它实际执行的程序路径,是更可靠的验证方式。

4.2 kill 与作业控制:不要上来就 -9,给进程一个体面退出的机会

向进程发送信号通常用 kill,但它真的不只是"杀死进程"。kill做的只是向进程发送一个信号,默认信号是SIGTERM,也就是 15。收到 SIGTERM 的进程可以自己处理收尾工作,比如保存配置、释放资源、关闭连接,然后再退出。这就像让人下班前自己关好电脑,是一种体面的退出方式。

kill -9发送的是SIGKILL,进程没有机会做任何收尾,直接消失。对大部分正常服务,上来就 -9 是粗暴且有害的:数据库可能没来得及落盘,文件可能写到一半,进程清理函数根本没跑。所以排查问题时我的习惯是:先kill pid(也就是 kill -15),等几秒看进程是否退出,没反应再kill -9。有些场景确实必须 -9,比如进程已经彻底卡死,或者状态不可中断且无法正常响应信号,但那是最后手段,不是默认手段。

kill 12345 # 发送 SIGTERM,优雅退出 kill -9 12345 # 发送 SIGKILL,强制退出 killall nginx # 按进程名发送信号,所有叫 nginx 的进程都会收到 pkill -f "python app.py" # 按命令行匹配进程,注意 -f 会匹配完整命令行,慎用 pgrep -la ssh # 只列出匹配 PID 和进程名,适合在执行 kill 前做确认

pkill -f是双刃剑。它匹配的是完整命令行,很方便,但也可能误杀很多看起来不一样的进程。我见过有人写pkill -f java,结果把其他包含 java 字样的进程全带走了。用之前先pgrep -f看一眼匹配结果,再动手。

作业控制这块,命令行后台化也是常用技能。普通程序跑起来占住终端的时候,可以按Ctrl+Z把进程挂起,bg放到后台继续跑,fg再拉回前台。如果想让命令完全不受关闭终端影响,用到的是 nohup 和&:

nohup python server.py > server.log 2>&1 &

这里&让命令在子 shell 后台执行,nohup让进程忽略挂断信号,> server.log 2>&1把标准输出和错误输出都重定向到日志文件。这样即使你关闭终端,进程也会继续跑。用jobs -l可以查看当前 shell 的后台任务,但需要注意:jobs只管理当前终端会话的任务,一旦终端退出,任务就和这个 shell 断开了,想管它就得用ps重新找 PID。

4.3 用户管理的常见组合:useradd、passwd、usermod 的正确用法

用户管理这块,日常高频的是新增用户、设密码、加组、授权这四个动作。新建用户的典型命令是:

useradd -m -s /bin/bash -G sudo dev passwd dev

-m创建家目录,-s指定登录 shell,-G指定附加组。这里有个发行版差异要记住:有些系统默认给 useradd 建家目录,有些默认不建,所以跨发行版操作时建议显式加-m,省得后来发现用户没有家目录。passwd dev是为用户设置或修改密码,根据系统策略,可能需要满足最小长度和复杂度要求。

usermod 最常用来修改用户属性,但有一个参数错误会导致连锁问题:-G是覆盖附加组,-aG是追加附加组。很多人一开始没看清楚,直接把用户追加到某个新组,用了usermod -G docker dev,结果 dev 原有的 sudo 组被覆盖掉了,用户一下子丢了管理员权限。这个问题我有段时间被反复问,原因基本都是这个参数。正确做法是追加用-aG:

usermod -aG docker dev

主组用-g修改,附加组用-G和-aG修改,这两个概念要分清楚。查看用户当前属于哪些组,groups dev即可。创建组用groupadd,把用户加入既有组也可以用gpasswd -a dev docker。

再来说 su 和 sudo。su dev是切换到 dev 用户,但仍保留当前用户的环境变量和工作目录,容易引起环境错乱;su - dev是完整登录式切换,会加载目标用户的完整环境。sudo则是"用授权的方式执行单条命令",把管理员能力按命令粒度下放。两者的安全性差异在于:su 需要目标用户的密码,共享 root 密码本身就是高风险操作;sudo 使用当前用户自己的密码,而且可以通过配置文件精确控制某人能执行什么命令,不能执行什么。

sudo -l # 查看当前用户有哪些 sudo 权限 sudo visudo # 编辑 sudoers 配置文件,注意必须用 visudo,不要直接手改

sudo 的配置文件在/etc/sudoers,任何改动都建议用visudo打开,因为它会做语法检查,避免你改出一个语法错误把自己锁在管理员权限之外。常见的授权写法是让某个用户组的人能执行所有管理员命令,比如%sudo ALL=(ALL:ALL) ALL,这行意味着 sudo 组的成员可以以任何用户身份执行任何命令。实际工作中,最小权限原则更值得提倡:只授权必要的命令,而不是无脑给所有权限。

这里再补一句和前面权限内容的衔接。新建用户之前,先想清楚这个用户要不要家目录、要不要能登录 shell、要不要有 sudo 权限;建好用户后,用id确认归属,用sudo -l确认授权范围。用户管理里的"少给权限、确认无误再扩大授权"这个原则,和文件权限里的"宁可收紧、不要无脑 777"是完全一致的,说到底都是把系统的默认信任边界收紧到最小。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询