Linux 系统编程 · 第 1 章:Linux 系统概述
本章涵盖 Linux 内核、Shell 与文件系统三大核心主题,配合代码示例深入理解底层机制。
目录
1. Linux 内核(Kernel)
1.1 内核概述
Linux 内核是操作系统的核心,运行在内核态(Kernel Space),负责管理硬件资源并向上层提供系统调用接口。
┌─────────────────────────────────────────┐
│ 用户应用程序 (User Space) │
│ Shell / Python / C程序 / 浏览器 ... │
├─────────────────────────────────────────┤
│ 系统调用接口 (System Call API) │ ← 用户态 / 内核态 边界
├─────────────────────────────────────────┤
│ Linux 内核 (Kernel) │
│ ┌──────────┐ ┌──────────┐ ┌─────────┐ │
│ │ 进程管理 │ │ 内存管理 │ │文件系统 │ │
│ └──────────┘ └──────────┘ └─────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌─────────┐ │
│ │ 网络子系统│ │ 设备驱动 │ │安全模块 │ │
│ └──────────┘ └──────────┘ └─────────┘ │
├─────────────────────────────────────────┤
│ 硬件 (Hardware) │
│ CPU / 内存 / 磁盘 / 网卡 ... │
└─────────────────────────────────────────┘
1.2 内核的主要功能模块
| 模块 | 职责 |
|---|---|
| 进程管理 | 进程创建、调度、销毁、进程间通信(IPC) |
| 内存管理 | 虚拟内存、分页、内存分配与回收 |
| 文件系统 | VFS 抽象层、ext4/xfs/tmpfs 等具体实现 |
| 网络子系统 | TCP/IP 协议栈、套接字接口 |
| 设备驱动 | 字符设备、块设备、网络设备驱动 |
| 安全模块 | SELinux、AppArmor、Capabilities |
1.3 用户态与内核态
程序运行在两种特权级别:
-
用户态(User Mode):受限访问,不能直接操作硬件
-
内核态(Kernel Mode):完全访问权限,可操作所有硬件资源
用户程序通过**系统调用(syscall)**陷入内核态执行特权操作。
/* 示例:通过系统调用写入数据
* write() 是 C 库封装,底层触发 sys_write 系统调用
* 执行流程:用户态 → 软中断/syscall指令 → 内核态 → 返回用户态
*/
#include <unistd.h>
#include <stdio.h>
int main(void) {
const char *msg = "Hello, Linux Kernel!\n";
/* write(fd, buf, count)
* fd=1 表示标准输出(STDOUT_FILENO)
* 返回实际写入的字节数,-1 表示出错
*/
ssize_t n = write(STDOUT_FILENO, msg, 21);
if (n == -1) {
perror("write"); /* perror 打印 errno 对应的错误信息 */
return 1;
}
printf("写入了 %zd 字节\n", n);
return 0;
}
编译与运行:
gcc -o hello_kernel hello_kernel.c
./hello_kernel
# 输出:
# Hello, Linux Kernel!
# 写入了 21 字节
使用 strace 追踪系统调用:
strace ./hello_kernel 2>&1 | grep write
# 输出类似:
# write(1, "Hello, Linux Kernel!\n", 21) = 21
💡
strace是调试利器,可以看到程序实际触发了哪些系统调用及其参数和返回值。
1.4 查看内核信息
# 查看内核版本
uname -r
# 示例输出:5.15.0-91-generic
# 查看完整系统信息
uname -a
# 示例输出:Linux hostname 5.15.0-91-generic #101-Ubuntu SMP x86_64 GNU/Linux
# 查看内核启动日志(需要权限)
dmesg | head -20
# 查看 /proc 虚拟文件系统中的内核信息
cat /proc/version # 内核版本
cat /proc/cpuinfo # CPU 信息
cat /proc/meminfo # 内存信息
cat /proc/sys/kernel/pid_max # 最大 PID 数
1.5 系统调用机制详解
/* 直接使用 syscall() 函数调用系统调用(绕过 C 库封装)
* 用于理解底层机制,实际开发中使用 C 库封装函数
*/
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
/* SYS_getpid 是系统调用号,对应内核中的 sys_getpid()
* 在 x86_64 上,getpid 的系统调用号为 39
*/
pid_t pid = (pid_t)syscall(SYS_getpid);
printf("当前进程 PID(通过 syscall): %d\n", pid);
/* 对比:使用 C 库封装的 getpid() */
printf("当前进程 PID(通过 libc): %d\n", getpid());
/* 两者结果相同,但 libc 版本有缓存优化 */
return 0;
}
gcc -o syscall_demo syscall_demo.c
./syscall_demo
# 输出:
# 当前进程 PID(通过 syscall): 12345
# 当前进程 PID(通过 libc): 12345
2. Shell
2.1 Shell 概述
Shell 是用户与内核之间的命令解释器,既是交互式命令行工具,也是脚本编程语言。
用户输入命令
│
▼
┌─────────────┐
│ Shell │ ← 解析命令、展开变量、处理重定向
│ (bash/zsh) │
└──────┬──────┘
│ fork() + exec()
▼
┌─────────────┐
│ 子进程执行 │ ← 实际运行 ls、grep、python 等程序
│ 外部命令 │
└──────┬──────┘
│ wait() 等待子进程
▼
返回结果给用户
2.2 常见 Shell 类型
| Shell | 路径 | 特点 |
|---|---|---|
bash |
/bin/bash |
最常用,兼容 POSIX,功能丰富 |
sh |
/bin/sh |
POSIX 标准 Shell,轻量 |
zsh |
/bin/zsh |
功能强大,macOS 默认 |
dash |
/bin/dash |
极轻量,Ubuntu 默认 /bin/sh |
fish |
/usr/bin/fish |
用户友好,语法不兼容 POSIX |
# 查看当前使用的 Shell
echo $SHELL # 登录 Shell
echo $0 # 当前 Shell 进程名
# 查看系统中所有可用 Shell
cat /etc/shells
# 查看 bash 版本
bash --version
2.3 Shell 变量与环境变量
#!/bin/bash
# 文件名:variables_demo.sh
# ── 1. 普通变量(局部于当前 Shell)──────────────────────
name="Linux"
version=5.15
echo "系统: $name, 内核版本: $version"
# 注意:= 两侧不能有空格!
# name = "Linux" ← 错误写法
# ── 2. 只读变量 ──────────────────────────────────────────
readonly MAX_RETRY=3
echo "最大重试次数: $MAX_RETRY"
# MAX_RETRY=5 ← 会报错:readonly variable
# ── 3. 环境变量(可被子进程继承)────────────────────────
export APP_ENV="production"
export DB_HOST="localhost"
# 验证子进程能否看到环境变量
bash -c 'echo "子进程看到 APP_ENV=$APP_ENV"'
# ── 4. 特殊变量 ──────────────────────────────────────────
echo "脚本名称: $0"
echo "第一个参数: $1"
echo "参数个数: $#"
echo "所有参数: $@"
echo "上条命令退出码: $?"
echo "当前进程 PID: $$"
echo "后台最后进程PID: $!"
chmod +x variables_demo.sh
./variables_demo.sh arg1 arg2
2.4 Shell 控制流
#!/bin/bash
# 文件名:control_flow.sh
# ── 1. 条件判断 ──────────────────────────────────────────
FILE="/etc/passwd"
if [ -f "$FILE" ]; then
echo "$FILE 存在且是普通文件"
elif [ -d "$FILE" ]; then
echo "$FILE 是目录"
else
echo "$FILE 不存在"
fi
# 常用文件测试操作符:
# -f 普通文件 -d 目录 -e 存在
# -r 可读 -w 可写 -x 可执行
# -s 非空文件 -L 符号链接
# ── 2. 字符串比较 ─────────────────────────────────────────
OS=$(uname -s)
case "$OS" in
Linux)
echo "运行在 Linux 系统上"
;;
Darwin)
echo "运行在 macOS 系统上"
;;
*)
echo "未知系统: $OS"
;;
esac
# ── 3. 循环 ───────────────────────────────────────────────
echo "--- for 循环遍历 /etc 下的配置文件 ---"
for conf in /etc/*.conf; do
# basename 去掉路径,只保留文件名
echo " 配置文件: $(basename "$conf")"
done
echo "--- while 循环读取文件内容 ---"
line_num=1
while IFS= read -r line; do
echo " 第${line_num}行: $line"
((line_num++))
[ $line_num -gt 3 ] && break # 只读前3行
done < /etc/os-release
2.5 Shell 函数与错误处理
#!/bin/bash
# 文件名:functions_demo.sh
# ── 严格模式(推荐在脚本开头加上)────────────────────────
set -euo pipefail
# -e 遇到错误立即退出
# -u 使用未定义变量时报错
# -o pipefail 管道中任一命令失败则整体失败
# ── 函数定义 ──────────────────────────────────────────────
# 检查命令是否存在
check_command() {
local cmd="$1" # local 声明局部变量,避免污染全局
if command -v "$cmd" &>/dev/null; then
echo "✓ $cmd 已安装: $(command -v "$cmd")"
return 0 # 返回值 0 表示成功
else
echo "✗ $cmd 未安装"
return 1 # 非零返回值表示失败
fi
}
# 获取文件大小(带单位)
get_file_size() {
local file="$1"
if [ ! -f "$file" ]; then
echo "错误: 文件不存在 '$file'" >&2 # 错误信息输出到 stderr
return 1
fi
# du -sh 以人类可读格式显示大小
du -sh "$file" | awk '{print $1}'
}
# ── 调用函数 ──────────────────────────────────────────────
check_command "bash"
check_command "python3"
check_command "nonexistent_tool" || echo "(上面的错误是预期的)"
size=$(get_file_size "/etc/passwd")
echo "/etc/passwd 大小: $size"
# ── 陷阱(trap):捕获信号和退出事件 ─────────────────────
cleanup() {
echo "脚本退出,执行清理操作..."
# 删除临时文件等清理工作
}
trap cleanup EXIT # 脚本退出时执行 cleanup
trap 'echo "收到 Ctrl+C"' INT # 捕获 SIGINT 信号
2.6 管道与重定向
# ── 重定向 ────────────────────────────────────────────────
# 标准输入(0)、标准输出(1)、标准错误(2)
# 输出重定向(覆盖)
echo "Hello" > output.txt
# 输出重定向(追加)
echo "World" >> output.txt
# 错误重定向
ls /nonexistent 2> error.log
# 同时重定向 stdout 和 stderr 到同一文件
ls /etc /nonexistent > all.log 2>&1
# 丢弃输出(/dev/null 是"黑洞")
command_with_noise > /dev/null 2>&1
# 输入重定向
sort < unsorted.txt > sorted.txt
# Here Document(内嵌多行文本)
cat << 'EOF' > config.txt
HOST=localhost
PORT=8080
DEBUG=false
EOF
# ── 管道(pipe)─────────────────────────────────────────
# 将前一个命令的 stdout 连接到后一个命令的 stdin
# 统计 /etc 目录下文件数量
ls /etc | wc -l
# 查找占用内存最多的前5个进程
ps aux --sort=-%mem | head -6
# 实时监控日志中的错误
tail -f /var/log/syslog | grep --line-buffered "ERROR"
# 复杂管道:统计 /etc 下各类型文件数量
find /etc -maxdepth 1 -type f -name "*.conf" 2>/dev/null \
| xargs wc -l 2>/dev/null \
| sort -rn \
| head -10
3. 文件系统结构
3.1 FHS 目录层次标准
Linux 遵循 FHS(Filesystem Hierarchy Standard,文件系统层次标准) ,所有文件从根目录 / 开始组织。
/ ← 根目录(所有文件的起点)
├── bin → /usr/bin ← 基本用户命令(ls, cp, mv...)
├── sbin → /usr/sbin ← 系统管理命令(fdisk, ifconfig...)
├── boot ← 启动文件(内核镜像、GRUB)
│ ├── vmlinuz-5.15.0 ← 压缩的内核镜像
│ └── grub/ ← GRUB 引导程序配置
├── dev ← 设备文件(字符/块设备)
│ ├── sda ← 第一块 SATA 硬盘
│ ├── tty0 ← 终端设备
│ ├── null ← 空设备(黑洞)
│ └── random ← 随机数生成器
├── etc ← 系统配置文件
│ ├── passwd ← 用户账户信息
│ ├── fstab ← 文件系统挂载表
│ └── hosts ← 主机名解析
├── home ← 普通用户主目录
│ └── alice/ ← 用户 alice 的家目录
├── lib → /usr/lib ← 共享库文件(.so)
├── media ← 可移动媒体挂载点(U盘、光盘)
├── mnt ← 临时挂载点
├── opt ← 第三方软件安装目录
├── proc ← 进程和内核信息虚拟文件系统
│ ├── 1/ ← PID=1 的进程信息(init/systemd)
│ ├── cpuinfo ← CPU 信息
│ └── meminfo ← 内存信息
├── root ← root 用户的主目录
├── run ← 运行时数据(PID文件、套接字)
├── srv ← 服务数据(Web、FTP)
├── sys ← 内核设备树虚拟文件系统(sysfs)
├── tmp ← 临时文件(重启后清空)
├── usr ← 用户程序和数据
│ ├── bin/ ← 用户命令
│ ├── include/ ← C/C++ 头文件
│ ├── lib/ ← 库文件
│ └── share/ ← 架构无关数据
└── var ← 可变数据
├── log/ ← 日志文件
├── spool/ ← 打印队列、邮件队列
└── tmp/ ← 持久临时文件
3.2 重要目录详解
/proc --- 进程与内核信息虚拟文件系统
/proc 不是真实的磁盘文件系统,而是内核在内存中动态生成的虚拟文件系统。
# 查看当前进程信息
PID=$$ # 当前 Shell 的 PID
echo "当前 Shell PID: $PID"
ls /proc/$PID/
# 输出:cmdline cwd environ exe fd maps mem stat status ...
# 查看进程命令行
cat /proc/$PID/cmdline | tr '\0' ' '
# 查看进程内存映射
cat /proc/$PID/maps | head -10
# 查看进程打开的文件描述符
ls -la /proc/$PID/fd
# 查看进程状态
cat /proc/$PID/status | grep -E "Name|Pid|VmRSS|Threads"
/* 用 C 程序读取 /proc/meminfo 获取内存信息 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
FILE *fp = fopen("/proc/meminfo", "r");
if (!fp) {
perror("fopen /proc/meminfo");
return 1;
}
char line[256];
int count = 0;
printf("=== 内存信息 ===\n");
while (fgets(line, sizeof(line), fp) && count < 5) {
/* 只打印前5行关键信息 */
printf("%s", line);
count++;
}
fclose(fp);
return 0;
}
gcc -o read_meminfo read_meminfo.c
./read_meminfo
# 输出示例:
# === 内存信息 ===
# MemTotal: 16384000 kB
# MemFree: 8192000 kB
# MemAvailable: 10240000 kB
# Buffers: 512000 kB
# Cached: 2048000 kB
/dev --- 设备文件
Linux 中"一切皆文件",硬件设备也以文件形式存在于 /dev。
# 查看块设备
lsblk
ls -la /dev/sd* # SATA/SCSI 磁盘
ls -la /dev/nvme* # NVMe 固态硬盘
# 特殊设备
# /dev/null - 丢弃所有写入,读取返回 EOF
# /dev/zero - 读取返回无限的 0 字节
# /dev/random - 阻塞式随机数(熵池)
# /dev/urandom - 非阻塞随机数
# 用 /dev/zero 创建指定大小的空文件
dd if=/dev/zero of=test_1MB.bin bs=1M count=1
ls -lh test_1MB.bin # 输出:-rw-r--r-- 1 user group 1.0M ...
# 生成随机密码(16字节,base64编码)
dd if=/dev/urandom bs=16 count=1 2>/dev/null | base64
/* 演示设备文件的读写操作 */
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(void) {
unsigned char buf[16];
int fd;
/* 从 /dev/urandom 读取随机字节 */
fd = open("/dev/urandom", O_RDONLY);
if (fd == -1) {
perror("open /dev/urandom");
return 1;
}
ssize_t n = read(fd, buf, sizeof(buf));
close(fd);
if (n != sizeof(buf)) {
fprintf(stderr, "读取不完整\n");
return 1;
}
printf("随机字节(十六进制): ");
for (int i = 0; i < (int)n; i++) {
printf("%02x ", buf[i]);
}
printf("\n");
return 0;
}
3.3 文件类型
Linux 有 7 种文件类型,用 ls -l 第一个字符标识:
| 标识符 | 类型 | 说明 |
|---|---|---|
- |
普通文件 | 文本、二进制、可执行文件 |
d |
目录 | 包含其他文件的容器 |
l |
符号链接 | 指向另一个文件的快捷方式 |
c |
字符设备 | 按字节流访问(终端、串口) |
b |
块设备 | 按块访问(硬盘、U盘) |
p |
命名管道 | FIFO,进程间通信 |
s |
套接字 | 网络/本地进程间通信 |
# 查看各种文件类型示例
ls -la /dev/null # c --- 字符设备
ls -la /dev/sda # b --- 块设备(如果存在)
ls -la /bin # l --- 符号链接(指向 /usr/bin)
ls -la /tmp # d --- 目录
ls -la /etc/passwd # - --- 普通文件
# 用 file 命令识别文件类型(不依赖扩展名)
file /bin/bash # ELF 64-bit LSB pie executable
file /etc/passwd # ASCII text
file /usr/lib/x86_64-linux-gnu/libc.so.6 # ELF shared object
/* 使用 stat() 系统调用获取文件元数据 */
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
/* 根据 st_mode 返回文件类型字符串 */
const char *file_type(mode_t mode) {
if (S_ISREG(mode)) return "普通文件";
if (S_ISDIR(mode)) return "目录";
if (S_ISLNK(mode)) return "符号链接";
if (S_ISCHR(mode)) return "字符设备";
if (S_ISBLK(mode)) return "块设备";
if (S_ISFIFO(mode)) return "命名管道";
if (S_ISSOCK(mode)) return "套接字";
return "未知类型";
}
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "用法: %s <文件路径>\n", argv[0]);
return 1;
}
struct stat st;
/* lstat 不跟随符号链接,stat 会跟随 */
if (lstat(argv[1], &st) == -1) {
perror("lstat");
return 1;
}
printf("文件路径: %s\n", argv[1]);
printf("文件类型: %s\n", file_type(st.st_mode));
printf("文件大小: %ld 字节\n", (long)st.st_size);
printf("硬链接数: %lu\n", (unsigned long)st.st_nlink);
printf("所有者 UID: %u\n", st.st_uid);
printf("所属组 GID: %u\n", st.st_gid);
printf("inode 号: %lu\n", (unsigned long)st.st_ino);
/* 格式化时间 */
char timebuf[64];
struct tm *tm_info = localtime(&st.st_mtime);
strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm_info);
printf("修改时间: %s\n", timebuf);
/* 打印权限位(rwxrwxrwx 格式) */
printf("权限: %c%c%c%c%c%c%c%c%c\n",
(st.st_mode & S_IRUSR) ? 'r' : '-',
(st.st_mode & S_IWUSR) ? 'w' : '-',
(st.st_mode & S_IXUSR) ? 'x' : '-',
(st.st_mode & S_IRGRP) ? 'r' : '-',
(st.st_mode & S_IWGRP) ? 'w' : '-',
(st.st_mode & S_IXGRP) ? 'x' : '-',
(st.st_mode & S_IROTH) ? 'r' : '-',
(st.st_mode & S_IWOTH) ? 'w' : '-',
(st.st_mode & S_IXOTH) ? 'x' : '-');
return 0;
}
gcc -o file_info file_info.c
./file_info /etc/passwd
# 输出示例:
# 文件路径: /etc/passwd
# 文件类型: 普通文件
# 文件大小: 2847 字节
# 硬链接数: 1
# 所有者 UID: 0
# 所属组 GID: 0
# inode 号: 131073
# 修改时间: 2024-01-15 10:30:00
# 权限: rw-r--r--
3.4 文件权限与 inode
权限模型
权限字符串示例:-rwxr-xr--
│││││││││└── 其他用户:只读
││││││└──── 所属组:读+执行
│││└─────── 所有者:读+写+执行
└────────── 文件类型:- 普通文件
数字表示:
r = 4, w = 2, x = 1
rwxr-xr-- = 754
# 修改权限
chmod 755 script.sh # 数字方式
chmod u+x,g-w,o-x file.txt # 符号方式
# 修改所有者
chown alice:developers file.txt
# 查看权限(详细)
stat /etc/passwd
ls -l /etc/passwd
# umask:新建文件的默认权限掩码
umask # 查看当前 umask(通常是 022)
umask 027 # 设置 umask(新文件权限 = 666 & ~027 = 640)
inode 机制
/* 演示硬链接与 inode 的关系 */
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void) {
/* 创建一个文件 */
FILE *fp = fopen("original.txt", "w");
fprintf(fp, "Hello, inode!\n");
fclose(fp);
/* 创建硬链接(两个文件名指向同一个 inode) */
if (link("original.txt", "hardlink.txt") == -1) {
perror("link");
return 1;
}
/* 创建软链接(符号链接,存储路径字符串) */
if (symlink("original.txt", "softlink.txt") == -1) {
perror("symlink");
return 1;
}
/* 比较三个文件的 inode 号 */
struct stat st1, st2, st3;
stat("original.txt", &st1);
stat("hardlink.txt", &st2);
lstat("softlink.txt", &st3); /* lstat 不跟随符号链接 */
printf("original.txt inode: %lu, 硬链接数: %lu\n",
(unsigned long)st1.st_ino, (unsigned long)st1.st_nlink);
printf("hardlink.txt inode: %lu, 硬链接数: %lu\n",
(unsigned long)st2.st_ino, (unsigned long)st2.st_nlink);
printf("softlink.txt inode: %lu (符号链接自身)\n",
(unsigned long)st3.st_ino);
printf("\n硬链接与原文件 inode 相同: %s\n",
st1.st_ino == st2.st_ino ? "是 ✓" : "否 ✗");
printf("软链接与原文件 inode 相同: %s\n",
st1.st_ino == st3.st_ino ? "是" : "否 ✓(符号链接有自己的inode)");
/* 清理 */
unlink("original.txt");
unlink("hardlink.txt");
unlink("softlink.txt");
return 0;
}
gcc -o inode_demo inode_demo.c
./inode_demo
# 输出示例:
# original.txt inode: 1234567, 硬链接数: 2
# hardlink.txt inode: 1234567, 硬链接数: 2
# softlink.txt inode: 1234568 (符号链接自身)
#
# 硬链接与原文件 inode 相同: 是 ✓
# 软链接与原文件 inode 相同: 否 ✓(符号链接有自己的inode)
3.5 文件系统挂载
# 查看当前挂载的文件系统
mount | column -t
# 或
df -hT # 显示磁盘使用情况和文件系统类型
# 挂载 U 盘(需要 root 权限)
# mount /dev/sdb1 /mnt/usb
# 查看 /etc/fstab(开机自动挂载配置)
cat /etc/fstab
# 格式:设备 挂载点 文件系统类型 选项 dump pass
# 查看支持的文件系统类型
cat /proc/filesystems
/* 使用 statvfs() 获取文件系统统计信息 */
#include <stdio.h>
#include <sys/statvfs.h>
int main(int argc, char *argv[]) {
const char *path = (argc > 1) ? argv[1] : "/";
struct statvfs vfs;
if (statvfs(path, &vfs) == -1) {
perror("statvfs");
return 1;
}
unsigned long long total = (unsigned long long)vfs.f_blocks * vfs.f_frsize;
unsigned long long free = (unsigned long long)vfs.f_bfree * vfs.f_frsize;
unsigned long long avail = (unsigned long long)vfs.f_bavail * vfs.f_frsize;
unsigned long long used = total - free;
printf("文件系统路径: %s\n", path);
printf("块大小: %lu 字节\n", vfs.f_bsize);
printf("总空间: %.2f GB\n", total / 1e9);
printf("已用空间: %.2f GB\n", used / 1e9);
printf("可用空间: %.2f GB\n", avail / 1e9);
printf("使用率: %.1f%%\n", (double)used / total * 100);
printf("总 inode 数: %lu\n", (unsigned long)vfs.f_files);
printf("空闲 inode 数: %lu\n", (unsigned long)vfs.f_ffree);
return 0;
}
gcc -o fs_info fs_info.c
./fs_info /
# 输出示例:
# 文件系统路径: /
# 块大小: 4096 字节
# 总空间: 107.37 GB
# 已用空间: 23.45 GB
# 可用空间: 78.56 GB
# 使用率: 21.8%
# 总 inode 数: 6553600
# 空闲 inode 数: 6123456
综合实践:系统信息汇总脚本
#!/bin/bash
# 文件名:sysinfo.sh
# 功能:综合展示内核、Shell、文件系统信息
set -euo pipefail
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
print_section() {
echo -e "\n${BLUE}══════════════════════════════════════${NC}"
echo -e "${GREEN} $1${NC}"
echo -e "${BLUE}══════════════════════════════════════${NC}"
}
# ── 1. 内核信息 ───────────────────────────────────────────
print_section "🐧 内核信息"
echo "内核版本: $(uname -r)"
echo "系统架构: $(uname -m)"
echo "主机名: $(hostname)"
echo "系统启动时间: $(uptime -s 2>/dev/null || uptime | awk '{print $3,$4}' | tr -d ',')"
# ── 2. Shell 信息 ─────────────────────────────────────────
print_section "🐚 Shell 信息"
echo "当前 Shell: $SHELL"
echo "Shell 版本: $($SHELL --version 2>&1 | head -1)"
echo "当前 PID: $$"
echo "登录用户: $(whoami)"
# ── 3. 内存信息(读取 /proc/meminfo)────────────────────
print_section "💾 内存信息"
awk '/^MemTotal|^MemFree|^MemAvailable|^SwapTotal|^SwapFree/ {
printf "%-20s %8.1f MB\n", $1, $2/1024
}' /proc/meminfo
# ── 4. 文件系统信息 ───────────────────────────────────────
print_section "📁 文件系统使用情况"
df -hT | grep -v "tmpfs\|devtmpfs\|udev" | \
awk 'NR==1{print} NR>1{
# 根据使用率着色
use=$6; gsub(/%/,"",use)
if(use+0 > 80) color="\033[0;31m"
else if(use+0 > 60) color="\033[1;33m"
else color="\033[0;32m"
printf "%s%s\033[0m\n", color, $0
}'
# ── 5. 重要目录大小 ───────────────────────────────────────
print_section "📊 重要目录大小"
for dir in /etc /var/log /tmp /home; do
if [ -d "$dir" ]; then
size=$(du -sh "$dir" 2>/dev/null | awk '{print $1}')
printf " %-15s %s\n" "$dir" "$size"
fi
done
# ── 6. 进程统计 ───────────────────────────────────────────
print_section "⚙️ 进程统计"
echo "总进程数: $(ps aux | wc -l)"
echo "运行中进程: $(ps aux | awk '$8~/^R/{count++} END{print count+0}')"
echo "CPU 核心数: $(nproc)"
echo "系统负载: $(cat /proc/loadavg | awk '{print $1,$2,$3}')"
echo -e "\n${GREEN}✓ 系统信息汇总完成${NC}\n"
chmod +x sysinfo.sh
./sysinfo.sh
知识点总结
Linux 系统编程第1章 核心知识图谱
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┌─────────────────────────────────────────────────────────┐
│ Linux 系统概述 │
└──────────────┬──────────────────┬───────────────────────┘
│ │ │
┌──────▼──────┐ ┌───────▼──────┐ ┌──────▼──────┐
│ 内核 │ │ Shell │ │ 文件系统 │
│ Kernel │ │ │ │ Structure │
└──────┬──────┘ └───────┬──────┘ └──────┬──────┘
│ │ │
┌──────▼──────┐ ┌───────▼──────┐ ┌──────▼──────┐
│ • 进程管理 │ │ • 变量/环境 │ │ • FHS 标准 │
│ • 内存管理 │ │ • 控制流 │ │ • /proc虚拟 │
│ • 设备驱动 │ │ • 函数/陷阱 │ │ • /dev设备 │
│ • 系统调用 │ │ • 管道/重定向│ │ • inode机制 │
│ • 用户/内核态│ │ • 错误处理 │ │ • 文件类型 │
└─────────────┘ └─────────────┘ └─────────────┘
关键系统调用:write() read() open() close() stat() lstat()
getpid() fork() exec() link() symlink()
关键虚拟文件:/proc/meminfo /proc/cpuinfo /proc/[pid]/
/proc/filesystems /proc/loadavg
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📚 参考资料
《Linux/UNIX 系统编程手册》--- Michael Kerrisk
《深入理解 Linux 内核》--- Daniel P. Bovet
Linux man pages:
man 2 syscall,man 7 hier,man 5 procLinux Kernel 官方文档: The Linux Kernel documentation --- The Linux Kernel documentation