CodeRoad Map 程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。
由 程序员柏风 独立维护 · 关于作者
学习 更多
文件系统与权限
这一课你将能做到
看懂 ls -l 输出的每一段:类型位、rwx 三组权限、链接数、属主/属组、大小、时间、文件名
用 chmod 数字法(644/755/600)和 chown 正确设置部署目录、JAR 包、配置文件、日志目录的权限
画出 Linux 目录树中与本站相关的关键路径(/etc、/var/log、/www/wwwroot、~/.ssh),并说出各自放什么
为什么要学
Permission denied 是 Java 后端部署的 第一大报错 :服务起不来、日志写不进、JAR 读不了、SSH 密钥被拒——十有八九都是权限。Linux 是多用户系统,每个进程都以某个用户身份运行,「谁能读、谁能写、谁能执行」由文件的权限位决定,不是靠重启碰运气。
本课结束时你要能回答生产高频问题:为什么 JAR 包 root 能读、www 用户起服务却 Permission denied?为什么私钥权限必须是 600 才肯让你用?为什么数据库目录绝不能 777?
先建立图像
一棵树:Linux 只有一个根
没有 C 盘 D 盘,一切从 / 出发:
/
├─ etc/ 所有配置文件(nginx、systemd、ssh…)
├─ var/
│ ├─ log/ 系统与应用日志(排障必来)
│ └─ lib/ 应用数据(MySQL 的数据目录在这)
├─ home/ 普通用户家目录(/home/deploy)
├─ root/ root 用户的家目录
├─ tmp/ 临时文件,重启可能清空
├─ usr/ 系统软件(apt 装的东西多在这)
├─ opt/ 第三方大件软件(自装 JDK 常放 /opt/jdk-17)
├─ www/ 宝塔约定:站点与部署目录(/www/wwwroot)
└─ proc/ 内核暴露的「伪文件」(进程信息等)
一行 ls -l 的完整解剖
-rw-r--r-- 1 www www 52M Oct 6 10:00 backend.jar
│└┬┘└┬┘└┬┘ │ └┬─┘ └┬─┘ └┬┘ └────┬────┘ └───┬───┘
│ │ │ │ │ │ │ │ │ └ 文件名
│ │ │ │ │ │ │ │ └ 修改时间
│ │ │ │ │ │ │ └ 大小
│ │ │ │ │ └────┴ 属组 group(www 组)
│ │ │ │ └ 属主 owner(www 用户)
│ │ │ └ 硬链接数
│ │ └ 其他人 others 的权限:r-- 只读
│ └ 属组 group 的权限:r-- 只读
└ 属主 owner 的权限:rw- 读写;第一位 - 表示普通文件(d=目录,l=软链接)
权限位读法:每三位一组 = r(4) 读 + w(2) 写 + x(1) 执行 。rw- = 4+2 = 6。所以:
数字 权限 典型用途 755 rwxr-xr-x 目录、可执行脚本(自己全权,他人可进可执行) 644 rw-r--r-- 普通文件、JAR 包(自己读写,他人只读) 600 rw------- 私钥、密码文件(只有自己能碰) 640 rw-r----- 生产配置(属主读写,属组只读,其他人无权) 777 rwxrwxrwx 几乎总是错的 ,等于对全世界开门
跟着做
全程在终端敲,建议开个实验目录练习,弄坏了也没负担。
步骤 1:把目录树走一遍 cd / && ls # 根目录长什么样
ls /etc/nginx 2>/dev/null || echo "nginx 未安装,装完再看(包管理课)"
ls /var/log | head -15 # 系统日志区:syslog、auth.log(Ubuntu)
ls ~ # 你的家目录:现在基本是空的
pwd # 确认当前位置
cd - # 回到上一个目录(- 是「刚才那」)
记住两个高频落点:配置在 /etc,日志在 /var/log 。排障时人在这两个目录之间来回跑。
步骤 2:读权限——用 ls -l 解剖真实文件 mkdir -p ~/files-lab && cd ~/files-lab
touch a.txt && echo "hello" > a.txt
ls -l a.txt
# 预期输出:-rw-r--r-- 1 ubuntu ubuntu 6 Oct 6 10:00 a.txt
ls -ld ~/files-lab
# 注意目录的权限位是 rwx 开头:目录的 x = 「能否进入该目录」
读法自查:-rw-r--r-- → 普通文件,属主可读写,属组和其他人只读。drwxr-xr-x → 目录,属主可进可改,其他人可进(x)可列(r)。
步骤 3:chmod 数字法实验 chmod 600 a.txt && ls -l a.txt # → -rw------- 只有属主能读写
chmod 755 a.txt && ls -l a.txt # → -rwxr-xr-x 变成了"可执行"
chmod 644 a.txt && ls -l a.txt # → 回到默认
mkdir subdir && chmod 750 subdir # 目录练习:属主全权、属组只进、他人无权
# 符号法(改局部更直观):u=属主 g=属组 o=其他 a=所有人;+加 -减 =设
chmod u+x a.txt # 给属主加执行位
chmod go-w a.txt # 去掉属组和其他人的写权限
ls -l a.txt subdir
步骤 4:chown——把文件「过户」给服务用户 # 生产场景:宝塔/服务常用 www 用户跑 Nginx 和站点。先建实验用户:
sudo useradd -m -s /bin/bash www || true # 已存在则忽略报错
# 模拟部署目录
sudo mkdir -p /www/wwwroot/demo/{logs,conf}
sudo chown -R www:www /www/wwwroot/demo # -R 递归:属主:属组 一起改
ls -ld /www/wwwroot/demo /www/wwwroot/demo/logs
# 验证「过户」效果:用 www 身份去写
sudo -u www touch /www/wwwroot/demo/logs/test.log # 成功,www 能写 logs
sudo -u www touch /www/wwwroot/demo/conf/x.yml 2>&1 # 能否写取决于 conf 权限设置
sudo -u 用户 命令 = 以指定用户身份执行——这是验证权限最直接的办法,比「猜」强一百倍。
步骤 5:权限拒绝的完整复现与修复(部署实况模拟) # 1) 模拟 root 上传的 JAR,属主是 root
sudo bash -c 'echo fake-jar > /www/wwwroot/demo/backend.jar'
# 2) 用 www 启动服务 → 读取被拒
sudo -u www cat /www/wwwroot/demo/backend.jar
# 预期:cat: /www/wwwroot/demo/backend.jar: Permission denied
# 3) 看属主 → root:root,而其他人对父目录或文件无读权
ls -l /www/wwwroot/demo/backend.jar
# 4) 修复:过户 + 合理权限
sudo chown www:www /www/wwwroot/demo/backend.jar
sudo chmod 644 /www/wwwroot/demo/backend.jar
sudo -u www cat /www/wwwroot/demo/backend.jar # 这次成功,输出 fake-jar
这就是真实部署中「root scp 上传后服务起不来」的标准病因与药方。
步骤 6:文件类型与软链接 ls -l /bin/sh # l 开头:软链接,Ubuntu 里指向 dash
ln -s /www/wwwroot/demo/backend.jar ~/backend-latest # 建软链接(可跨分区,源删了就悬空)
ls -l ~/backend-latest # → l ... backend-latest -> /www/.../backend.jar
cat ~/backend-latest # 通过链接照样读到内容
rm ~/backend-latest # 删链接不影响源文件
软链接在发布策略里很常用:/www/wwwroot/coderoadmap/backend.jar 始终是链接,指向 releases/ 里的真实版本,回滚只需改链接指向。
讲清楚要点
权限挂在文件上,判断发生在进程上 :每个进程带着自己的 uid/gid 运行,访问文件时内核拿进程身份和文件的属主/属组/其他人三组权限位比对。所以「root 能跑、www 起不来」不是玄学,是身份不同。
目录的 x 是「进入权」,w 是「增删权」,r 是「列目录权」 :对目录给 r 不给 x,能看名字进不去;给 x 不给 r,进得去但列不了清单——排查「目录明明 755 还进不去」时往父目录的权限找。
生产权限三件套 :部署目录属主=运行用户(chown -R www:www);JAR/静态资源 644、目录 755;私钥/含密钥配置 600 或 640。任何 777 都是偷懒埋雷 ——上传目录要可写,用属主对齐而不是放开所有人。
-R 递归是双刃剑 :chown -R 对了省事、错了灾难(把系统目录属主改掉会让系统罢工)。敲 -R 前先 pwd 确认自己在哪。
umask 决定「新建文件的默认权限」 :Ubuntu 默认 022 → 文件 644、目录 755。umask 可查可改(写进 ~/.bashrc 持久化),一般不动它。
软链接 vs 硬链接 :软链接是「快捷方式」(有自己的 inode,可指向目录、可跨分区、源删则悬空);硬链接是「同一文件的两个名字」(同一 inode,不能跨分区、不能指目录)。生产 99% 用软链接。
你可能会懵的地方
为什么我明明是 root,文件属主是别人时还会有权限问题?
root 几乎不受权限限制(读、写、chown 都可以),但 执行位为 0 的文件 root 也不能直接执行 ,且挂载了 noexec/NFS root_squash 等特殊选项时另有规则。见 Permission denied 先看 ls -l,别急着怀疑人生。
chmod 777 为什么是错的?正确做法是什么?
777 让任何用户(包括被攻破的低权限进程)都能改你的文件。正确思路是「最小权限」:先问运行服务的用户是谁,chown 把属主给他,再给 644/755。要「特定其他用户也能写」用组(chgrp + 775)。
sudo 到底和权限什么关系?
sudo 是「临时借用特权」的机制,与文件权限互补:普通用户默认改不了 /etc/nginx/nginx.conf,sudo vim /etc/nginx/nginx.conf 是以 root 身份编辑。sudo 详见用户课。
Windows 上写的脚本传到 Linux 报 /bin/bash^M: bad interpreter?
Windows 换行是 \r\n,多出的 \r 让解释器路径变成 /bin/bash^M。修复:sed -i 's/\r$//' script.sh 或用编辑器转为 LF 换行。
磁盘没满但「No space left on device」?
可能是 inode 耗尽:海量小文件把 inode 表占满了。用 df -i 查 IUse%,通常是大目录里塞了几十万个小文件(如碎片化缓存),找到并清理即可。
错法对照 错法 问题 正解 遇到 Permission denied 就 chmod 777 安全洞,且掩盖真正的属主问题 先 ls -l 看属主,chown 对齐运行用户 root 上传 JAR 后直接启动服务 服务用户读不了:Permission denied 上传后 chown www:www + chmod 644 chown -R 在 / 或 /usr 上误敲系统文件属主被改,命令报错成片 敲 -R 前先 pwd;养成「先 ls 验证路径」习惯 日志目录给了 644 目录没有 x,进程进不去写不了 目录至少 755(或 750+同组),文件 644 私钥权限 644 还疑惑 SSH 拒用 ssh 检查到私钥过宽直接拒绝 chmod 600 ~/.ssh/id_ed25519(SSH 课详解)用 rm -rf 清理时路径手误多一个空格 rm -rf / www 级灾难rm 前 ls 确认目标;重要目录用绝对路径且不加尾随空格
卡点急救
服务起不来,日志报 Permission denied :三步定位——① 确认运行用户(systemd 单元里的 User= 或 ps 的第一列);② ls -l 看目标文件/目录属主和权限位;③ sudo -u 运行用户 cat 文件(读)/ touch 目录/test(写)复现验证,然后 chown/chmod 修复。
chmod 报 Operation not permitted :只有文件属主或 root 能改权限。加 sudo;若连 sudo 都不行,检查是否在只读挂载上(mount | grep ro)。
删文件报 Permission denied 但文件是 666 :删除/创建取决于所在目录 的 w+x 权限,不是文件本身的权限。给父目录对运行用户开 w。
误把家目录或系统目录 chmod 错了 :单文件误改直接改回来;大范围误改且无备份时,Ubuntu 可参考同版本正常机器的权限逐层修复,极端情况重装比修复快——所以 -R 之前先备份属主信息:ls -lR > /tmp/perm.bak。
面试常问
Linux 文件权限 755、644 分别代表什么?
三组 rwx,r=4/w=2/x=1。755 = rwxr-xr-x,属主全权、属组与他人可读可执行,常用于目录和脚本;644 = rw-r--r--,属主读写、他人只读,常用于普通文件。生产原则:最小权限,杜绝 777。
软链接和硬链接的区别?
软链接存的是路径,类似快捷方式:可指向目录、可跨文件系统、删除源后悬空;硬链接是同一 inode 的多个目录项:不能跨文件系统、不能对目录建立,删掉任一名字文件仍存在(直到链接数归零)。日常发布回滚用软链接。
如何排查一个进程无法读写某文件?
先确定进程的运行身份(ps aux | grep 进程 第一列或 systemd User=),再看目标路径的属主与权限位(ls -l,含每一级父目录),用 sudo -u 该用户 复现验证;必要时 namei -l 路径 一次性列出整条路径各层的权限。
/etc、/var、/opt 各放什么?
/etc:系统与应用的文本配置(nginx、systemd、fstab);/var:可变数据,重点是 /var/log 日志与 /var/lib 应用数据(如 MySQL);/opt:第三方独立软件(自装 JDK、ES 等)。这是 FHS(文件系统层次标准)的约定,跨发行版通用。
为什么服务要用专用低权限用户跑,而不用 root?
最小权限原则:服务被入侵或出现 RCE 漏洞时,攻击者拿到的只是该低权用户的能力,写不了系统配置、读不了他人数据;root 进程出漏洞等于整机沦陷。所以 Nginx/Java 服务各用专门用户,配合 systemd 的 User= 指定。
自测三问
-rwxr-x--- 对应的数字是多少?属组用户能对它做什么?
服务用户 www 写日志报 Permission denied,日志目录是 /www/wwwroot/app/logs,写出你的排查命令序列。
目录的 x 权限和 r 权限分别控制什么?为什么删除一个 666 的文件仍可能被拒?
小结一句话 Linux 用「属主/属组/其他人 × 读/写/执行」三组权限位管住每个文件,判断者是以某个用户身份运行的进程——部署排障时永远先问:这个进程是谁,它碰的文件归谁,权限位给了它什么 。
延伸