一句话总结:内核管硬件、Shell 管命令——Linux 启动的每一步、你敲的每条命令,都能在这套结构里找到位置。
环境:Rocky Linux / CentOS 9,bash。
一、Linux 系统构成(功能维度)
操作系统从运行空间上大致分为内核空间和用户空间。用户空间跑应用和系统库,内核空间跑内核。两者之间通过系统调用接口交互:应用通过系统库发起系统调用,CPU 从用户态切换到内核态,内核处理请求——可能是操作硬件,也可能是管理进程、内存、文件系统等——处理完再返回用户态。用户态和内核态是 CPU 的特权级状态,系统调用、中断、异常是进入内核态的入口。
从功能上看,系统分成两大部分:
| 部分 | 职责 | 举例 |
|---|---|---|
| 内核(kernel) | 和下层硬件打交道:进程调度、内存管理、文件系统、网络协议栈、设备驱动 | 分配 CPU 时间片、把文件读到内存 |
| 系统调用接口(syscall) | 和上层应用程序打交道:给程序提供“请求内核干活”的入口 | open()、read()、write()、fork() |
由此产生两个“态”:
用户态和内核态是 CPU 的两种特权级别,不是接口。
用户态运行应用,内核态运行内核和驱动。
系统调用接口是用户态进入内核的通道。
硬件由内核态管理,应用通过系统调用请求内核服务。
| 状态 | 运行什么代码 | 权限 | 说明 |
|---|---|---|---|
| 用户态(user mode) | 应用程序、Shell、系统调用接口代码 | 受限,不能直接碰硬件 | 你敲的命令绝大多数在这里跑 |
| 内核态(kernel mode) | 内核代码 | 最高权限 | 只有陷入内核(系统调用、中断)才进入 |
一次
cat /etc/passwd的路径:bash(用户态)→open()/read()系统调用 → 内核态读磁盘 → 把数据拷回用户态 → 打印。
二、文件维度:bootfs 与 rootfs
操作系统来自 ISO 镜像,里面是一堆系统文件,分两大类:
| 组成 | 阶段 | 内容 |
|---|---|---|
| bootfs | 系统启动前 | bootloader 程序(写在硬盘第一个扇区 MBR,512B)、内核文件 vmlinuz、initramfs |
| rootfs | 系统启动后 | 本质就是一堆文件夹和文件:/etc、/usr、/var…… |
BIOS 固定只找第一个扇区;后续文件怎么找,是 bootloader 的事。
三、进程与程序
| 概念 | 定义 | 特点 |
|---|---|---|
| 程序 | 一个或一系列代码文件 | 静态,躺在硬盘上 |
| 进程 | 程序运行的过程 | 动态,占用 CPU、内存、文件描述符 |
操作系统把硬盘里的程序读进内存,再让 CPU 执行指令,程序才“跑起来”变成进程。
进程是操作系统最核心的概念——CPU 调度、内存分配、权限控制,全是围着进程转的。
ps aux # 看所有进程
ps -ef | grep sshd
top # 动态看资源占用
kill -9 PID # 强杀
四、启动流程(BIOS → 内核 → systemd)
加电
└─> BIOS/UEFI 自检,找启动盘(固定读第一个扇区)
└─> MBR/GPT 里的 bootloader(GRUB2)
└─> 加载内核 vmlinuz + initramfs 到内存
└─> 内核初始化硬件、挂载根文件系统
└─> 启动第一个用户态进程 systemd(PID 1)
└─> 按 default.target 拉起各项服务
进程的“族谱”:
| PID | 进程 | 作用 |
|---|---|---|
| 0 | swapper / idle | 内核创建的调度进程,空闲时占着 CPU |
| 1 | systemd(旧版 init) | 所有用户态进程的祖先,管服务、管挂载、管登录 |
| 2 | kthreadd | 所有内核线程的祖先 |
pstree -p | head # 看进程树
ps -p 1 -o pid,comm # 确认 1 号进程
五、运行级别与 target
| 运行级别 | systemd target | 含义 |
|---|---|---|
| 0 | poweroff.target | 关机 |
| 1 | rescue.target | 单用户救援 |
| 2 | multi-user.target | 多用户,无 NFS |
| 3 | multi-user.target | 字符终端(默认,进入 bash) |
| 4 | — | 未使用 |
| 5 | graphical.target | 图形界面(需装桌面) |
| 6 | reboot.target | 重启 |
systemctl get-default # 查看默认
systemctl set-default multi-user.target # 永久切字符界面
systemctl set-default graphical.target # 永久切图形界面
init 3 / init 5 / init 0 / init 6 # 临时切换(老写法)
yum -y group install "Server with GUI" # 装图形桌面
六、Shell 是什么
Shell = 命令解释器,翻译成“壳”,表达了对系统调用封装的思想:你在壳里敲命令,它替你去调内核。
- 启动级别为 3 时,系统登录后给你一个铺满屏幕的字符终端,里面跑的就是 shell。
- 常见种类:
bash(默认)、sh、zsh、ksh、csh。 - 查看当前 shell:
echo $SHELL、ps -p $$。
配置文件(登录时读哪些):
| 文件 | 作用 |
|---|---|
/etc/profile |
全局,登录 shell 读 |
/etc/bashrc |
全局,每次开 bash 读 |
~/.bash_profile |
用户级,登录时读(常会再 source ~/.bashrc) |
~/.bashrc |
用户级,每次开新 shell 读,别名一般写这里 |
~/.bash_logout |
退出时执行 |
source ~/.bashrc # 改完立即生效,等价于 . ~/.bashrc
七、命令的三段式与查找优先级
ls -l /boot
│ │ └── 参数:操作目标
│ └───── 选项:控制怎么做(可省略)
└──────── 命令:做什么
同一个名字可能有很多来源,bash 按固定顺序找:
| 顺序 | 类型 | 例子 | 说明 |
|---|---|---|---|
| 0 | 带 / 的路径 |
/bin/ls、./a.sh |
指名道姓,直接执行,不查其他 |
| 1 | 别名 alias | alias ll='ls -l' |
最先展开 |
| 2 | 函数 function | myfunc(){ ... } |
shell 函数 |
| 3 | 内建命令 builtin | cd、echo、type |
bash 自带,不需要外部文件 |
| 4 | 哈希缓存 hash | hash |
之前找到过的外部命令路径缓存 |
| 5 | PATH 里的外部命令 | /usr/bin/ls |
从左到右找目录 |
| 6 | 找不到 | — | command not found |
type ls # 看它到底是什么:别名/内建/文件
type -a ls # 所有可能来源
which ls # 只找 PATH 里的可执行文件
whereis ls
hash -r # 清空哈希缓存(换了新版本命令后常用)
八、各种“命令”的写法
# 别名:临时生效,写进 ~/.bashrc 才永久
alias ll='ls -lh'
unalias ll
# 函数
f() { echo 111; echo 222; }
f
# 复合命令
for ((i=0;i<3;i++)); do echo 66666; done
# 内建命令
type cd pwd type
九、bash 快捷键与历史
| 快捷键 | 作用 |
|---|---|
Ctrl+C |
强制终止当前命令 |
Ctrl+L |
清屏(等于 clear) |
Ctrl+A / Ctrl+E |
光标跳到行首 / 行尾 |
Ctrl+U / Ctrl+K |
删到行首 / 删到行尾 |
Ctrl+R |
反向搜索历史命令 |
↑ ↓ |
翻历史命令 |
Tab |
补全命令/路径(按两次列出候选) |
history # 带编号的历史
!123 # 执行第 123 条
!! # 上一条命令
!$ # 上一条命令的最后一个参数
history -c # 清空历史
十、环境变量与 PATH
环境变量是进程运行环境的配置字典:路径、语言、代理、编辑器参数都靠它,让程序不改代码就能适配环境。
| 命令 | 作用 |
|---|---|
env / set |
查看环境变量 / 所有变量 |
echo $PATH |
查看 PATH |
export NAME=value |
定义并导出,对当前进程及子进程生效 |
unset NAME |
删除变量 |
export PATH="$PATH:$HOME/bin" # 让 shell 额外去 ~/bin 找命令
echo $PATH # 冒号分隔的一堆目录
永久生效:写进 ~/.bashrc(用户)或 /etc/profile.d/xxx.sh(全局)。
十一、Shell 脚本入门
脚本 = 把完成某个任务的一串命令放进一个文件。
#!/bin/bash
# 第一行 shebang 指定解释器
echo "hello"
ls -l /tmp
chmod +x mmm # 赋予执行权限
./mmm # 用相对路径执行
想让 mmm 在任何目录都能直接敲,两种办法:
- 把脚本放进 PATH 里的某个目录(如
~/bin、/usr/local/bin); - 把脚本所在目录加进 PATH。
十二、常用系统命令
| 命令 | 作用 | 例子 |
|---|---|---|
hostname / hostnamectl |
查看 / 永久设置主机名 | hostnamectl set-hostname test |
date |
时间 | date "+%F %T"、date -s "2024-04-15 11:11:11" |
timedatectl |
时区 / NTP | timedatectl set-timezone Asia/Shanghai |
ip a / ifconfig |
看网卡和 IP | ip a show ens33 |
nmcli |
NetworkManager | nmcli conn up ens33 |
shutdown |
关机/重启 | shutdown -h +10、shutdown -c |
reboot / init 6 |
重启 | |
uname -a |
内核与架构 | uname -m、uname -r |
cat /etc/redhat-release |
系统版本 | |
man ls / ls --help |
帮助 |
集群里时间一致非常重要:所有机器都向 NTP 服务器对时(
chronyd)。生产环境时间漂移会导致日志对不上、证书校验失败、数据库主从异常。
速查表
systemctl get-default
systemctl set-default multi-user.target
hostnamectl set-hostname test
ip a; nmcli conn up ens33
date "+%F %T"; timedatectl
uname -a; cat /etc/redhat-release
type -a ls; which ls; hash -r
alias ll='ls -lh'; unalias ll
export PATH="$PATH:$HOME/bin"
echo $SHELL; ps -p $$
history | tail; !$
systemctl status sshd; systemctl restart sshd
常见坑
alias只在当前 shell 有效,想永久必须写进~/.bashrc再source。- 改
~/.bashrc后不source,新开窗口才生效。 ./script.sh和script.sh不一样:不带./会去 PATH 里找。- 脚本没有
chmod +x就执行会报Permission denied。 export只影响当前进程及其子进程,不影响父进程。- 误改 PATH 导致命令全找不到时,用绝对路径
/bin/ls先救急,再改回配置。 history -c清空后,~/.bash_history里可能还留着(退出时才写入)。
练习(15 题)
- 内核和系统调用接口分别负责和谁打交道?
- 用户态和内核态最本质的区别是什么?
- bootfs 和 rootfs 各包含什么?
- 写出 Linux 从加电到 systemd 启动的主要步骤。
- PID 1 和 PID 2 分别是什么进程,各自负责什么?
- 把系统默认启动方式永久改成字符终端,写出命令。
- 查看当前系统默认 target 和上次启动耗时。
- 当前 shell 是哪种?用什么命令查看?
- 一条命令同时显示
ls是别名、函数还是外部命令。 - 清除 bash 的命令哈希缓存,写命令并说明为什么要清。
!$和!!分别代表什么?- 把
~/bin加入 PATH,并且只对当前会话生效。 - 写一个最简单的 shell 脚本并让它能执行。
- 查看主机名、系统版本、内核版本,各一条命令。
- 集群为什么必须保证时间一致?用什么服务对时?
答案与解析
1.
系统调用接口负责和上层应用程序打交道,内核负责和下层硬件打交道。
2.
用户态代码权限受限、不能直接操作硬件,只能通过系统调用请求内核;内核态拥有最高权限,可以直接访问硬件和所有内存。
3.
bootfs 在系统启动前使用,包含 bootloader、内核 vmlinuz、initramfs;rootfs 是启动后挂载的根文件系统,就是
/etc、/usr、/var这些目录和文件。
4.
加电 → BIOS/UEFI 自检并找启动盘 → 读取第一个扇区的 bootloader(GRUB2)→ 加载内核和 initramfs → 内核初始化硬件并挂载根文件系统 → 启动 systemd(PID 1)→ 按 default.target 拉起服务。
5.
PID 1 是 systemd(旧系统是 init),所有用户态进程的祖先,负责服务管理、挂载、登录;PID 2 是 kthreadd,所有内核线程的祖先。PID 0 是内核的 swapper/idle 进程。
6.
systemctl set-default multi-user.target
7.
systemctl get-default
systemd-analyze
systemd-analyze还能看到内核、initrd、用户空间各阶段耗时。
8.
echo $SHELL
ps -p $$
$SHELL是登录 shell 的路径,$$是当前 shell 的 PID。
9.
type -a ls
它会按别名、函数、内建、PATH 的顺序全部列出来。
10.
hash -r
哈希缓存记录了命令的绝对路径;换了新版本、改了 PATH 或删了旧命令后,缓存会导致仍执行旧路径,需要清掉。
11.
!!执行上一条命令;!$取上一条命令的最后一个参数,例如上一条是ls /etc,cat !$就等于cat /etc。
12.
export PATH="$PATH:$HOME/bin"
只影响当前 shell 和它的子进程,关掉窗口就失效。
13.
cat > ~/hello.sh <<'EOF'
#!/bin/bash
echo "hello"
EOF
chmod +x ~/hello.sh
~/hello.sh
14.
hostname
cat /etc/redhat-release
uname -r
15.
时间不一致会让日志对不上、证书校验失败、数据库主从复制异常、定时任务错乱。用
chronyd(NTP 客户端)向时间服务器同步,timedatectl set-ntp yes开启。