【Linux 系统编程】权限(一):身份、提权,和那 9 个权限位

这一篇只讲三件事:

  1. 你是谁 ------ Linux 里的两种用户,su / su - 怎么切换身份,以及它背后的进程原理。
  2. 你能借谁的权力 ------ sudo,它的白名单机制和提权窗口期。
  3. 那 9 个权限位是什么 ------ u / g / o、文件类型、r / w / x 对文件和对目录分别意味着什么、怎么用字符和数字表示。

至于"怎么改权限"(chmod / chown / chgrp / umask)和"粘滞位",放到下一篇。

这三章的关系是这样的:

复制代码
第一章(人)你是谁      ──┐
第二章(人)你能变成谁  ──┴──→  你的身份,决定你对某个文件算 u / g / o 中的哪一个
第三章(物)权限位本身  ─────→  那三组 rwx 到底在管什么

所以顺序不能反:先把"人"讲清楚,才知道"物"上的三组权限是写给谁看的。


第一章 人:Linux 只有两种用户,但权限是用来限制普通用户的

1.1 两种身份

身份 提示符 能干什么
超级用户 root # 想做任何事,不受限制
普通用户 $ 只能做有限的事

先立一句总纲:这套权限机制,本质上是在限制普通用户。 root 只是被"提醒",不是被"限制"------这个判断后面会反复被验证(粘滞位的例外名单里有它,sudoers 里也专门给它留了一句 "Allow root to run any commands anywhere")。

提示符是最快的判断方式,一眼就知道自己现在是哪个身份。

1.2 切换身份:su 和 su -

1.2.1 两个命令,只差一个横杠

bash 复制代码
su  [用户名]      # 只切换身份
su - [用户名]     # 模拟一次完整登录

差别只有那一横,行为差很远:

su su -
身份 换成目标用户 换成目标用户
环境变量 保留原用户的 换成目标用户的
当前目录 不变 切到目标用户的家目录
本质 在外层套一层身份 重新登录一次

一句话记:su 只换人,su - 换人又换房间。

1.2.2 怎么一眼看出自己站在哪一层

看提示符。提示符的格式是 [用户名@主机名 当前目录]

bash 复制代码
[root@VM-0-9-opencloudos ~]# whoami
root
[root@VM-0-9-opencloudos ~]# su zbc
[zbc@VM-0-9-opencloudos root]$ whoami
zbc

重点看第二段提示符:[zbc@VM-0-9-opencloudos root]$

  • 用户名已经是 zbc身份换了
  • 当前目录还是 root目录没换

用户变了、目录没变,这就是 su 最直观的特征。如果这里用的是 su - zbc,提示符末尾会变成 ~,也就是 /home/zbc

1.2.3 密码规则

  • 普通用户 → root:要输 root 的密码
  • root → 普通用户:不用输密码

原因很直白:root 的权限最大,不需要向谁证明自己是谁。

1.3 一个案例看懂 su 和 su - 的真实区别

先接受一个前提:根目录 / 对普通用户没有 r 和 w 权限。(r 和 w 具体指什么,第三章会讲透,这里先当结论用。)

现在让 root 在 / 目录下 su 成 zbc:

bash 复制代码
[root@VM-0-9-opencloudos ~]# su zbc
[zbc@VM-0-9-opencloudos root]$ mkdir mulu01
mkdir: cannot create directory 'mulu01': Permission denied
[zbc@VM-0-9-opencloudos root]$ ls -al
ls: cannot open directory '.': Permission denied

两条命令都失败了,但失败的原因不一样,这个必须分开记

  • mkdir 失败 → 对 / 没有 w 权限,不能在目录里创建东西
  • ls 失败 → 对 / 没有 r 权限,读不出目录里有什么

一个缺 w、一个缺 r,两条报错长得一模一样(都是 Permission denied),但根因完全不同。以后看报错要能拆到这一层。

