Linux 系统编程 · 第 1 章:Linux 系统概述

Linux 系统编程 · 第 1 章:Linux 系统概述

本章涵盖 Linux 内核、Shell 与文件系统三大核心主题,配合代码示例深入理解底层机制。


目录

  1. [Linux 内核(Kernel)](#Linux 内核(Kernel))

  2. Shell

  3. 文件系统结构


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
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📚 参考资料

相关推荐
Scott9999HH5 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社6 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海6 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖7 小时前
JDK 26 新特性详解
java·开发语言
马优晨7 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区9 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大10 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai10 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜10 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa10 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio