Linux 内核开发

一、环境配置

首先肯定要先用虚拟机起一个linux操作系统,我用的是Ubuntu22.04发行版。

1.ssh远程登陆

使用远程登陆的方法就可以在物理机器上远程操作了,复制粘贴什么的也特别方便;

还有一个需要注意的地方是远程的时候,一定要使用tmux创建一个新的会话,这样网络断了,你的进程不会丢失。

关于tmux的一些使用可以看我后面的记录。

Windows/Linux → 物理服务器 → Docker容器 → QEMU虚拟机

ssh 账号@193.x.x.x
ssh 账号@172.x.x.x
exit    #退出

配置 SSH 免密登录到 Docker 容器:ssh-copy-id root@xxx.xxx.xxx.xxx 若报错(如密钥不存在),先执行 ssh-keygen。

为什么要这么麻烦,从物理机起一个虚拟机后在用一个qemu起一个?

首先我们去测试和分析内核代码的时候,肯定要针对不同的内核版本分析,用qemu可以很好的去启动不同的内核版本。

如果内核源码在qemu内部启动,镜像坏了或在内核错误了,可以重新再拉一个内核和镜像。

安装 code-server

curl -fsSL https://code-server.dev/install.sh | sh

生成代码索引

make gtags # 用于 VSCode 的代码跳转功能

安装这两个主要想着方便我用类似VScode的形式去查看内核源码

当然也可以用vim去查看内核,后面将vim的使用的时候也有具体的操作,我对vim接触太少了我还是用不太习惯。

2.内核编译

首先需要下载内核源码,内核源码的地址,这个地址里面有各个版本的内核源码。

我用到的是内核4.19版本的源码,然后创建好的虚拟机编译去内核源码。

先建立专用编译目录

cd llinux-4.9.19/ mkdir build

配置内核选项

make O=build menuconfig

编译内核镜像

make O=build bzImage -j$(nproc)

make内容补充

可以使用make help | less查看make命令的具体内容

#清理环境相关
make mrproper           #清理环境 彻底清理所有生成文件、配置文件及备份文件(需重新配置内核)
make distclean          #在 mrproper 基础上,额外删除编辑器备份和补丁文件(完全干净的环境)

#配置内核相关
make defconfig          # 生成默认配置
make menuconfig         # 自定义调整配置
make xconfig / make gconfig #分别使用 Qt 或 GTK+ 图形界面配置内核(适合桌面环境)
make localmodconfig     #根据当前加载的模块生成精简配置(禁用未使用的模块)
make allyesconfig       #将所有选项设置为 yes(生成最大化的内核,用于测试
make allnoconfig        #将所有选项设置为 no(最小化内核,需手动启用功能)
make testconfig         #运行 Kconfig 的单元测试
make debug.config       # 将 debug.config 合并到当前 .config 文件
make x86_debug.config       #针对 x86/x86_64 架构的 调试和测试选项
make kvm_guest.config       #优化内核配置以作为 KVM 虚拟机客户机(Guest OS) 运行

#编译内核与模块
make vmlinux        #编译生成内核的裸二进制文件(未压缩的内核镜像)
make modules_install    #将编译好的模块安装到 /lib/modules/$(uname -r)/ 目录
make headers_install    #安装内核头文件到 ./usr(用于开发用户空间程序)

模块独立编译

修改makefie文件

CONFIG_EXT2_FS := m # 设置为模块编译

执行编译

make -C /path/to/kernel/source M=$(pwd) modules

3.qemu启动内核

内核编译好了之后,就需要用一个仿真器启动内核了,如何安装qemu和配置qemu的启动选项是最重要的。

qemu安装参考:qemu官网

也可以下载qemu源码进行安装: 先安装qemu所需的依赖软件

# ubuntu 22.04
sudo apt-get install libattr1-dev libcap-ng-dev -y
sudo apt install ninja-build -y
sudo apt-get install libglib2.0-dev -y
sudo apt-get install libpixman-1-dev -y
sudo apt install python3-pip
pip install tomli

然后再下载qemu源码,编译安装

git clone https://gitlab.com/qemu-project/qemu.git #从git上获取qemu源码
cd qemu
git submodule init  #初始化git子模块
git submodule update --recursive #更新git子模块
mkdir build
cd build/
../configure --enable-kvm --enable-virtfs --prefix=${HOME}/dingpenglong/sw/qemu/ #--启用kvm硬件虚拟化加速--启用Virfs支持 --指定安装路径
make -j`nproc`
make install

完成这些还不够,还需要解开qemu的一些权限,以方便我们启动qemu: - qemu-bridge-helper