换成 su - zbc,直接落到自己的家目录,一切正常:

bash 复制代码
[root@VM-0-9-opencloudos ~]# su - zbc
Last login: Sat Apr 25 18:32:25 CST 2026 on pts/0
[zbc@VM-0-9-opencloudos ~]$ pwd
/home/zbc
[zbc@VM-0-9-opencloudos ~]$ ls -l
drwxr-xr-x 4 zbc zbc      40 Apr 23 15:43 gitcage
-rw-r--r-- 1 zbc zbc      87 Apr 25 14:19 ma6174
-rw-r--r-- 1 zbc zbc     149 Apr 25 14:21 makefile
-rw-r--r-- 1 zbc zbc       0 Apr 25 12:57 PassWordForZbc

因为家目录是 zbc 自己的东西,权限归它。

顺带一条规则:每新建一个用户,系统都会在 /home 下建一个同名目录当他的家目录。 root 是例外,装系统的时候就已经有 /root 了。

1.4 底层原理:su 不是"变身",是"套娃"

这一节是第一章的核心。

1.4.1 关键结论

执行 su 不是把 root 进程改造成 zbc,而是通过 fork() 派生出一个新进程。

复制代码
第一层(父进程):root 的 shell      PID 1001     ← 没消失,只是挂起了
        ↓ fork()
第二层(子进程):zbc 的 shell       PID 1050     ← 前台,终端归它用

所以"切换用户"这个说法其实有点误导------没有任何东西被切换,是多了一个进程。

1.4.2 那父进程去哪了

root 的 shell 没有退出,它做了一件事:把自己挂起,然后调用 wait() 盯着子进程。

它暂时交出了终端的控制权,在后台等着。因为一个终端同一时间只能有一个前台进程,这个位置让给了子进程。

1.4.3 Ctrl+D 为什么能"退回去"

因为它根本不是"回退操作",是自毁操作

  1. 子进程(zbc 的 shell)收到退出指令 → 终止 → 内核回收它占用的所有资源
  2. 一直在 wait() 的父进程(root 的 shell)被系统唤醒
  3. 父进程重新拿到终端的读写权,回到前台

所以不是"退回去",是里面那层塌了,外面那层露出来

这里有个特别容易搞反的点:Ctrl+D 不是"返回上层用户"的快捷键,它的本意是"结束当前输入"(EOF),顺带成了退出当前 shell 的方式。 之所以看起来像"返回",是因为上层本来就一直在那儿等着,从来没走。

在这个场景下,Ctrl+Dexitlogout 效果一致。

1.4.4 用 who 验证一下

who 能直接证明"登录的还是 root":

bash 复制代码
[root@VM-0-9-opencloudos ~]# who
root     pts/0        2026-04-26 08:10 (223.104.73.140)
root     pts/1        2026-04-26 09:14 (223.104.73.140)
[root@VM-0-9-opencloudos ~]# su - zbc
[zbc@VM-0-9-opencloudos ~]$ who
root     pts/0        2026-04-26 08:10 (223.104.73.140)
root     pts/1        2026-04-26 09:14 (223.104.73.140)

su 成 zbc 之后,who 里还是没有 zbc。

因为 who 读的是登录记录。 pts/0 这个终端从头到尾都是 root 远程登录时开的,su 只是在别人已经开好的终端里生了个子进程,并没有产生新的登录。

我同学的比喻很准:root 把房子租给了 zbc,但房产证上写的还是 root。

1.5 把这一整套看成栈

既然是一层层套出来的,结构就是栈:

状态 进程栈内容 当前活跃用户
初始 [ root ] root
su - zbc [ root → zbc ] zbc(栈顶)
su - guest [ root → zbc → guest ] guest(栈顶)
Ctrl+D [ root → zbc ] zbc(回到上一层)
Ctrl+D [ root ] root

理论上可以无限套下去,每按一次 Ctrl+D 弹一层,直到露出最核心的那个原始进程。

