跳转至

Linux内核调试环境搭建完全指南

🤖 Claw创作 - 本文由OpenClaw AI助手协助创作,提供完整的Linux内核调试环境搭建方案

本文详细介绍了如何从零开始搭建完整的Linux内核调试环境,包含QEMU模拟器配置、GDB调试、内核源码获取和编译,以及实用的调试技巧。

📋 目录

🎯 项目背景

Linux内核是操作系统的核心,理解其工作原理对于系统开发者和安全研究人员至关重要。然而,直接在实际硬件上调试内核存在诸多限制和风险。本指南将帮助你搭建一个安全、可控的虚拟调试环境。

为什么需要内核调试环境?

  1. 安全性:避免因调试导致系统崩溃
  2. 可控性:可以随时暂停、单步执行、查看内存
  3. 可重复性:相同的环境可以重复测试
  4. 学习性:深入理解内核工作原理

环境组成

  • QEMU:硬件模拟器,提供虚拟的x86_64环境
  • Linux内核:可调试版本的内核源码
  • BusyBox:精简的根文件系统
  • GDB:GNU调试器,支持远程调试
  • 编译工具链:gcc, make, binutils等

🛠️ 环境要求

硬件要求

  • CPU: x86_64架构,支持KVM加速(推荐)
  • 内存: 至少4GB RAM
  • 存储: 至少20GB可用空间

软件要求

  • 操作系统: Ubuntu 20.04/22.04 LTS 或兼容系统
  • 权限: 需要sudo权限安装软件包
  • 网络: 需要互联网连接下载源码和工具

推荐配置

  • Ubuntu 22.04 LTS
  • 8GB RAM
  • 50GB可用存储空间
  • 支持KVM的CPU(Intel VT-x或AMD-V)

🚀 环境搭建步骤

1. 安装必要工具

创建安装脚本 setup-debug-env.sh

#!/bin/bash
# setup-debug-env.sh - Linux内核调试环境安装脚本

set -e

echo "========================================"
echo "Linux内核调试环境安装工具"
echo "========================================"

# 检查是否为root用户
if [ "$EUID" -ne 0 ]; then
    echo "请使用sudo运行此脚本"
    exit 1
fi

echo "步骤1: 更新系统包列表..."
apt update

echo "步骤2: 安装编译工具链..."
apt install -y \
    build-essential \
    libncurses-dev \
    libssl-dev \
    libelf-dev \
    bison \
    flex \
    dwarves \
    git \
    wget \
    curl

echo "步骤3: 安装QEMU模拟器..."
apt install -y \
    qemu-system-x86 \
    qemu-system-arm \
    qemu-utils \
    qemu-kvm \
    bridge-utils

echo "步骤4: 安装调试工具..."
apt install -y \
    gdb \
    cgdb \
    kgdb \
    kgdboc \
    crash \
    systemtap

echo "步骤5: 安装其他有用工具..."
apt install -y \
    tmux \
    screen \
    vim \
    tree \
    htop \
    net-tools \
    iproute2

echo "步骤6: 验证KVM支持..."
if kvm-ok; then
    echo "✅ KVM加速可用"
else
    echo "⚠️  KVM不可用,性能可能受影响"
fi

echo "步骤7: 添加当前用户到kvm组..."
usermod -aG kvm $SUDO_USER

echo "========================================"
echo "安装完成!"
echo "请重新登录使kvm组权限生效"
echo "========================================"

# 显示环境信息
echo ""
echo "环境信息:"
echo "- 系统: $(lsb_release -d | cut -f2)"
echo "- 内核: $(uname -r)"
echo "- 架构: $(uname -m)"
echo "- QEMU: $(qemu-system-x86_64 --version | head -1)"
echo "- GCC: $(gcc --version | head -1)"
echo "- GDB: $(gdb --version | head -1)"

运行安装脚本:

chmod +x setup-debug-env.sh
sudo ./setup-debug-env.sh

2. 创建项目目录结构

#!/bin/bash
# create-project-dirs.sh - 创建项目目录结构

set -e

echo "创建Linux内核调试项目目录结构..."

# 项目根目录
PROJECT_ROOT="$HOME/linux-kernel-debug"
mkdir -p "$PROJECT_ROOT"

# 子目录结构
DIRS=(
    "src"           # 源码目录
    "build"         # 构建目录
    "images"        # 镜像文件
    "scripts"       # 工具脚本
    "configs"       # 配置文件
    "docs"          # 文档
    "tests"         # 测试用例
)

for dir in "${DIRS[@]}"; do
    mkdir -p "$PROJECT_ROOT/$dir"
    echo "创建目录: $PROJECT_ROOT/$dir"
done

# 创建常用文件
touch "$PROJECT_ROOT/README.md"
touch "$PROJECT_ROOT/.gitignore"

echo "项目目录创建完成: $PROJECT_ROOT"
echo ""
tree "$PROJECT_ROOT" -d

📦 获取Linux内核源码

方法1:从kernel.org获取(推荐)

#!/bin/bash
# download-kernel.sh - 下载Linux内核源码

set -e

KERNEL_VERSION="6.6"  # 可以选择其他稳定版本
PROJECT_ROOT="$HOME/linux-kernel-debug"
KERNEL_DIR="$PROJECT_ROOT/src/linux-$KERNEL_VERSION"

echo "下载Linux内核 $KERNEL_VERSION 源码..."