# 源码安装的
sudo chown root libexec/qemu-bridge-helper #将文件所有者改为root
sudo chmod u+s libexec/qemu-bridge-helper  #设置setuid
# apt安装的
sudo chown root /usr/lib/qemu/qemu-bridge-helper
sudo chmod u+s /usr/lib/qemu/qemu-bridge-helper
groups | grep kvm   #检查当前用户是否在kvm组里面
sudo usermod -aG kvm $USER  #将当前用户加入 kvm 组
su - $USER # 或退出shell重新登录, 但在tmux中不起作用,tmux需要执行newgrp kvm
# 源码安装的 QEMU
mkdir -p etc/qemu
vim etc/qemu/bridge.conf   # 添加内容:allow virbr0

# apt 安装的 QEMU
sudo mkdir -p /etc/qemu/
sudo vim /etc/qemu/bridge.conf  # 添加内容:allow virbr0
virsh net-list                   # 查看虚拟网络列表
virsh net-edit default           # 编辑名为 "default" 的网络配置
# 在 XML 中修改 <ip> 段,例如:
# <ip address='192.168.100.1' netmask='255.255.255.0'>
#   <dhcp>
#     <range start='192.168.100.2' end='192.168.100.254'/>
#   </dhcp>
# </ip>

virsh net-destroy default        # 停止当前网络
virsh net-start default          # 用新配置启动网络

安装和配置完成之后呢,就需要知道如何启动qemu了,启动配置的参数也很重要,可以把启动命令编写成一个shell脚本。

vim start.sh编辑内容

kernel_version=stable  # 设置内核版本变量为"stable"

qemu-system-x86_64 \   # 启动x86_64架构的虚拟机
-enable-kvm \          # 启用KVM硬件虚拟化加速
-cpu host \            # 使用宿主机CPU的所有特性
-smp 16 \              # 分配16个CPU核心给虚拟机
-m 4096 \              # 分配4096MB(4GB)内存给虚拟机
-kernel /home/dingpenglong/code/$kernel_version/x86_64-build/arch/x86_build/boot/bzImage \  # 指定自定义内核镜像路径
-virtfs local,id=kmod_dev,path=/home/dingpenglong/,readonly,mount_tag=9p,security_model=none \  # 共享宿主机目录作为9p只读文件系统
-vga none \            # 禁用虚拟显卡输出
-nographic \           # 完全禁用图形界面,输出重定向到当前终端
-append "nokaslr console=ttyS0 root=/dev/vda rw kmemleak=on" \  # 内核启动参数:禁用地址随机化、串口控制台、根设备可读写、启用内存泄漏检测
-device virtio-scsi-pci \  # 添加virtio-scsi控制器设备
-drive file=image.qcow2,if=none,format=qcow2,cache=writeback,file.locking=off,id=root \  # 定义qcow2格式的根磁盘镜像
-device virtio-blk,drive=root,id=d_root \  # 将根磁盘作为virtio块设备挂载
-net tap \             # 使用TAP模式连接宿主机网络
-net nic,model=virtio,macaddr=00:11:22:33:44:03 \  # 添加virtio网卡并指定MAC地址
-drive file=1,if=none,format=raw,cache=writeback,file.locking=off,id=dd_1 \  # 定义原始格式(raw)的附加磁盘
-device scsi-hd,drive=dd_1,id=disk_1,logical_block_size=512,physical_block_size=512 \  # 将附加磁盘作为SCSI硬盘挂载
-gdb tcp::5555        # 开启GDB调试服务,监听5553端口

在每次启动qemu'虚拟机的时候,可以先使用ps -e -o pid,ppid,args | grep qemu

先查看有没有多个虚拟机开启,可以看到,三个进程都是开启的 qenu-system-x86_64

输出的父子进程关系如下所示: 5601 → 5602 (sudo) → 5603 (sudo) → 5604 (qenu-system-x86_64)

qemu启动后的三种杀死方式 1. 终端快捷键:Ctrl + A 然后按 X。 2. 按快捷键ctrl+a c(先按ctrl+a松开后再按c)再输入quit强制退出qemu。 3. 找到qemu的进程 执行sudo kill -9 <进程号>

二、GDB调试

GDB调试qemu,GDB调试需要去调试内核源码build/vmlinux,至于为什么是这个文件?

我查了一些内容:vmlinux 是编译生成的 未压缩、带完整调试符号的 ELF 可执行文件,也就是说vmlinux是完整的内核数据库,bzImage则是压缩过的。