这个模型还能顺手解释一件事:为什么 su 不会破坏原来的登录环境------因为原来那层压根就没动过。

1.6 su 的两个毛病

su 能干的事不少,但有两个硬伤:

  1. 得知道 root 的密码。 多人协作的环境里,这等于把最高权限的钥匙抄给了所有人,而且给了之后很难收回来(改密码会影响所有人)。
  2. 粒度太粗。 一旦切过去就是彻底的 root。你可能只是想重启一个服务,却拿到了一整个 root shell,一不小心什么都能干。

第二章的 sudo,就是来解决这两个问题的。


第二章 人:sudo ------ 不换身份,只借一条命令的权力

2.1 一句话分清 su 和 sudo

  • su:换个人来做。 整个 shell 都变成另一个人,之后你敲的每条命令都是他的。
  • sudo:还是你,但这一条命令以目标用户(默认 root)的身份运行。
su sudo
身份 真的切过去了 没变,还是你
作用范围 切换之后的所有命令 只有这一条命令
要谁的密码 目标用户的密码(通常是 root) 你自己的密码
权限粒度 要么全有,要么全无 能精确到"哪个用户在哪台机器上以谁的身份跑哪些命令"

"sudo 比 su 更安全、更可控"这话不是空话,它就体现在上面这张表的每一行。

2.2 sudo 的两个案例

2.2.1 案例一:跨过那道门

bash 复制代码
[zbc@VM-0-9-opencloudos ~]$ ls /root
ls: cannot open directory '/root': Permission denied
[zbc@VM-0-9-opencloudos ~]$ sudo ls /root
[sudo] password for zbc:
test
[zbc@VM-0-9-opencloudos ~]$ sudo ls /root
test                      # 五分钟内再执行,不用输密码

三行输出,三个信息点:

  • 第一行:普通用户读 /root 直接被拒(和 1.3 里 / 的情况同理)
  • 第二行:sudo 让这条命令以 root 身份跑,成了
  • 第三行:五分钟窗口期,第二次不用再输密码(2.4 细讲)

注意 sudo 要的是 zbc 自己的密码,不是 root 的。这是它和 su 最根本的区别之一。

2.2.2 案例二:文件归属暴露了真相

这个案例更能说明 sudo 的本质:

bash 复制代码
[zbc@VM-0-9-opencloudos ~]$ touch test01.c
[zbc@VM-0-9-opencloudos ~]$ sudo touch test02.c
[zbc@VM-0-9-opencloudos ~]$ ls -l test01.c test02.c
-rw-r--r-- 1 zbc  zbc  0 Apr 26 10:12 test01.c
-rw-r--r-- 1 root root 0 Apr 26 10:12 test02.c

两个文件都是 zbc 敲出来的,但第二个归 root。

这条现象把 sudo 的底层含义直接摆出来了:提权不是给 zbc 贴了一个"特权"标签,而是这次操作换了个身份去执行。

文件是 root 创建的,所有者就写 root------ls -l 里的所有者和所属组那两列不会撒谎。

反过来说,这里埋了一个坑:用 sudo 创建或修改过的文件,普通用户自己反而改不动了,因为它的所有者变成了 root。(怎么改回来,是下一篇 chown 的事。)

2.3 /etc/sudoers:权力白名单

"凭什么别人 sudo 可以,我不行?"------不是不行,是你没被写进白名单。

2.3.1 为什么普通用户打不开它

白名单就是 /etc/sudoers

bash 复制代码
[root@VM-0-9-opencloudos ~]# vim /etc/sudoers
# 必须以 root 用户打开,其他用户打开什么都看不到

那句话要单独理解:不是文件不存在,是你没有读它的权限。(对照第三章:读一个文件需要 r 权限。)

2.3.2 格式怎么读

找到这一行:

复制代码
## Allow root to run any commands anywhere