# 创建源码目录
mkdir -p "$PROJECT_ROOT/src"
cd "$PROJECT_ROOT/src"

# 下载内核源码
if [ ! -f "linux-$KERNEL_VERSION.tar.xz" ]; then
    echo "下载内核压缩包..."
    wget "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-$KERNEL_VERSION.tar.xz"
fi

# 解压源码
echo "解压内核源码..."
tar -xf "linux-$KERNEL_VERSION.tar.xz"

# 创建符号链接
cd "$PROJECT_ROOT"
ln -sfn "src/linux-$KERNEL_VERSION" linux

echo "内核源码下载完成: $KERNEL_DIR"
echo "符号链接创建: $PROJECT_ROOT/linux -> $KERNEL_DIR"

方法2:从Git仓库获取(最新开发版)

#!/bin/bash
# clone-kernel-git.sh - 从Git克隆Linux内核

set -e

PROJECT_ROOT="$HOME/linux-kernel-debug"
KERNEL_DIR="$PROJECT_ROOT/src/linux-git"

echo "从Git克隆Linux内核源码..."

# 创建源码目录
mkdir -p "$PROJECT_ROOT/src"
cd "$PROJECT_ROOT/src"

# 克隆内核仓库(使用镜像加速)
if [ ! -d "linux" ]; then
    echo "克隆内核仓库(这可能需要一些时间)..."
    # 使用清华镜像加速
    git clone https://mirrors.tuna.tsinghua.edu.cn/git/linux.git
    mv linux linux-git
fi

cd linux-git

# 切换到稳定版本
echo "切换到稳定分支..."
git checkout v6.6

echo "内核源码克隆完成: $KERNEL_DIR"

方法3:使用系统自带内核源码

#!/bin/bash
# use-system-kernel.sh - 使用系统自带内核源码

set -e

PROJECT_ROOT="$HOME/linux-kernel-debug"

echo "安装系统内核源码..."

# 安装内核头文件
sudo apt install -y linux-headers-$(uname -r)

# 查找源码位置
KERNEL_SRC="/usr/src/linux-headers-$(uname -r)"
if [ -d "$KERNEL_SRC" ]; then
    ln -sfn "$KERNEL_SRC" "$PROJECT_ROOT/linux"
    echo "系统内核源码链接创建: $PROJECT_ROOT/linux -> $KERNEL_SRC"
else
    echo "错误: 内核源码未找到"
    exit 1
fi

⚙️ 配置和编译内核

1. 内核配置

#!/bin/bash
# configure-kernel.sh - 配置Linux内核

set -e

PROJECT_ROOT="$HOME/linux-kernel-debug"
KERNEL_DIR="$PROJECT_ROOT/linux"
BUILD_DIR="$PROJECT_ROOT/build/linux"

echo "配置Linux内核..."

# 进入内核目录
cd "$KERNEL_DIR"

# 清理之前的配置
make mrproper

# 创建构建目录
mkdir -p "$BUILD_DIR"

# 使用当前系统配置作为基础
echo "复制当前系统配置..."
cp /boot/config-$(uname -r) "$BUILD_DIR/.config"

# 更新配置
echo "更新配置..."
make O="$BUILD_DIR" olddefconfig

# 启用调试选项
echo "启用调试选项..."
scripts/config --file "$BUILD_DIR/.config" \
    --set-val CONFIG_DEBUG_INFO y \
    --set-val CONFIG_DEBUG_INFO_DWARF5 y \
    --set-val CONFIG_GDB_SCRIPTS y \
    --set-val CONFIG_DEBUG_KERNEL y \
    --set-val CONFIG_DEBUG_INFO_REDUCED n \
    --set-val CONFIG_FRAME_POINTER y \
    --set-val CONFIG_KGDB y \
    --set-val CONFIG_KGDB_SERIAL_CONSOLE y \
    --set-val CONFIG_KGDB_KDB y \
    --set-val CONFIG_KDB_KEYBOARD y

# 运行配置界面
echo "启动配置界面..."
make O="$BUILD_DIR" menuconfig

echo "内核配置完成"
echo "配置文件: $BUILD_DIR/.config"

2. 编译内核

#!/bin/bash
# build-kernel.sh - 编译Linux内核

set -e

PROJECT_ROOT="$HOME/linux-kernel-debug"
BUILD_DIR="$PROJECT_ROOT/build/linux"
IMAGE_DIR="$PROJECT_ROOT/images"

echo "编译Linux内核..."

# 进入构建目录
cd "$BUILD_DIR"

# 获取CPU核心数
CPU_CORES=$(nproc)
echo "使用 $CPU_CORES 个CPU核心编译..."

# 编译内核
echo "开始编译内核(这可能需要一些时间)..."
time make -j$CPU_CORES

# 编译模块
echo "编译内核模块..."
time make -j$CPU_CORES modules

# 创建镜像目录
mkdir -p "$IMAGE_DIR"

# 复制内核镜像
cp arch/x86/boot/bzImage "$IMAGE_DIR/vmlinuz-debug"
cp System.map "$IMAGE_DIR/System.map-debug"
cp .config "$IMAGE_DIR/config-debug"

echo "编译完成!"
echo "内核镜像: $IMAGE_DIR/vmlinuz-debug"
echo "系统映射: $IMAGE_DIR/System.map-debug"
echo "配置文件: $IMAGE_DIR/config-debug"

# 显示内核信息
echo ""
echo "内核信息:"
file "$IMAGE_DIR/vmlinuz-debug"
ls -lh "$IMAGE_DIR/vmlinuz-debug"

3. 快速编译脚本

#!/bin/bash
# quick-build.sh - 快速编译脚本

set -e

PROJECT_ROOT="$HOME/linux-kernel-debug"
BUILD_DIR="$PROJECT_ROOT/build/linux"
CPU_CORES=$(($(nproc) * 3 / 4))  # 使用75%的CPU核心

echo "快速编译Linux内核..."
echo "使用 $CPU_CORES 个CPU核心"

cd "$BUILD_DIR"

# 只编译必要的部分
make -j$CPU_CORES bzImage modules

echo "快速编译完成"

📁 准备根文件系统

1. 使用BusyBox创建最小根文件系统

#!/bin/bash
# create-rootfs.sh - 创建BusyBox根文件系统

set -e

PROJECT_ROOT="$HOME/linux-kernel-debug"
ROOTFS_DIR="$PROJECT_ROOT/build/rootfs"
IMAGE_DIR="$PROJECT_ROOT/images"

echo "创建BusyBox根文件系统..."

# 创建根文件系统目录
mkdir -p "$ROOTFS_DIR"
cd "$ROOTFS_DIR"

# 创建基本目录结构
mkdir -p {bin,dev,etc,home,lib,lib64,mnt,opt,proc,root,run,sbin,sys,tmp,usr,var}
mkdir -p usr/{bin,lib,sbin}
mkdir -p var/log

# 创建设备文件
sudo mknod dev/console c 5 1
sudo mknod dev/null c 1 3
sudo mknod dev/zero c 1 5
sudo mknod dev/tty c 5 0
sudo mknod dev/tty0 c 4 0
sudo mknod dev/tty1 c 4 1

# 下载和编译BusyBox
if [ ! -f "$PROJECT_ROOT/src/busybox-1.36.1.tar.bz2" ]; then
    echo "下载BusyBox..."
    wget -P "$PROJECT_ROOT/src" \
        https://busybox.net/downloads/busybox-1.36.1.tar.bz2
fi

if [ ! -d "$PROJECT_ROOT/src/busybox-1.36.1" ]; then
    echo "解压BusyBox..."
    tar -xjf "$PROJECT_ROOT/src/busybox-1.36.1.tar.bz2" \
        -C "$PROJECT_ROOT/src"
fi

# 编译BusyBox
echo "编译BusyBox..."
cd "$PROJECT_ROOT/src/busybox-1.36.1"
make defconfig
sed -i 's/.*CONFIG_STATIC.*/CONFIG_STATIC=y/' .config
make -j$(nproc)
make install

# 复制BusyBox到根文件系统
cp -av _install/* "$ROOTFS_DIR"

# 创建初始化脚本
cat > "$ROOTFS_DIR/init" << 'EOF'
#!/bin/sh

# 挂载虚拟文件系统
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
mount -t tmpfs none /tmp

# 设置主机名
hostname qemu-debug

# 设置PATH
export PATH=/bin:/sbin:/usr/bin:/usr/sbin

# 启动shell
echo "Welcome to Linux Kernel Debug Environment!"
echo "Kernel: $(uname -r)"
echo ""

# 如果指定了init参数,则执行它
if [ -n "$1" ]; then
    exec $1
else
    exec /bin/sh
fi
EOF

chmod +x "$ROOTFS_DIR/init"

# 创建/etc/passwd
cat > "$ROOTFS_DIR/etc/passwd" << 'EOF'
root::0:0:root:/root:/bin/sh
EOF

# 创建/etc/group
cat > "$ROOTFS_DIR/etc/group" << 'EOF'
root:x:0:
EOF

# 创建inittab
cat > "$ROOTFS_DIR/etc/inittab" << 'EOF'
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init
EOF

# 创建rcS脚本
mkdir -p "$ROOTFS_DIR/etc/init.d"
cat > "$ROOTFS_DIR/etc/init.d/rcS" << 'EOF'
#!/bin/sh

# 挂载文件系统
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
mount -t tmpfs none /tmp

# 设置权限
chmod 0666 /dev/tty*
chmod 0666 /dev/console

# 设置主机名
hostname qemu-debug

# 设置网络(如果需要)
# ifconfig lo 127.0.0.1 up
# ifconfig eth0 10.0.2.15 up
# route add default gw 10.0.2.2
EOF

chmod +x "$ROOTFS_DIR/etc/init.d/rcS"

# 创建根文件系统镜像
echo "创建根文件系统镜像..."
cd "$ROOTFS_DIR"
find . | cpio -H newc -o | gzip > "$IMAGE_DIR/rootfs.cpio.gz"

echo "根文件系统创建完成!"
echo "镜像文件: $IMAGE_DIR/rootfs.cpio.gz"
echo "大小: $(ls -lh "$IMAGE_DIR/rootfs.cpio.gz")"

2. 使用ext4文件系统镜像

#!/bin/bash
# create-ext4-rootfs.sh - 创建ext4根文件系统

set -e

PROJECT_ROOT="$HOME/linux-kernel-debug"
ROOTFS_DIR="$PROJECT_ROOT/build/rootfs-ext4"
IMAGE_DIR="$PROJECT_ROOT/images"
ROOTFS_IMG="$IMAGE_DIR/rootfs.ext4"
ROOTFS_SIZE="1G"  # 根文件系统大小

echo "创建ext4根文件系统..."

# 创建根文件系统目录
mkdir -p "$ROOTFS_DIR"

# 创建ext4镜像文件
echo "创建 $ROOTFS_SIZE 的ext4镜像..."
dd if=/dev/zero of="$ROOTFS_IMG" bs=1M count=$(( ${ROOTFS_SIZE%G} * 1024 )) 2>/dev/null
mkfs.ext4 "$ROOTFS_IMG"

# 挂载镜像并复制文件
echo "准备根文件系统内容..."
mkdir -p /mnt/rootfs-temp
sudo mount -o loop "$ROOTFS_IMG" /mnt/rootfs-temp

# 使用之前创建的BusyBox根文件系统
if [ -d "$PROJECT_ROOT/build/rootfs" ]; then
    sudo cp -a "$PROJECT_ROOT/build/rootfs/"* /mnt/rootfs-temp/
fi

# 卸载镜像
sudo umount /mnt/rootfs-temp
rmdir /mnt/rootfs-temp

echo "ext4根文件系统创建完成!"
echo "镜像文件: $ROOTFS_IMG"
echo "大小: $(ls -lh "$ROOTFS_IMG")"

🖥️ 使用QEMU启动内核

1. 基本启动脚本

#!/bin/bash
# run-qemu.sh - 使用QEMU启动Linux内核

set -e

PROJECT_ROOT="$HOME/linux-kernel-debug"
IMAGE_DIR="$PROJECT_ROOT/images"
KERNEL_IMAGE="$IMAGE_DIR/vmlinuz-debug"
ROOTFS_IMAGE="$IMAGE_DIR/rootfs.cpio.gz"

echo "启动QEMU Linux内核调试环境..."

# 检查文件是否存在
if [ ! -f "$KERNEL_IMAGE" ]; then
    echo "错误: 内核镜像未找到: $KERNEL_IMAGE"
    exit 1
fi

if [ ! -f "$ROOTFS_IMAGE" ]; then
    echo "错误: 根文件系统未找到: $ROOTFS_IMAGE"
    exit 1
fi

# QEMU启动参数
QEMU_OPTS=(
    -kernel "$KERNEL_IMAGE"
    -initrd "$ROOTFS_IMAGE"
    -append "console=ttyS0 root=/dev/ram rdinit=/init nokaslr"
    -nographic
    -m 512M
    -smp 2
    -serial mon:stdio
    -netdev user,id=net0,hostfwd=tcp::5555-:22
    -device e1000,netdev=net0
)

# 启用KVM加速(如果可用)
if kvm-ok >/dev/null 2>&1; then
    QEMU_OPTS+=(-enable-kvm -cpu host)
    echo "使用KVM加速"
else
    QEMU_OPTS+=(-cpu qemu64)
    echo "KVM不可用,使用软件模拟"
fi

echo "启动命令:"
echo "qemu-system-x86_64 ${QEMU_OPTS[@]}"
echo ""
echo "按 Ctrl+A 然后 X 退出QEMU"
echo "按 Ctrl+A 然后 C 进入QEMU控制台"
echo ""

# 启动QEMU
qemu-system-x86_64 "${QEMU_OPTS[@]}"

2. 带调试支持的启动脚本

#!/bin/bash
# run-qemu-debug.sh - 启动带GDB调试支持的QEMU

set -e

PROJECT_ROOT="$HOME/linux-kernel-debug"
IMAGE_DIR="$PROJECT_ROOT/images"
KERNEL_IMAGE="$IMAGE_DIR/vmlinuz-debug"
ROOTFS_IMAGE="$IMAGE_DIR/rootfs.cpio.gz"

echo "启动QEMU Linux内核调试环境(GDB支持)..."

# 检查文件是否存在
if [ ! -f "$KERNEL_IMAGE" ]; then
    echo "错误: 内核镜像未找到: $KERNEL_IMAGE"
    exit 1
fi

if [ ! -f "$ROOTFS_IMAGE" ]; then
    echo "错误: 根文件系统未找到: $ROOTFS_IMAGE"
    exit 1
fi

# QEMU启动参数(带GDB支持)
QEMU_OPTS=(
    -kernel "$KERNEL_IMAGE"
    -initrd "$ROOTFS_IMAGE"
    -append "console=ttyS0 root=/dev/ram rdinit=/init nokaslr kgdboc=ttyS0,115200"
    -nographic
    -m 1G
    -smp 2
    -serial mon:stdio
    -netdev user,id=net0,hostfwd=tcp::5555-:22
    -device e1000,netdev=net0
    -S  # 启动时暂停,等待GDB连接
    -s  # 在1234端口启动GDB服务器
)

# 启用KVM加速
if kvm-ok >/dev/null 2>&1; then
    QEMU_OPTS+=(-enable-kvm -cpu host)
    echo "使用KVM加速"
else
    QEMU_OPTS+=(-cpu qemu64)
    echo "KVM不可用,使用软件模拟"
fi

echo "启动命令:"
echo "qemu-system-x86_64 ${QEMU_OPTS[@]}"
echo ""
echo "GDB调试信息:"
echo "- QEMU在1234端口启动GDB服务器"
echo "- 在另一个终端运行: gdb vmlinux"
echo "- 在GDB中运行: target remote :1234"
echo "- 然后输入: continue"
echo ""
echo "按 Ctrl+A 然后 X 退出QEMU"

# 启动QEMU
qemu-system-x86_64 "${QEMU_OPTS[@]}"

3. 图形界面启动脚本

#!/bin/bash
# run-qemu-gui.sh - 使用图形界面启动QEMU

set -e

PROJECT_ROOT="$HOME/linux-kernel-debug"
IMAGE_DIR="$PROJECT_ROOT/images"
KERNEL_IMAGE="$IMAGE_DIR/vmlinuz-debug"
ROOTFS_IMAGE="$IMAGE_DIR/rootfs.cpio.gz"

echo "启动QEMU Linux内核调试环境(图形界面)..."

# 检查文件是否存在
if [ ! -f "$KERNEL_IMAGE" ]; then
    echo "错误: 内核镜像未找到: $KERNEL_IMAGE"
    exit 1
fi

if [ ! -f "$ROOTFS_IMAGE" ]; then
    echo "错误: 根文件系统未找到: $ROOTFS_IMAGE"
    exit 1
fi

# QEMU启动参数(图形界面)
QEMU_OPTS=(
    -kernel "$KERNEL_IMAGE"
    -initrd "$ROOTFS_IMAGE"
    -append "console=tty0 console=ttyS0 root=/dev/ram rdinit=/init"
    -m 2G
    -smp 4
    -vga std
    -serial mon:stdio
    -netdev user,id=net0,hostfwd=tcp::5555-:22
    -device e1000,netdev=net0
    -usb
    -device usb-tablet
    -soundhw hda
)

# 启用KVM加速
if kvm-ok >/dev/null 2>&1; then
    QEMU_OPTS+=(-enable-kvm -cpu host)
    echo "使用KVM加速"
else
    QEMU_OPTS+=(-cpu qemu64)
    echo "KVM不可用,使用软件模拟"
fi

echo "启动命令:"
echo "qemu-system-x86_64 ${QEMU_OPTS[@]}"
echo ""
echo "图形界面启动中..."

# 启动QEMU
qemu-system-x86_64 "${QEMU_OPTS[@]}"

🐛 GDB调试内核

1. GDB配置脚本

#!/bin/bash
# setup-gdb.sh - 配置GDB调试环境

set -e

PROJECT_ROOT="$HOME/linux-kernel-debug"
BUILD_DIR="$PROJECT_ROOT/build/linux"
GDBINIT_FILE="$PROJECT_ROOT/.gdbinit"

echo "配置GDB调试环境..."

# 创建.gdbinit文件
cat > "$GDBINIT_FILE" << 'EOF'
# Linux内核调试GDB配置

# 设置源码路径
set substitute-path /build/linux $HOME/linux-kernel-debug/build/linux

# 加载Linux内核GDB脚本
add-auto-load-safe-path $HOME/linux-kernel-debug/build/linux
source $HOME/linux-kernel-debug/build/linux/vmlinux-gdb.py

# 设置调试选项
set pagination off
set history save on
set history filename ~/.gdb_history
set history size 10000

# 设置反汇编风格
set disassembly-flavor intel

# 设置漂亮打印
python
import gdb.printing
gdb.printing.register_pretty_printer(gdb.current_objfile(), gdb.printing.RegexpCollectionPrettyPrinter("linux"))
end

# 常用命令别名
define k
  p/x $lx_current().pid
  p $lx_current().comm
end

document k
  打印当前进程信息
end

define mm
  p (struct mm_struct *)$lx_current().mm
end

document mm
  打印当前进程内存管理结构
end

define mod
  p (struct module *)$lx_current().module
end

document mod
  打印当前进程模块信息
end

echo "GDB配置加载完成!\\n"
EOF

echo "GDB配置文件创建: $GDBINIT_FILE"

# 创建GDB启动脚本
cat > "$PROJECT_ROOT/scripts/gdb-start.sh" << 'EOF'
#!/bin/bash
# gdb-start.sh - 启动GDB调试会话

set -e

PROJECT_ROOT="$HOME/linux-kernel-debug"
BUILD_DIR="$PROJECT_ROOT/build/linux"
VMLINUX="$BUILD_DIR/vmlinux"

echo "启动GDB调试会话..."
echo "内核文件: $VMLINUX"

if [ ! -f "$VMLINUX" ]; then
    echo "错误: vmlinux未找到"
    exit 1
fi

# 启动GDB
gdb -q \
    -ex "file $VMLINUX" \
    -ex "target remote :1234" \
    -ex "break start_kernel" \
    -ex "continue"
EOF

chmod +x "$PROJECT_ROOT/scripts/gdb-start.sh"

echo "GDB启动脚本创建: $PROJECT_ROOT/scripts/gdb-start.sh"
echo ""
echo "使用方法:"
echo "1. 首先启动QEMU: ./run-qemu-debug.sh"
echo "2. 然后启动GDB: ./scripts/gdb-start.sh"
echo "3. 在GDB中使用: continue 继续执行"

2. 常用GDB命令参考

#!/bin/bash
# gdb-cheatsheet.sh - GDB调试命令速查表

cat > "$PROJECT_ROOT/docs/gdb-cheatsheet.md" << 'EOF'
# Linux内核GDB调试命令速查表

## 基本命令

### 启动和连接
```gdb
# 加载内核符号
file vmlinux

# 连接到QEMU GDB服务器
target remote :1234

# 连接到KGDB(如果内核已启动)
target remote /dev/ttyS0

断点管理

# 设置断点
break function_name
break filename.c:line_number
break *address

# 条件断点
break function_name if condition
break schedule if prev->pid == 100

# 观察点
watch variable
watch *(data_type*)address

# 删除断点
delete breakpoint_number
delete  # 删除所有断点

# 禁用/启用断点
disable breakpoint_number
enable breakpoint_number

执行控制

# 继续执行
continue
c

# 单步执行
step
s
next
n

# 单步汇编指令
stepi
si
nexti
ni

# 直到函数返回
finish

# 直到指定位置
until location

内核特定命令

进程相关

# 打印当前进程
lx-ps
p $lx_current().pid
p $lx_current().comm

# 打印进程列表
lx-list-check list_head_address "struct task_struct" tasks

# 切换进程上下文
set $task = (struct task_struct*)address
lx-set-current-task $task

内存相关

# 查看内存
x/nfu address
x/10x 0xffffffff81000000  # 查看10个十六进制值
x/20s address  # 查看20个字符串

# 搜索内存
find start_address, end_address, value

# 查看页表
lx-page-info address

模块相关

# 列出加载的模块
lx-lsmod

# 查看模块符号
info functions module_name
info variables module_name

# 添加模块符号
add-symbol-file module.ko address

堆栈相关

# 查看调用栈
backtrace
bt

# 查看完整调用栈
backtrace full

# 切换栈帧
frame number
up
down

# 查看局部变量
info locals

# 查看参数
info args

实用技巧

自定义命令

# 定义命令别名
define k
  p/x $lx_current().pid
  p $lx_current().comm
end

# 打印结构体
define ps
  p *(struct task_struct*)arg0
end

脚本自动化

# 执行脚本文件
source script.gdb

# 命令行执行
gdb -ex "command1" -ex "command2" vmlinux

调试宏

# 宏展开
macro expand expression

# 查看宏定义
info macro macro_name

常见问题

符号找不到

# 添加搜索路径
set solib-search-path /path/to/modules

# 设置源码路径
directory /path/to/source
set substitute-path /old/path /new/path

优化代码调试

# 查看汇编代码
disassemble function_name

# 混合源码和汇编
set disassemble-next-line on

多线程调试

# 查看所有线程
info threads

# 切换线程
thread thread_number

# 所有线程执行命令
thread apply all command
EOF

echo "GDB命令速查表创建: $PROJECT_ROOT/docs/gdb-cheatsheet.md"

## 🔬 实战调试示例 {#_9}

### 1. 调试系统调用

```bash
#!/bin/bash
# debug-syscall.sh - 调试系统调用示例

cat > "$PROJECT_ROOT/scripts/debug-syscall.sh" << 'EOF'
#!/bin/bash
# 调试系统调用示例

echo "系统调用调试示例"
echo "=================="

# 创建测试程序
cat > test_syscall.c << 'CODE'
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>

int main() {
    printf("测试系统调用...\n");

    // 调用getpid系统调用
    pid_t pid = getpid();
    printf("进程ID: %d\n", pid);

    // 直接使用syscall
    pid_t pid2 = syscall(SYS_getpid);
    printf("直接系统调用进程ID: %d\n", pid2);

    return 0;
}
CODE

echo "编译测试程序..."
gcc -g -o test_syscall test_syscall.c

echo "GDB调试命令:"
echo "1. 在sys_getpid设置断点: break sys_getpid"
echo "2. 运行程序: run"
echo "3. 查看调用栈: backtrace"
echo "4. 查看参数: info args"
echo "5. 单步执行: step"

# 清理
rm -f test_syscall test_syscall.c
EOF

chmod +x "$PROJECT_ROOT/scripts/debug-syscall.sh"

2. 调试进程调度

#!/bin/bash
# debug-scheduler.sh - 调试进程调度器

cat > "$PROJECT_ROOT/scripts/debug-scheduler.sh" << 'EOF'
#!/bin/bash
# 调试进程调度器示例

echo "进程调度器调试示例"
echo "=================="

echo "常用调度器相关函数:"
echo "1. schedule() - 主调度函数"
echo "2. __schedule() - 实际调度实现"
echo "3. pick_next_task() - 选择下一个任务"
echo "4. context_switch() - 上下文切换"
echo "5. finish_task_switch() - 完成任务切换"

echo ""
echo "GDB调试命令:"
echo "1. 在schedule设置断点: break schedule"
echo "2. 查看运行队列: p runqueues"
echo "3. 查看当前进程: p current"
echo "4. 查看调度统计: p cpu_rq(cpu)->clock"
EOF

chmod +x "$PROJECT_ROOT/scripts/debug-scheduler.sh"

3. 调试内存管理

#!/bin/bash
# debug-memory.sh - 调试内存管理

cat > "$PROJECT_ROOT/scripts/debug-memory.sh" << 'EOF'
#!/bin/bash
# 调试内存管理示例

echo "内存管理调试示例"
echo "================"

echo "常用内存管理函数:"
echo "1. kmalloc() - 内核内存分配"
echo "2. kfree() - 内核内存释放"
echo "3. vmalloc() - 虚拟内存分配"
echo "4. alloc_pages() - 页面分配"
echo "5. do_page_fault() - 页面错误处理"

echo ""
echo "GDB调试命令:"
echo "1. 在kmalloc设置断点: break kmalloc"
echo "2. 查看slab缓存: p kmalloc_caches"
echo "3. 查看内存区域: cat /proc/iomem"
echo "4. 查看页面信息: lx-page-info address"
EOF

chmod +x "$PROJECT_ROOT/scripts/debug-memory.sh"

🎓 高级调试技巧

1. 使用SystemTap进行动态跟踪

#!/bin/bash
# setup-systemtap.sh - 配置SystemTap

set -e

echo "配置SystemTap动态跟踪..."

# 安装SystemTap
sudo apt install -y systemtap systemtap-runtime

# 安装调试符号
sudo apt install -y linux-image-$(uname -r)-dbgsym

# 创建测试脚本
cat > "$PROJECT_ROOT/scripts/trace-syscall.stp" << 'EOF'
#!/usr/bin/stap

probe begin {
    printf("开始跟踪系统调用...\n")
}

probe syscall.* {
    printf("%s(%d) -> %s\n", execname(), pid(), name)
}

probe end {
    printf("跟踪结束\n")
}
EOF

chmod +x "$PROJECT_ROOT/scripts/trace-syscall.stp"

echo "SystemTap配置完成"
echo "使用示例: sudo stap trace-syscall.stp"

2. 使用Ftrace进行内核跟踪

#!/bin/bash
# setup-ftrace.sh - 配置Ftrace

cat > "$PROJECT_ROOT/scripts/ftrace-example.sh" << 'EOF'
#!/bin/bash
# Ftrace使用示例

echo "Ftrace内核跟踪示例"
echo "=================="

# 挂载debugfs(如果未挂载)
sudo mount -t debugfs none /sys/kernel/debug

echo "可用跟踪器:"
cat /sys/kernel/debug/tracing/available_tracers

echo ""
echo "基本使用:"
echo "1. 选择跟踪器: echo function > /sys/kernel/debug/tracing/current_tracer"
echo "2. 开始跟踪: echo 1 > /sys/kernel/debug/tracing/tracing_on"
echo "3. 执行操作..."
echo "4. 停止跟踪: echo 0 > /sys/kernel/debug/tracing/tracing_on"
echo "5. 查看结果: cat /sys/kernel/debug/tracing/trace"

echo ""
echo "过滤特定函数:"
echo "echo schedule > /sys/kernel/debug/tracing/set_ftrace_filter"
echo "echo > /sys/kernel/debug/tracing/set_ftrace_filter  # 清除过滤"

echo ""
echo "跟踪特定进程:"
echo "echo pid > /sys/kernel/debug/tracing/set_event_pid"
EOF

chmod +x "$PROJECT_ROOT/scripts/ftrace-example.sh"

3. 使用Kprobes进行动态插桩

#!/bin/bash
# setup-kprobes.sh - 配置Kprobes

cat > "$PROJECT_ROOT/scripts/kprobe-example.c" << 'EOF'
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>

static struct kprobe kp = {
    .symbol_name = "do_fork",
};

static int handler_pre(struct kprobe *p, struct pt_regs *regs) {
    printk(KERN_INFO "do_fork被调用,PID: %d\n", current->pid);
    return 0;
}

static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags) {
    printk(KERN_INFO "do_fook调用完成\n");
}

static int __init kprobe_init(void) {
    int ret;

    kp.pre_handler = handler_pre;
    kp.post_handler = handler_post;

    ret = register_kprobe(&kp);
    if (ret < 0) {
        printk(KERN_INFO "注册kprobe失败: %d\n", ret);
        return ret;
    }

    printk(KERN_INFO "kprobe注册成功\n");
    return 0;
}

static void __exit kprobe_exit(void) {
    unregister_kprobe(&kp);
    printk(KERN_INFO "kprobe卸载\n");
}

module_init(kprobe_init);
module_exit(kprobe_exit);
MODULE_LICENSE("GPL");
EOF

echo "Kprobes示例代码创建: $PROJECT_ROOT/scripts/kprobe-example.c"

🔧 故障排除

常见问题及解决方案

#!/bin/bash
# troubleshooting.sh - 故障排除指南

cat > "$PROJECT_ROOT/docs/troubleshooting.md" << 'EOF'
# Linux内核调试环境故障排除

## 1. QEMU启动问题

### 问题: QEMU无法启动
错误: Could not access KVM kernel module: No such file or directory
**解决方案:**
```bash
# 检查KVM模块是否加载
lsmod | grep kvm

# 加载KVM模块
sudo modprobe kvm
sudo modprobe kvm_intel  # Intel CPU
sudo modprobe kvm_amd    # AMD CPU

# 检查CPU虚拟化支持
grep -E '(vmx|svm)' /proc/cpuinfo

问题: 权限不足

错误: /dev/kvm: Permission denied
解决方案:
# 添加用户到kvm组
sudo usermod -aG kvm $USER

# 重新登录或重启
# 或者临时使用sudo
sudo qemu-system-x86_64 ...

2. 内核编译问题

问题: 缺少依赖

错误: fatal error: openssl/opensslv.h: No such file or directory
解决方案:
# 安装开发包
sudo apt install libssl-dev

问题: 配置错误

错误: Your configuration is incorrect
解决方案:
# 使用默认配置
make defconfig

# 或使用当前系统配置
cp /boot/config-$(uname -r) .config
make olddefconfig

3. GDB调试问题

问题: 符号找不到

错误: No symbol table is loaded
解决方案:
# 确保编译时启用了调试信息
# 在.config中设置:
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF5=y

# 重新编译内核

问题: 无法连接GDB

错误: Connection refused
解决方案:
# 确保QEMU以调试模式启动
qemu-system-x86_64 -S -s ...

# 检查端口是否被占用
netstat -tlnp | grep 1234

# 使用不同端口
qemu-system-x86_64 -gdb tcp::1235

4. 根文件系统问题

问题: 内核恐慌 (Kernel Panic)

Kernel panic - not syncing: VFS: Unable to mount root fs
解决方案:
# 检查根文件系统路径
# 确保initrd参数正确
-append "root=/dev/ram rdinit=/init"

# 重新创建根文件系统
cd rootfs
find . | cpio -H newc -o | gzip > ../rootfs.cpio.gz

问题: 无法执行init

Failed to execute /init
解决方案:
# 检查init文件权限
chmod +x init

# 检查init文件内容
cat init
#!/bin/sh

5. 性能问题

问题: QEMU运行缓慢

解决方案:

# 启用KVM加速
-enable-kvm -cpu host

# 增加内存
-m 2G

# 增加CPU核心
-smp 4

问题: 编译时间过长

解决方案:

# 使用多核编译
make -j$(nproc)

# 只编译必要部分
make bzImage  # 仅编译内核镜像

6. 网络问题

问题: 网络不可用

解决方案:

# QEMU网络配置
-netdev user,id=net0 -device e1000,netdev=net0

# 在客户机中配置网络
ifconfig eth0 up
dhclient eth0

7. 存储问题

问题: 磁盘空间不足

解决方案:

# 清理临时文件
make clean
make mrproper

# 删除旧的构建
rm -rf build/old-*

# 压缩根文件系统
gzip -9 rootfs.cpio

调试技巧

1. 启用早期控制台

-append "earlyprintk=serial,ttyS0,115200"

2. 增加日志级别

-append "loglevel=7"

3. 禁用地址随机化

-append "nokaslr"

4. 启用KGDB

-append "kgdboc=ttyS0,115200 kgdbwait"

获取帮助

1. 查看内核日志

dmesg | tail -50

2. 查看QEMU日志

qemu-system-x86_64 -d help  # 查看可用调试选项

3. 在线资源

  • Linux内核文档: https://www.kernel.org/doc/
  • QEMU文档: https://www.qemu.org/docs/
  • GDB文档: https://sourceware.org/gdb/documentation/ EOF

echo "故障排除指南创建: $PROJECT_ROOT/docs/troubleshooting.md"

## 🎉 总结 {#_12}

### 环境搭建完成

经过以上步骤,你已经成功搭建了一个完整的Linux内核调试环境,包括:

1. **完整的工具链**:编译器、调试器、模拟器
2. **可调试的内核**:包含调试符号和GDB支持
3. **虚拟运行环境**:QEMU模拟的x86_64系统
4. **根文件系统**:基于BusyBox的最小系统
5. **调试脚本**:各种实用工具和示例

### 学习路径建议

#### 初级阶段
1. **熟悉环境**:编译内核,启动QEMU
2. **基本调试**:设置断点,单步执行,查看变量
3. **系统调用**:跟踪系统调用流程

#### 中级阶段
1. **进程调度**:调试schedule()函数
2. **内存管理**:跟踪内存分配和释放
3. **文件系统**:了解VFS和具体文件系统

#### 高级阶段
1. **网络协议栈**:调试TCP/IP实现
2. **设备驱动**:编写和调试简单驱动
3. **性能分析**:使用perf和ftrace

### 实用技巧

#### 1. 保存环境状态
```bash
# 创建环境快照
tar -czf kernel-debug-env-$(date +%Y%m%d).tar.gz \
    ~/linux-kernel-debug/

2. 自动化测试

# 创建测试脚本
cat > test-all.sh << 'EOF'
#!/bin/bash
echo "测试内核编译..."
make -j$(nproc) bzImage

echo "测试QEMU启动..."
./run-qemu.sh &

echo "测试GDB连接..."
sleep 2
gdb -ex "target remote :1234" -ex "quit"
EOF

3. 扩展环境

# 添加新架构支持
apt install gcc-aarch64-linux-gnu
make ARCH=arm64 defconfig

资源推荐

文档资源

  1. 内核文档Documentation/ 目录
  2. LWN.net:Linux内核新闻和教程
  3. 内核邮件列表:学习最新开发动态

书籍推荐

  1. 《Linux内核设计与实现》
  2. 《深入理解Linux内核》
  3. 《Linux设备驱动程序》

在线课程

  1. Linux基金会内核开发课程
  2. MIT 6.828: Operating System Engineering
  3. 各种内核研讨会和会议视频

下一步行动

  1. 开始调试:选择一个简单的内核模块开始
  2. 贡献代码:修复简单的内核bug
  3. 深入学习:阅读内核源码和文档
  4. 分享经验:写博客或教程帮助他人

最后建议

Linux内核调试是一个需要耐心和实践的过程。不要期望一开始就能理解所有细节,从简单的问题开始,逐步深入。记住:

  • 实践是最好的老师:多动手调试
  • 阅读源码:理解实现细节
  • 参与社区:提问和分享
  • 保持好奇:探索未知领域

祝你在Linux内核的世界里探索愉快! 🚀


最后更新: 2024年3月13日 作者: [你的名字] 博客: [你的博客链接]

内核调试不是魔术,而是系统性的探索。每一个崩溃都是学习的机会,每一个bug都是理解的窗口。