首先肯定要先用虚拟机起一个linux操作系统,我用的是Ubuntu22.04发行版。
使用远程登陆的方法就可以在物理机器上远程操作了,复制粘贴什么的也特别方便;
还有一个需要注意的地方是远程的时候,一定要使用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内部启动,镜像坏了或在内核错误了,可以重新再拉一个内核和镜像。
curl -fsSL https://code-server.dev/install.sh | sh
make gtags # 用于 VSCode 的代码跳转功能
安装这两个主要想着方便我用类似VScode的形式去查看内核源码
当然也可以用vim去查看内核,后面将vim的使用的时候也有具体的操作,我对vim接触太少了我还是用不太习惯。
首先需要下载内核源码,内核源码的地址,这个地址里面有各个版本的内核源码。
我用到的是内核4.19版本的源码,然后创建好的虚拟机编译去内核源码。
cd llinux-4.9.19/ mkdir build
make O=build menuconfig
make O=build bzImage -j$(nproc)
可以使用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(用于开发用户空间程序)CONFIG_EXT2_FS := m # 设置为模块编译
make -C /path/to/kernel/source M=$(pwd) modules
内核编译好了之后,就需要用一个仿真器启动内核了,如何安装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-helpergroups | 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 virbr0virsh 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调试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 # 修改内存值tar -xzf filename.tar.gz # 对于.tar.gz文件
tar -xJf filename.tar.xz # 对于.tar.xz文件
sudo apt install ./filename.deb #对于安装deb文件tmux new -t <name> #创建名为 <name> 的新会话
Ctrl + B D #退出当前会话
tmux det #退出当前tmux到后台
tmux ls #显示所有会话tm
tmux attach -t <name> #重新连接到指定会话
tmux kill-session -t <name> #删除会话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虚拟机的时候要确保没有同时开启多个虚拟机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 #将公钥复制到远程主机,在远程登陆的时候可以先将远程的主机公钥复制过来,实现免密登陆博客怎么搭建大致流程就是如下:
写博客有个好处就算可以让自己做过的东西再梳理一遍,这样更有效的加深记忆。
yourname.comGitHub Pages 的 IP 地址: 185.199.108.153 185.199.109.153 185.199.110.153 185.199.111.153创建 <username>.github.io 仓库
启用 GitHub Pages 服务
配置自定义域名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这种情况只能重装虚拟机系统
注:做实验千万不要在Vitrual manager的虚拟机上跑,最好在qemu上跑。
patch -p1 < /home/linux/blog/course/kernel/src/0001-debug-vfs.patchmake menuconfig、编译内核make bzImage -j8sudo systemctl disable firewalldsudo insmod your_module.ko 或者sudo modprobe your_modulelsmod | grep your_modulesudo rmmod your_module有时候umount挂载文件会出现'device is busy'的问题
可以使用以下命令查看使用挂载点的进程: lsof +D <挂载点> fuser -m <挂载点> 找到进程号后,就可以使用kill -SIGKILL <进程号> 杀死进程
mount -t nfs -o vers=3 ${server_ip}:/tmp/s_test /mnt