在它下面加上一行:

复制代码
zbc     ALL=(ALL)       ALL

四个字段拆开看:

复制代码
zbc        ALL       =    (ALL)        ALL
 ↑          ↑              ↑            ↑
用户名   允许登录的主机   可切换的身份   允许执行的命令

按字面读就是:zbc 可以从任何主机登录,以任何用户(包括 root)的身份,执行任何命令。

(ALL) 那一栏是 sudo 最精细的地方------你可以把它收窄,比如只允许以 root 的身份跑某几个具体命令,这就是所谓的"权限精细化管理"。

2.3.3 NOPASSWD:免密

这个文件里还能加 NOPASSWD 标记,让某个用户执行 sudo 时完全不用输密码。

它和 2.4 那个"五分钟窗口期"是两回事:窗口期是暂时的(几分钟自动过期),NOPASSWD 是长期的(写进配置就一直有效)。

用哪个,取决于你把"安全"和"方便"的秤砣放在哪一边。

2.3.4 另一种改法,和一个作死行为

课件里还给了另一种写法------先把权限放开再改:

bash 复制代码
# chmod 740 /etc/sudoers
# vi /etc/sudoers

以及 sudo 的调用形式:

bash 复制代码
$ sudo -u 用户名 命令
$ sudo -u root /usr/sbin/useradd u21      # 以 root 身份执行 useradd

-u 就是在说"这次以谁的身份跑",不写默认 root。

最后记一个作死行为(知道就行,别试):把 sudoers 里 root 那一行删掉,等于让 root 自己失去大量权力,会搞出一个很尴尬的局面。

2.4 提权窗口期

规则:第一次执行 sudo 时输密码,之后五分钟是一个窗口期,窗口期里再执行 sudo 不用输密码。

这是"安全"和"好用"之间的折中------否则你连着敲十条 sudo 就要输十遍密码。

2.5 综合案例:把一个程序"装"进系统

课件里这个案例一次性串起了好几件事,很适合给第二章收尾:

bash 复制代码
[zbc@VM-0-9-opencloudos ~]$ cp test /usr/bin
cp: cannot create regular file '/usr/bin/test': Permission denied
[zbc@VM-0-9-opencloudos ~]$ sudo cp test /usr/bin
[sudo] password for zbc:
[zbc@VM-0-9-opencloudos ~]$ test              # 现在直接敲名字就能跑了
[zbc@VM-0-9-opencloudos ~]$ sudo rm -f /usr/bin/test

拆开看,四件事:

  1. 为什么自己编译的程序要用 ./test,系统命令却直接敲名字就行?
    因为系统命令放在 /usr/bin 里,而 /usr/bin 在 PATH 里。PATH 是系统"去哪找命令"的那份地址清单,.(当前目录)通常不在清单上,所以你得手动加 ./ 告诉 shell:就在当前目录。
  2. 为什么 cp/usr/bin 需要管理员权限?
    因为把文件放进 /usr/bin,效果上就等于"装了一个命令"。普通用户装不进去,是权限拦住的------这个拦截是对的。
  3. 装上去之后谁都能用。
    任何一个用户在同一台机器上装的命令,所有人都能用(除了某些只有 root 能用的特殊命令)。
  4. 用完记得删掉。
    sudo rm -f /usr/bin/test,不然它就真的留在系统里了。

到这里,"人"这条线走完:你是谁 → 你能变成谁 → 你能借谁的权力。

接下来是"物"------那些权限位到底在说什么。


第三章 物:那 9 个权限位到底在说什么

前面一直在喊 "Permission denied",现在把权限本身拆开看。

3.1 访问者分类:u / g / o

任何文件,站在它面前的人只分三种:

符号 含义 记忆
u User,文件的所有者 中国平民(因为有法律问题)
g Group,所有者所在组的用户 同组同事
o Others,其他用户 外国人

