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

📚 参考资料

相关推荐
2601_951645741 小时前
C语言环境搭建指南
c语言·编译器·开发环境·helloworld·集成开发环境
码云骑士1 小时前
05-Python字典底层原理-Hash表与有序性的真相
开发语言·python·哈希算法
J2虾虾1 小时前
Android支持Java语言的标准
android·java·开发语言
Cloud_Shy6181 小时前
解读《Effective Python 3rd Edition》:从练气到老魔(第六章 Item 44 - 47)
开发语言·人工智能·经验分享·笔记·python
mxlwd1681 小时前
movielen 100k lr模型训练过程
开发语言·python·机器学习
SongYuLong的博客1 小时前
openWRT补丁文件生成
c语言
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题 第113题】【并发篇】第13题:说一下乐观锁的优点和缺点?
java·开发语言·面试