#基础调试
target remote:5555          #你的调试器(GDB)连接到那台"远程电脑",让 GDB 可以控制它的执行
target remote :1234             # 连接 QEMU 调试端口
c                               # 继续执行 (Continue)
Ctrl+C                           # 中断程序执行
si                              # 单步执行汇编指令 (Step Instruction)
n                               # 源代码级单步跳过 (Next)
s                               # 源代码级单步进入 (Step)

#断点调试
b start_kernel              # 在函数入口设置断点
b *0xffffffff81000000           # 在指定地址设置断点
watch *(int*)0x1234         # 监控内存地址写入
rwatch [expression]         # 监控内存读取
info breakpoints            # 查看所有断点
disable 2               # 禁用 2 号断点
delete 3                # 删除 3 号断点

#上下文查看
bt                  # 查看调用栈 (BackTrace)
info registers              # 显示所有寄存器值
p $rax                  # 查看 RAX 寄存器值
p $lx_current().pid         #打印断点的进程
p $lx_current().comm            #打印断点的进程
&((struct cifsFileInfo *)0)->tlink  #打印结构体偏移量

#内存操作
x/10x 0x1234                # 以十六进制查看 10 个内存字
x/20s 0xabcd                # 查看内存中的 ASCII 字符串
x/i $pc                 # 反汇编当前指令
disassemble /r              # 带机器码反汇编当前函数
set {int}0x1234 = 5         # 修改内存值

三、常用命令/工具(必须掌握)

Linux命令大全

tar -xzf filename.tar.gz  # 对于.tar.gz文件
tar -xJf filename.tar.xz  # 对于.tar.xz文件
sudo apt install ./filename.deb  #对于安装deb文件

1.vim编辑器

点击查看vim的使用

2.Git的使用

点击查看git的使用

3.Tmux终端复用

tmux new -t <name>  #创建名为 <name> 的新会话
Ctrl + B D      #退出当前会话
tmux det   #退出当前tmux到后台
tmux ls         #显示所有会话tm
tmux attach -t <name>   #重新连接到指定会话
tmux kill-session -t  <name> #删除会话

4.ps进程管理

ps aux          #查看所有进程的详细信息    
ps -eo pid,ppid,cmd #显示进程的 PID、PPID 和完整命令   
ps -ef | grep <进程名> #过滤特定进程 ps -ef | grep nginx
ps -e -o pid,ppid,args | grep qemu    #用于查询qemu虚拟机的pid、ppid、args 每次启动qemu虚拟机的时候要确保没有同时开启多个虚拟机

5.SSH&SCP远程连接

ssh user@host               #例如ssh root@192.168.1.100
ssh -p <port> user@host         #指定端口登录
scp file user@host:/path        #上传文件到远程主机
scp user@host:/path/file local_dir  #从远程主机下载文件
ssh-copy-id user@host           #将公钥复制到远程主机,在远程登陆的时候可以先将远程的主机公钥复制过来,实现免密登陆

四、个人博客搭建

博客怎么搭建大致流程就是如下:

写博客有个好处就算可以让自己做过的东西再梳理一遍,这样更有效的加深记忆。

域名配置流程

  1. 阿里云注册yourname.com
  2. 添加 DNS 解析记录
GitHub Pages 的 IP 地址: 185.199.108.153 185.199.109.153 185.199.110.153 185.199.111.153
  1. GitHubPages 配置
创建 <username>.github.io 仓库
启用 GitHub Pages 服务
配置自定义域名
  1. 使用pandoc将markdown文件生成类似Linux源码格式的html文件
# 下载Linux内核官方CSS
wget https://kernel.org/doc/html/latest/_static/css/theme.css
pandoc input.md -o output.html --css=theme.css --toc --standalone

内核源码解读

点击查看Linux内核源码解读(持续更新中)

文件系统

点击查看文件系统

内存管理

点击查看内存管理

进程、线程管理与调度

点击查看进程、线程管理与调度

内核调试工具

点击查看内核调试工具

Linux 内核实战

点击查看nfs实战问题分析(持续更新中)

遇到的常见问题的小Tips

1. 虚拟机卡在了Boot form Hard Disk阶段

这种情况只能重装虚拟机系统
注:做实验千万不要在Vitrual manager的虚拟机上跑,最好在qemu上跑。

2. 调试补丁

3.git clone时候:Failed to connect to github.com port 443 after 0 ms: 连接被拒绝

4.编译的.ko文件,如何映射到内核文件里面?

5.遇到无法卸载挂载文件?

6.mount 使用nfs3.0版本无法挂载的问题?

7.从源码安装bochs的时候报错 出现错误”install: cannot stat `./bochsdbg': No such file or directory“?

8.在使用ssh登陆的时候可以,修改/etc/ssh/ssh_config