你现在是什么身份,决定你在这三者里算哪一个。 这就是为什么前面非得先把"人"讲清楚------那三组权限,本来就是写给三种不同的人看的。

举个例子,-rw-rw-r--

  • 所有者(u)能读能写
  • 同组(g)能读能写
  • 其他人(o)只能读

3.2 文件类型:第一个字符

ls -l 输出的第一列一共 10 个字符,第一个是类型,后面 9 个是权限

字符 类型
d 目录
- 普通文件
l 软链接(类似 Windows 的快捷方式)
b 块设备文件(硬盘、光驱等)
c 字符设备文件(屏幕等串口设备)
p 管道文件
s 套接字文件

3.3 完整解剖一行 ls -l

先把第一列拆开:

bash 复制代码
-rw-rw-r--  1 whb whb    0 Jan 11 14:22 file.txt
↑ ↑↑↑ ↑↑↑ ↑↑↑
|  |   |   └── 其他人(o):r--   只读
|  |   └────── 同组(g):rw-    读 + 写
|  └────────── 所有者(u):rw-   读 + 写
└───────────── 文件类型:-       普通文件

再看一整行的完整结构:

复制代码
d   rwx  rwx  r-x   3    whb    whb    4096   Jan 11 15:01   file.txt
↑   ↑    ↑    ↑     ↑     ↑      ↑      ↑       ↑              ↑
类型 所有者 同组 其他人 链接数 所有者 所属组  大小  最后修改时间   文件名

这张图值得记住,后面所有关于权限的讨论都是在这上面展开的。

3.4 r / w / x:同一个字母,含义完全不同

这一节是第三章的核心,也是整个权限体系里最容易搞错的地方。

权限 对文件 对目录
r (4) 读取文件内容 浏览目录里有什么(能不能 ls
w (2) 修改文件内容 在目录里创建 / 删除 / 移动文件
x (1) 把文件当程序执行 进入 这个目录(能不能 cd
- 没有该权限 没有该权限

逐个拆:

r ------ 读

  • 对文件:能打开看内容
  • 对目录:能列出里面有哪些文件(ls

w ------ 写

  • 对文件:能改内容
  • 对目录:能在里面创建、删除、移动 文件
    ← 注意,这里管的是"目录里有什么东西",不是文件内容

x ------ 执行

  • 对文件:能被当作程序运行
  • 对目录:能进去(cd

3.4.1 由这张表推出的三条结论

  1. 目录没有 x 就进不去。 哪怕它有 r 权限也没用。
    这是最容易搞错的一条------很多人以为"有读权限就能进目录看文件"。不行,cd 需要的是 x。
  2. 有 x 但没有 r :能 cd 进去、能在里面执行命令,但 ls 读不出目录内容。
    合起来说就是:你能干活,但看不见活在哪。
  3. 只要你对目录有 w,你就能删掉目录里的任何文件------不管你对该文件本身有没有权限。
    这条是权限设计上的一个洞。(怎么补它是下一篇粘滞位的事,这里先记住"有这么一个洞"。)

3.4.2 回到第一章那个案例

现在回头看 1.3 里 / 目录打不开的情形,就通了:

bash 复制代码
[zbc@VM-0-9-opencloudos root]$ mkdir mulu01
mkdir: cannot create directory 'mulu01': Permission denied     # 缺 w
[zbc@VM-0-9-opencloudos root]$ ls -al
ls: cannot open directory '.': Permission denied                # 缺 r
  • mkdir 要在目录里创建东西 → 需要目录的 w
  • ls 要读出目录里有什么 → 需要目录的 r

第一章埋的伏笔,在这里收掉。

3.5 权限的两种记法

3.5.1 字符法

就是看到的那 9 位,分成三组:

复制代码
rwx   rw-   r--
 ↑     ↑     ↑
所有者  同组  其他人

3.5.2 8 进制法

把每组三位当成数字来算:

复制代码
r = 4    w = 2    x = 1      (没有就是 0)

三个一组分别相加,每组得到 0~7:

权限 含义
rwx 7 读 + 写 + 执行
rw- 6 读 + 写
r-x 5 读 + 执行
r-- 4 只读
-wx 3 写 + 执行
-w- 2 只写
--x 1 只执行
--- 0 什么权限都没有

为什么是 4 / 2 / 1?因为三个权限位各自独立、互不重叠,用二进制写出来正好是 100 / 010 / 001,加起来不会有歧义。这就是权限数字能用一个 0~7 表示一位的原因。

3.5.3 常见的几种组合

8 进制 等价字符 典型场景
644 rw-r--r-- 普通文件
664 rw-rw-r-- 同组可写的文件
640 rw-r----- 只给同组看
700 rwx------ 只给自己
755 rwxr-xr-x 目录、可执行文件
777 rwxrwxrwx 全开,危险

看这几个数字有个小技巧:第一位(所有者)几乎总是 6 或 7,真正决定"这份文件敞开多少"的是后两位(同组、其他人)。

3.6 file 指令:先认清它是什么

bash 复制代码
file [选项] 文件或目录...
  -c   详细显示指令执行过程,便于排错或分析程序执行的情形
  -z   尝试解读压缩文件的内容

命令本身不复杂,但它的位置很重要:先知道这是什么东西,再决定该给它什么权限。

对着一堆来路不明的文件直接 chmod 755,是很多麻烦的开端。


本篇小结

第一章 + 第二章(人)

命令 作用 关键点
su 用户 切换身份 只换人:环境变量、当前目录都不变
su - 用户 模拟完整登录 换人又换房间:家目录、环境变量全换
sudo 命令 以 root 身份执行这一条 自己的密码;五分钟窗口期
sudo -u 用户 命令 以指定用户身份执行 -u 指定身份
vim /etc/sudoers 改白名单 用户 主机=(身份) 命令;必须 root 打开
Ctrl+D / exit 退出当前 shell 自毁当前进程,外层弹回来

第三章(物)

字符 字符
7 rwx 3 -wx
6 rw- 2 -w-
5 r-x 1 --x
4 r-- 0 ---

三条必须记住的结论

  1. su 不是变身,是 fork 出一个子进程;Ctrl+D 是自毁内层,不是回退。
  2. sudo 不改身份,只是让这一条命令换个身份执行------所以它创建的文件归 root。
  3. rwx 对文件和目录含义完全不同,尤其是:目录的 w 管的是"能不能删掉里面的任何文件"。

下一篇:怎么改权限 ------ chmod / chown / chgrp / umask,以及粘滞位怎么补上 3.4.1 第三条那个洞。

相关推荐
艾莉丝努力练剑1 小时前
【AI大模型接入SDK】SQLite基础概念与C的API开发
网络·c++·人工智能·学习·架构
東隅已逝,桑榆非晚1 小时前
List(类函数学习)
c++·笔记·学习
byte轻骑兵1 小时前
【BlueZ 】Linux 内核蓝牙子系统入门:hci_core 模块与 BlueZ 的交互
linux·人工智能·bluez·电脑蓝牙·嵌入式蓝牙
Dr_Fourier1 小时前
AWQ量化
c++·人工智能·pytorch·ai
江屿风1 小时前
【Linux系统】【Linux 进程程序替换机制解析及自定义 Shell 核心逻辑实现 】流食般投喂
linux·运维·服务器·开发语言·笔记
好不玩呀1 小时前
一次PXE服务器上IPV6问题的记录
linux·运维·服务器
-dzk-1 小时前
【回溯】LC 22.括号生成
算法·回溯
杜 硕2 小时前
顺序表算法
数据结构·算法
AgentMaster3 小时前
智能客服多渠道接入,5 款产品的全渠道整合能力对比与实战
大数据·人工智能·算法