├── 最终报告.pdf ├── .DS_Store ├── 决赛答辩.pptx ├── 初赛报告文档.pdf ├── 技术交流报告.pptx ├── 技术实践分享.pptx ├── ppt ├── .DS_Store ├── 初步设想.pdf ├── 2022-4-16.pptx ├── 2022-4-23.pptx ├── 2022-4-30.pptx ├── 2022-5-14.pptx ├── 2022-5-21.pptx ├── 2022-5-28.pptx ├── 2022-6-11.pptx ├── 2022-6-4.pptx ├── uIPC一阶段设计.pptx ├── 展示文档 │ ├── .DS_Store │ ├── utils.md │ ├── qemu.md │ ├── uring.md │ ├── linux-uring.md │ ├── ipc-bench.md │ └── linux-kernel.md ├── 用户态中断流程梳理.pptx ├── OS大实验分享-用户态中断简介.pptx ├── qemu工作文档分块 │ ├── .DS_Store │ ├── 各类用户程序.md │ └── 编译技巧和环境.md ├── uintr-intel-linux.pptx ├── 利用uintr优化网络服务性能.pptx ├── OS大实验中期报告-usr-uintr.pptx ├── Proj6 user interrupt.docx ├── uintr-linux-kernel commit summary.pptx ├── uring_problem.md ├── 物理机器.md ├── how-to-build-a-ubuntu-rootfs.md ├── 应用场景.md ├── 直接发中断6-4.md ├── qemu-workinglog.md ├── debug-log-5-5.md ├── uintr_in_kernel.md ├── 调度问题5-28.md ├── worklog5-6.md ├── qemu-tutorial.md └── asm分析.md ├── 资料 └── io_uring-徐浩-阿里云.pdf ├── 最终报告.assets ├── image-20220815084334928.png ├── image-20220815124543486.png ├── image-20220815124637849.png ├── image-20220815134413941.png ├── image-20220815134736044.png ├── image-20220815153436838.png ├── image-20220815153642964.png ├── image-20220815155324261.png ├── image-20220815155519329.png ├── image-20220815164033900.png ├── image-20220815165830700.png ├── image-20220815165849010.png ├── image-20220815182728082.png ├── image-20220815183045320.png ├── image-20220815210804904.png ├── image-20220815210811630.png └── image-20220815210834494.png ├── ~$技术实践分享.pptx ├── 初赛报告文档.md ├── README.md └── 最终报告.md /最终报告.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.pdf -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/.DS_Store -------------------------------------------------------------------------------- /决赛答辩.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/决赛答辩.pptx -------------------------------------------------------------------------------- /初赛报告文档.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/初赛报告文档.pdf -------------------------------------------------------------------------------- /技术交流报告.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/技术交流报告.pptx -------------------------------------------------------------------------------- /技术实践分享.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/技术实践分享.pptx -------------------------------------------------------------------------------- /ppt/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/.DS_Store -------------------------------------------------------------------------------- /ppt/初步设想.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/初步设想.pdf -------------------------------------------------------------------------------- /ppt/2022-4-16.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/2022-4-16.pptx -------------------------------------------------------------------------------- /ppt/2022-4-23.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/2022-4-23.pptx -------------------------------------------------------------------------------- /ppt/2022-4-30.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/2022-4-30.pptx -------------------------------------------------------------------------------- /ppt/2022-5-14.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/2022-5-14.pptx -------------------------------------------------------------------------------- /ppt/2022-5-21.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/2022-5-21.pptx -------------------------------------------------------------------------------- /ppt/2022-5-28.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/2022-5-28.pptx -------------------------------------------------------------------------------- /ppt/2022-6-11.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/2022-6-11.pptx -------------------------------------------------------------------------------- /ppt/2022-6-4.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/2022-6-4.pptx -------------------------------------------------------------------------------- /ppt/uIPC一阶段设计.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/uIPC一阶段设计.pptx -------------------------------------------------------------------------------- /ppt/展示文档/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/展示文档/.DS_Store -------------------------------------------------------------------------------- /ppt/用户态中断流程梳理.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/用户态中断流程梳理.pptx -------------------------------------------------------------------------------- /资料/io_uring-徐浩-阿里云.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/资料/io_uring-徐浩-阿里云.pdf -------------------------------------------------------------------------------- /ppt/OS大实验分享-用户态中断简介.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/OS大实验分享-用户态中断简介.pptx -------------------------------------------------------------------------------- /ppt/qemu工作文档分块/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/qemu工作文档分块/.DS_Store -------------------------------------------------------------------------------- /ppt/uintr-intel-linux.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/uintr-intel-linux.pptx -------------------------------------------------------------------------------- /ppt/利用uintr优化网络服务性能.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/利用uintr优化网络服务性能.pptx -------------------------------------------------------------------------------- /ppt/OS大实验中期报告-usr-uintr.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/OS大实验中期报告-usr-uintr.pptx -------------------------------------------------------------------------------- /ppt/Proj6 user interrupt.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/Proj6 user interrupt.docx -------------------------------------------------------------------------------- /最终报告.assets/image-20220815084334928.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815084334928.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815124543486.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815124543486.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815124637849.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815124637849.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815134413941.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815134413941.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815134736044.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815134736044.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815153436838.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815153436838.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815153642964.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815153642964.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815155324261.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815155324261.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815155519329.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815155519329.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815164033900.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815164033900.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815165830700.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815165830700.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815165849010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815165849010.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815182728082.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815182728082.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815183045320.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815183045320.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815210804904.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815210804904.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815210811630.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815210811630.png -------------------------------------------------------------------------------- /最终报告.assets/image-20220815210834494.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/最终报告.assets/image-20220815210834494.png -------------------------------------------------------------------------------- /ppt/uintr-linux-kernel commit summary.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OS-F-4/usr-intr/HEAD/ppt/uintr-linux-kernel commit summary.pptx -------------------------------------------------------------------------------- /~$技术实践分享.pptx: -------------------------------------------------------------------------------- 1 | Microsoft Office User Microsoft Office User -------------------------------------------------------------------------------- /ppt/uring_problem.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | who does dequeue operation of cq ? 4 | 5 | 阅读的文章和代码: 6 | 7 | ``` 8 | https://unixism.net/2020/04/io-uring-by-example-part-3-a-web-server-with-io-uring/ 9 | 10 | ``` 11 | 12 | -------------------------------------------------------------------------------- /ppt/物理机器.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | # 在物理机器上安装uintr linux 6 | 7 | 8 | 9 | 10 | 11 | ## centos上安装和使能gcc-11 12 | 13 | ``` 14 | yum list installed 15 | yum install gcc-toolset-11 16 | scl enable gcc-toolset-11 bash 17 | ``` 18 | 19 | 20 | 21 | 22 | 23 | 编译内核 24 | 25 | ``` 26 | cp /boot/configxxx .config 27 | make menuconfig 28 | # select uintr 29 | make -j 24 30 | sudo make modules_install 31 | sudo make install 32 | reboot 33 | ``` 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | ## 物理机器编译qemu 42 | 43 | ``` 44 | sudo apt install pkg-config 45 | sudo apt install libglib2.0-dev 46 | sudo apt install libpixman-1-dev 47 | sudo apt install bison flex 48 | ``` 49 | 50 | 51 | 52 | 53 | 54 | ## 在x86台式机 55 | 56 | 开始编译出来的内核模块太大,导致initramfs也太大,导致夹在时内存溢出, 需要经过压缩或者去除后才能启动 57 | 58 | ``` 59 | SHW@SHW:/tmp# cd /lib/modules/ 60 | SHW@SHW:/tmp# find . -name *.ko -exec strip --strip-unneeded {} + 61 | ``` 62 | 63 | -------------------------------------------------------------------------------- /ppt/展示文档/utils.md: -------------------------------------------------------------------------------- 1 | # 其他编译帮助 2 | 3 | 4 | 5 | ## 如何安装gcc-11 6 | 7 | ### ubuntu: 8 | 9 | ```shell 10 | # 安装gcc-11 11 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test 12 | sudo apt install build-essential manpages-dev software-properties-common 13 | sudo apt update && sudo apt install gcc-11 14 | ``` 15 | 16 | 切换默认的gcc到gcc-11 17 | 18 | ```shell 19 | sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100 20 | ``` 21 | 22 | 其中最后的数字100表示优先级, 只要比其他gcc高, 该gcc就会成为默认的gcc。 23 | 24 | 25 | 26 | ### centos: 27 | 28 | ```shell 29 | yum install gcc-toolset-11 30 | scl enable gcc-toolset-11 bash 31 | ``` 32 | 33 | centos 暗转工具链后可以通过`as -v`查看版本, 只要版本大于2.36就可以编译uintr程序, 否则需要手动编译安装或者通过更新软件包的方式升级汇编器。 34 | 35 | 36 | 37 | ## 如何安装binutils-2.38 38 | 39 | 从网上下载`binutils-2.38.tar.bz2` 40 | 41 | ```shell 42 | tar -jxvf binutils-2.38.tar.bz2 43 | cd binutils-2.38 44 | ./configure 45 | make -j 46 | sudo make install 47 | ``` 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /ppt/how-to-build-a-ubuntu-rootfs.md: -------------------------------------------------------------------------------- 1 | # 如何构建一个 ubuntu 文件系统 2 | 3 | > 提示:执行一些操作可能需要有 root 权限 4 | 5 | ## 依赖 6 | 7 | ```bash 8 | apt-get install -y debootstrap 9 | ``` 10 | 11 | ## 指令 12 | 13 | ```bash 14 | debootstrap --components=main,universe focal ./rootfs "http://mirrors.tuna.tsinghua.edu.cn/ubuntu" 15 | ``` 16 | 17 | ## 操作 18 | 19 | ```bash 20 | cd rootfs 21 | chroot . 22 | # 在启动的新 shell 中,执行以下指令: 23 | export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 24 | apt-get install -y build-essential manpages-dev software-properties-common 25 | add-apt-repository ppa:ubuntu-toolchain-r/test 26 | apt-get update 27 | apt-get install -y gcc-11 g++-11 cmake pkg-config libzmqpp-dev wget texinfo git 28 | update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 60 --slave /usr/bin/g++ g++ /usr/bin/g++-11 29 | apt-get clean 30 | wget https://mirrors.tuna.tsinghua.edu.cn/gnu/binutils/binutils-2.38.tar.bz2 31 | tar -jxf binutils-2.38.tar.bz2 32 | cd binutils-2.38 && ./configure && make -j && make install && cd .. 33 | rm -rf binutils-2.38 binutils-2.38.tar.bz2 34 | git clone -b linux-rfc-v1 https://github.com/OS-F-4/ipc-bench.git 35 | cd ipc-bench 36 | mkdir build 37 | cd build 38 | cmake .. 39 | make 40 | ``` 41 | 42 | ## 用到 qemu 中 43 | 44 | 记得在根目录下新建一个可执行文件 `init`,内容如下: 45 | 46 | ```bash 47 | #!/bin/sh 48 | mount -t proc none /proc 49 | mount -t sysfs none /sys 50 | echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n" 51 | exec /bin/bash 52 | ``` 53 | 54 | ## 构建文件系统压缩包 55 | 56 | ```bash 57 | # 在 rootfs 目录下 58 | find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../ubuntu-x86_64.cpio.gz 59 | ``` 60 | 61 | -------------------------------------------------------------------------------- /ppt/展示文档/qemu.md: -------------------------------------------------------------------------------- 1 | # uintr-qemu说明文档 2 | 3 | - [toc] 4 | 5 | ## 项目简介 6 | 7 | 本项目旨在基于qemu实现intel x86的用户态中断特性, 使得在没有相关硬件的条件下, 我们也能通过模拟器的方式来利用我们的硬件特性, 编写和新的操作系统和用户程序。 8 | 9 | 我们总体的开发过程如下, 具体的问题解决过程以及解决方式详见[探究过程](https://github.com/OS-F-4/usr-intr/blob/main/ppt/qemu%E5%B7%A5%E4%BD%9C%E6%96%87%E6%A1%A3%E5%88%86%E5%9D%97/%E9%97%AE%E9%A2%98%E4%BB%A5%E5%8F%8A%E6%8E%A2%E7%A9%B6%E8%BF%87%E7%A8%8B.md), 其中有上万字的详细的开发过程中的困难以及解决方式。 10 | 11 | 1. 环境准备 12 | 2. 指令捕捉,向软件反馈硬件特性 13 | 3. 内存读写实现发送 14 | 4. 修改中断处理实现接收 15 | 5. 中断收尾实现完整运行 16 | 6. 实现直接发中断,提高性能 17 | 7. 多次调试,完善实现细节 18 | 19 | 20 | 21 | ## 使用方法 22 | 23 | ### 编译qemu 24 | 25 | ```shell 26 | cd qemu 27 | mkdir build 28 | ../configure --enable-debug --target-list=x86_64-softmmu --enable-trace-backends=log 29 | make -j 30 | ``` 31 | 32 | 遇到缺少的包, 安装即可: 33 | 34 | ```shell 35 | ERROR: Cannot find Ninja 36 | sudo apt install ninja-build 37 | ``` 38 | 39 | 40 | 41 | ### 运行linux内核 42 | 43 | 编译内核详见[如何编译支持用户态中断的内核](https://github.com/OS-F-4/usr-intr/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/linux-kernel.md)。 44 | 45 | ```shell 46 | #!/bin/bash 47 | # 指定文件系统的路径 48 | ubuntu=~/qemu_uintr/ubuntu-x86_64.cpio.gz 49 | box=~/qemu_uintr/initramfs/initramfs-busybox-x86_64.cpio.gz 50 | PORT=2333 51 | # 指定qemu可执行文件路径 52 | QEMU=~/qemu_uintr/qemu/build/x86_64-softmmu/qemu-system-x86_64 53 | # 指定内核路径 54 | KERNEL=~/qemu_uintr/uintr-linux-kernel/build/arch/x86_64/boot/bzImage 55 | $QEMU -smp 2 \ 56 | -machine q35,kernel_irqchip=split \ 57 | -m 2048M -nographic -cpu qemu64 \ 58 | -kernel $KERNEL \ 59 | -initrd $ubuntu \ 60 | -append "root=/dev/ram0 rw rootfstype=ext4 console=ttyS0 init=/linuxrc" \ 61 | -net user,id=net,hostfwd=tcp::$(PORT)-:22 -net nic,model=e1000e \ 62 | -serial mon:stdio 63 | ``` 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /ppt/展示文档/uring.md: -------------------------------------------------------------------------------- 1 | # 基于用户态中断和io_uring的异步IO用户程序 2 | 3 | ## 项目简介 4 | 5 | 本项目是`uintr+io_uring`异步IO的用户程序仓库, 程序的运行需要在我们修改过的[内核](https://github.com/OS-F-4/uintr-linux-kernel/tree/uring)_上, 并且操作系统运行在支持用户态中断的硬件环境中。 6 | 7 | 文件树: 8 | 9 | ``` 10 | . 11 | ├── apic 12 | ├── bench 13 | ├── go-cat 14 | ├── io_uring-by-example 15 | └── liburing 16 | ``` 17 | 18 | 其中`apic`目录下是一个简单的测试程序, 仅仅测试我们能否收到io_uring完成的用户态中断通知, 程序输出`in handler`代表收到中断。 19 | 20 | `bench`是使用不同方法测试完成同样的计算和IO任务所需用的总用时以及IO的延时, 我们的结果表明基于用户态中断和io_uring的方法即能实现计算和io的重叠, 使得完成任务的总用时更小, 同时由于中断的通知机制, IO的延迟方面也比单纯用io_uring要更出色。 21 | 22 | `go-cat`是采用Cgo编程的样例程序, 为了使得中断处理函数更快的退出, 从而不会因为中断不能嵌套从而导致部分中断的延迟较高, 我们希望在中断函数中调用线程或者协程去处理IO, 启用线程的开销比协程更大, 我们期望使用协程, 但是在C语言中还没有对协程的支持, 所以我们采用Cgo编程, 将go语言的特性嵌入C语言中, 从而达到更加高效的中断处理。 23 | 24 | `io_uring-by-example`包含了几个io_uring的简单用户程序, 能够帮助新手更快的了解io_uring的使用以及liburing的使用。在其中我们主要修改了`03_cat_liburing`, 使得改用户程序能够支持用户态中断来处理IO的完成, 这个版本的实现是线程版本。 25 | 26 | `liburing`是经过我们适配的liburing库, 在编译和链接以上的用户程序时, 需要链接次文件夹中对应的头文件。 27 | 28 | ## 使用方法 29 | 30 | ```shell 31 | cd liburing 32 | make # 先进入liburing 目录, 编译后其他仓库的代码才能成功编译和链接 33 | cd xxx # 进入其他目录进行编译 34 | make 35 | ``` 36 | 37 | 每个目录下均有`Makefile`, 一般情况下直接make即可, 由于不同系统版本用户态中断的编号可能会改变, 使用时需要查看内核对应的系统调佣编号是否正确, 可以通过定义宏来改变, 或者手动修改源代码。 38 | 39 | 注意在链接阶段必须链接本仓库内的liburing, 必要时修改Makefile中liburing的路径。 40 | 41 | ## bench 结果 42 | 43 | 我们在执行200个IO任务和1000个计算任务的情况下获得了bench的测试结果, 罗列如下: 44 | 45 | 其中normal是使用普通同步IO方式来进行IO的读写, uring是单独使用io_uring的情况, uintr是使用io_uring以及用户态中断的情况。本测试中, 单纯的计算用时3410806us。一下是不同大小的IO的延时和总的任务完成时间的情况。 46 | 47 | | 方法 | 总用时(us) | 4K 延时 | 4M 延时 | 40M 延时 | 48 | | ------ | ---------- | ------- | ------- | -------- | 49 | | normal | 4163422 | 259 | 38753 | 698487 | 50 | | uring | 3546226 | 205714 | 269079 | 1283943 | 51 | | uintr | 3591993 | 360 | 41431 | 727977 | 52 | 53 | 更多细致的分析见我们的的[技术分享报告](https://github.com/OS-F-4/usr-intr/blob/main/%E6%8A%80%E6%9C%AF%E4%BA%A4%E6%B5%81%E6%8A%A5%E5%91%8A.pptx)。以及[最终报告](https://github.com/OS-F-4/usr-intr/blob/main/%E6%9C%80%E7%BB%88%E6%8A%A5%E5%91%8A.md)。 -------------------------------------------------------------------------------- /ppt/展示文档/linux-uring.md: -------------------------------------------------------------------------------- 1 | # linux-uring 2 | 3 | ## 项目简介 4 | 5 | 本项目基于linux的io_uring子系统, 利用用户态中断的硬件特性作为io完成的通知机制, 实现io的异步提交和io完成的异步返回的双向异步过程, 预期在达到io和计算重叠这样基本的异步框架下更低io延迟。 6 | 7 | 我们的内核程序位于[仓库](https://github.com/OS-F-4/uintr-linux-kernel/tree/uring)。我们的用户程序以及测试程序位于[仓库](https://github.com/OS-F-4/uring)。 8 | 9 | 详细的原理说明详见我们的[技术分享报告](https://github.com/OS-F-4/usr-intr/blob/main/%E6%8A%80%E6%9C%AF%E4%BA%A4%E6%B5%81%E6%8A%A5%E5%91%8A.pptx)。以及[最终报告](https://github.com/OS-F-4/usr-intr/blob/main/%E6%9C%80%E7%BB%88%E6%8A%A5%E5%91%8A.md)。 10 | 11 | ## 使用方法 12 | 13 | ### 编译内核 14 | 15 | 编译内核需要`gcc-11`, 查看如何[安装并设置编译器](https://github.com/OS-F-4/usr-intr/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/utils.md)。 16 | 17 | 内核的编译方法同`uintr-linux` 18 | 19 | 注: 在内核编译的过程中可能需要安装支持编译内核的软件包, 通过包管理器安装即可。 20 | 21 | ```shell 22 | cd uintr-linux-kernel/ 23 | make O=build x86_64_defconfig 24 | make O=build menuconfig 25 | ``` 26 | 27 | 在选择构建参数时: 28 | 29 | 在`General setup`目录下, 选择`Initial RAM filesystem and RAM disk (initramfs/initrd) support`。 30 | 31 | 在`Device Drivers`目录中`Block device`下, 选择`RAM block device support`, 并设置` Default RAM disk size (kbytes)`为65536KB。 32 | 33 | 在`Processor type and features ---> `设置`User Interrupts (UINTR) ` 34 | 35 | 随后开始构建: 36 | 37 | ```shell 38 | make O=build bzImage -j 39 | make O=build modules -j 4 40 | ``` 41 | 42 | 构建完成后, 内核的镜像文件会在`uintr-linux-kernel/build/arch/x86_64/boot/bzImage`, 在后续过程中可以直接调用。 43 | 44 | 45 | 46 | ### 文件系统的构建 47 | 48 | 参考[uintr-linux](https://github.com/OS-F-4/usr-intr/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/linux-kernel.md)中有关构建文件系统的章节。 49 | 50 | 51 | 52 | ### 启动linux的shell脚本如下: 53 | 54 | ```shell 55 | #!/bin/bash 56 | # 指定文件系统的路径 57 | ubuntu=~/qemu_uintr/ubuntu-x86_64.cpio.gz 58 | box=~/qemu_uintr/initramfs/initramfs-busybox-x86_64.cpio.gz 59 | PORT=2333 60 | # 指定qemu可执行文件路径 61 | QEMU=~/qemu_uintr/qemu/build/x86_64-softmmu/qemu-system-x86_64 62 | # 指定内核路径 63 | KERNEL=~/qemu_uintr/uintr-linux-kernel/build/arch/x86_64/boot/bzImage 64 | $QEMU -smp 2 \ 65 | -machine q35,kernel_irqchip=split \ 66 | -m 2048M -nographic -cpu qemu64 \ 67 | -kernel $KERNEL \ 68 | -initrd $ubuntu \ 69 | -append "root=/dev/ram0 rw rootfstype=ext4 console=ttyS0 init=/linuxrc" \ 70 | -net user,id=net,hostfwd=tcp::$(PORT)-:22 -net nic,model=e1000e \ 71 | -serial mon:stdio 72 | ``` 73 | 74 | -------------------------------------------------------------------------------- /ppt/应用场景.md: -------------------------------------------------------------------------------- 1 | --- 2 | marp: true 3 | --- 4 | 5 | ## 进展与应用场景设计 6 | 7 | **设计进展** 8 | 9 | * 解决一个问题:链接数量 10 | * 发现新问题:链式调用,load-balance 11 | 12 | **应用场景** 13 | 14 | * xpc / *bridge 的用户测例 15 | 16 | --- 17 | 18 | ## 设计进展 19 | 20 | * 链接数量 21 | * 基础:64 22 | * 需求:512 (skybridge) ? 23 | * 编号复用 24 | 25 | --- 26 | 27 | ### 与 *bridge 的相同 28 | 29 | *bridge 的优势 (与已有同步 IPC 相比) 30 | 31 | * context switch 小 32 | * 链接一但建立,通信的代码路径很短 33 | 34 | 核心:专用通信通道,bypass kernel 35 | 36 | | 操作 | cycles | 37 | | :------------------------: | :--------: | 38 | | func call | 24 | 39 | | write to CR3 | 186 | 40 | | no-op system call w/ KPTI | 431 | 41 | | no-op system call w/o KPTI | 181 | 42 | | VMFUNC | 134 | 43 | | wrpkru | 28 | 44 | | xcall | 150 ~ 21 | 45 | | uintr | ??? (~150) | 46 | 47 | 48 | 49 | ![bg right contain vertical](sky1.jpg) 50 | 51 | --- 52 | 53 | ### 与 *bridge 的区别 54 | 55 | 同步 vs 异步 56 | 57 | 异步的缺点: 58 | 59 | * 延迟不好控制:链式调用 60 | 61 | * eg: 应用 -> 数据库 -> 文件系统服务 -> 磁盘驱动服务 62 | * 问题:所有服务都需要在线,否则整体延时崩了 63 | * 需要 load-balance 64 | * 平衡 sender / receiver 的 load 65 | 66 | 67 | 异步优势: 吞吐量 68 | * 专核专用 -> 核多多益善 69 | * (同步性能与核数关系不大) 70 | * 最底层硬件是异步的:硬件中断 71 | 72 | --- 73 | 74 | ### *bridge 测试 75 | 76 | * skybridge / underbridge 77 | * IPC ping-pong: 无负载平均延时 78 | * KVS: 有负载的简单测试 79 | * 微内核 sqlite3 + xv6fs + ram-disk: 真实 app 80 | * XPC 81 | * IPC ping-pong: 无负载平均延时 82 | * 微内核 fs / TCP: 有负载的简单测试 83 | * 微内核 sqlite3 / 微内核 android binder / 微内核 TCP HTTP server: 真实 app 84 | 85 | 86 | --- 87 | 88 | ### uintr 的优势 89 | 90 | * 安全:不会破坏隔离性 91 | * 对内核修改小 ??? 92 | 93 | --- 94 | 95 | ### 应用场景:难点 96 | 97 | 最理想:sender / receiver / driver(syscall) 全异步 / 全在线 / 负载均衡 98 | 99 | 全异步 (工作量) 100 | 101 | * 同步 sender: 线程库,尽力挖掘异步潜能 102 | * receiver: 基于事件机制,适配比较容易 103 | * driver / syscall: 需要一层异步封装 104 | 105 | 全在线 (设计专门场景,通用性?) 106 | 107 | * 设计针对的 OS 调度 108 | * 如何尽量驻留:尽力多核,专核专用 109 | * 如何快速唤醒:新调度类(低于 RT,高于 CFS) ? 110 | * 如何切换状态:自适应 111 | 112 | 负载均衡 (设计专门场景,难。。。) 113 | 114 | * 同步 receiver 运行状态: 115 | * // todo 116 | 117 | * receiver 负载不够,减少核数 118 | 119 | * sender 负载不够 ==> 增加负载?需要切换? 120 | * 批量提交请求? 121 | * 轮询? 122 | 123 | 通用性 124 | 125 | * 简单、阻塞的 IPC 适合异步吗? 126 | * 如何兼容同步? 127 | 128 | --- 129 | 130 | ### 应用场景:设计 131 | 132 | * sender 有一定工作负载,异步 133 | * 链路层数不多:容易满足 134 | * 终点 server 异步/高速:DPDK 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /ppt/展示文档/ipc-bench.md: -------------------------------------------------------------------------------- 1 | # ipc-bench 2 | 3 | ## 简介: 4 | 5 | 本项目希望通过编写bench程序, 使用不同的方法来进行程序间的通信, 设定benchmark来测定不同进程间通信方法的性能的区别。 6 | 7 | 项目地址: https://github.com/OS-F-4/ipc-bench/tree/linux-rfc-v1 8 | 9 | ## 运行方式: 10 | 11 | 在根文件夹下: 12 | 13 | ```shell 14 | mkdir build 15 | cd build 16 | cmake .. 17 | make 18 | ``` 19 | 20 | 在`build/source`会出现对应ipc方法的文件夹, 文件夹中有可执行文件。如执行`build/source//tcp/tcp -c 200 -s 5` 代表使用tcp方法, 收发200次, 每次的大小为5个字节。 21 | 22 | 程序会有类似如下的输出: 23 | 24 | ``` 25 | [root@localhost source]# ./uintr-shm/uintr-shm -s 1 26 | ============ RESULTS ================ 27 | Message size: 1 28 | Message count: 1000 29 | Total duration: 2.648 ms 30 | Average duration: 1.934 us 31 | Minimum duration: 1.792 us 32 | Maximum duration: 71.680 us 33 | Standard deviation: 2.210 us 34 | Message rate: 377670 msg/s 35 | ===================================== 36 | ``` 37 | 38 | 其中`uintrfd-bi`是两个线程来回发送消息。 `uintrfd-uni`是两个线程一发一收, 单向发送。`uintr-sm-bi`是两个线程公用同一块内存进行更大的消息传递。`uint-shm` 是两个进程通过共享内存的方式进行更大的消息的传递。 39 | 40 | 41 | 42 | ## qemu结果 43 | 44 | | IPC type | Message rate (msg/s, size = 1) | Message rate (msg/s, size = 4096) | 45 | | --------------- | ------------------------------- | ---------------------------------- | 46 | | uintr (thread) | 28935 | 23305 | 47 | | uintr (process) | 34392 | 22662 | 48 | | signal | 6286 | N/A | 49 | | eventfd | 6649 | N/A | 50 | | pipe | 2858 | 2256 | 51 | | fifo | 2782 | 2674 | 52 | | domain | 4835 | 2781 | 53 | 54 | 55 | 56 | ## 物理机器结果 57 | 58 | ### size = 1 59 | 60 | | method | Average(us) | min(us) | max(us) | rate(msg/s) | 61 | | ----------- | ----------- | ------- | ------- | ----------- | 62 | | eventfd-bi | 37.250 | 11.520 | 274.944 | 26630 | 63 | | mq | 126.553 | 19.968 | 380.672 | 7841 | 64 | | shm | 1.568 | 1.280 | 11.264 | 503577 | 65 | | signal | 97.651 | 7.936 | 326.144 | 10159 | 66 | | uintrfd-bi | 4.366 | 4.096 | 5.632 | 191868 | 67 | | uintrfd-uni | 2.035 | 1.792 | 3.584 | 280440 | 68 | | domain | 88.644 | 16.640 | 342.016 | 11183 | 69 | | fifo | 38.607 | 22.528 | 364.800 | 25544 | 70 | | mmap | 1.314 | 1.024 | 11.008 | 574026 | 71 | | pipe | 105.078 | 25.088 | 339.456 | 9436 | 72 | | tcp | 170.654 | 36.864 | 367.104 | 5828 | 73 | | uintr-shm | 1.934 | 1.792 | 71.680 | 377670 | 74 | 75 | 76 | 77 | ### size = 4096 78 | 79 | | method | Average(us) | min(us) | max(us) | rate(msg/s) | 80 | | ------------- | ----------- | ------- | ------- | ----------- | 81 | | shm | 7.034 | 6.400 | 88.320 | 133986 | 82 | | uintrfd-sm-bi | 7.744 | 7.168 | 44.032 | 116340 | 83 | | domain | 50.049 | 21.760 | 276.992 | 19758 | 84 | | fifo | 59.966 | 23.808 | 280.576 | 16527 | 85 | | mmap | 6.660 | 6.400 | 15.872 | 141050 | 86 | | pipe | 70.535 | 26.624 | 296.960 | 14011 | 87 | | tcp | 162.982 | 39.168 | 423.424 | 6098 | 88 | | uintr-shm | 2.173 | 2.048 | 74.240 | 347037 | 89 | 90 | 91 | 92 | ## 结果分析: 93 | 94 | 我们可以看到, 基于用户态中断方法的进程间通信性能, 相比所有的直接通知的IPC机制都要高效, 可以获得**数十倍的性能提升**。对于`shm`和`mmap`而言, 其实现为两边用户轮询一块共享的内存, 所以延迟和速度比较低, 但是这对cpu的占用较高, cpu无法进行其他任务, 对于真正的应用而言, 这样的进程间通信方式是不实际的。 95 | 96 | 对比qemu和物理机的数据, 同样条件下, qemu的运行速度慢于物理机, 这符合模拟器运行速度慢的直觉。相同执行环境中不同方法之间的相对差距幅度也比较相似, 这也说明了qemu对硬件的性质的模拟是比较到位的, 同时也验证了我们在qemu中实现的用户态中断支持是有效且到位的。 97 | -------------------------------------------------------------------------------- /ppt/直接发中断6-4.md: -------------------------------------------------------------------------------- 1 | # 直接发中断 2 | 3 | 本部分主要介绍借助apic进行直接发中断相关的逻辑, 主要的问题是函数参数的传递, 在实现过程中, 还有一个有关权级的小问题也值得讨论。 4 | 5 | 在`hw/intc/apic.c` 中存在比较符合功能的函数`apic_deliver`, 但是其中的参数意义以及如何传递是个问题, 经过一些尝试以及和学长的讨论, 最后确定传递参数的形式, 并编写了新的函数。 6 | 7 | ```c 8 | static void apic_deliver(DeviceState *dev, uint8_t dest, uint8_t dest_mode, 9 | uint8_t delivery_mode, uint8_t vector_num, 10 | uint8_t trigger_mode) { //... 11 | } 12 | 13 | void send_ipi(DeviceState *dev, uint8_t dest, uint8_t nv){ 14 | qemu_mutex_lock_iothread(); 15 | apic_deliver(dev, dest, 0 ,APIC_DM_FIXED, nv, APIC_TRIGGER_EDGE); 16 | qemu_mutex_unlock_iothread(); 17 | } 18 | ``` 19 | 20 | `helper_senduipi`函数结尾添加如下逻辑: 21 | 22 | ```c 23 | if(sendNotify){ 24 | if(current)qemu_log("direct sending\n"); 25 | send_ipi(cpu_get_current_apic(), upid.nc.ndst, upid.nc.nv); 26 | } 27 | ``` 28 | 29 | 但是在运行后, 内核会出现panic的情况, 主要的原因是在内核态执行了用户程序的代码。这应当和uintr中断接受处理相关, 在中断处理程序中查看接受中断时的权级。注意到确实是在内核态跳转到了handler从而报错。 30 | 31 | ```c 32 | bool send = false; 33 | if(intno == UINTR_UINV ){ 34 | //查看当前的权级 35 | cpl = env->hflags & HF_CPL_MASK; 36 | qemu_log("-|-| perv: %d \n", cpl); 37 | if(env->uintr_uif == 0){ 38 | qemu_log("--uif not zero, return\n"); 39 | helper_clear_eoi(env); 40 | return; 41 | } 42 | ``` 43 | 44 | 在中断处理中添加权级的识别和处理: 45 | 46 | ```c 47 | //查看当前的权级 48 | cpl = env->hflags & HF_CPL_MASK; 49 | qemu_log("-|-| perv: %d \n", cpl); 50 | if(env->uintr_uif == 0){ 51 | qemu_log("--uif not zero, return\n"); 52 | helper_clear_eoi(env); 53 | return; 54 | } 55 | if(cpl != 3){ 56 | helper_clear_eoi(env); 57 | qemu_log("not in user mode return\n"); 58 | return; 59 | } 60 | ``` 61 | 62 | 随后程序可以正常结束。 63 | 64 | 验证是否成功发中断, 在内核给自己发中断的逻辑中添加pin, 如果是内核给自己发中断实现uintr, 则说明直接发中断没有成功, 否则即成功。 65 | 66 | ```c 67 | if (READ_ONCE(upid->puir)){ 68 | printk("sending self ipi\n"); 69 | apic->send_IPI_self(UINTR_NOTIFICATION_VECTOR); 70 | } 71 | ``` 72 | 73 | 运行`uipi_sample`, 得到如下结果: 74 | 75 | ```shell 76 | / # uipi_sample 77 | [ 81.653132] uintr_register_handler called 78 | [ 81.653936] recv: register handler task=93 flags 0 handler 401de5 ret 0 79 | [ 81.654885] uintr_create_fd called 80 | [ 81.655452] recv: Alloc vector success uintrfd 3 uvec 0 for task=93 81 | Receiver enabled interrupts 82 | [ 81.660718] uintr_register_sender called 83 | [ 81.663076] send: register sender task=94 flags 0 ret(uipi_id)=0 84 | Sending IPI from sender thread 85 | direct sending 86 | [ 81.665972] uintr_unregister_sender called 87 | [ 81.666642] send: unregister sender uintrfd 3 for task=94 ret 0 88 | -|-| perv: 0 89 | not in user mode return 90 | [ 81.667348] sending self ipi 91 | -|-| perv: 3 92 | -- User Interrupt handler -- 93 | [ 81.669100] recv: Release uintrfd for r_task 93 uvec 0 94 | [ 81.669775] uintr_unregister_handler called 95 | [ 81.670522] recv: unregister handler task=93 flags 0 ret 0 96 | ``` 97 | 98 | 可以看到内核还是给自己发中断的逻辑。经过和学长讨论,因为接收方是不断调用sleep,而在sleep就非常依赖内核的调度, 如果尝试不进行sleep系统调用, 一直while 死循环, 一直让接收方保持活跃且处于用户态, 则可能成功。 99 | 100 | 为此编译死循环等待的接受放进行调试, 发现没有`sending self ipi`, 直接发送ipi成功。 101 | 102 | ```c 103 | [ 14.778752] uintr_register_handler called 104 | [ 14.780159] recv: register handler task=79 flags 0 handler 401de5 ret 0 105 | [ 14.781533] uintr_create_fd called 106 | [ 14.783074] recv: Alloc vector success uintrfd 3 uvec 0 for task=79 107 | Receiver enabled interrupts 108 | [ 14.796844] uintr_register_sender called 109 | [ 14.800406] send: register sender task=80 flags 0 ret(uipi_id)=0 110 | Sending IPI from sender thread 111 | direct sending 112 | -|-| perv: 3 113 | [ 14.804970] uintr_unregister_sender called 114 | [ 14.806680] send: unregister sender uintrfd 3 for task=80 ret 0 115 | -- User Interrupt handler -- 116 | [ 14.819362] recv: Release uintrfd for r_task 79 uvec 0 117 | [ 14.820615] uintr_unregister_handler called 118 | [ 14.821756] recv: unregister handler task=79 flags 0 ret 0 119 | Success 120 | ``` 121 | -------------------------------------------------------------------------------- /初赛报告文档.md: -------------------------------------------------------------------------------- 1 | # 2022操作系统功能挑战初赛报告文档 2 | 3 | 报告小组:QUINT 4 | 5 | 成员:清华大学 王之栋,项晨东,孙迅 6 | 7 | ## 目标描述 8 | 9 | 我们小组的选题为 **[proj6-RV64N-user-level-interrupt](https://github.com/oscomp/proj6-RV64N-user-level-interrupt)** 。 10 | 11 | 我们希望探索运用新一代 Intel 硬件特性**用户态中断**(User Interrupt) ,搭建运行环境,设计新的 IPC 场景和框架,不断与传统 IPC 及 XPC、underbridge、skybridge 等新 IPC 进行性能比较并优化。 12 | 13 | 即选题仓库中第三题、第四题和第六题所描述的大致工作方向。一方面,我们希望测试用户态中断是否更快,理解它快在哪里,相比之下传统方式的性能瓶颈在哪里。另一方面,我们也希望把这个特性用起来,扩展操作系统内核,搭建我们设计的 IPC 框架,并找出合适的应用场景,说明它优化的有效性。 14 | 15 | 更多具体内容见文档后续部分。 16 | 17 | ## 比赛题目分析和相关资料调研 18 | 19 | 微内核正在变得越来越有影响力,相比之下宏内核,各模块之间的协同由函数调用的形式变成了进程间通信(IPC)的形式,因此如何优化IPC的性能变成了一个关键问题。 20 | 21 | 近几年,陈海波老师团队提出了诸如 XPC 、skybridge 、underbridge 等优化 IPC 问题的方式,但都各有一些缺陷——如 XPC 对硬件和内核的修改过大,underbridge 在安全上总让人感到担忧。 22 | 23 | Intel 推出的新一届硬件特性中,有一种名为用户态中断的机制(在Intel的手硬件规范手册中进行了介绍),可以在不陷入内核的情况下,将中断发到用户空间中处理。Linux应用该特性已经实现了一版内核,并进行了简单的性能测试,说明了 uintr 在 IPC 上的性能优势。 24 | 25 | 但是 Linux 的工作并没有深入,只是提供了一个工作基础。一方面,uintr本身是一种通知机制,不具备信息传递的功能,因此为了实现 IPC 的需求,uintr需要与信息传递结合在一起,并通过系统调用的形式以统一接口为用户呈现。另一方面,关于 uintr 的潜力如何,有哪些高效的应用场景,也仍是等待发掘和设计。 26 | 27 | 因此,设计 IPC 框架,拓展内核实现,并测试性能和应用场景,便是我们小组的工作目标。 28 | 29 | 我们已进行了一系列的充分调研: 30 | 31 | 阅读了 XPC 、 underbridge 、skybridge 论文。 32 | 33 | 阅读与整理了 Intel Architecture Instruction Set Extensions and Future Features 中与用户态中断相关的硬件规范,主要是第2章与第11章。详见 `ppt/uintr-intel-linux.pptx` 。 34 | 35 | 阅读与整理了 uintr-linux-kernel (基于 linux 运用 uintr 特性的内核版本)仓库中的 commits 。详见 `ppt/uintr-intel-kernel commit summary.pptx` 。 36 | 37 | 对之后希望实现的 IPC 场景与框架进行了简单的调研与设计,受限于当前工作重心,该部分暂没有成熟的想法。阶段性想法产出如下: 38 | 39 | 对 XPC 和 uintr 进行了比较分析,提出 uintr+shmem 实现的 IPC 框架,详见 `ppt/初步设想.pdf` 。 40 | 41 | 对 uintr 的实际应用场景和会遇到的问题进行了简单分析,详见 `ppt/应用场景.md` 。 42 | 43 | ## 开发计划 44 | 45 | #### 第一步 46 | 47 | 受限于疫情影响,我们没办法拿到有 uintr 特性的物理机。为了方便后续工作开展,第一步我们将基于 qemu 实现支持 uintr 的模拟器。 48 | 49 | 预期产出:能在我们的 qemu 上跑 uintr-linux-kernel ,并正确通过 linux 实现的简单功能测例 uipi_sample.c 。 50 | 51 | #### 第二步 52 | 53 | 进行性能测试,基于 Linux RFC 报告使用的 ipc-bench 作测试,比较 uintr 与 pipe等传统 IPC 方式的性能异同。 54 | 55 | 预期产出:复现 Linux RFC 的性能测试结果。 56 | 57 | #### 第三步 58 | 59 | 自行编写 uintr+shmem 或其他运用 uintr 形式的用户测例,与传统 IPC 方式作比较。 60 | 61 | 预期产出:比 Linux RFC 更丰富的性能测试结果,验证 IPC 框架设计的可行性。 62 | 63 | #### 第四步 64 | 65 | 修改 Linux 内核,为 IPC 框架提供更通用的系统调用接口,并通过测试。 66 | 67 | 预期产出:一个初步成型的原型系统。 68 | 69 | #### 第五步 70 | 71 | 寻找能发挥我们系统优势的有意义应用场景,在调度、负载均衡等方面继续改进 IPC 框架和内核。 72 | 73 | ## 比赛过程中的重要进展 74 | 75 | 我们的项目进展可在主仓库的 `README.md` 中看到,其同时链接了我们得到相应进展时的工作记录文档或报告 ppt 。 76 | 77 | 在6月4日,我们基本完成了开发计划中前两步的内容,即完成了支持 uintr 的 qemu 的开发,并复现了 Linux RFC 的结果。 78 | 79 | ## 系统测试情况 80 | 81 | ### 第一步 82 | 83 | 用我们更改的 `qemu-uintr` (仓库地址:https://github.com/OS-F-4/qemu-uintr)运行 Linux 开发的使用 uintr 特性的内核 uintr-linux-kernel(仓库地址:https://github.com/intel/uintr-linux-kernel/),并用该内核运行功能测例 uipi_sample.c (位于 `uintr-linux-kernel/tools/uintr/sample/` 下),可看到运行成功,能看到输出了测例中输出的 `Success` ,并且可以正常返回到内核。 84 | 85 | image-20220605194420832 86 | 87 | 若希望复现该部分工作,可参考 `ppt/qemu-worklinglog.md` 中的”编译intel实现的linux内核“、“编译测试程序”、“编译qemu”几个部分。 88 | 89 | ### 第二步 90 | 91 | 用我们更改的 `qemu-uintr` 运行 Linux 开发的使用 uintr 特性的内核 uintr-linux-kernel,并用该内核运行 Linux RFC 报告(https://lore.kernel.org/lkml/20210913200132.3396598-1-sohil.mehta@intel.com/)使用的 ipc-bench 性能测试框架(仓库地址:https://github.com/intel/uintr-ipc-bench/tree/linux-rfc-v1/),对 RFC 报告提到的测试结果进行复现和验证。 92 | 93 | RFC中测试结果: 94 | 95 | image-20220605194930002 96 | 97 | RFC里限制传递消息大小为 $1$ ,比较 uintr 与传统 IPC 方式的延迟,说明其的有效性。 98 | 99 | 我们也能运行其框架并复现这一结果,以 uintr 与 pipe 的测试为例: 100 | 101 | image-20220605195041687 102 | 103 | ## 遇到的主要问题和解决方法 104 | 105 | 最大的问题是受限于疫情,无法获得有 uintr 硬件的物理机。因此我们必须将工作从自行开发功能环境(即 qemu-uintr)开始。 106 | 107 | 开发过程中,我们对 qemu 也不熟悉,很多地方不知如何动手。除了问及校内各位学长以外,还通过 qemu 官网提供的邮件列表,对社区成员进行了邮件询问,其中比较幸运有一位来着台湾的朋友,回复比较积极,也给我们提供了不少的帮助。 108 | 109 | 其他各种开发细节问题,可以查看我们仓库 `README/md` 以及 `ppt/` 下对应的文档来了解。遇到的每一个问题,我们都留下了解决过程的文档记录,如 `ppt/调度问题5-28.md` 、`ppt/debug-log-5-5.md` 等,都是我们开发中遇到的一些困难的思考和解决过程。 110 | 111 | ## 分工和协作 112 | 113 | 王之栋:前期调研,外部沟通,项目主导,qemu调试 114 | 115 | 项晨东:qemu开发 116 | 117 | 孙迅:qemu开发调试,性能测试 118 | 119 | ## 提交仓库目录和文件描述 120 | 121 | 我们提交的仓库是项目的主仓库,基本没有项目代码,项目代码通过链接的形式放在主仓库根目录的 `README.md` 中。当前,项目代码主要包括 `qemu-uintr` 。 122 | 123 | 仓库根目录的文件夹 `ppt/` 存放我们在项目进展过程产出的报告和文档,可配合 `README.md` 中的链接定位。 124 | 125 | ## 比赛收获 126 | 127 | 通过这次比赛,增进了知识,开阔了眼界,也收获了一段探索的经历,希望能进入决赛,继续完成后半段的探索。 -------------------------------------------------------------------------------- /ppt/qemu-workinglog.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 尝试在qemu上实现新的硬件特性 4 | 5 | [toc] 6 | 7 | ## 整理qemu代码 8 | 9 | ### 指令翻译部分 10 | 11 | 阅读intel手册,得到 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ### 指令模拟部分 20 | 21 | 首先研究几个具体的函数: 22 | 23 | ```c 24 | gen_helper_rdtsc(cpu_env); //target/i386/tcg/translate.c 7304 25 | ``` 26 | 27 | 28 | 29 | 30 | 31 | ### 核间中断部分 32 | 33 | apic, 中断控制器, localapic lapic //内核态 34 | 35 | cs寄存器中,cpl的位标志着cpu权级0是内核态,3是用户态 iret返回,切换权级。 36 | 37 | x2是读写msr的 38 | 39 | 核通过内存访问 40 | 41 | hw/intc/apic.c/apic_delever_irq 42 | 43 | s->icr[0] 发送方,写了就可以发ipi 44 | 45 | 46 | 47 | ### cpu状态设定 48 | 49 | ``` 50 | target/i386/cpu.h CPUX86State 51 | 400行左右,msr编号定义 52 | ``` 53 | 54 | 55 | 56 | ### 内存模拟定位 57 | 58 | 59 | 60 | ### 一些接口 61 | 62 | ``` 63 | target/i386/hax/hax-interface 65 msr array 64 | target/i386/hvf/x86emu.c 670 simulate_rdmsr 65 | ``` 66 | 67 | 68 | 69 | 70 | 71 | # qemu -d help 查看log 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | ## 编译intel实现的linux内核 82 | 83 | ```shell 84 | cd uintr-linux-kernel/ 85 | make O=build x86_64_defconfig 86 | make O=build menuconfig 87 | ``` 88 | 89 | 在这里需要有包依赖, 执行 90 | 91 | ```shell 92 | sudo apt-get install ncurses-dev 93 | ``` 94 | 95 | 在选择构建参数时: 96 | 97 | 在`General setup`目录下, 选择`Initial RAM filesystem and RAM disk (initramfs/initrd) support`。 98 | 99 | 在`Device Drivers`目录中`Block device`下, 选择`RAM block device support`, 并设置` Default RAM disk size (kbytes)`为65536KB。 100 | 101 | 随后开始构建: 102 | 103 | ```shell 104 | make O=build bzImage -j 105 | ``` 106 | 107 | 出现缺少头文件报错: ` fatal error: gelf.h: No such file or directory`, `fatal error: openssl/bio.h: No such file or directory`,进行安装: 108 | 109 | ```shell 110 | sudo apt install libssl-dev 111 | sudo apt-get install libelf-dev 112 | ``` 113 | 114 | 115 | 116 | #### 创建文件系统 117 | 118 | 下载`busybox`, 执行以下 119 | 120 | ```shell 121 | mkdir build 122 | make O=build menuconfig 123 | # 在 settings Build Options 中选择 # [*] Build static binary (no shared libs) 124 | cd build 125 | make -j4 126 | make install 127 | ``` 128 | 129 | ```shell 130 | ./_install//usr/sbin/ubirmvol -> ../../bin/busybox 131 | ./_install//usr/sbin/ubirsvol -> ../../bin/busybox 132 | ./_install//usr/sbin/ubiupdatevol -> ../../bin/busybox 133 | ./_install//usr/sbin/udhcpd -> ../../bin/busybox 134 | 135 | 136 | -------------------------------------------------- 137 | You will probably need to make your busybox binary 138 | setuid root to ensure all configured applets will 139 | work properly. 140 | -------------------------------------------------- # 记录以下log 141 | ``` 142 | 143 | ```shell 144 | mkdir -pv initramfs/x86_64_busybox 145 | cd initramfs/x86_64_busybox/ 146 | mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}} 147 | cp -av ../../busybox-1.32.0/build/_install/* . 148 | ``` 149 | 150 | 在`x86_64_busybox`目录下, 执行打包当前文件系统: 151 | 152 | ```shell 153 | find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs-busybox-x86_64.cpio.gz 154 | ``` 155 | 156 | 启动linux的shell脚本如下: 157 | 158 | ```shell 159 | qemu-system-x86_64 -smp 2 \ 160 | -m 1024M -nographic \ 161 | -kernel ./uintr-linux-kernel/build/arch/x86_64/boot/bzImage \ 162 | -initrd ./initramfs/initramfs-busybox-x86_64.cpio.gz \ 163 | -append "root=/dev/ram0 rw rootfstype=ext4 console=ttyS0 init=/linuxrc" 164 | ``` 165 | 166 | 167 | 168 | 169 | 170 | ## 编译测试程序 171 | 172 | #### 编译简单测试程序: 173 | 174 | ```shell 175 | gcc -static compute.c -o compute 176 | chmod a+x compute 177 | ``` 178 | 179 | #### 编译设计uintr的测试程序: 180 | 181 | 直接编译: 182 | 183 | ```shell 184 | gcc -Wall -static -muintr -mgeneral-regs-only -minline-all-stringops uipi_sample.c -lpthread 185 | gcc: error: unrecognized command line option ‘-muintr’ 186 | ``` 187 | 188 | 用`gcc-11`编译: 189 | 190 | ```shell 191 | # 安装gcc-11 192 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test 193 | sudo apt install build-essential manpages-dev software-properties-common 194 | sudo apt update && sudo apt install gcc-11 195 | 196 | gcc-11 -Wall -static -muintr -mgeneral-regs-only -minline-all-stringops uipi_sample.c -lpthread -o /home/xcd/uintr_sample/uipi_sample 197 | /tmp/ccmkHYQa.s: Assembler messages: 198 | /tmp/ccmkHYQa.s:74: Error: no such instruction: `uiret' 199 | /tmp/ccmkHYQa.s:120: Error: no such instruction: `senduipi %rax' 200 | /tmp/ccmkHYQa.s:196: Error: no such instruction: `stui' 201 | ``` 202 | 203 | 安装最新的汇编器: 204 | 205 | 首先下载对应版本的安装包`binutils-2.38.tar.bz2 ` 206 | 207 | ```shell 208 | tar -jxvf 209 | binutils-2.38.tar.bz2 210 | cd binutils-2.38 211 | ./configure 212 | make -j 213 | sudo make install 214 | ``` 215 | 216 | 随后编译,可以成功。 217 | 218 | ```shell 219 | xxy@7af409e42583:~/uintr-linux-kernel/tools/uintr/sample$ make 220 | gcc-11 -Wall -static -muintr -mgeneral-regs-only -minline-all-stringops uipi_sample.c -lpthread -o /home/xxy/uintr-linux-kernel/tools/uintr/sample/uipi_sample 221 | xxy@7af409e42583:~/uintr-linux-kernel/tools/uintr/sample$ ls 222 | Makefile README uipi.s uipi_sample uipi_sample.c 223 | ``` 224 | 225 | 执行出现错误: 226 | 227 | ```shell 228 | ./uipi_sample 229 | Interrupt handler register error 230 | ``` 231 | 232 | gcc -S 得到汇编代码,主要的新增代码如下 233 | 234 | ```assembly 235 | uiret # 这里是之前出错的指令 236 | senduipi %rax # 这里是出错的汇编指令 237 | stui # 这里也是之前出错的指令 238 | ``` 239 | 240 | 241 | 242 | ## 编译qemu 243 | 244 | ```shell 245 | cd qemu 246 | mkdir build 247 | ../configure --enable-debug --target-list=x86_64-softmmu 248 | make -j 249 | ``` 250 | 251 | 遇到缺少的包: 252 | 253 | ```shell 254 | ERROR: Cannot find Ninja 255 | ``` 256 | 257 | ## ref 258 | 259 | 1. https://hackmd.io/@octobersky/qemu-run-ubuntu 260 | 2. http://blog.vmsplice.net/2011/03/qemu-internals-big-picture-overview.html 261 | 3. https://vmsplice.net/~stefan/qemu-code-overview.pdf 262 | -------------------------------------------------------------------------------- /ppt/debug-log-5-5.md: -------------------------------------------------------------------------------- 1 | # debug-log 2 | 3 | ## 问题 4 | 5 | - 使用 `qemu-uintr` 执行测例代码 `uipi_sample.c`,发送方函数 `sender_thread` 理应被执行一次,但实际被执行两次。 6 | 7 | - 执行 `senduipi` 时入栈的 `esp` 值与执行 `uiret` 时出栈的 `esp` 值不相等。 8 | 9 | ## 调试过程 10 | 11 | ### 定位异常代码 12 | 13 | 通过断点输出的方式,定位到 `esp` 值异常改变的位置。发现该处 `eip` 值也发生了异常改变。该处代码为 14 | 15 | ```c 16 | // accel/tcg/cpu-exec.c 17 | ret = tcg_qemu_tb_exec(env, tb_ptr); 18 | ``` 19 | 20 | 即 qemu 动态执行用户程序的代码。 21 | 22 | 发生异常现象时,qemu 正在执行测例代码的 `uintr_handler` 函数。 23 | 24 | ### 查看 qemu 运行状态 25 | 26 | 进一步输出 qemu 此刻的运行状态,尤其是当前正在执行的 TranslationBlock 的状态。 27 | 28 | 可以发现,当前正在执行的 TranslationBlock 起始地址为 `0x4016f5` ,大小为 `0x55` 字节,指令条数为 23 条。 29 | 30 | ### 反汇编 31 | 32 | 对测例代码 `uipi_sample.c` 采用 `objdump` 反汇编,以下是一个关键片段。 33 | 34 | ```assembly 35 | 00000000004016f5 : 36 | 4016f5: 55 push %rbp 37 | 4016f6: 48 89 e5 mov %rsp,%rbp 38 | 4016f9: 50 push %rax 39 | 4016fa: 48 8d 45 10 lea 0x10(%rbp),%rax 40 | 4016fe: 48 89 45 f0 mov %rax,-0x10(%rbp) 41 | 401702: 48 8b 45 08 mov 0x8(%rbp),%rax 42 | 401706: 48 89 45 e8 mov %rax,-0x18(%rbp) 43 | 40170a: c7 05 bc 5b 0d 00 01 movl $0x1,0xd5bbc(%rip) # 4d72d0 44 | 401711: 00 00 00 45 | 401714: 90 nop 46 | 401715: 48 8b 45 f8 mov -0x8(%rbp),%rax 47 | 401719: c9 leave 48 | 40171a: 48 83 c4 08 add $0x8,%rsp 49 | 40171e: f3 0f 01 ec uiret 50 | 51 | 0000000000401722 : 52 | 401722: 55 push %rbp 53 | 401723: 48 89 e5 mov %rsp,%rbp 54 | 401726: 48 83 ec 20 sub $0x20,%rsp 55 | 40172a: 48 89 7d e8 mov %rdi,-0x18(%rbp) 56 | 40172e: 8b 05 a0 5b 0d 00 mov 0xd5ba0(%rip),%eax # 4d72d4 57 | 401734: ba 00 00 00 00 mov $0x0,%edx 58 | 401739: 89 c6 mov %eax,%esi 59 | 40173b: bf c4 01 00 00 mov $0x1c4,%edi 60 | 401740: b8 00 00 00 00 mov $0x0,%eax 61 | 401745: e8 c6 e8 04 00 call 450010 62 | 40174a: 89 45 f4 mov %eax,-0xc(%rbp) 63 | 40174d: 83 7d f4 00 cmpl $0x0,-0xc(%rbp) 64 | 401751: 79 19 jns 40176c 65 | 401753: 48 8d 05 ae 38 0a 00 lea 0xa38ae(%rip),%rax # 4a5008 <_IO_stdin_used+0x8> 66 | 40175a: 48 89 c7 mov %rax,%rdi 67 | 40175d: e8 6e ea 00 00 call 4101d0 <_IO_puts> 68 | 401762: bf 01 00 00 00 mov $0x1,%edi 69 | 401767: e8 a4 7c 00 00 call 409410 70 | 40176c: 8b 45 f4 mov -0xc(%rbp),%eax 71 | 40176f: 89 c6 mov %eax,%esi 72 | 401771: 48 8d 05 a8 38 0a 00 lea 0xa38a8(%rip),%rax # 4a5020 <_IO_stdin_used+0x20> 73 | 401778: 48 89 c7 mov %rax,%rdi 74 | 40177b: b8 00 00 00 00 mov $0x0,%eax 75 | 401780: e8 fb 85 00 00 call 409d80 <_IO_printf> 76 | 401785: 8b 45 f4 mov -0xc(%rbp),%eax 77 | 401788: 48 98 cltq 78 | 40178a: 48 89 45 f8 mov %rax,-0x8(%rbp) 79 | 40178e: 48 8b 45 f8 mov -0x8(%rbp),%rax 80 | 401792: f3 0f c7 f0 senduipi %rax 81 | 401796: 90 nop 82 | 401797: 8b 05 37 5b 0d 00 mov 0xd5b37(%rip),%eax # 4d72d4 83 | 40179d: ba 00 00 00 00 mov $0x0,%edx 84 | 4017a2: 89 c6 mov %eax,%esi 85 | 4017a4: bf c5 01 00 00 mov $0x1c5,%edi 86 | 4017a9: b8 00 00 00 00 mov $0x0,%eax 87 | 4017ae: e8 5d e8 04 00 call 450010 88 | 4017b3: b8 00 00 00 00 mov $0x0,%eax 89 | 4017b8: c9 leave 90 | 4017b9: c3 ret 91 | ``` 92 | 93 | TranslationBlock 的起始地址加上大小,也就是 `0x4016f5 + 0x55 = 0x40174a`,得到 TranslationBlock 的终止地址。 94 | 95 | 这样,我们就能看出 qemu 此刻的 TranslationBlock 是哪一段了。 96 | 97 | ```assembly 98 | 00000000004016f5 : 99 | 4016f5: 55 push %rbp 100 | 4016f6: 48 89 e5 mov %rsp,%rbp 101 | 4016f9: 50 push %rax 102 | 4016fa: 48 8d 45 10 lea 0x10(%rbp),%rax 103 | 4016fe: 48 89 45 f0 mov %rax,-0x10(%rbp) 104 | 401702: 48 8b 45 08 mov 0x8(%rbp),%rax 105 | 401706: 48 89 45 e8 mov %rax,-0x18(%rbp) 106 | 40170a: c7 05 bc 5b 0d 00 01 movl $0x1,0xd5bbc(%rip) # 4d72d0 107 | 401711: 00 00 00 108 | 401714: 90 nop 109 | 401715: 48 8b 45 f8 mov -0x8(%rbp),%rax 110 | 401719: c9 leave 111 | 40171a: 48 83 c4 08 add $0x8,%rsp 112 | 40171e: f3 0f 01 ec uiret 113 | 114 | 0000000000401722 : 115 | 401722: 55 push %rbp 116 | 401723: 48 89 e5 mov %rsp,%rbp 117 | 401726: 48 83 ec 20 sub $0x20,%rsp 118 | 40172a: 48 89 7d e8 mov %rdi,-0x18(%rbp) 119 | 40172e: 8b 05 a0 5b 0d 00 mov 0xd5ba0(%rip),%eax # 4d72d4 120 | 401734: ba 00 00 00 00 mov $0x0,%edx 121 | 401739: 89 c6 mov %eax,%esi 122 | 40173b: bf c4 01 00 00 mov $0x1c4,%edi 123 | 401740: b8 00 00 00 00 mov $0x0,%eax 124 | 401745: e8 c6 e8 04 00 call 450010 125 | ``` 126 | 127 | 这段代码恰好由 23 条指令组成,与我们输出看到的 qemu 运行状态信息相吻合。 128 | 129 | 此外,由于这一基本块包含了部分 `sender_thread` 函数的指令,因此发送方函数 `sender_thread` 被执行两次的现象也得到了解释。 130 | 131 | ## 结论 132 | 133 | qemu 在划分 TranslationBlock 的时候,并没有将 `uiret` 指令视为一个跳转语句。这导致上面这一整段指令均被视为一个基本块,而这显然是不合理的。 134 | 135 | 正是因为这整段指令被视为一个基本块,才导致 `esp` 值发生了预期之外的变化,且 `eip` 也跑飞了。 136 | 137 | 后续可以从 qemu 划分 TranslationBlock 的层面入手,修复这个问题。 138 | -------------------------------------------------------------------------------- /ppt/展示文档/linux-kernel.md: -------------------------------------------------------------------------------- 1 | # uintr-linux-kernel说明文档 2 | 3 | - [toc] 4 | 5 | ## 项目简介 6 | 7 | 本项目为支持用户态中断的linux, 使得能够运行使用用户态中断特性的用户程序。 8 | 9 | 对于操作系统而言, 主要的改变为增加了若干的系统调用, 主要有如下几个系统调用: 10 | 11 | ```c++ 12 | #define uintr_register_handler(handler, flags) syscall(__NR_uintr_register_handler, handler, flags) 13 | #define uintr_unregister_handler(flags) syscall(__NR_uintr_unregister_handler, flags) 14 | #define uintr_create_fd(vector, flags) syscall(__NR_uintr_create_fd, vector, flags) 15 | #define uintr_register_sender(fd, flags) syscall(__NR_uintr_register_sender, fd, flags) 16 | #define uintr_unregister_sender(fd, flags) syscall(__NR_uintr_unregister_sender, fd, flags) 17 | ``` 18 | 19 | 其中系统调用的编号未固定, 编写用户程序时需要对特性操作系统的系统调用编号做适配, 接下来依次介绍系统调用的用途和含义。 20 | 21 | #### uintr_register_handler 22 | 23 | 用于用户态中断的接收方注册中断处理函数, 操作系统会将中断处理函数的地址写入硬件, 从而使得接收到中断后可以跳转到指定的函数入口进行执行。 24 | 25 | #### uintr_unregister_handler 26 | 27 | 注销中断处理函数, 注销后硬件将不在记录中断处理函数地址, 接受方也无法收到中断。 28 | 29 | #### uintr_create_fd 30 | 31 | 用于接收方创建链接, 接收方向操作系统注册设定的中断向量`vector`, 所有的注册信息将被操作系统封装为一个文件描述符, 用于后续给发送方注册`sender`。注册成功后, 发送方若发送中断, 则收到的中断函数中的参数会携带中断向量`vector`, 从而接收方可以区分不同的发送方。 32 | 33 | #### uintr_register_sender 34 | 35 | 用于发送发注册`sender`, 向操作系统提供的是接收方所注册的文件描述符, 操作系统根据注册的文件描述符来为发送方写入接受方的相关信息, 同时返回接受方信息在`UITT(User Interrupt Target Table)`中的下标, 发送方可以通过下标来给不同的接受方发送用户态中断。 36 | 37 | #### uintr_unregister_sender 38 | 39 | 注销用户态中断的发送方, 注销后对应的下标将无法成功发送中断。 40 | 41 | 42 | 更详细的流程和特性介绍详见[manpage](https://github.com/OS-F-4/uintr-linux-kernel/tree/uintr-next/tools/uintr/manpages)。 43 | 44 | 45 | 46 | ## 使用方法 47 | 48 | ### 编译内核(用于qemu) 49 | 50 | 注: 在内核编译的过程中可能需要安装支持编译内核的软件包, 通过包管理器安装即可。 51 | 52 | ```shell 53 | cd uintr-linux-kernel/ 54 | make O=build x86_64_defconfig 55 | make O=build menuconfig 56 | ``` 57 | 58 | 在选择构建参数时: 59 | 60 | 在`General setup`目录下, 选择`Initial RAM filesystem and RAM disk (initramfs/initrd) support`。 61 | 62 | 在`Device Drivers`目录中`Block device`下, 选择`RAM block device support`, 并设置` Default RAM disk size (kbytes)`为65536KB。 63 | 64 | 在`Processor type and features ---> `设置`User Interrupts (UINTR) ` 65 | 66 | 随后开始构建: 67 | 68 | ```shell 69 | make O=build bzImage -j 70 | make O=build modules -j 4 71 | ``` 72 | 73 | 构建完成后, 内核的镜像文件会在`uintr-linux-kernel/build/arch/x86_64/boot/bzImage`, 在后续过程中可以直接调用。 74 | 75 | 76 | 77 | ### 创建文件系统(以busybox为例) 78 | 79 | 接下来应当创建相应的文件系统, 这里以busybox为例, 同时我们也有构建[ubuntu文件系统](https://github.com/OS-F-4/usr-intr/blob/main/ppt/how-to-build-a-ubuntu-rootfs.md)的方法, 并且安装相应的包后可以直接在模拟器中编译用户程序。 80 | 81 | 下载`busybox`, 执行以下 82 | 83 | ```shell 84 | mkdir build 85 | make O=build menuconfig 86 | # 在 settings Build Options 中选择 87 | # [*] Build static binary (no shared libs) 88 | cd build 89 | make -j4 90 | make install 91 | ``` 92 | 93 | ```shell 94 | ./_install//usr/sbin/ubirmvol -> ../../bin/busybox 95 | ./_install//usr/sbin/ubirsvol -> ../../bin/busybox 96 | ./_install//usr/sbin/ubiupdatevol -> ../../bin/busybox 97 | ./_install//usr/sbin/udhcpd -> ../../bin/busybox 98 | 99 | 100 | -------------------------------------------------- 101 | You will probably need to make your busybox binary 102 | setuid root to ensure all configured applets will 103 | work properly. 104 | -------------------------------------------------- # log 105 | ``` 106 | 107 | ```shell 108 | mkdir -pv initramfs/x86_64_busybox 109 | cd initramfs/x86_64_busybox/ 110 | mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}} 111 | cp -av ../../busybox-1.32.0/build/_install/* . 112 | ``` 113 | 114 | 文件系统需要初始化脚本,如下: 115 | 116 | ```shell 117 | #!/bin/sh 118 | mount -t proc none /proc 119 | mount -t sysfs none /sys 120 | mknod -m 666 /dev/ttyS0 c 4 64 121 | echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n" 122 | setsid cttyhack sh 123 | exec /bin/sh 124 | ``` 125 | 126 | `chmod u+x init` 127 | 128 | init放在文件系统根目录下 129 | 130 | 在`x86_64_busybox`目录下, 执行打包当前文件系统: 131 | 132 | ```shell 133 | find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs-busybox-x86_64.cpio.gz 134 | ``` 135 | 136 | ### 启动linux的shell脚本如下: 137 | 138 | ```shell 139 | #!/bin/bash 140 | # 指定文件系统的路径 141 | ubuntu=~/qemu_uintr/ubuntu-x86_64.cpio.gz 142 | box=~/qemu_uintr/initramfs/initramfs-busybox-x86_64.cpio.gz 143 | PORT=2333 144 | # 指定qemu可执行文件路径 145 | QEMU=~/qemu_uintr/qemu/build/x86_64-softmmu/qemu-system-x86_64 146 | # 指定内核路径 147 | KERNEL=~/qemu_uintr/uintr-linux-kernel/build/arch/x86_64/boot/bzImage 148 | $QEMU -smp 2 \ 149 | -machine q35,kernel_irqchip=split \ 150 | -m 2048M -nographic -cpu qemu64 \ 151 | -kernel $KERNEL \ 152 | -initrd $ubuntu \ 153 | -append "root=/dev/ram0 rw rootfstype=ext4 console=ttyS0 init=/linuxrc" \ 154 | -net user,id=net,hostfwd=tcp::$(PORT)-:22 -net nic,model=e1000e \ 155 | -serial mon:stdio 156 | ``` 157 | 158 | 159 | 160 | 161 | 162 | ### 编译内核(用于物理机) 163 | 164 | ```shell 165 | cp /boot/configxxx .config # 其中configxxx是当前系统的版本的config文件, 这是为了保持最好的兼容 166 | make menuconfig 167 | ``` 168 | 169 | 在`Processor type and features ---> `设置`User Interrupts (UINTR) ` 170 | 171 | ```shell 172 | # select uintr 173 | make -j 24 174 | sudo make modules_install 175 | sudo make install 176 | ``` 177 | 178 | 此时内核已经安装完毕, 可以通过`grubby --default-kernel` 查看当期默认的启动内核, 确认无误后, 选择重启。 179 | 180 | ```shell 181 | reboot 182 | ``` 183 | 184 | 185 | 186 | 如果开始编译出来的内核模块太大,导致initramfs也太大,导致夹在时内存溢出, 需要经过压缩或者去除后才能启动。 187 | 188 | ```shell 189 | cd /lib/modules/ 190 | find . -name *.ko -exec strip --strip-unneeded {} + 191 | ``` 192 | 193 | 194 | 195 | ### 编译用户态中断程序 196 | 197 | 编译需要用到`gcc-11`, 具体的安装方式见[其他编译帮助](https://github.com/OS-F-4/usr-intr/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/utils.md)。 198 | 199 | 样例程序位于`uintr-linux-kernel/tools/uintr/sample`, 有对应的Makefile, 若需要自己编写用户程序, 只需要添加`-muintr`, `-mgeneral-regs-only`, ` -minline-all-stringops`三个编译选项即可。 200 | 201 | ```shell 202 | gcc -Wall -static -muintr -mgeneral-regs-only -minline-all-stringops uipi_sample.c -lpthread 203 | ``` 204 | 205 | -------------------------------------------------------------------------------- /ppt/qemu工作文档分块/各类用户程序.md: -------------------------------------------------------------------------------- 1 | # 这里是用户程序列表 2 | 3 | 4 | 5 | ## O3 6 | 7 | 编译选项: 8 | 9 | ```makefile 10 | CFLAGS += -Wall -static -muintr -mgeneral-regs-only -minline-all-stringops -fno-stack-protector -O3 11 | LDFLAGS += -lpthread 12 | ``` 13 | 14 | 源代码: 15 | 16 | ```c 17 | // SPDX-License-Identifier: GPL-2.0+ 18 | /* 19 | * Copyright (c) 2020, Intel Corporation. 20 | * 21 | * Sohil Mehta 22 | */ 23 | unsigned int uintr_received; 24 | unsigned int uintr_fd; 25 | 26 | void __attribute__ ((interrupt)) uintr_handler(struct __uintr_frame *ui_frame, 27 | unsigned long long vector) 28 | { 29 | uintr_received = 1; 30 | } 31 | 32 | void *sender_thread(void *arg) 33 | { 34 | int uipi_index; 35 | uipi_index = uintr_register_sender(uintr_fd, 0); 36 | if (uipi_index < 0) { 37 | printf("Sender register error\n"); 38 | exit(EXIT_FAILURE); 39 | } 40 | printf("----------Sending IPI from sender thread index:%d \n", uipi_index); 41 | fflush(stdout); 42 | _senduipi(uipi_index); 43 | uintr_unregister_sender(uintr_fd, 0); 44 | return NULL; 45 | } 46 | 47 | int main(int argc, char *argv[]) 48 | { 49 | pthread_t pt; 50 | int ret; 51 | int ret1 = uintr_register_handler(uintr_handler, 0); 52 | ret = uintr_create_fd(0, 0); 53 | uintr_fd = ret; 54 | _stui(); 55 | if (pthread_create(&pt, NULL, &sender_thread, NULL)) { 56 | printf("Error creating sender thread\n"); 57 | exit(EXIT_FAILURE); 58 | } 59 | /* Do some other work */ 60 | while (!uintr_received) 61 | usleep(1000); 62 | pthread_join(pt, NULL); 63 | close(uintr_fd); 64 | uintr_unregister_handler(0); 65 | printf("Success\n"); 66 | fflush(stdout); 67 | exit(EXIT_SUCCESS); 68 | } 69 | 70 | ``` 71 | 72 | 反汇编的handler部分, 可见移动了栈, 但是并没有回退栈 : 73 | 74 | ```assembly 75 | 0000000000401ec0 : 76 | 401ec0: f3 0f 1e fa endbr64 77 | 401ec4: c7 05 26 e5 0d 00 01 movl $0x1,0xde526(%rip) # 4e03f4 78 | 401ecb: 00 00 00 79 | 401ece: 48 83 c4 08 add $0x8,%rsp 80 | 401ed2: f3 0f 01 ec uiret 81 | 401ed6: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1) 82 | 401edd: 00 00 00 83 | ``` 84 | 85 | 对栈-8进行pop, 程序可以正常结束。 86 | 87 | 88 | 89 | ## nO3 90 | 91 | 去掉O3编译选项, 源代码与O3相同。反汇编结果: 92 | 93 | 程序不能正常结束。 94 | 95 | ```assembly 96 | 0000000000401de5 : 97 | 401de5: f3 0f 1e fa endbr64 98 | 401de9: 55 push %rbp 99 | 401dea: 48 89 e5 mov %rsp,%rbp 100 | 401ded: 50 push %rax 101 | 401dee: 48 8d 45 10 lea 0x10(%rbp),%rax 102 | 401df2: 48 89 45 f0 mov %rax,-0x10(%rbp) 103 | 401df6: 48 8b 45 08 mov 0x8(%rbp),%rax 104 | 401dfa: 48 89 45 e8 mov %rax,-0x18(%rbp) 105 | 401dfe: c7 05 c8 e5 0d 00 01 movl $0x1,0xde5c8(%rip) # 4e03d0 106 | 401e05: 00 00 00 107 | 401e08: 90 nop 108 | 401e09: 48 8b 45 f8 mov -0x8(%rbp),%rax 109 | 401e0d: c9 leave 110 | 401e0e: 48 83 c4 08 add $0x8,%rsp 111 | 401e12: f3 0f 01 ec uiret 112 | ``` 113 | 114 | 对栈进行-8进行pop,pop出来的值正确,但是不能正常结束(卡住) 115 | 116 | 117 | 118 | ## stackpro 119 | 120 | 去掉O3, 去掉 -fno-stack-protector, 源代码同上。 121 | 122 | 反汇编结果: 123 | 124 | ```c 125 | 0000000000401de5 : 126 | 401de5: f3 0f 1e fa endbr64 127 | 401de9: 55 push %rbp 128 | 401dea: 48 89 e5 mov %rsp,%rbp 129 | 401ded: 50 push %rax 130 | 401dee: 48 8d 45 10 lea 0x10(%rbp),%rax 131 | 401df2: 48 89 45 f0 mov %rax,-0x10(%rbp) 132 | 401df6: 48 8b 45 08 mov 0x8(%rbp),%rax 133 | 401dfa: 48 89 45 e8 mov %rax,-0x18(%rbp) 134 | 401dfe: c7 05 c8 e5 0d 00 01 movl $0x1,0xde5c8(%rip) # 4e03d0 135 | 401e05: 00 00 00 136 | 401e08: 90 nop 137 | 401e09: 48 8b 45 f8 mov -0x8(%rbp),%rax 138 | 401e0d: c9 leave 139 | 401e0e: 48 83 c4 08 add $0x8,%rsp 140 | 401e12: f3 0f 01 ec uiret 141 | ``` 142 | 143 | 对栈进行-8进行pop,可以正常结束 144 | 145 | 146 | 147 | 148 | 149 | ## uipi_sample 150 | 151 | 编译选项: 152 | 153 | ``` 154 | CFLAGS += -Wall -static -muintr -mgeneral-regs-only -minline-all-stringops 155 | LDFLAGS += -lpthread 156 | ``` 157 | 158 | intel原版代码 159 | 160 | ```c 161 | unsigned int uintr_received; 162 | unsigned int uintr_fd; 163 | 164 | void __attribute__ ((interrupt)) uintr_handler(struct __uintr_frame *ui_frame, 165 | unsigned long long vector) 166 | { 167 | static const char print[] = "\t-- User Interrupt handler --\n"; 168 | 169 | write(STDOUT_FILENO, print, sizeof(print) - 1); 170 | uintr_received = 1; 171 | } 172 | 173 | void *sender_thread(void *arg) 174 | { 175 | int uipi_index; 176 | 177 | uipi_index = uintr_register_sender(uintr_fd, 0); 178 | if (uipi_index < 0) { 179 | printf("Sender register error\n"); 180 | exit(EXIT_FAILURE); 181 | } 182 | 183 | printf("Sending IPI from sender thread\n"); 184 | _senduipi(uipi_index); 185 | 186 | uintr_unregister_sender(uintr_fd, 0); 187 | 188 | return NULL; 189 | } 190 | 191 | int main(int argc, char *argv[]) 192 | { 193 | pthread_t pt; 194 | int ret; 195 | 196 | if (uintr_register_handler(uintr_handler, 0)) { 197 | printf("Interrupt handler register error\n"); 198 | exit(EXIT_FAILURE); 199 | } 200 | 201 | ret = uintr_create_fd(0, 0); 202 | if (ret < 0) { 203 | printf("Interrupt vector allocation error\n"); 204 | exit(EXIT_FAILURE); 205 | } 206 | 207 | uintr_fd = ret; 208 | 209 | _stui(); 210 | printf("Receiver enabled interrupts\n"); 211 | 212 | if (pthread_create(&pt, NULL, &sender_thread, NULL)) { 213 | printf("Error creating sender thread\n"); 214 | exit(EXIT_FAILURE); 215 | } 216 | 217 | /* Do some other work */ 218 | while (!uintr_received) 219 | usleep(1); 220 | 221 | pthread_join(pt, NULL); 222 | close(uintr_fd); 223 | uintr_unregister_handler(0); 224 | 225 | printf("Success\n"); 226 | exit(EXIT_SUCCESS); 227 | } 228 | ``` 229 | 230 | 对栈进行-8进行pop,可以正常结束。 -------------------------------------------------------------------------------- /ppt/qemu工作文档分块/编译技巧和环境.md: -------------------------------------------------------------------------------- 1 | # 编译等技巧 2 | 3 | ## 编译intel实现的linux内核 4 | 5 | ```shell 6 | cd uintr-linux-kernel/ 7 | make O=build x86_64_defconfig 8 | make O=build menuconfig 9 | ``` 10 | 11 | 在这里需要有包依赖, 执行 12 | 13 | ```shell 14 | sudo apt-get install ncurses-dev 15 | ``` 16 | 17 | 在选择构建参数时: 18 | 19 | 在`General setup`目录下, 选择`Initial RAM filesystem and RAM disk (initramfs/initrd) support`。 20 | 21 | 在`Device Drivers`目录中`Block device`下, 选择`RAM block device support`, 并设置` Default RAM disk size (kbytes)`为65536KB。 22 | 23 | 还需要设置网络; 在`Device Drivers`目录中`Network device support`中`Universal TUN/TAP driver support` 按下`M`键 24 | 25 | #### 注意到qemu定义msr并无引用后: 26 | 27 | 查看linux-uintr-kernel的每一次commit,发现如下说明: 28 | 29 | ```txt 30 | x86/cpu: Enumerate User Interrupts support 31 | User Interrupts support including user IPIs is enumerated through cpuid. 32 | The 'uintr' flag in /proc/cpuinfo can be used to identify it. The 33 | recommended mechanism for user applications to detect support is calling 34 | the uintr related syscalls. 35 | 36 | Use CONFIG_X86_USER_INTERRUPTS to compile with User Interrupts support. 37 | The feature can be disabled at boot time using the 'nouintr' kernel 38 | parameter. 只有定义了相关的宏,相关特性才会被启用 39 | 40 | SENDUIPI is a special ring-3 instruction that makes a supervisor mode 41 | memory access to the UPID and UITT memory. Currently, KPTI needs to be 42 | off for User IPIs to work. Processors that support user interrupts are 43 | not affected by Meltdown so the auto mode of KPTI will default to off. 44 | 45 | Users who want to force enable KPTI will need to wait for a later 46 | version of this patch series that is compatible with KPTI. We need to 47 | allocate the UPID and UITT structures from a special memory region that 48 | has supervisor access but it is mapped into userspace. The plan is to 49 | implement a mechanism similar to LDT. 50 | ``` 51 | 52 | 重新编译linux kernel,在`build/.confg`中修改`CONFIG_X86_USER_INTERRUPTS=y`, 随后重新编译, 貌似还不行。仔细再menu中寻找相关参数,最后找到==在`Processor type and features ---> `==设置`User Interrupts (UINTR) ` 53 | 54 | 随后开始构建: 55 | 56 | ```shell 57 | make O=build bzImage -j 58 | make O=build modules -j 4 59 | sudo make O=build modules_install INSTALL_MOD_PATH=/home/xcd/qemu_uintr/rootfs -j 4 60 | ``` 61 | 62 | 出现缺少头文件报错: ` fatal error: gelf.h: No such file or directory`, `fatal error: openssl/bio.h: No such file or directory`,进行安装: 63 | 64 | ```shell 65 | sudo apt install libssl-dev 66 | sudo apt-get install libelf-dev 67 | ``` 68 | 69 | #### 创建文件系统 70 | 71 | 下载`busybox`, 执行以下 72 | 73 | ```shell 74 | mkdir build 75 | make O=build menuconfig 76 | # 在 settings Build Options 中选择 77 | # [*] Build static binary (no shared libs) 78 | cd build 79 | make -j4 80 | make install 81 | ``` 82 | 83 | ```shell 84 | ./_install//usr/sbin/ubirmvol -> ../../bin/busybox 85 | ./_install//usr/sbin/ubirsvol -> ../../bin/busybox 86 | ./_install//usr/sbin/ubiupdatevol -> ../../bin/busybox 87 | ./_install//usr/sbin/udhcpd -> ../../bin/busybox 88 | 89 | 90 | -------------------------------------------------- 91 | You will probably need to make your busybox binary 92 | setuid root to ensure all configured applets will 93 | work properly. 94 | -------------------------------------------------- # 记录以下log 95 | ``` 96 | 97 | ```shell 98 | mkdir -pv initramfs/x86_64_busybox 99 | cd initramfs/x86_64_busybox/ 100 | mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}} 101 | cp -av ../../busybox-1.32.0/build/_install/* . 102 | ``` 103 | 104 | 文件系统需要初始化脚本,如下: 105 | 106 | ```shell 107 | #!/bin/sh 108 | mount -t proc none /proc 109 | mount -t sysfs none /sys 110 | mknod -m 666 /dev/ttyS0 c 4 64 111 | echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n" 112 | setsid cttyhack sh 113 | exec /bin/sh 114 | ``` 115 | 116 | `chmod u+x init` 117 | 118 | init放在文件系统根目录下 119 | 120 | 在`x86_64_busybox`目录下, 执行打包当前文件系统: 121 | 122 | ```shell 123 | find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs-busybox-x86_64.cpio.gz 124 | ``` 125 | 126 | 启动linux的shell脚本如下: 127 | 128 | ```shell 129 | qemu-system-x86_64 -smp 2 \ 130 | -m 1024M -nographic \ 131 | -kernel ./uintr-linux-kernel/build/arch/x86_64/boot/bzImage \ 132 | -initrd ./initramfs/initramfs-busybox-x86_64.cpio.gz \ 133 | -append "root=/dev/ram0 rw rootfstype=ext4 console=ttyS0 init=/linuxrc" 134 | ``` 135 | 136 | 137 | 138 | ## 编译测试程序 139 | 140 | #### 编译简单测试程序: 141 | 142 | ```shell 143 | gcc -static compute.c -o compute 144 | chmod a+x compute 145 | ``` 146 | 147 | #### 编译设计uintr的测试程序: 148 | 149 | 直接编译: 150 | 151 | ```shell 152 | gcc -Wall -static -muintr -mgeneral-regs-only -minline-all-stringops uipi_sample.c -lpthread 153 | gcc: error: unrecognized command line option ‘-muintr’ 154 | ``` 155 | 156 | 用`gcc-11`编译: 157 | 158 | ```shell 159 | # 安装gcc-11 160 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test 161 | sudo apt install build-essential manpages-dev software-properties-common 162 | sudo apt update && sudo apt install gcc-11 163 | 164 | gcc-11 -Wall -static -muintr -mgeneral-regs-only -minline-all-stringops uipi_sample.c -lpthread -o /home/xcd/uintr_sample/uipi_sample 165 | /tmp/ccmkHYQa.s: Assembler messages: 166 | /tmp/ccmkHYQa.s:74: Error: no such instruction: `uiret' 167 | /tmp/ccmkHYQa.s:120: Error: no such instruction: `senduipi %rax' 168 | /tmp/ccmkHYQa.s:196: Error: no such instruction: `stui' 169 | ``` 170 | 171 | 安装最新的汇编器: 172 | 173 | 首先下载对应版本的安装包`  ` 174 | 175 | ```shell 176 | tar -jxvf binutils-2.38.tar.bz2 177 | cd binutils-2.38 178 | ./configure 179 | make -j 180 | sudo make install 181 | ``` 182 | 183 | 随后编译,可以成功。 184 | 185 | ```shell 186 | xxy@7af409e42583:~/uintr-linux-kernel/tools/uintr/sample$ make 187 | gcc-11 -Wall -static -muintr -mgeneral-regs-only -minline-all-stringops uipi_sample.c -lpthread -o /home/xxy/uintr-linux-kernel/tools/uintr/sample/uipi_sample 188 | xxy@7af409e42583:~/uintr-linux-kernel/tools/uintr/sample$ ls 189 | Makefile README uipi.s uipi_sample uipi_sample.c 190 | ``` 191 | 192 | 执行出现错误: 193 | 194 | ```shell 195 | ./uipi_sample 196 | Interrupt handler register error 197 | ``` 198 | 199 | gcc -S 得到汇编代码,主要的新增代码如下 200 | 201 | ```assembly 202 | uiret # 这里是之前出错的指令 203 | senduipi %rax # 这里是出错的汇编指令 204 | stui # 这里也是之前出错的指令 205 | ``` 206 | 207 | 208 | 209 | ## 编译qemu 210 | 211 | ```shell 212 | cd qemu 213 | mkdir build 214 | ../configure --enable-debug --target-list=x86_64-softmmu --enable-trace-backends=log 215 | make -j 216 | ``` 217 | 218 | 遇到缺少的包, 安装即可: 219 | 220 | ```shell 221 | ERROR: Cannot find Ninja 222 | sudo apt install ninja-build 223 | ``` 224 | 225 | ## -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # usr-intr 2 | 3 | ### 项目描述 4 | 5 | 选题为 **[proj6-RV64N-user-level-interrupt](https://github.com/oscomp/proj6-RV64N-user-level-interrupt)** 。 6 | 7 | 探索运用新一代 Intel 硬件特性**用户态中断**( User Interrupt ),搭建运行环境,设计新的 IPC 场景和框架,不断与传统 IPC 及 XPC、underbridge、skybridge 等新 IPC 进行性能比较并优化, 我们的结果表明, 利用用户态中断来进行进程间通信, 因其不需要进过内核, 从而性能相比传统进程间通信有数量级上的优势。此外我们还通过将用户态中断和linux io_uring子系统结合, 以用户态中断作为io完成的通知机制, 实现了双向的异步调用过程, 在保证异步基本的特性的情况下获取了更低的io延迟。 8 | 9 | 为了更好更方便地调试代码, 我们**基于qemu实现了x86的用户态中断**的支持, 可以让更多没有支持用户态中断硬件设备的人可以尝试用户态中断的特性。同时在修改qemu代码的过程中, 我们还总结qemu代码的框架和部分实现的细节, 制作为[**qemu tutorial**](https://github.com/OS-F-4/qemu-tutorial), 实现了代码级别的qemu教程, 帮助更多的人了解qemu的原理和实现。 10 | 11 | 一些产出的 ppt 放在本仓库文件夹 `ppt` 下, 主要内容为前期调研, 每周进度汇报, debug日志, 程序输出log, 问题探索和解决过程等。 12 | 13 | 我们的[**最终完整报告**](https://github.com/OS-F-4/usr-intr/blob/main/%E6%9C%80%E7%BB%88%E6%8A%A5%E5%91%8A.md), 位于仓库的根目录下, 命名为`最终报告.md`或`最终报告.pdf`。 14 | 15 | **注意, 我们文档和报告的链接默认以github为地址, 如果遇到网络问题, 可以切换到gitlab的网址, 而项目的相对路径不变。** 16 | 17 | # 成果请看这里 18 | 19 | | 成果 | github地址 | 详细说明以及使用 | gitlab地址 | 详细说明以及使用 | 20 | | ------------------------------ | ------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | 21 | | 实现用户态中断的qemu | https://github.com/OS-F-4/qemu-uintr | https://github.com/OS-F-4/usr-intr/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/qemu.md | https://gitlab.eduxiji.net/quintr/qemu | https://gitlab.eduxiji.net/quintr/report/-/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/qemu.md | 22 | | 实现用户态中断的linux内核 | https://github.com/OS-F-4/uintr-linux-kernel | https://github.com/OS-F-4/usr-intr/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/linux-kernel.md | https://gitlab.eduxiji.net/quintr/uintr-linux-kernel/-/tree/uintr-next | https://gitlab.eduxiji.net/quintr/report/-/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/linux-kernel.md | 23 | | ipc-bench性能测试 | https://github.com/OS-F-4/ipc-bench/tree/linux-rfc-v1 | https://github.com/OS-F-4/usr-intr/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/ipc-bench.md | https://gitlab.eduxiji.net/quintr/ipc-bench | https://gitlab.eduxiji.net/quintr/report/-/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/ipc-bench.md | 24 | | 基于io_uring的异步系统调用内核 | https://github.com/OS-F-4/uintr-linux-kernel/tree/uring | https://github.com/OS-F-4/usr-intr/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/linux-uring.md | https://gitlab.eduxiji.net/quintr/uintr-linux-kernel/-/tree/uring | https://gitlab.eduxiji.net/quintr/report/-/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/linux-uring.md | 25 | | 异步系统调用用户程序 | https://github.com/OS-F-4/uring | https://github.com/OS-F-4/usr-intr/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/uring.md | https://gitlab.eduxiji.net/quintr/uring | https://gitlab.eduxiji.net/quintr/report/-/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/uring.md | 26 | | qemu tutorial | https://github.com/OS-F-4/qemu-tutorial | https://github.com/OS-F-4/qemu-tutorial | https://gitlab.eduxiji.net/quintr/qemu-tutorial | https://gitlab.eduxiji.net/quintr/qemu-tutorial | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | ### 与导师的沟通情况 35 | 36 | 每周都有定时例会与导师进行沟通。 37 | 38 | 和导师建立了微信群。 39 | ### 工作规划 40 | 41 | #### 第一步 42 | 43 | 受限于疫情影响,我们没办法拿到有 uintr 特性的物理机。为了方便后续工作开展,第一步我们将基于 qemu 实现支持 uintr 的模拟器。 44 | 45 | 预期产出:能在我们的 qemu 上跑 uintr-linux-kernel ,并正确通过 linux 实现的简单功能测例 uipi_sample.c 。 46 | 47 | #### 第二步 48 | 49 | 进行性能测试,基于 Linux RFC 报告使用的 ipc-bench 作测试,比较 uintr 与 pipe等传统 IPC 方式的性能异同。自行编写 uintr+shmem 或其他运用 uintr 形式的用户测例,与传统 IPC 方式作比较。 50 | 51 | 预期产出:复现并得到比 Linux RFC 更丰富的性能测试结果,验证 IPC 框架设计的可行性。 52 | 53 | #### 第三步 54 | 55 | 修改 Linux 内核,为 IPC 框架提供更通用的系统调用接口,并通过测试。 56 | 57 | 预期产出:一个初步成型的原型系统。 58 | 59 | #### 第四步 60 | 61 | 寻找能发挥我们系统优势的有意义应用场景,在调度、负载均衡等方面继续改进 IPC 框架和内核。 62 | 63 | 预期产出:一篇有价值或影响力的学术性论文 64 | 65 | 66 | 67 | ## 项目进度 68 | 69 | #### 2022-4-9 70 | 71 | 完成了一些前期调研工作。 72 | 73 | 阅读了 XPC 、 underbridge 、skybridge 论文。 74 | 75 | 阅读与整理了 Intel Architecture Instruction Set Extensions and Future Features 中与用户态中断相关的硬件规范,主要是第2章与第11章。详见 `ppt/uintr-intel-linux.pptx` 。 76 | 77 | 阅读与整理了 uintr-linux-kernel (基于 linux 运用 uintr 特性的内核版本)仓库中的 commits 。详见 `ppt/uintr-intel-kernel commit summary.pptx` 。 78 | 79 | 对之后希望实现的 IPC 场景与框架进行了简单的调研与设计,受限于当前工作重心,该部分暂没有成熟的想法。阶段性想法产出如下(感谢张译仁学长): 80 | 81 | 对 XPC 和 uintr 进行了比较分析,提出 uintr+shmem 实现的 IPC 框架,详见 `ppt/初步设想.pdf` 。 82 | 83 | 对 uintr 的实际应用场景和会遇到的问题进行了简单分析,详见 `ppt/应用场景.md` 。 84 | 85 | #### 2022-4-16 86 | 87 | 编译了 Intel linux 和 qemu ,定位到二进制翻译的代码,定位到 MSR 寄存器定义的代码和部分中断代码。在 qemu 上跑 linux kernel 和用户态中断样例程序,目前可识别新添硬件指令(stui、senduipi等),未定义所需 MSR 寄存器,因此内核使用 `RDMSR` 和 `WRMSR` 读写失败,用户程序第一个系统调用失败获得对应输出结果。 88 | 89 | 详见 `ppt/2022-4-16.pptx` 90 | 91 | #### 2022-4-23 92 | 93 | qemu-uintr 完成 MSR 寄存器的定义和访问,正在实现不发送 IPI 的 senduipi 指令,主要困难是进行访存。初步调研了一些基于 linux 的传统 IPC 方式,使用工具 perf 进行了性能分析的上手,为之后的工作打下基础。 94 | 95 | 详见 `ppt/2022-4-23.pptx` 96 | 97 | #### 2022-4-30 98 | 99 | qemu-uintr 研究清楚了访存如何做,将 senduipi 指令实现到手册伪代码的第二个 if 。 100 | 101 | 详见 `ppt/2022-4-30.pptx` 102 | 103 | #### 2022-5-7 104 | 105 | 实现了 senduipi 与 uiret 的执行逻辑,完成中断定位与识别。 106 | 107 | 详见 `ppt/worklog5-6.md` 与 `ppt/debug-log-5-5.md` 108 | 109 | #### 2022-5-14 110 | 111 | 修复了一些 bug,现在可以完整跑通 `uipi_sample.c`。 112 | 113 | 对输出进行了反汇编分析,详情见`ppt/asm分析.md` 114 | 115 | 详见 `ppt/2022-5-14.pptx` 116 | 117 | #### 2022-5-21 118 | 119 | 修复了一些bug,现在可以在多线程情况下正常结束程序, 栈偏移问题也得到了一定的解释, 对文档进行了分块整理。 120 | 121 | 详见`ppt/2022-5-21.pptx` , `qemu工作文档分块` 122 | 123 | 124 | 125 | #### 2022-5-28 126 | 127 | 解决了cpu调度的问题, 最后总结问题为中断处理问题。 128 | 129 | 构建了一个 ubuntu 文件系统,尝试在其中运行 Intel 的 uintr ipc benchmark。 130 | 131 | 详见`ppt/2022-5-28.pptx` , `调度问题5-28.md`, `how-to-build-a-ubuntu-rootfs.md` 132 | 133 | 134 | 135 | #### 2022-6-4 136 | 137 | 实现了直接发中断的逻辑,修复了部分特权级判断的bug,详见`ppt/直接发中断6-4.md`。 138 | 139 | 跑通 intel ipc-bench,取得了初步的性能测试结果,并对性能瓶颈进行了分析。 140 | 141 | 编写了`qemu-tutorial.md`主要面向手把手修改qemu代码并进行调试, 详见`qemu-tutorial.md` 142 | 143 | 详见`ppt/2022-6-4.pptx` 144 | 145 | 146 | 147 | #### 2022-6-11 148 | 149 | 修补bug, 多线程情况下跑通`uintrfd-uni` 150 | 151 | 新增5个测试程序, 均测试通过 152 | 153 | 修补bug, 使得`uintrfd-bi` 能够高概率直接发送中断, 性能大幅度提升。 154 | 155 | 测试了基于共享内存的`uintrfd-bi`, 新能依旧碾压传统方法。 156 | 157 | 将 linux 内核的终端正确挂载到 `/dev/ttyS0` 上,从而能够跑起其它传统形式的 ipc 测例程序。相关参考资料 [1](https://unix.stackexchange.com/questions/529935 158 | )、[2](https://stackoverflow.com/questions/36529881) 159 | 160 | #### 2022-7-9 161 | 162 | 与蚂蚁沟通协调,可以使用蚂蚁的物理机了。 163 | 164 | 与贾越凯学长交流,了解网络请求性能优化的大致思路。 165 | 166 | 初步调研 Nginx 对网络请求的处理流程。 167 | 168 | 见 `ppt/利用uintr优化网络服务性能.pptx` 169 | 170 | ### 2022-7-23 171 | 172 | 和intel沟通,发现他们也在做io_uring的优化。 173 | 174 | qemu调试,单核,不打开sqpoll可以收到中断,打开sqpoll后无法发出中断,可能是内核线程没有注册sender导致,正在解决(将注册sender的函数在内核线程初始化的地方进行调用和尝试) 175 | 176 | 之后还有编写用户程序以及设计benchmark 的问题 177 | 178 | 负载变化剧烈的情况 179 | 180 | 物理机器还没消息,intel不给, 181 | 182 | ### 2022-7-30 183 | 184 | 完成异步内核的编写, 编写了相关了用户程序, 并用cgo编写了基于协程的程序 185 | 186 | go调度和操作系统调度的独立性还不太清楚。 187 | 188 | 189 | 190 | ### 2022-8 191 | 192 | 获得物理机的支持, 经过努力成功将内核安装到物理机器, 并重新测试了ipc-bench的结果, 同时测试了异步IO的结果。 193 | 194 | 编写技术报告, 文档, 以及最终报告。 -------------------------------------------------------------------------------- /ppt/uintr_in_kernel.md: -------------------------------------------------------------------------------- 1 | # 尝试在kernel里加senduipi 并编译成功 2 | 3 | 4 | 5 | ### 尝试在makefile中修改gcc 为 gcc-11, 并添加-muintr flag(和用户程序一致) 6 | 7 | ``` 8 | HOSTCC = gcc-11 9 | HOSTCXX = g++ 10 | endif 11 | 12 | export KBUILD_USERCFLAGS := -Wall -Wmissing-prototypes -Wstrict-prototypes \ 13 | -O2 -fomit-frame-pointer -std=gnu89 14 | export KBUILD_USERLDFLAGS := 15 | 16 | KBUILD_HOSTCFLAGS := $(KBUILD_USERCFLAGS) $(HOST_LFS_CFLAGS) $(HOSTCFLAGS) -muintr -O1 17 | KBUILD_HOSTCXXFLAGS := -Wall -O2 $(HOST_LFS_CFLAGS) $(HOSTCXXFLAGS) -muintr 18 | KBUILD_HOSTLDFLAGS := $(HOST_LFS_LDFLAGS) $(HOSTLDFLAGS) 19 | 20 | ``` 21 | 22 | 结果找不到对应的头文件 23 | 24 | ``` 25 | CC security/selinux/ss/avtab.o 26 | CC fs/io_uring.o 27 | CC security/selinux/ss/policydb.o 28 | ../fs/io_uring.c:82:10: fatal error: x86gprintrin.h: No such file or directory 29 | 82 | #include 30 | | ^~~~~~~~~~~~~~~~ 31 | compilation terminated. 32 | make[2]: *** [../scripts/Makefile.build:277: fs/io_uring.o] Error 1 33 | make[1]: *** [/home/xcd/qemu_uintr/uintr-linux-kernel/Makefile:1874: fs] Error 2 34 | make[1]: *** Waiting for unfinished jobs.... 35 | CC security/selinux/ss/services.o 36 | CC security/selinux/ss/conditional.o 37 | CC security/selinux/ss/mls.o 38 | CC security/selinux/ss/context.o 39 | CC security/selinux/netlabel.o 40 | CC security/lsm_audit.o 41 | AR security/selinux/built-in.a 42 | CC security/device_cgroup.o 43 | CC security/integrity/iint.o 44 | CC security/integrity/integrity_audit.o 45 | AR security/integrity/built-in.a 46 | AR security/built-in.a 47 | make[1]: Leaving directory '/home/xcd/qemu_uintr/uintr-linux-kernel/build' 48 | make: *** [Makefile:219: __sub-make] Error 2 49 | ``` 50 | 51 | 52 | 53 | ## 切换系统默认gcc为gcc-11 54 | 55 | ``` 56 | (base) ➜ uintr-linux-kernel git:(uring) ✗ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 49 57 | update-alternatives: using /usr/bin/gcc-11 to provide /usr/bin/gcc (gcc) in auto mode 58 | ``` 59 | 60 | 61 | 62 | ``` 63 | Initialize kernel stack variables at function entry 64 | > 1. no automatic stack variable initialization (weakest) (INIT_STACK_NONE) 65 | choice[1]: 1 66 | Enable heap memory zeroing on allocation by default (INIT_ON_ALLOC_DEFAULT_ON) [N/y/?] n 67 | Enable heap memory zeroing on free by default (INIT_ON_FREE_DEFAULT_ON) [N/y/?] n 68 | Enable register zeroing on function exit (ZERO_CALL_USED_REGS) [N/y/?] (NEW) y 69 | * 70 | * KCSAN: dynamic data race detector 71 | * 72 | KCSAN: dynamic data race detector (KCSAN) [N/y/?] (NEW) y 73 | Perform short selftests on boot (KCSAN_SELFTEST) [Y/n/?] (NEW) y 74 | Early enable during boot (KCSAN_EARLY_ENABLE) [Y/n/?] (NEW) y 75 | Number of available watchpoints (KCSAN_NUM_WATCHPOINTS) [64] (NEW) 76 | Delay in microseconds (for tasks) (KCSAN_UDELAY_TASK) [80] (NEW) 77 | Delay in microseconds (for interrupts) (KCSAN_UDELAY_INTERRUPT) [20] (NEW) 78 | Randomize above delays (KCSAN_DELAY_RANDOMIZE) [Y/n/?] (NEW) 79 | Skip instructions before setting up watchpoint (KCSAN_SKIP_WATCH) [4000] (NEW) 80 | Randomize watchpoint instruction skip count (KCSAN_SKIP_WATCH_RANDOMIZE) [Y/n/?] (NEW) 81 | Interruptible watchers (KCSAN_INTERRUPT_WATCHER) [N/y/?] (NEW) 82 | Duration in milliseconds, in which any given race is only reported once (KCSAN_REPORT_ONCE_IN_MS) [3000] (NEW) 83 | Report races of unknown origin (KCSAN_REPORT_RACE_UNKNOWN_ORIGIN) [Y/n/?] (NEW) 84 | Strict data-race checking (KCSAN_STRICT) [N/y/?] (NEW) 85 | Only report races where watcher observed a data value change (KCSAN_REPORT_VALUE_CHANGE_ONLY) [Y/n/?] (NEW) 86 | Assume that plain aligned writes up to word size are atomic (KCSAN_ASSUME_PLAIN_WRITES_ATOMIC) [Y/n/?] (NEW) 87 | Do not instrument marked atomic accesses (KCSAN_IGNORE_ATOMICS) [N/y/?] (NEW) 88 | Enable all additional permissive rules (KCSAN_PERMISSIVE) [N/y/?] (NEW) 89 | ``` 90 | 91 | 中途warning 92 | 93 | ``` 94 | ../arch/x86/kernel/cpu/common.c: In function ‘setup_uintr’: 95 | ../arch/x86/kernel/cpu/common.c:330:9: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation] 96 | 330 | if (!cpu_feature_enabled(X86_FEATURE_UINTR)) 97 | | ^~ 98 | ../arch/x86/kernel/cpu/common.c:332:17: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’ 99 | 332 | goto disable_uintr; 100 | | ^~~~ 101 | ../arch/x86/kernel/cpu/common.c:335:9: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation] 102 | 335 | if (!cpu_has(c, X86_FEATURE_UINTR)) 103 | | ^~ 104 | 105 | ../arch/x86/kernel/cpu/common.c:337:17: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’ 106 | 337 | goto disable_uintr; 107 | | ^~~~ 108 | ../arch/x86/kernel/uintr_fd.c: In function ‘__do_sys_uintr_create_fd’: 109 | ../arch/x86/kernel/uintr_fd.c:73:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] 110 | 73 | struct uintrfd_ctx *uintrfd_ctx; 111 | | ^~~~~~ 112 | ../arch/x86/kernel/uintr_fd.c: In function ‘__do_sys_uintr_register_handler’: 113 | ../arch/x86/kernel/uintr_fd.c:137:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] 114 | 137 | int ret; 115 | | ^~~ 116 | ../arch/x86/kernel/uintr_fd.c: In function ‘__do_sys_uintr_unregister_handler’: 117 | ../arch/x86/kernel/uintr_fd.c:163:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] 118 | 163 | int ret; 119 | | ^~~ 120 | ../arch/x86/kernel/uintr_fd.c: In function ‘__do_sys_uintr_register_sender’: 121 | ../arch/x86/kernel/uintr_fd.c:186:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] 122 | 186 | struct uintr_sender_info *s_info; 123 | | ^~~~~~ 124 | ../arch/x86/kernel/uintr_fd.c: In function ‘__do_sys_uintr_unregister_sender’: 125 | ../arch/x86/kernel/uintr_fd.c:257:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] 126 | 257 | struct uintr_sender_info *s_info; 127 | ``` 128 | 129 | 130 | 131 | ``` 132 | In file included from /usr/lib/gcc/x86_64-linux-gnu/11/include/x86gprintrin.h:79, 133 | from ../fs/io_uring.c:82: 134 | ../fs/io_uring.c: In function ‘__io_cqring_fill_event’: 135 | /usr/lib/gcc/x86_64-linux-gnu/11/include/uintrintrin.h:65:1: error: inlining failed in call to ‘always_inline’ ‘_senduipi’: target specific option mismatch 136 | 65 | _senduipi (unsigned long long __R) 137 | | ^~~~~~~~~ 138 | ../fs/io_uring.c:1775:9: note: called from here 139 | 1775 | _senduipi(0); 140 | | ^~~~~~~~~~~~ 141 | In file included from /usr/lib/gcc/x86_64-linux-gnu/11/include/x86gprintrin.h:79, 142 | from ../fs/io_uring.c:82: 143 | /usr/lib/gcc/x86_64-linux-gnu/11/include/uintrintrin.h:65:1: error: inlining failed in call to ‘always_inline’ ‘_senduipi’: target specific option mismatch 144 | 65 | _senduipi (unsigned long long __R) 145 | | ^~~~~~~~~ 146 | ../fs/io_uring.c:1775:9: note: called from here 147 | 1775 | _senduipi(0); 148 | ``` 149 | 150 | 151 | 152 | 编译一般的用户程序: 153 | 154 | ``` 155 | #include 156 | int main(){ 157 | _senduipi(0); 158 | return 0; 159 | } 160 | ``` 161 | 162 | 163 | 164 | ```shell 165 | $gcc-11 test.c 166 | In file included from /usr/lib/gcc/x86_64-linux-gnu/11/include/x86gprintrin.h:79, 167 | from test.c:1: 168 | test.c: In function ‘main’: 169 | /usr/lib/gcc/x86_64-linux-gnu/11/include/uintrintrin.h:65:1: error: inlining failed in call to ‘always_inline’ ‘_senduipi’: target specific option mismatch 170 | 65 | _senduipi (unsigned long long __R) 171 | | ^~~~~~~~~ 172 | test.c:5:9: note: called from here 173 | 5 | _senduipi(0); 174 | | ^~~~~~~~~~~~ 175 | ``` 176 | 177 | 而使用命令‘gcc-11 test.c -muintr’ 可以成功, 说明在编译uring.c 文件时, -muintr 这个flag并没有被添加到编译选项中。 178 | -------------------------------------------------------------------------------- /ppt/调度问题5-28.md: -------------------------------------------------------------------------------- 1 | # 调度问题解决 2 | 3 | ### 记录之前的实现 4 | 5 | 在清除eoi时,采用了查页表的方法然后写入物理地址。 6 | 7 | ```c 8 | int prot; 9 | uint64_t APICaddress = get_hphys2(cs, APIC_DEFAULT_ADDRESS, MMU_DATA_LOAD, &prot); 10 | uint64_t EOI; 11 | uint64_t zero = 0; 12 | cpu_physical_memory_rw(APICaddress + 0xb0, &EOI, 8, false); 13 | qemu_log("the physical address of APIC 0x%lx the EOI content: 0x%lx\n", APICaddress,EOI); 14 | cpu_physical_memory_rw(APICaddress + 0xb0, &zero, 4, true); 15 | ``` 16 | 17 | 以上的实现在使用了用户态中断程序之后出现cpu调度的问题, 操作系统报错如下。 18 | 19 | ```shell 20 | / # [ 27.363369] rcu: INFO: rcu_sched detected stalls on CPUs/tasks: 21 | [ 27.364142] rcu: 0-...!: (0 ticks this GP) idle=472/0/0x0 softirq=149/149 fqs=0 (false positive?) 22 | [ 27.364142] (detected by 1, t=21002 jiffies, g=-891, q=18) 23 | [ 27.364142] Sending NMI from CPU 1 to CPUs 0: 24 | [ 24.853640] NMI backtrace for cpu 0 25 | [ 24.853640] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.15.0-rc1+ #6 26 | [ 24.853640] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 27 | [ 24.853640] RIP: 0010:amd_e400_idle+0x16/0x40 28 | [ 24.853640] Code: b6 00 5b 48 c7 c7 80 02 25 9e 5d 41 5c 41 5d e9 90 01 b7 00 48 8b 05 49 1f 65 01 a8 10 75 0c eb 07 0f 00 2d 34 c0 fd 00 fb f4 bf 01 00 00 00 e8 1f 29 0c 04 29 | [ 24.853640] RSP: 0018:ffffffff9e203ea0 EFLAGS: 00000246 30 | [ 24.853640] RAX: 000000000001a940 RBX: 0000000000000000 RCX: 0000000000000000 31 | [ 24.853640] RDX: ffff96f9bea264a0 RSI: ffffffff9e203e30 RDI: 0000000000001472 32 | [ 24.853640] RBP: ffffffff9e214940 R08: 0000000000001471 R09: ffff96f98112d1c0 33 | [ 24.853640] R10: ffff96f9bea25740 R11: 0000000000025400 R12: ffffffff9e214940 34 | [ 24.853640] R13: ffffffff9e214940 R14: 0000000000000000 R15: 0000000000000000 35 | [ 24.853640] FS: 0000000000000000(0000) GS:ffff96f9bea00000(0000) knlGS:0000000000000000 36 | [ 24.853640] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 37 | [ 24.853640] CR2: 00007f635c574e78 CR3: 000000003720c000 CR4: 00000000000006f0 38 | [ 24.853640] Call Trace: 39 | [ 24.853640] default_idle_call+0x2c/0xa0 40 | [ 24.853640] do_idle+0x1d9/0x230 41 | [ 24.853640] cpu_startup_entry+0x14/0x20 42 | [ 24.853640] start_kernel+0x673/0x698 43 | [ 24.853640] secondary_startup_64_no_verify+0xc2/0xcb 44 | [ 27.364142] rcu: rcu_sched kthread timer wakeup didn't happen for 20999 jiffies! g-891 f0x0 RCU_GP_WAIT_FQS(5) ->state=0x402 45 | [ 27.364142] rcu: Possible timer handling issue on cpu=0 timer-softirq=138 46 | [ 24.853640] INFO: NMI handler (nmi_cpu_backtrace_handler) took too long to run: 29.077 msecs 47 | [ 27.364142] rcu: rcu_sched kthread starved for 21002 jiffies! g-891 f0x0 RCU_GP_WAIT_FQS(5) ->state=0x402 ->cpu=0 48 | [ 27.364142] rcu: Unless rcu_sched kthread gets sufficient CPU time, OOM is now expected behavior. 49 | [ 27.364142] rcu: RCU grace-period kthread stack dump: 50 | [ 27.364142] task:rcu_sched state:I stack:14856 pid: 10 ppid: 2 flags:0x00004000 51 | [ 27.364142] Call Trace: 52 | [ 27.364142] __schedule+0x26c/0x6c0 53 | [ 27.364142] schedule+0x3f/0xa0 54 | [ 27.364142] schedule_timeout+0x18b/0x290 55 | [ 27.364142] ? del_timer_sync+0x30/0x30 56 | [ 27.364142] rcu_gp_fqs_loop+0xee/0x3b0 57 | [ 27.364142] rcu_gp_kthread+0xe2/0x1c0 58 | [ 27.364142] ? rcu_gp_cleanup+0x460/0x460 59 | [ 27.364142] kthread+0x122/0x140 60 | [ 27.364142] ? set_kthread_struct+0x40/0x40 61 | [ 27.364142] ret_from_fork+0x22/0x30 62 | [ 27.364142] rcu: Stack dump where RCU GP kthread last ran: 63 | [ 27.364142] Sending NMI from CPU 1 to CPUs 0: 64 | ``` 65 | 66 | 引发这个报错的原因较多, 参考`https://blog.csdn.net/m0_37105371/article/details/118367133`, 初步判断很可能和中断控制相关, 在x86中关闭中断需要通过内存映射访问apic的寄存器进行控制, 之前的实现通过qemu中查页表的方式实现, 但可能在qemu中这样的内存映射不会在这里的接口呈现, 尝试寻找apic相关的线索。 67 | 68 | ### 尝试寻找apic相关的代码 69 | 70 | #### 这里我们找到sipi相关的流程线 71 | 72 | ```c 73 | //target/i386/sysemu/seg_helper.c/x86_cpu_exec_interrupt 74 | if(Debug) printf("x86 cpu exec interrupt called sipi \n"); 75 | do_cpu_sipi(cpu); 76 | 77 | //target/i386/helper.c/do_cpu_sipi 78 | void do_cpu_sipi(X86CPU *cpu) 79 | { 80 | apic_sipi(cpu->apic_state); //此处调用的是第一个函数 81 | } 82 | 83 | //hw/intc/apic.c 84 | void apic_sipi(DeviceState *dev) 85 | { 86 | if(Debug)printf("qemu: apic sipi called\n"); 87 | APICCommonState *s = APIC(dev); 88 | 89 | cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_SIPI); 90 | 91 | if (!s->wait_for_sipi) 92 | return; 93 | cpu_x86_load_seg_cache_sipi(s->cpu, s->sipi_vector); 94 | s->wait_for_sipi = 0; 95 | } 96 | ``` 97 | 98 | 可以在linux启动的日志中看到相关的输出: 99 | 100 | ```shell 101 | [ 0.334116] x86: Booting SMP configuration: 102 | x86 cpu exec interrupt called sipi 103 | qemu: apic sipi called 104 | x86 cpu exec interrupt called sipi 105 | qemu: apic sipi called 106 | [ 0.334220] .... node #0, CPUs: 107 | ``` 108 | 109 | 根据以上的文件组织路径, 我们尝试在clear_eoi中构建相同的调用路径, 调用到apic.c中的函数 110 | 111 | 112 | 113 | ### eoi线索 114 | 115 | ```c 116 | static void apic_eoi(APICCommonState *s) //可能和eoi有关, 但是是静态函数 117 | 118 | // apic_eoi调用位置 119 | static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val,unsigned size){ 120 | //其中的语句, 但函数的第一个参数来源不明 121 | case 0x0b: /* EOI */ 122 | apic_eoi(s); 123 | } 124 | 125 | //最后函数被放入了一个结构体内 126 | static const MemoryRegionOps apic_io_ops = { 127 | .read = apic_mem_read, 128 | .write = apic_mem_write, 129 | .impl.min_access_size = 1, 130 | .impl.max_access_size = 4, 131 | .valid.min_access_size = 1, 132 | .valid.max_access_size = 4, 133 | .endianness = DEVICE_NATIVE_ENDIAN, 134 | }; 135 | 136 | //最后这个结构体被传入一个函数 137 | static void apic_realize(DeviceState *dev, Error **errp) 138 | { 139 | //..... 140 | memory_region_init_io(&s->io_memory, OBJECT(s), &apic_io_ops, s, "apic-msi",APIC_SPACE_SIZE); 141 | //...... 142 | } 143 | /* apic在代码中一般是一个APICCommonState 144 | include/hw/i386/apic_internal.h 145 | */ 146 | struct APICCommonState { 147 | /*< private >*/ 148 | DeviceState parent_obj; 149 | /*< public >*/ 150 | 151 | MemoryRegion io_memory; 152 | X86CPU *cpu; 153 | uint32_t apicbase; 154 | uint8_t id; /* legacy APIC ID */ 155 | uint32_t initial_apic_id; 156 | uint8_t version; 157 | uint8_t arb_id; 158 | uint8_t tpr; 159 | uint32_t spurious_vec; 160 | uint8_t log_dest; 161 | uint8_t dest_mode; 162 | uint32_t isr[8]; /* in service register */ 163 | uint32_t tmr[8]; /* trigger mode register */ 164 | uint32_t irr[8]; /* interrupt request register */ 165 | uint32_t lvt[APIC_LVT_NB]; 166 | uint32_t esr; /* error register */ 167 | uint32_t icr[2]; 168 | 169 | uint32_t divide_conf; 170 | int count_shift; 171 | uint32_t initial_count; 172 | int64_t initial_count_load_time; 173 | int64_t next_time; 174 | QEMUTimer *timer; 175 | int64_t timer_expiry; 176 | int sipi_vector; 177 | int wait_for_sipi; 178 | 179 | uint32_t vapic_control; 180 | DeviceState *vapic; 181 | hwaddr vapic_paddr; /* note: persistence via kvmvapic */ 182 | bool legacy_instance_id; 183 | }; 184 | 185 | /* MemoryRegion定义 186 | include/exec/memory.h 187 | */ 188 | struct MemoryRegion { 189 | Object parent_obj; 190 | 191 | /* private: */ 192 | 193 | /* The following fields should fit in a cache line */ 194 | bool romd_mode; 195 | bool ram; 196 | bool subpage; 197 | bool readonly; /* For RAM regions */ 198 | bool nonvolatile; 199 | bool rom_device; 200 | bool flush_coalesced_mmio; 201 | uint8_t dirty_log_mask; 202 | bool is_iommu; 203 | RAMBlock *ram_block; 204 | Object *owner; 205 | 206 | const MemoryRegionOps *ops; 207 | void *opaque; 208 | MemoryRegion *container; 209 | int mapped_via_alias; /* Mapped via an alias, container might be NULL */ 210 | Int128 size; 211 | hwaddr addr; 212 | void (*destructor)(MemoryRegion *mr); 213 | uint64_t align; 214 | bool terminates; 215 | bool ram_device; 216 | bool enabled; 217 | bool warning_printed; /* For reservations */ 218 | uint8_t vga_logging_count; 219 | MemoryRegion *alias; 220 | hwaddr alias_offset; 221 | int32_t priority; 222 | QTAILQ_HEAD(, MemoryRegion) subregions; 223 | QTAILQ_ENTRY(MemoryRegion) subregions_link; 224 | QTAILQ_HEAD(, CoalescedMemoryRange) coalesced; 225 | const char *name; 226 | unsigned ioeventfd_nb; 227 | MemoryRegionIoeventfd *ioeventfds; 228 | RamDiscardManager *rdm; /* Only for RAM */ 229 | }; 230 | ``` 231 | 232 | 看到这里我们希望做到调用`apic_eoi`这个函数, 因此要寻找`apic_mem_write`这个函数,为此要寻找对应的apic 设备的`MemoryRegion`中的`MemoryRegionOps`中的`write`函数。另一种方法时直接调用EOI,但是我们需要找到正确的参数进行传递。 233 | 234 | 235 | 236 | 我们尝试对关键节点添加pin进行监控。 237 | 238 | ```c 239 | static void do_interrupt64() 240 | //.... 241 | if(intno == UINTR_UINV ){ 242 | qemu_log("recognize uintr\n"); 243 | recognized = true; //在识别到用户态中断后看eoi是否被写 244 | } 245 | 246 | void helper_uiret(CPUX86State *env){ // 进入uiret后结束监控 247 | if(Debug)qemu_log("\n\n---------\nhelper uiret called,\neip: 0x%lx | sp: 0x%lx\n", env->eip,env->regs[R_ESP]); 248 | in_uiret_called = true; 249 | recognized = false; 250 | 251 | static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val,unsigned size){ 252 | //.... 253 | case 0x0b: /* EOI */ 254 | if(Debug && recognized)qemu_log("~ ~ ~ ~EOI called in mem wirte\n"); 255 | apic_eoi(s); 256 | break; 257 | } 258 | 259 | static void apic_realize(DeviceState *dev, Error **errp) 260 | { 261 | if(Debug)qemu_log("~ ~ ~ ~apic realize called\n"); 262 | //... 263 | } 264 | 265 | // 监控自己写的eoi的函数中监控apic数据结构的地址 266 | static void helper_clear_eoi(CPUX86State *env){ 267 | CPUState *cs = env_cpu(env); 268 | //.. 269 | DeviceState *dev = cpu_get_current_apic(); 270 | X86CPU *cpu = X86_CPU(cs); 271 | qemu_log("~ ~ ~ ~ addr of curdev 0x%p | apic state 0x%p \n", dev, cpu->apic_state); 272 | // APICCommonState *apic = APIC_COMMON(cpu->apic_state); 273 | 274 | } 275 | ``` 276 | 277 | 通过grep查询输出前缀,输出相邻的3行。结果表示,EOI没有真正的被触发 278 | 279 | ```shell 280 | qemu-system-x86_64: warning: expand featrue called 281 | 282 | qemu-system-x86_64: warning: x86 cpu filter feature called 283 | ~ ~ ~ ~apic realize called # cpu初始化 284 | qemu-system-x86_64: warning: expand featrue called 285 | 286 | qemu-system-x86_64: warning: x86 cpu filter feature called 287 | ~ ~ ~ ~apic realize called # 第二个cpu初始化 288 | x86 cpu exec interrupt called sipi 289 | qemu: apic sipi called 290 | SeaBIOS (version rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org) 291 | -- 292 | !!! interrupt 2 intno:236 293 | recognize uintr 294 | the physical address of APIC 0x7f1e9ea1e610 the EOI content: 0x0 295 | ~ ~ ~ ~ addr of curdev 0x0x55f6bb8fc400 | apic state 0x0x55f6bb8fc400 # 两种方式取到的地址一致,说明是一个设备 296 | ------ 297 | rrnzero called handler: 0x401ec0 rr: 0x1 298 | origin |esp 0x7ffd38853420 | eip 0x49db3f | eflags: 0x202 299 | -- 300 | -------- 301 | 302 | xxxx in uiret called after exec tb; 303 | ~ ~ ~ ~EOI called in mem wirte # 这条输出可能来自于其他地方 304 | [ 6.145380] send: unregister sender uintrfd 3 for task=78 ret 0 305 | [ 6.147092] debug! 306 | [ 6.147969] rdmsrl 1 307 | ``` 308 | 309 | 以下样例没有eoi相关的输出 310 | 311 | ```shell 312 | qemu-system-x86_64: warning: expand featrue called 313 | 314 | qemu-system-x86_64: warning: x86 cpu filter feature called 315 | ~ ~ ~ ~apic realize called 316 | qemu-system-x86_64: warning: expand featrue called 317 | 318 | qemu-system-x86_64: warning: x86 cpu filter feature called 319 | ~ ~ ~ ~apic realize called 320 | x86 cpu exec interrupt called sipi 321 | qemu: apic sipi called 322 | SeaBIOS (version rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org) 323 | -- 324 | !!! interrupt 2 intno:236 325 | recognize uintr 326 | the physical address of APIC 0x7f7729a9f610 the EOI content: 0x0 327 | ~ ~ ~ ~ addr of curdev 0x0x55c04290e400 | apic state 0x0x55c04290e400 328 | ------ 329 | rrnzero called handler: 0x401ec0 rr: 0x1 330 | origin |esp 0x7ffc37502ce0 | eip 0x49db3f | eflags: 0x202 331 | ``` 332 | 333 | 尝试仿照sipi方式进行实现,主要是如何在调用方找到传入的参数: 334 | 335 | ```c 336 | static void apic_eoi(APICCommonState *s) //需要传入的参数是APICCommonState 337 | 338 | void apic_sipi(DeviceState *dev){ 339 | APICCommonState *s = APIC(dev); // 在这里找到从DeviceState转化为APICCommonState的方式 340 | //.. 341 | } 342 | 343 | //target/i386/helper.c 344 | void do_cpu_sipi(X86CPU *cpu) // X86CPU的一个属性是DeviceState * 类型 345 | { 346 | apic_sipi(cpu->apic_state); 347 | } 348 | ``` 349 | 350 | 再根据pin的输出, 可知cpu_get_current_apic() 和 cpu结构体中的apic_state地址相同, 说明可以用这样的方式进行传参。: 351 | 352 | ```c 353 | ~ ~ ~ ~ addr of curdev 0x0x55c04290e400 | apic state 0x0x55c04290e400 354 | 355 | DeviceState *dev = cpu_get_current_apic(); 356 | X86CPU *cpu = X86_CPU(cs); 357 | qemu_log("~ ~ ~ ~ addr of curdev 0x%p | apic state 0x%p \n", dev, cpu->apic_state); 358 | ``` 359 | 360 | 在`//hw/intc/apic.c`中编写新的可直接调用的eio函数: 361 | 362 | ```c 363 | void apic_clear_eoi(DeviceState *dev){ 364 | APICCommonState *s = APIC(dev); 365 | int isrv; 366 | isrv = get_highest_priority_int(s->isr); 367 | if (isrv < 0) 368 | return; 369 | apic_reset_bit(s->isr, isrv); 370 | if (!(s->spurious_vec & APIC_SV_DIRECTED_IO) && apic_get_bit(s->tmr, isrv)) { 371 | ioapic_eoi_broadcast(isrv); 372 | } 373 | apic_sync_vapic(s, SYNC_FROM_VAPIC | SYNC_TO_VAPIC); 374 | apic_update_irq(s); 375 | } 376 | 377 | //在中断处理部分调用的函数: 378 | static void helper_clear_eoi(CPUX86State *env){ 379 | CPUState *cs = env_cpu(env); 380 | DeviceState *dev = cpu_get_current_apic(); 381 | X86CPU *cpu = X86_CPU(cs); 382 | qemu_log("~ ~ ~ ~ addr of curdev 0x%p | apic state 0x%p \n", dev, cpu->apic_state); 383 | apic_clear_eoi(dev); 384 | } 385 | ``` 386 | 387 | 随后运行程序,程序可以正常结束,且可以多次执行,操作系统不报错。 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | -------------------------------------------------------------------------------- /ppt/worklog5-6.md: -------------------------------------------------------------------------------- 1 | # 工作概要 2 | 3 | 4 | 5 | ## 中断定位和控制流跳转 6 | 7 | ### 中断定位部分 8 | 9 | 通过添加输出的方式来定位中断触发的位置 10 | 11 | ```c 12 | void do_interrupt_all(X86CPU *cpu, int intno, int is_int, 13 | int error_code, target_ulong next_eip, int is_hw) // 接收方执行中断? 14 | { 15 | CPUX86State *env = &cpu->env; 16 | if (qemu_loglevel_mask(CPU_LOG_INT)) { 17 | if ((env->cr[0] & CR0_PE_MASK)) { 18 | static int count; 19 | 20 | qemu_log("%6d: v=%02x e=%04x i=%d cpl=%d IP=%04x:" TARGET_FMT_lx 21 | " pc=" TARGET_FMT_lx " SP=%04x:" TARGET_FMT_lx, 22 | count, intno, error_code, is_int, 23 | env->hflags & HF_CPL_MASK, 24 | env->segs[R_CS].selector, env->eip, 25 | (int)env->segs[R_CS].base + env->eip, 26 | env->segs[R_SS].selector, env->regs[R_ESP]); 27 | if (intno == 0x0e) { 28 | qemu_log(" CR2=" TARGET_FMT_lx, env->cr[2]); 29 | } else { 30 | qemu_log(" env->regs[R_EAX]=" TARGET_FMT_lx, env->regs[R_EAX]); 31 | } 32 | qemu_log("\n"); 33 | log_cpu_state(CPU(cpu), CPU_DUMP_CCOP); 34 | #if 0 35 | { 36 | int i; 37 | target_ulong ptr; 38 | 39 | qemu_log(" code="); 40 | ptr = env->segs[R_CS].base + env->eip; 41 | for (i = 0; i < 16; i++) { 42 | qemu_log(" %02x", ldub(ptr + i)); 43 | } 44 | qemu_log("\n"); 45 | } 46 | #endif 47 | count++; 48 | } 49 | } 50 | if (env->cr[0] & CR0_PE_MASK) { // 改, 中断具体分发,应该不涉及user only 51 | #if !defined(CONFIG_USER_ONLY) 52 | if (env->hflags & HF_GUEST_MASK) { 53 | qemu_log("HF_GUEST_MASK even \n"); 54 | handle_even_inj(env, intno, is_int, error_code, is_hw, 0); 55 | } 56 | #endif 57 | #ifdef TARGET_X86_64 58 | if (env->hflags & HF_LMA_MASK) { 59 | do_interrupt64(env, intno, is_int, error_code, next_eip, is_hw); 60 | } else 61 | #endif 62 | { 63 | qemu_log("interrupt protected \n"); 64 | do_interrupt_protected(env, intno, is_int, error_code, next_eip, 65 | is_hw); 66 | } 67 | } else { 68 | #if !defined(CONFIG_USER_ONLY) 69 | if (env->hflags & HF_GUEST_MASK) { 70 | qemu_log("HF_GUEST_MASK even inj \n"); 71 | handle_even_inj(env, intno, is_int, error_code, is_hw, 1); 72 | } 73 | #endif 74 | do_interrupt_real(env, intno, is_int, error_code, next_eip); 75 | } 76 | 77 | #if !defined(CONFIG_USER_ONLY) 78 | if (env->hflags & HF_GUEST_MASK) { 79 | qemu_log("HF_GUEST_MASK do real \n"); 80 | CPUState *cs = CPU(cpu); 81 | uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + 82 | offsetof(struct vmcb, 83 | control.event_inj)); 84 | 85 | x86_stl_phys(cs, 86 | env->vm_vmcb + offsetof(struct vmcb, control.event_inj), 87 | event_inj & ~SVM_EVTINJ_VALID); 88 | } 89 | #endif 90 | } 91 | ``` 92 | 93 | 94 | 95 | 96 | 97 | ### 中断识别部分 98 | 99 | ```c 100 | //target/i386/tcg/seg_helper.c 101 | #define UINTR_UINV 0xec 102 | static void do_interrupt64(CPUX86State *env, int intno, int is_int, 103 | int error_code, target_ulong next_eip, int is_hw) // 在用户态中断中 is_hw = 1 104 | { 105 | SegmentCache *dt; 106 | target_ulong ptr; 107 | int type, dpl, selector, cpl, ist; 108 | int has_error_code, new_stack; 109 | uint32_t e1, e2, e3, ss; 110 | target_ulong old_eip, esp, offset; 111 | 112 | has_error_code = 0; 113 | if (!is_int && !is_hw) { 114 | has_error_code = exception_has_error_code(intno); 115 | } 116 | if (is_int) { 117 | old_eip = next_eip; 118 | } else { 119 | old_eip = env->eip; 120 | } 121 | if(intno == UINTR_UINV ){ 122 | qemu_log("recognize uintr\n"); 123 | 124 | if(env->uintr_uif == 0){ 125 | qemu_log("--uif not zero, return\n"); 126 | return; 127 | } 128 | int prot; 129 | CPUState *cs = env_cpu(env); 130 | bool send = false; 131 | uint64_t upid_phyaddress = get_hphys2(cs, env->uintr_pd, MMU_DATA_LOAD, &prot); 132 | uintr_upid upid; 133 | cpu_physical_memory_rw(upid_phyaddress, &upid, 16, false); 134 | upid.nc.status &= (~1); // clear on 135 | if(upid.puir != 0){ 136 | env->uintr_rr = upid.puir; 137 | upid.puir = 0; // clear puir 138 | cpu_physical_memory_rw(upid_phyaddress, &upid, 16, true); // write back 139 | send = true; 140 | } 141 | cpu_physical_memory_rw(upid_phyaddress, &upid, 16, true); 142 | 143 | 144 | uint64_t APICaddress = get_hphys2(cs, APIC_DEFAULT_ADDRESS, MMU_DATA_LOAD, &prot); 145 | uint64_t EOI; 146 | uint64_t zero = 0; 147 | cpu_physical_memory_rw(APICaddress + 0xb0, &EOI, 8, false); 148 | qemu_log("the physical address of APIC 0x%lx the EOI content: 0x%lx\n", APICaddress,EOI); 149 | cpu_physical_memory_rw(APICaddress + 0xb0, &zero, 4, true); 150 | // apic_mem_write(cs, ) 151 | // uint64_t EOI; 152 | // cpu_physical_memory_rw(APIC_DEFAULT_ADDRESS + 0xb0, &EOI, 8, false); 153 | // qemu_log("\n\n the EOI content: 0x%lx\n\n",EOI); 154 | // cpu_physical_memory_rw(APIC_DEFAULT_ADDRESS + 0xb0, 0, 4, true); 155 | if(send)helper_rrnzero(env); 156 | return; 157 | } 158 | 159 | 160 | ``` 161 | 162 | 163 | 164 | ### 中断控制和跳转部分 165 | 166 | ```c 167 | static bool Debug = true; 168 | void helper_rrnzero(CPUX86State *env){ // 改 169 | if(Debug)qemu_log("rrnzero called handler: 0x%lx rr: 0x%lx\n", env->uintr_handler,env->uintr_rr); 170 | target_ulong temprsp = env->regs[R_ESP]; 171 | qemu_log("qemu:origin exp 0x%lx eip 0x%lx eflags: 0x%lx\n",env->regs[R_ESP], env->eip, env->eflags); 172 | if(env->uintr_stackadjust &1){ // adjust[0] = 1 173 | env->regs[R_ESP] = env->uintr_stackadjust; 174 | qemu_log("qemu:set statck 0x%lx\n",env->regs[R_ESP]); 175 | }else{ 176 | env->regs[R_ESP] -= env->uintr_stackadjust; 177 | qemu_log("qemu:move statck 0x%lx\n",env->regs[R_ESP]); 178 | } 179 | env->regs[R_ESP] &= ~0xfLL; /* align stack */ 180 | target_ulong esp = env->regs[R_ESP]; 181 | qemu_log("qemu:after align statck 0x%lx\n",env->regs[R_ESP]); 182 | PUSHQ(esp, temprsp); 183 | // qemu_log("qemu: pushed rsp\n"); 184 | PUSHQ(esp, env->eflags); // PUSHQ(esp, cpu_compute_eflags(env)); 185 | // qemu_log("qemu: pushed eflags\n"); 186 | PUSHQ(esp, env->eip); 187 | // qemu_log("the uirr is 0x%016lx \n", env->uintr_rr); 188 | PUSHQ(esp, env->uintr_rr & 0x3f); // // 64-bit push; upper 58 bits pushed as 0 189 | qemu_log("qemu:push finish now esp is: 0x%lx",esp); 190 | env->uintr_rr = 0; // clear rr 191 | env->regs[R_ESP] = esp; 192 | env->eflags &= ~(TF_MASK | RF_MASK); 193 | env->eip = env->uintr_handler; 194 | env->uintr_uif = 0; 195 | qemu_log("qemu: eip: 0x%lx\n",env->eip); 196 | } 197 | 198 | void helper_uiret(CPUX86State *env){ 199 | if(Debug)qemu_log("helper uiret called, now eip: 0x%lx\n", env->eip); 200 | qemu_log("qemu: now esp is: 0x%lx\n",env->regs[R_ESP]); 201 | target_ulong temprip, temprfalgs, temprsp, uirrv; 202 | target_ulong esp = env->regs[R_ESP]; 203 | esp += 0x60; 204 | POPQ(esp, uirrv); 205 | POPQ(esp, temprip); 206 | POPQ(esp, temprfalgs); 207 | POPQ(esp, temprsp); 208 | qemu_log("qemu:poped values:uirrv:0x%lx rip:0x%lx eflags:0x%lx rsp:0x%lx \n",uirrv,temprip, temprfalgs, temprsp); 209 | env->eip = temprip; 210 | env->regs[R_ESP] = temprsp; 211 | env->eflags = (env->eflags & ~0x254dd5) |(temprfalgs & 0x254dd5); 212 | env->uintr_uif = 1; 213 | } 214 | ``` 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | ## XSAVE的实现 225 | 226 | 搜索xsave, 找到如下引用: 227 | 228 | ```c 229 | //target/i386/cpu.h 230 | #define XSTATE_FP_BIT 0 231 | #define XSTATE_SSE_BIT 1 232 | #define XSTATE_YMM_BIT 2 233 | #define XSTATE_BNDREGS_BIT 3 234 | #define XSTATE_BNDCSR_BIT 4 235 | #define XSTATE_OPMASK_BIT 5 236 | #define XSTATE_ZMM_Hi256_BIT 6 237 | #define XSTATE_Hi16_ZMM_BIT 7 238 | #define XSTATE_PKRU_BIT 9 239 | #define XSTATE_UINTR_BIT 14 240 | //改 XSTAVE 根据手册,添加对应的bitmap标识 241 | #define XSTATE_XTILE_CFG_BIT 17 242 | #define XSTATE_XTILE_DATA_BIT 18 243 | #define XSTATE_UINTR_MASK (1ULL << XSTATE_UINTR_BIT) 244 | #define XSTATE_FP_MASK (1ULL << XSTATE_FP_BIT) 245 | #define XSTATE_SSE_MASK (1ULL << XSTATE_SSE_BIT) 246 | #define XSTATE_YMM_MASK (1ULL << XSTATE_YMM_BIT) 247 | #define XSTATE_BNDREGS_MASK (1ULL << XSTATE_BNDREGS_BIT) 248 | #define XSTATE_BNDCSR_MASK (1ULL << XSTATE_BNDCSR_BIT) 249 | #define XSTATE_OPMASK_MASK (1ULL << XSTATE_OPMASK_BIT) 250 | #define XSTATE_ZMM_Hi256_MASK (1ULL << XSTATE_ZMM_Hi256_BIT) 251 | #define XSTATE_Hi16_ZMM_MASK (1ULL << XSTATE_Hi16_ZMM_BIT) 252 | #define XSTATE_PKRU_MASK (1ULL << XSTATE_PKRU_BIT) 253 | #define XSTATE_XTILE_CFG_MASK (1ULL << XSTATE_XTILE_CFG_BIT) 254 | #define XSTATE_XTILE_DATA_MASK (1ULL << XSTATE_XTILE_DATA_BIT) 255 | 256 | //target/i386/tcg/fpuhelper.c 257 | static bool Debug = true; 258 | static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, 259 | uint64_t inuse, uint64_t opt, uintptr_t ra) 260 | { 261 | uint64_t old_bv, new_bv; 262 | if(Debug)printf("do xsave called\n"); // 改 xsave 263 | /* The OS must have enabled XSAVE. */ 264 | if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { 265 | raise_exception_ra(env, EXCP06_ILLOP, ra); 266 | } 267 | 268 | /* The operand must be 64 byte aligned. */ 269 | if (ptr & 63) { 270 | raise_exception_ra(env, EXCP0D_GPF, ra); 271 | } 272 | /* Never save anything not enabled by XCR0. */ 273 | rfbm &= env->xcr0; 274 | opt &= rfbm; 275 | if (opt & XSTATE_FP_MASK) { 276 | do_xsave_fpu(env, ptr, ra); 277 | } 278 | if (rfbm & XSTATE_SSE_MASK) { 279 | /* Note that saving MXCSR is not suppressed by XSAVEOPT. */ 280 | do_xsave_mxcsr(env, ptr, ra); 281 | } 282 | if (opt & XSTATE_SSE_MASK) { 283 | do_xsave_sse(env, ptr, ra); 284 | } 285 | if (opt & XSTATE_BNDREGS_MASK) { 286 | do_xsave_bndregs(env, ptr + XO(bndreg_state), ra); 287 | } 288 | if (opt & XSTATE_BNDCSR_MASK) { 289 | do_xsave_bndcsr(env, ptr + XO(bndcsr_state), ra); 290 | } 291 | if (opt & XSTATE_PKRU_MASK) { 292 | do_xsave_pkru(env, ptr + XO(pkru_state), ra); 293 | } 294 | if (opt & XSTATE_UINTR_MASK) {// 改 295 | do_xsave_uintr(env, ptr , ra); 296 | } 297 | 298 | /* Update the XSTATE_BV field. */ 299 | old_bv = cpu_ldq_data_ra(env, ptr + XO(header.xstate_bv), ra); 300 | new_bv = (old_bv & ~rfbm) | (inuse & rfbm); 301 | cpu_stq_data_ra(env, ptr + XO(header.xstate_bv), new_bv, ra); 302 | } 303 | 304 | /* 305 | 在这里介绍一下一个红展开 306 | #define XO(X) offsetof(X86XSaveArea, X) 307 | #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) 308 | __builtin_offsetof 的作用是什么? 309 | 这里使用的是一个利用编译器技术的小技巧,即先求得结构成员变量在结构体中的相对于结构体的首地址的偏移地址,然后根据结构体的首地址为0,从而得出该偏移地址就是该结构体变量在该结构体中的偏移,即:该结构体成员变量距离结构体首的距离。 310 | */ 311 | 312 | static void do_xsave_uintr(CPUX86State *env, target_ulong ptr, uintptr_t ra){ //改 313 | cpu_stq_data_ra(env, ptr, env->uintr_handler, ra); 314 | cpu_stq_data_ra(env, ptr+8, env->uintr_stackadjust, ra); 315 | cpu_stq_data_ra(env, ptr+16, env->uintr_misc, ra); 316 | cpu_stq_data_ra(env, ptr+24, env->uintr_pd, ra); 317 | cpu_stq_data_ra(env, ptr+32, env->uintr_rr, ra); 318 | cpu_stq_data_ra(env, ptr+40, env->uintr_tt, ra); 319 | } 320 | 321 | static void do_xrstor_uintr(CPUX86State *env, target_ulong ptr, uintptr_t ra){ //改 322 | env->uintr_handler = cpu_ldq_data_ra(env, ptr, ra); 323 | env->uintr_stackadjust = cpu_ldq_data_ra(env, ptr+8, ra); 324 | env->uintr_misc = cpu_ldq_data_ra(env, ptr+16, ra); 325 | env->uintr_pd = cpu_ldq_data_ra(env, ptr+24, ra); 326 | env->uintr_rr = cpu_ldq_data_ra(env, ptr+32, ra); 327 | env->uintr_tt = cpu_ldq_data_ra(env, ptr+40, ra); 328 | } 329 | 330 | static void clear_uintr_reg(CPUX86State *env){ // 改 331 | env->uintr_handler=0; 332 | env->uintr_stackadjust=0; 333 | env->uintr_misc=0; 334 | env->uintr_pd=0; 335 | env->uintr_rr=0; 336 | env->uintr_tt=0; 337 | } 338 | 339 | //在helper_xrstor中添加如下 340 | if (rfbm & XSTATE_UINTR_MASK){ // 改 341 | if (xstate_bv & XSTATE_UINTR_MASK) { 342 | do_xrstor_uintr(env, ptr + XO(uintr_state), ra); 343 | } else { 344 | clear_uintr_reg(env); 345 | } 346 | } 347 | 348 | //target/i386/tcg/tcg-cpu.h 349 | typedef struct X86XSaveArea { 350 | X86LegacyXSaveArea legacy; 351 | X86XSaveHeader header; 352 | 353 | /* Extended save areas: startoffset:0x240 */ 354 | 355 | /* AVX State: */ 356 | XSaveAVX avx_state; 357 | 358 | /* Ensure that XSaveBNDREG is properly aligned. */ 359 | uint8_t padding[XSAVE_BNDREG_OFFSET 360 | - sizeof(X86LegacyXSaveArea) 361 | - sizeof(X86XSaveHeader) 362 | - sizeof(XSaveAVX)]; 363 | /* MPX State: */ 364 | XSaveBNDREG bndreg_state; 365 | XSaveBNDCSR bndcsr_state; 366 | /* AVX-512 State: */ 367 | XSaveOpmask opmask_state; 368 | XSaveZMM_Hi256 zmm_hi256_state; 369 | XSaveHi16_ZMM hi16_zmm_state; 370 | /* PKRU State: */ 371 | XSavePKRU pkru_state; 372 | XSaveUINTR uintr_state; // 改 373 | } X86XSaveArea; 374 | 375 | 376 | //target/i386/cpu.h 添加如下区域 377 | /* Ext. save area 14: UINTR state*/ 378 | typedef struct XSaveUINTR { 379 | uint64_t handler; 380 | uint64_t stack_adjust; 381 | struct{ 382 | uint32_t uittsz; 383 | uint8_t uinv; 384 | uint16_t reserved; 385 | uint8_t uif; // bit7 is the uif 386 | }; 387 | uint64_t upidaddr; 388 | uint64_t uirr; 389 | uint64_t uittaddr; 390 | 391 | }XSaveUINTR; 392 | 393 | ``` 394 | 395 | -------------------------------------------------------------------------------- /最终报告.md: -------------------------------------------------------------------------------- 1 | # 基于用户态中断的进程间通信以及异步IO 2 | 3 | - [toc] 4 | 5 | ## 摘要 6 | 7 | 近年来, 微内核的影响力越来越大, 但是微内核的服务调用收到IPC性能的约束, 性能优越的解决方案(如XPC)对硬件和内核的修改成本过大。对于宏内核而言, 频繁的通过系统调用操作外设中上下文切换的开销也变得不容忽视, 而基于用户态的驱动实现又依赖轮询, 消耗cpu资源。**用户态中断( User Interrupt )**是一种新的硬件特性, 使得用户态的程序能够接受到中断, 并进入指定的中断处理函数中, 中断过程由硬件发送, 无需经过内核。本项目探索运用新一代 Intel 硬件特性用户态中断,搭建运行环境, 进行IPC的性能测试并与传统IPC方式进行比较。 我们的结果表明, 利用用户态中断来进行进程间通信, 因其不需要进过内核, 从而性能相比传统进程间通信有数量级上的优势。此外我们还通过将用户态中断和linux io_uring子系统结合, 以用户态中断作为IO完成的通知机制, 实现了双向的异步调用过程, 在保证异步基本的特性的情况下获取了更低的io延迟。 8 | 9 | 10 | 11 | ## 需求背景 12 | 13 | ### 微内核需求 14 | 15 | #### IPC: 性能瓶颈 16 | 17 | 近年来, 微内核的影响力越来越大, 微内核的重要特征是将内核与系统服务层分离出来,如文件系统、网络、GUI等都变成用户态进程,内核只保留最重要的进程管理、内存管理、进程间通信等功能。微内核架构使得服务之间相互独立, 提供了良好的安全性, 容错性; 同时有利于各个服务的模块化和可定制化, 不必于内核一起编译。 18 | 19 | 但由于微内核的每个服务都是一个独立的进程, 从而服务的访问需要进行**进程间的通信( IPC,Inter-Process Communication )**。具体而言, 服务的请求方向操作系统提交服务请求, 操作系统再将相应的请求转交到对应的服务进程, 服务完成后再返回给服务请求方。其中涉及权级的切换, 进程的切换, 上下文的切换, 在大量服务访问的情况下这些切换的开销不可忽视。 20 | 21 | ![image-20220815210834494](./最终报告.assets/image-20220815210834494.png) 22 | 23 | 在上图中, 红色部分为IPC的开销, 蓝色部分真正的服务执行时间, 可见IPC的开销在一些微内核的实现情况下成为了主要的开销。而传统实现IPC的开销在几百个时钟周期到上千个时间周期不等, 是不可忽视的一部分。IPC成为微内核的性能瓶颈。 24 | 25 | #### 现有方案: 26 | 27 | 传统的IPC依赖内核的通知, 而陷入内核需要较大的上下文切换开销, 同时如果采用内核页表隔离的技术, 会面临缓存失效带来的性能下降; 同时由于进程之间是地址空间隔离的, 如果需要传递数据, 数据的拷贝也会带来很大的开销, 可行的方案是通过共享内存的方式进行交互。 28 | 29 | 近年来学术界也有相关的研究成果, 如陈海波团队提出的XPC( Cross Process Call, ISC19), 提供了一种同步, 安全, 高效, 易用的IPC机制。主要通过新增硬件指令(xcall, xret), 由硬件直接完成进程的切换, 无需进过操作系统内核。其缺点在于对硬件和操作系统修改的成本太大。 30 | 31 | 综上所述, **对软硬件修改小, 同时可以实现高效的IPC**成为了微内核的重要需求。 32 | 33 | ### 宏内核需求 34 | 35 | #### 外设控制 36 | 37 | 对于宏内核(如linux), 大部分的硬件驱动运行与内核态, 是内核代码的一部分。这样的设计优点在于直观, 方便用户调用而无需关心驱动的具体实现, 同时相比微内核而言, 除了系统调用的开销不需要额外IPC的开销。但是这样设计的缺点在于安全性、扩展性、可维护性均存在一定的问题。调用驱动任然需要经过统调用从而带来的上下文切换开销, 在频繁访问外设时会带来显著的性能下降。 38 | 39 | #### 现有方案: 40 | 41 | 目前也有通过用户态的程序来控制外设的实现(如SPDK, DPDK), 其主要实现方式为通过将外设的内核驱动重新绑定到 uio 或 vfio ,用户进程可以直接访问外设的地址空间,操作外设, 省去了系统调用和 IPC 的开销。这些方案通常是基于**轮询**实现的, 而轮询对CPU的占用和消耗是比较大的, 在低密度的访问情况下会造成资源的浪费。而用户态的结局方案均采用轮询机制的一个重要原因是现有的硬件不支持将外设中断交由用户态程序处理。 42 | 43 | 综上所述, **让用户态程序能够处理中断**, 成为了高效外设控制的需求。 44 | 45 | 46 | 47 | ## 用户态中断 48 | 49 | ### 用户态中断简介 50 | 51 | 用户态中断(User Interrupt)指的是能够在用户态注册中断处理函数,并且也能够在用户态触发指定的中断处理函数。 52 | 53 | RISC-V于2020年11月, 提出了[N指令扩展规范](https://five-embeddev.com/riscv-isa-manual/latest/n.html) 其中就有支持用户态中断的设计。Intel于2021年5月发布的[软件开发指南](https://www.intel.com/content/dam/develop/external/us/en/documents/architecture-instruction-set-extensions-programming-reference.pdf)中表述了x86架构下用户态中断的实现以及特性, 这也是本项目采用的平台。 54 | 55 | 用户态中断有低延迟, 低CPU占用的特点, 中断的接收和发送无需经过操作系统内核。同时用户态中断的发送方可以是用户态的程序, 也可以是内核。对于支持用户态中断的程序, 硬件是商业化的硬件, 对操作系统的修改也只是增量的添加系统调用。 56 | 57 | 用户态的以上特性满足了微内核对于在低修改成本的情况下获得高IPC性能的需求, 同时也满足宏内核对于内核能够发送中断到用户态的需求。 58 | 59 | ### x86用户态中断的实现和原理 60 | 61 | 本小节适当地介绍x86用户态中断的总体实现, 主要通硬件, 操作系统, 用户程序三个方面介绍用户态中断的运作方式, 如果需要更详细的硬件特性说明, 请参考[intel sdm](https://www.intel.com/content/dam/develop/external/us/en/documents/architecture-instruction-set-extensions-programming-reference.pdf)。如果希望更多操作系统相关的实现, 可以参考对应的[man page](https://github.com/OS-F-4/uintr-linux-kernel/tree/uintr-next/tools/uintr/manpages)。 62 | 63 | #### 用户程序 64 | 65 | 接受方需要注册中断处理函数, 同时分配中断向量给发送方, 控制中断的使能(stui)。发送方需要经过系统调用注册, 随后使用`senduipi`指令发送中断。 66 | 67 | 我们通过一个最简单的样例程序和注释来介绍其工作的机制。 68 | 69 | ```c 70 | unsigned int uintr_received, uintr_fd; 71 | void __attribute__ ((interrupt)) uintr_handler(struct __uintr_frame *ui_frame, 72 | unsigned long long vector){ // 用户态中断处理函数, 功能为将uintr_received置1 73 | uintr_received = 1; 74 | } 75 | 76 | void *sender_thread(void *arg){ // 发送方线程执行的函数 77 | int uipi_index = uintr_register_sender(uintr_fd, 0);// 注册sender 78 | _senduipi(uipi_index); // 通过senduipi发送中断 79 | uintr_unregister_sender(uintr_fd, 0);// 注销sender 80 | return NULL; 81 | } 82 | 83 | int main(int argc, char *argv[]) 84 | { 85 | pthread_t pt; 86 | uintr_register_handler(uintr_handler, 0) // 注册handler 87 | uintr_fd = uintr_create_fd(0, 0); // 接收方创建链接, 给发送方调用 88 | 89 | _stui(); // 使能中断 90 | //创建线程调用发送函数 91 | pthread_create(&pt, NULL, &sender_thread, NULL) 92 | 93 | /* 进入循环, 等待中断 */ 94 | while (!uintr_received) 95 | usleep(1); 96 | // 收尾工作 97 | pthread_join(pt, NULL); close(uintr_fd); 98 | uintr_unregister_handler(0); 99 | printf("Success\n"); exit(EXIT_SUCCESS); 100 | } 101 | 102 | // 如果没有用户态中断将uintr_received置为1, 整个程序将陷入死循环, 而由于用户态中断, 程序收到中断后可以进入中断处理函数, 随后将uintr_received置为1, 退出死循环, 正常退出程序。 103 | ``` 104 | 105 | 106 | 107 | #### 操作系统 108 | 109 | 操作系统主要的修改为增加系统调用, 为接受方维护UPID(User Posted Interrupt Descriptor), 为发送方维护UITT(User Interrupt Target Table)。 110 | 111 | 系统调用列表: 112 | 113 | | 名称 | 功能 | 114 | | ------------------------ | ------------------------------------------------------------ | 115 | | uintr_register_handler | 用于用户态中断的接收方注册中断处理函数, 操作系统会将中断处理函数的地址写入硬件, 从而使得接收到中断后可以跳转到指定的函数入口进行执行。 | 116 | | uintr_unregister_handler | 注销中断处理函数, 注销后硬件将不在记录中断处理函数地址, 接受方也无法收到中断。 | 117 | | uintr_create_fd | 用于接收方创建链接, 接收方向操作系统注册设定的中断向量`vector`, 所有的注册信息将被操作系统封装为一个文件描述符, 用于后续给发送方注册`sender`。注册成功后, 发送方若发送中断, 则收到的中断函数中的参数会携带中断向量`vector`, 从而接收方可以区分不同的发送方。 | 118 | | uintr_register_sender | 用于发送发注册`sender`, 向操作系统提供的是接收方所注册的文件描述符, 操作系统根据注册的文件描述符来为发送方写入接受方的相关信息, 同时返回接受方信息在`UITT(User Interrupt Target Table)`中的下标, 发送方可以通过下标来给不同的接受方发送用户态中断。 | 119 | | uintr_unregister_sender | 注销用户态中断的发送方, 注销后对应的下标将无法成功发送中断。 | 120 | 121 | 接下来简要介绍一下操作系统维护的两种数据结构: 122 | 123 | ![image-20220815153642964](./最终报告.assets/image-20220815153642964.png) 124 | 125 | - UPID(User Posted Interrupt Descriptor) 126 | 127 | UPID是操作系统为用户态中断接收方维护的数据结构, 主要标志了使能, 中断向量等位。 128 | 129 | | 区块 | 含义 | 130 | | ----- | -------------------------------------------------------- | 131 | | ON | 标志当前是否有中断 | 132 | | SN | 中断使能状态 | 133 | | NV | 中断编号 | 134 | | NDEST | 标志接受方所在的物理核号, 硬件在发送时会根据核号发送中断 | 135 | | PIR | 中断向量, 标志当前哪个或者哪几个发送方发送了用户态中断 | 136 | 137 | - UITT(User Interrupt Target Table) 138 | 139 | UITT是操作系统为中断发送方维护的数据结构, 为一张表, 表中的一项为UITTE的指针, 指向UITTE结构体, UITTE中主要标记了发送UPID的地址以及使能以及当前发送方持有的中断向量。 140 | 141 | | 区块 | 含义 | 142 | | -------- | -------------------------------------------------- | 143 | | V | 合法位 | 144 | | UV | 发送方持有的中断向量, 最后接受方的第1< 197 | 198 | 目前qemu通过新增硬件寄存器, 新增指令支持, 新增中断处理支持, 直接发中断支持等方式实现了对用户态中断的支持, 并在收发延迟中相比传统方法(经过操作系统)有数十倍的性能的提升, 这也印证了我们在qemu中对硬件模拟的实现的有效性。 199 | 200 | 下图为性能对比图, 我们在qemu中进行计时, 计时的开始节点是`senduipi`这条指令执行开始, 到硬件收到对应的中断结束。其中带数字的是内核输出。我们可以看到, 如果没有进入内核, 完全由硬件发送中断, 只需要8us, 而如果中断发送过程中如果由于调度或者其他原因经过内核, 中断的收发延迟就会大大增加, 有数十倍的差距。 201 | 202 | ![image-20220815165830700](./最终报告.assets/image-20220815165830700.png)![image-20220815165849010](./最终报告.assets/image-20220815165849010.png) 203 | 204 | 我们总体的开发过程如下, 具体的问题解决过程以及解决方式详见[**探究过程**](https://github.com/OS-F-4/usr-intr/blob/main/ppt/qemu%E5%B7%A5%E4%BD%9C%E6%96%87%E6%A1%A3%E5%88%86%E5%9D%97/%E9%97%AE%E9%A2%98%E4%BB%A5%E5%8F%8A%E6%8E%A2%E7%A9%B6%E8%BF%87%E7%A8%8B.md), 其中有**上万字的详细的开发过程**中的困难以及解决方式。 205 | 206 | 1. 环境准备 207 | 2. 指令捕捉,向软件反馈硬件特性 208 | 3. 内存读写实现发送 209 | 4. 修改中断处理实现接收 210 | 5. 中断收尾实现完整运行 211 | 6. 实现直接发中断,提高性能 212 | 7. 多次调试,完善实现细节 213 | 214 | 我们的代码位于https://github.com/OS-F-4/qemu-uintr。 215 | 216 | 在编写的过中, 我们将遇到的问题和检索的信息进行提炼, 总结成**[qemu tutorial](https://github.com/OS-F-4/qemu-tutorial),** 方便更多的人从源代码去理解qemu的运行机制, 修改qemu。 217 | 218 | 219 | 220 | ## 基于用户态中断的进程间通信 221 | 222 | 我们利用用户态中断来编写程序, 实现程序之间的IPC通信, 同时希望和传统的IPC方式作比较。我们在qemu和物理机上均做了测试, 实验结果表明, 使用用户态中断进行进程间通信在性能上和传统IPC有量级上的优势。 223 | 224 | ### 实验设定 225 | 226 | 给定两个进程, 互相通过某种IPC机制完成固定数量的IPC, 每次IPC携带一定量的数据, 比较完成所有IPC的时长, 测试平均每秒能够发送的IPC次数。 227 | 228 | ### 性能结果 229 | 230 | 基于qemu运行结果: 231 | 232 | | IPC type | Message rate (msg/s, size = 1) | Message rate (msg/s, size = 4096) | 233 | | --------------- | ------------------------------- | ---------------------------------- | 234 | | uintr (thread) | 28935 | 23305 | 235 | | uintr (process) | 34392 | 22662 | 236 | | signal | 6286 | N/A | 237 | | eventfd | 6649 | N/A | 238 | | pipe | 2858 | 2256 | 239 | | fifo | 2782 | 2674 | 240 | | domain | 4835 | 2781 | 241 | 242 | 基于物理机器运行结果: 243 | 244 | | IPC type | Message rate (msg/s, size = 1) | Message rate (msg/s, size = 4096) | 245 | | --------------- | ------------------------------- | ---------------------------------- | 246 | | uintr (thread) | 191868 | 116340 | 247 | | uintr (process) | 377670 | 347037 | 248 | | signal | 31798 | N/A | 249 | | eventfd | 26630 | N/A | 250 | | pipe | 9436 | 14011 | 251 | | fifo | 25544 | 16527 | 252 | | domain | 11183 | 19758 | 253 | 254 | 255 | 256 | 其中`uintr(thread)`是两个之间利用用户态中断相互通通信, `uintr(process)`是两个进程之间利用用户态中断通信, 其他方法是一些经典的IPC方法例如pipe(管道), signal(信号)。 257 | 258 | 从结果可以看到, 利用用户态中断进行进程间通信相比传统方法有**量级上的性能优势**, 无论在屋里机器上还是qemu环境中。 259 | 260 | 对比qemu和物理机的数据, 同样条件下, qemu的运行速度慢于物理机, 这符合模拟器运行速度慢的直觉。相同执行环境中不同方法之间的相对差距幅度也比较相似, 这也说明了qemu对硬件的性质的模拟是比较到位的, 同时也验证了我们在qemu中实现的用户态中断支持是有效且到位的。 261 | 262 | 我们的[代码](https://github.com/OS-F-4/ipc-bench/tree/linux-rfc-v1)和[文档](https://github.com/OS-F-4/usr-intr/blob/main/ppt/%E5%B1%95%E7%A4%BA%E6%96%87%E6%A1%A3/ipc-bench.md)在对应的链接中。 263 | 264 | 265 | 266 | ## 基于用户态中断的异步IO 267 | 268 | ### 系统调用和进程间通信关系的思考 269 | 270 | 在前文中我们已经证实了用户态中断在IPC中的高效性, 我们期望用户态中断在别的地方也有用武之地。 271 | 272 | 用户态中断为IPC提供了双向的通知机制, 实现了进程间通信。如果将操作系统内核抽象为一个广义的进程(在内核页表隔离的情况下, 内核表现的更像进程), 则用户程序和内核之间的交互也可以理解为进程间通信。用户程序和内核之间的交互(系统调用), 是用户同步触发的(通过syscall硬件指令), 而这种触发类似于中断, 当前的控制流会切换进入到内核的中断处理入口。系统调用与IPC的不同之处在于, IPC的双向的, 而基于用户态中断的IPC是双向且异步的; 而系统调用是单向的, 用户使用系统调用由用户决定, 但是系统调用的返回是由操作系统决定, 并且用户程序一般会被阻塞。如果内核能够利用用户态中断作为通知机制作为系统调用的返回, 这会使得编写更高效且延时更短的异步程序。 273 | 274 | 接下来我们会介绍异步编程的需求以及分类, 适当介绍较为新颖的linux io_uring异步IO子系统, 介绍我们将用户态中断和io_uring结合的工作, 并分析实验的结果。 275 | 276 | ### 异步编程简介 277 | 278 | #### 异步的需求和本质 279 | 280 | 随着互联网上数据量的不断增大, 用户量不断增多, web服务的不断多样化, 如何让高并发且IO密集的程序更高效的工作变得格外重要。经典的阻塞IO或者系统调用显然不能满足对性能的需求。 281 | 282 | 纯粹的同步并行并不能处理海量的数据, 原因在于一个系统能够运行的线程总数总是小于海量的网络请求, 并且如果所有的线程都因为IO而被阻塞, 则当前没有可以调度的线程, CPU处于空转状态, 而额外的网络请求也不能受到处理。此外过多的线程会导致过多的内存占用, 同时线程切换的开销会很大, 影响程序的性能。 283 | 284 | 而异步的编程方式能够使得线程不被IO阻塞, 当等待IO时线程依然可以利用CPU进行其他任务的处理, 从而提高了CPU的有效利用率, 提高总体的吞吐量, 获得更高的性能。 285 | 286 | 综上所述, 异步编程的优势在于线程不会被IO阻塞, 这样就使得CPU任务和非CPU任务相互分离, 从而更好地提高CPU和其他设备的利用率。 287 | 288 | #### 简单的同步/异步实现分类 289 | 290 | 我们按照IO的实现方式, 使用的方便性和性能对IO的实现进行简单的分类, 从上到下, 接口的易用性会变差, 但性能会更好。 291 | 292 | 1. 同步阻塞:例如 read,阻塞当前线程直到读取完成。 293 | 2. 同步非阻塞:例如 read(NONBLOCK) ,如果当前读尚未就绪则立即返回。 294 | 3. 多路复用:例如 select,poll,epoll,阻塞当前线程直到给定事件中的任何一个发生。常配合同步非阻塞接口使用。 295 | 4. 异步:例如 MPI_iread,发出读请求,返回一个 token。之后可以查询请求状态(轮询)或者用 wait 接口阻塞等待操作完成。 296 | 5. 循环队列:例如 io_uring 及硬件设备,背后有一个内核线程(或硬件上的处理器)同时处理 IO 操作,二者通过共享内存中的两个循环队列(请求队列和完成队列)传递请求状态。 297 | 298 | 以上非阻塞/异步的特征: 异步地提交请求, 但是完成请求后的操作仍然需要在程序的特定节点调用相关的函数来**获取请求的完成情况**。延迟由请求完成本身的耗时和查询请求完成情况的频率共同决定。而我们的工作就是利用用户态中断作为提醒机制, 无需用户程序查询请求的完成状况, 从而达到更短的延迟。 299 | 300 | ### io_uring异步框架简介 301 | 302 | 我们的工作基于linux io_uring子系统, 由于该系统较为新, 我们适当介绍其工作原理。 303 | 304 | ![image-20220815084334928](./最终报告.assets/image-20220815084334928.png) 305 | 306 | io_uring子系统可以简单的抽象为生产者消费者模型。其中有两个重要的队列, 一个为提交队列(SQ), 提交队列中每个项简称为SQE, 其中主要标记了IO的类型(如read, write), 对应系统调用的参数, 以及用户额外添加的用户数据(一般是一个指针)。一个为完成队列(CQ), 完成队列中的每个项简称CQE, 主要包含请求的返回值(是否正常完成), 以及用户刚开始传入的用户数据(SQE中的用户数据)。两个队列一般通过共享内存的方式在内核与用户空间中共享。 307 | 308 | 对于IO的提交, 用户程序是生产者, 生产IO的请求; 内核是消费者, 完成用户程序提交的IO。用户程序只需要将IO提交到提交队列, 随后就可以进行其他任务, 最后通过检查完成队列的情况来获取IO的完成情况。 309 | 310 | 对于IO的结果返回, 内核是生产者, 将完成的IO放入完成队列; 用户程序是消费者, 检查完成队列, 并取出完成结果。 311 | 312 | io_uring是一个高效的异步框架, 用户提交IO后可以直接执行其他任务, 同时也可以一次性提交多个IO任务, 只进行一次系统调用通知内核执行; 同时io_uring支持请求类型非常丰富, 覆盖网络和读写等, 比专门的异步框架(如AIO只支持磁盘读写)有更高的灵活性; 此外还支持开启内核线程轮询等多种模式, 最大化框架带来的效率提升。 313 | 314 | io_uring只提供了三个系统调用, 却囊括了非常复杂的功能, 所以在系统调用的使用上会给用户带来一定的困难, 所以开发者还开发了liburing, 对系统调用进行了封装, 更方便用户的使用。 315 | 316 | 更多介绍详见[链接](https://arthurchiao.art/blog/intro-to-io-uring-zh/), 以及[man page](https://man.archlinux.org/man/io_uring.7.en)。 317 | 318 | 319 | 320 | ### 用户态中断+io_uring实现 321 | 322 | 在实现方面, 对于内核的修改在于, 在初始化io_ring时, 用户可以传递注册好的fd给内核, 使得内核获得接受方的必要信息, 并将信息记录在io_uring的上下文结构体中。在IO完成, 内核将CQE方如完成队列时, 给用户态的程序发送用户态中断进行提醒。 323 | 324 | ![image-20220815182728082](./最终报告.assets/image-20220815182728082.png) 325 | 326 | 对于用户程序或者共享库, 一方面我们对liburing进行修改以适配我们对内核的修改, 同时在用户程序使用方面, 需要将注册好的fd传入给内核, 一本是在io_uring初始化时进行传递; 同时用户程序需要设置中断处理函数, 在内核通知到达时及时查看IO的完成状态并进行对应的操作。 327 | 328 | 整个系统达到双向的异步, 实现了从操作系统->用户程序方向从无异步通知到有异步通知的**从无到有**的突破。 329 | 330 | 我们的[内核代码](https://github.com/OS-F-4/uintr-linux-kernel/tree/uring)和[用户程序](https://github.com/OS-F-4/uring)均位于仓库中。我们进行了测试实验, 结果表明我们的实现在IO计算重叠以及IO延迟方面表现出色。 331 | 332 | ### 实验设定和方法 333 | 334 | 我们的实验采用如下的设定, 我们将程序的任务抽象为IO任务和计算任务, 这样的抽象符合绝大多数程序的设定。 335 | 336 | 1. 给定一定量的io任务(read)和计算任务 337 | 2. 计算任务大小随机(固定的随机序列, 700k~7000k次乘法) 338 | 3. IO任务分为4kB, 4MB, 40MB三种, 随机交替 339 | 4. 不涉及并行, IO只能顺序提交 340 | 5. 评价指标: 完成所有任务的总时长, 每类io的延迟 341 | 342 | 我们采用三种方法进行实验, 分别是 343 | 344 | 1. 普通的同步IO方式, 仅使用同步的系统调用完成IO。 345 | 2. io_uring + SQPOL, 用户程序异步提交IO请求到请求队列, IO完成后会由内核放入完成队列, 用户程序需要调用接口检查完成队列的完成情况。 346 | 3. io_uring + SQPOLL + uintr, 用户程序异步提交IO到请求队列, IO完成后由内核放入完成队列, 同时利用用户态中断的机制进行提醒, 用户程序无需显示调用相关的接口, 只需要在中断处理函数中处理完成的IO即可。 347 | 348 | 按照我们的设定, 我们预期使io_uring的方案会有更短的完成所有任务的时间(因为可以利用异步使得计算和IO重叠), 同时预期同步的方式会有最短的IO延迟。而io_uring + 用户态中断的实现方式既能利用IO和计算的重叠提高总体的速度, 同时又能利用用户态中断的提醒机制达到短的延迟。 349 | 350 | ![image-20220815134736044](./最终报告.assets/image-20220815134736044.png) 351 | 352 | 上图, 从左到右分别为同步IO( 后简称norma l), 使用io_uring( 后简称uring ), 使用io_uring+用户态中断( 后简称uintr )。从图中可以看出, normal方式由于IO完成立即返回, IO的延迟较小, 但无法将IO和计算重叠; uring的方式能够利用计算和IO的重叠, 但是如果IO先于计算完成, 但是程序仍然在执行计算任务, 只有等当期计算任务完成后才能调用相关的接口检查IO的完成情况, 所以可能会带来大的IO延迟。uintr的实现方式利用了io_uring从而将计算和IO重叠, 同时利用用户态中断的通知机制能够无条件打破计算的执行从而先处理IO的完成, 达到了小的IO延迟。 353 | 354 | ### 实验结果 355 | 356 | 在200个IO任务和1000计算任务的情况下获得如下结果(延时为所用同类延时相加, 单位为us): 357 | 358 | 其中单纯的计算用时约为3410806us。 359 | 360 | | 方法 | 总用时(us) | 4K 延时 | 4M 延时 | 40M 延时 | 361 | | ------ | ---------- | ------- | ------- | -------- | 362 | | normal | 4163422 | 259 | 38753 | 698487 | 363 | | uring | 3546226 | 205714 | 269079 | 1283943 | 364 | | uintr | 3591993 | 360 | 41431 | 727977 | 365 | 366 | 以4MB的IO为例, 我们统计了最大和最小的延时的情况: 367 | 368 | | 方法 | 4M min | 4M max | 369 | | ------ | ------ | ------ | 370 | | normal | 329 | 3090 | 371 | | uring | 707 | 10601 | 372 | | uintr | 330 | 3580 | 373 | 374 | 从实验结果可以看出实验符合我们的预期, normal有最长的完成时间, 但是有最短的IO延时, uring的完成时间最短, 但是IO的延迟较大, 并且延时的方差更大。uintr的方法在保证计算和IO重叠的情况下同时拥有较低的IO延迟, 并且和同步方法延迟相差不大, 在两个评价指标上有优秀的表现。 375 | 376 | 377 | 378 | ## 讨论 379 | 380 | 在我们的实验中, 用户态中断表现出了惊人的性能, 其独特的通知特性也使得在一些方面可以实现新的实现方式上的突破。除了进程间通信以及异步IO, 还有更多的领域可以使用用户态中断, 如用户态外设控制, 利用用户态中断进行程序的检测和控制等等。 除了以上的优势, 用户态中断也存在些许不足之处。第一, 建立的链接数量有限, 硬件的中断向量位数只有64位, 接受方在当前的实现下只能区分64个不同的发送方, 这在一些情况下显然是不能满足需求的, 这个问题或许可以通过软件的方式进行解决; 第二, 用户态中断只是提醒机制, 且不方便使用, 用户态中断是指通过中断进行提醒, 中断函数中的内容需要由用户定义, 同时数据的传递也需要额外的方式进行处理, 如何让用户态中断更加易用, 编写更加方便和标准的接口也是值得探索的方向。 381 | 382 | ## 结论 383 | 384 | 在本项目中, 我们实现了支持用户态中断的qemu, 探索了x86用户态中断在进程间通信以及异步IO中的妙用, 我们的结果表明利用用户态进行进程间通行与传统的进程间通信有量级上的性能优势, 在异步IO的应用中作为通知机制能够有效降低IO的延迟。对于用户态中断这样的新特性, 仍然还有许多应用的探索空间。 385 | 386 | ## 感想 387 | 388 | 项晨东: 本项目从2022年2月开始, 历时半年多, 期间因为上海疫情的原因我们团队无法接触到物理机, 从而着手开始开发qemu, 在qemu的开发过程中, 从硬件-操作系统-软件全流程的调试让我有了很多的收获, 对软硬件协同配合的方式有了更深刻的理解, 有了更好的成长。在后续探索uintr新的应用场景如异步IO框架中, 我也更加充分理解了异步的需求和实现。在尝试在物理机器上运行内核的过程中也遇到了很多困难, 但都意义克服了, 看到最后性能实验结果时我的内心是十分激动的, 感觉长期以来的努力没有白费。 389 | 390 | ## 致谢 391 | 392 | 感谢陈渝老师和向勇老师的指导! 393 | 394 | 感谢校外导师提供的帮助和硬件支持! 395 | 396 | 感谢贾越凯、贺鲲鹏、尤予阳、张译仁以及其他学长的帮助! -------------------------------------------------------------------------------- /ppt/qemu-tutorial.md: -------------------------------------------------------------------------------- 1 | # qemu-tutorial 2 | 3 | [toc] 4 | 5 | 欢迎来到qemu-tutorial! 这是一个介绍qemu的代码级的中文教程, 希望它能够对你了解qemu有所帮助。 6 | 7 | qemu体量庞大, 这个教程不可能面面俱到, 欢迎大家补充和纠正。 8 | 9 | 下面的部分主要依据我在qemu上实现intel x86用户态中断硬件特性涉及的内容。主要涉及新增指令, 指令翻译, 寄存器定义, 中断处理, 访存等操作, 架构主要和x86相关。教程更偏向定位和一些调试技巧, 更希望通过一些小的调试过程的介绍来提升对qemu的理解。 10 | 11 | 12 | 13 | ## qemu初体验 14 | 15 | ### 编译和运行 16 | 17 | 我们开始吧! qemu github 主页`https://github.com/qemu/qemu`。 18 | 19 | ``` 20 | git clone git@github.com:qemu/qemu.git 21 | cd qemu 22 | mkdir build 23 | cd build 24 | ../configure --enable-debug --target-list=x86_64-softmmu 25 | make 26 | ``` 27 | 28 | 完整编译qemu需要非常长的时间, 在这里选择`--target-list=x86_64-softmmu` 可以只编译支持运行`x86`架构的部分, 可以提升编译的速度, 如果需要编译不同架构的qemu, 则可以修改list中的内容。 29 | 30 | 编译完成后, 可执行文件中在`/qemu/build/x86_64-softmmu/qemu-system-x86_64`, 我们在此备用。 31 | 32 | 我们这里使用linux作为内核进行加载, 为了更好的契合修改以及应用的需要, 我们这里使用intel编写的支持用户态中断的内核, 并创建相应的文件的系统。具体的编译方式见`编译技巧和环境.md`。 33 | 34 | 假设编译出的内核位置在`$kernelbase`, 文件系统在`$fsbase`, qemu文件夹在`$qemubase`, 我们可以用以下命令将编译好的内核运行在我们的qemu上。 35 | 36 | ``` 37 | $qemubase/qemu/build/x86_64-softmmu/qemu-system-x86_64 -smp 2 \ 38 | -m 1024M -nographic \ 39 | -kernel $kernelbase/bzImage \ 40 | -initrd $fsbase/initramfs-busybox-x86_64.cpio.gz \ 41 | -append "root=/dev/ram0 rw rootfstype=ext4 console=ttyS0 init=/linuxrc" 42 | ``` 43 | 44 | 我们可以将以上命令包装为一个`run.sh`方便我们快速启动qemu并运行内核。 45 | 46 | "smp"指定了处理器的个数,"m"指定了内存的大小。"kernel"指定编译生成的内核镜像的存放位置。的"append"是内核启动的附加参数。”initrd” 指定了文件系统镜像所在的位置。 47 | 48 | 运行qemu后可以进入内核镜像的shell程序。 49 | 50 | 可以通过`control + a` 随后按`x`退出qemu。 51 | 52 | ### 源代码概览 53 | 54 | 在本教程中, 我们主要关注和指令和体系架构的部分以及中断的部分的。 55 | 56 | 指令架构部分的代码主要在`target`目录下, 可见目录下有各类指令集对应的子文件夹, 其中`x86`对应的文件夹为`i386`。 57 | 58 | ```shell 59 | $ ls target 60 | Kconfig arm cris hppa m68k microblaze nios2 ppc rx sh4 tricore 61 | alpha avr hexagon i386 meson.build mips openrisc riscv s390x sparc xtensa 62 | ``` 63 | 64 | 在`i386`文件夹下又有大量文件, 我们主要关注`tcg`文件夹下的内容, 主要涉及指令翻译以及中断异常处理逻辑的实现, 其中`translate.c` 是最为核心的部分, 代码量也非常大, 但是仔细解构可以发现, 这部分的代码主要工作就是解析二进制字节, 匹配翻译, 并将指令转化为一系列宿主机器指令的集合或者若干函数的集合。`cpu.h` 和`cpu.c`中的内容也是重要的, 主要涉及体系寄存器的定义以及不同的硬件配置。 65 | 66 | `hw`目录主要涉及硬件周围设备相关的代码, 其中`apic`, 即中断控制器, 相关的代码在`hw/intc`下, 在涉及apic时, 我们会修改这一部分的代码。 67 | 68 | ### 在qemu中输出调试 69 | 70 | 在这里我们希望在qemu初始化cpu的时候进行输出调试。在qemu中, 输出日志的方式是通过`qemu_log()`函数, 用法和`printf()`相同, 需要包括`"qemu/log.h"` 头文件。 71 | 72 | ```c 73 | //target/i386/helper.c 74 | void do_cpu_init(X86CPU *cpu) 75 | { 76 | qemu_log("hello qemu!!\n"); 77 | CPUState *cs = CPU(cpu); 78 | CPUX86State *env = &cpu->env; 79 | CPUX86State *save = g_new(CPUX86State, 1); 80 | int sipi = cs->interrupt_request & CPU_INTERRUPT_SIPI; 81 | *save = *env; 82 | cpu_reset(cs); 83 | cs->interrupt_request = sipi; 84 | memcpy(&env->start_init_save, &save->start_init_save, 85 | offsetof(CPUX86State, end_init_save) - 86 | offsetof(CPUX86State, start_init_save)); 87 | g_free(save); 88 | if (kvm_enabled()) { 89 | kvm_arch_do_init_vcpu(cpu); 90 | } 91 | apic_init_reset(cpu->apic_state); 92 | } 93 | ``` 94 | 95 | 重新编译qemu, 执行`run.sh`, 你可能可以在某个瞬间看到自己想要的输出。但其实输出非常短暂, 因为linux启动时会清空输出界面, 导致看不到我们想要的输出。这里可以用重定向的方法来把linux的输出导出到别的地方, 或者简单的利用管道即可。 96 | 97 | ```shell 98 | $ ./run.sh | echo nothing 99 | nothing 100 | hello qemu!! 101 | hello qemu!! 102 | ``` 103 | 104 | 因为启动了两个虚拟cpu, 所以有两行输出。至此我们已经找到了cpu启动部分的代码, 并且成功修改代码后进行输出。 105 | 106 | 107 | 108 | ## 第一条指令 109 | 110 | 我们尝试在qemu中定位到一条指令, 每次该指令被翻译, 就输出trace信息。 111 | 112 | ```c 113 | //target/i386/tcg/translate.c 114 | case 0xc3: /* ret */ 115 | qemu_log("ret instruction finded when translate\n"); 116 | ot = gen_pop_T0(s); 117 | gen_pop_update(s, ot); 118 | /* Note that gen_pop_T0 uses a zero-extending load. */ 119 | gen_op_jmp_v(s->T0); 120 | gen_bnd_jmp(s); 121 | gen_jr(s, s->T0); 122 | break; 123 | ``` 124 | 125 | 再次编译运行qemu然后进行输出, 会发现屏幕上出现了大量的输出语句, 记得及时`control + a` 后`x` 退出qemu。 126 | 127 | 我们在这里介绍translate.c中主要的翻译机制: 128 | 129 | 本质上来说, 识别指令就是一个查二进制表的过程, 每个二进制码对应一条汇编指令, 翻译过程类似自动机, 识别一个字节后进行二进制case的switch, 如果非法则跳入非法指令处理流程, 如果合法则继续匹配一下一个字节。这里我们以`rdtsc`这条指令为例子, 从源代码查看指令的翻译过程。 130 | 131 | 通过查阅资料, 我们得知`rdtsc`指令的二进制编码为`0f 31`, 主要的功能是读取时间寄存器, 随后写入edx:eax。 132 | 133 | 总的翻译过程是从`static target_ulong disas_insn(DisasContext *s, CPUState *cpu)` 函数开始的, 这个函数十分庞大, 所以我们只取其中比较关键的部分进行讲解。 134 | 135 | ```c 136 | next_byte: 137 | b = x86_ldub_code(env, s); 138 | /* Collect prefixes. */ 139 | switch (b) { 140 | case 0xf3: 141 | prefixes |= PREFIX_REPZ; 142 | goto next_byte; 143 | case 0xf2: 144 | prefixes |= PREFIX_REPNZ; 145 | goto next_byte; 146 | case 0xf0: 147 | prefixes |= PREFIX_LOCK; 148 | goto next_byte; 149 | //.... 150 | ``` 151 | 152 | 我们说过, 翻译过程是按字节翻译的`x86_ldub_code`这个函数就是取出待翻译的块中的一个字节, 赋值到b中, 然后进行指令的匹配。而字节之间很容易重复, qemu通过前缀的位表示来区分之前翻译的几个字节中记录的信息, 对此我们只需要大概有印象, 具体的指令相关的信息可以通过查阅`x86`的手册来辅助理解。在`rdtsc`这条指令中, `0f`这个字节不会匹配到任何前缀, 程序继续向下运行。 153 | 154 | 前缀识别完毕后, 开始识别操作符。 155 | 156 | ```c 157 | /* now check op code */ 158 | reswitch: 159 | switch(b) { 160 | case 0x0f: 161 | /**************************/ 162 | /* extended op code */ 163 | b = x86_ldub_code(env, s) | 0x100; 164 | goto reswitch; 165 | /**************************/ 166 | /* arith & logic */ 167 | case 0x00 ... 0x05: 168 | case 0x08 ... 0x0d: 169 | ``` 170 | 171 | 这里的`0x0f`恰好是`rdtsc`的第一个字节, 则程序会再读出下一个字节, 并或上`0x100`, 这里的作用是一种添加前缀信息的体现, 标志当前的前一个字节是`0x0f`这意味着`rdtsc`中的`0x31`和其他情况下的`0x31`走的匹配路径是不同的分支。 172 | 173 | 我们直接搜索`0x131`, 发现确实直接定位到了`rdtsc`中的内容。 174 | 175 | ```c 176 | case 0x131: /* rdtsc */ 177 | gen_update_cc_op(s); 178 | gen_jmp_im(s, pc_start - s->cs_base); 179 | if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { 180 | gen_io_start(); 181 | } 182 | gen_helper_rdtsc(cpu_env); 183 | if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { 184 | gen_jmp(s, s->pc - s->cs_base); 185 | } 186 | break; 187 | ``` 188 | 189 | 在此添加相关输出, 在运行相关的程序包含此指令时, 就可以看到相应的输出。理论上大多数的指令都会在注释中列出, 直接搜索相关相关的指令的名称如`rdtsc`就可找到对应的操作, 我们介绍此过程主要帮助读者理解翻译, 在尝试自己添加新的指令或者操作时, 能够更加高效和符合qemu中的规范。 190 | 191 | ## 理解翻译块机制 192 | 193 | QEMU 是一个动态翻译器。当它第一次遇到一段代码时,它将其转换为主机指令集。通常是动态翻译器非常复杂且高度依赖CPU。 QEMU 使用了一些技巧这使得它相对容易携带和简单,同时实现良好的表现。 194 | 195 | QEMU 的动态翻译后端叫做 TCG, `Tiny Code Generator`的简称, 翻译过程为`目标机器代码->tcg->宿主机器代码`, 如果是简单的指令, 可能通过几条tcg中间代码代替, 如果是复杂的指令, 则通过helper函数来进行实现。其中一个重要的过程为翻译块机制, 为了方便加速, qemu不是翻译一条指令执行一条指令, 而是翻译一些指令, 然后同时执行这些指令。指令分块的依据和编译原理中基本快的划分比较类似, 当在块内翻译到跳转语句时, qemu会停止翻译, 执行已经翻译的部分, 根据执行结果寻找下个需要翻译的块进行翻译。这样的翻译粒度能够避免全局代码翻译带来的巨大翻译量, 同时也比边翻译边执行的效率要高, 如果翻译结果块(宿主机器代码)在缓存中停留时间较长, 也能够很好的起到加速的作用, 变相借助了宿主机器的缓存机制, 使得模拟的行为更加接近真实。 196 | 197 | 所以这里需要注意的有两点。 第一, 翻译和执行是分离的, 我们在`translate.c `中进行的输出均是在翻译阶段进行的, 也就是说如果我们像在执行阶段进行操作, 需要有别的方法, 这也是为什么翻译阶段存在许多`gen_`开头的函数, 这些函数的本质就是将翻译出的代码加入翻译块中, 而并不执行这些代码。经过全局搜索我们容易发现, `gen_`开头的函数没有相应的定义, 这里其实是宏在起作用, 但是具体宏的转化以及背后的运行机制笔者也没有彻底弄懂, 这里我们将举一个简单的例子进行演示。第二, 在我们尝试修改qemu代码时, 要注意翻译过程中是否是跳转语句, 是否需要让翻译过程终止, 先执行当前翻译块的内容。 198 | 199 | 我们依然通过`rdtsc`这条指令来进行翻译块机制的演示。首先编写一个简单的应用程序, 编译后放入qemu挂载的文件系统后制作文件系统镜像。 200 | 201 | ```c 202 | #include 203 | typedef unsigned long long int uint64_t; 204 | uint64_t rdtsc() // linux 205 | { 206 | unsigned int lo, hi; 207 | __asm__ volatile ("rdtsc" : "=a" (lo), "=d" (hi)); 208 | return ((uint64_t)hi << 32) | lo; 209 | } 210 | int main(){ 211 | uint64_t time = rdtsc(); 212 | printf("the time is %llu", time); 213 | return 0; 214 | } 215 | ``` 216 | 217 | 我们在以下几个地方添加pin, `(env->hflags & HF_CPL_MASK) == 3` 主要作用是标准当前是用户态(内核态调用rdtsc非常多, 所以通过这个条件进行过滤): 218 | 219 | 在`translate.c`中添加, 这个不再过多赘述`rdtsc_translated`用于标志是否进行了翻译, 这个变量将会在其他地方引用, 。 220 | 221 | ```c 222 | //target/i386/tcg/translate.c 223 | bool rdtsc_translated = false; 224 | /* convert one instruction. s->base.is_jmp is set if the translation must 225 | be stopped. Return the next pc value */ 226 | static target_ulong disas_insn(DisasContext *s, CPUState *cpu) 227 | //... 228 | case 0x131: /* rdtsc */ 229 | gen_update_cc_op(s); 230 | if((env->hflags & HF_CPL_MASK) == 3){ 231 | qemu_log("caught rdtsc in translate.c\n"); 232 | rdtsc_translated = true; 233 | } 234 | gen_jmp_im(s, pc_start - s->cs_base); 235 | if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { 236 | gen_io_start(); 237 | } 238 | gen_helper_rdtsc(cpu_env); 239 | if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { 240 | gen_jmp(s, s->pc - s->cs_base); 241 | } 242 | break; 243 | ``` 244 | 245 | 我们再找到这个`gen_helper_rdtsc(cpu_env);`这个函数的真实位置, 观察函数语义, 很明显可以知道这和硬件定义的。 246 | 247 | ```c 248 | //target/i386/tcg/misc_helper.c 249 | bool rdtsc_execed = false; 250 | void helper_rdtsc(CPUX86State *env) 251 | { 252 | uint64_t val; 253 | if((env->hflags & HF_CPL_MASK) == 3){ 254 | qemu_log("rdtsc execed\n"); 255 | rdtsc_execed = true; 256 | } 257 | if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { 258 | raise_exception_ra(env, EXCP0D_GPF, GETPC()); 259 | } 260 | cpu_svm_check_intercept_param(env, SVM_EXIT_RDTSC, 0, GETPC()); 261 | val = cpu_get_tsc(env) + env->tsc_offset; 262 | env->regs[R_EAX] = (uint32_t)(val); 263 | env->regs[R_EDX] = (uint32_t)(val >> 32); 264 | } 265 | ``` 266 | 267 | 我们找到块翻译和执行相关的代码, 其中核心的是`cpu_loop_exec_tb`这个函数, 我们分别在函数的前后进行添加pin进行输出。 268 | 269 | ```c 270 | //accel/tcg/cpu-exec.c 271 | extern bool rdtsc_translated; 272 | extern bool rdtsc_execed; 273 | int cpu_exec(CPUState *cpu){ 274 | int ret; 275 | SyncClocks sc = { 0 }; 276 | //.... 277 | /* if an exception is pending, we execute it here */ 278 | while (!cpu_handle_exception(cpu, &ret)) { 279 | TranslationBlock *last_tb = NULL; 280 | int tb_exit = 0; 281 | while (!cpu_handle_interrupt(cpu, &last_tb)) { 282 | TranslationBlock *tb; 283 | target_ulong cs_base, pc; 284 | uint32_t flags, cflags; 285 | cpu_get_tb_cpu_state(cpu->env_ptr, &pc, &cs_base, &flags); 286 | cflags = cpu->cflags_next_tb; 287 | if (cflags == -1) { 288 | cflags = curr_cflags(cpu); 289 | } else { 290 | cpu->cflags_next_tb = -1; 291 | } 292 | if (check_for_breakpoints(cpu, pc, &cflags)) { break; } 293 | tb = tb_lookup(cpu, pc, cs_base, flags, cflags); 294 | if (tb == NULL) { 295 | mmap_lock(); 296 | tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); 297 | mmap_unlock(); 298 | qatomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); 299 | } 300 | #ifndef CONFIG_USER_ONLY 301 | if (tb->page_addr[1] != -1) { 302 | last_tb = NULL; 303 | } 304 | #endif 305 | /* See if we can patch the calling TB. */ 306 | if (last_tb) {tb_add_jump(last_tb, tb_exit, tb);} 307 | if(rdtsc_translated){ 308 | qemu_log("translated before exec tb\n"); 309 | rdtsc_translated = false; 310 | }else if (rdtsc_execed){ 311 | qemu_log("execd before exec tb\n"); 312 | rdtsc_execed = false; 313 | } 314 | 315 | cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit); 316 | 317 | if(rdtsc_translated){ 318 | qemu_log("translated after exec tb\n"); 319 | rdtsc_translated = false; 320 | }else if (rdtsc_execed){ 321 | qemu_log("execd after exec tb\n"); 322 | rdtsc_execed = false; 323 | } 324 | /* Try to align the host and virtual clocks 325 | if the guest is in advance */ 326 | align_clocks(&sc, cpu); 327 | } 328 | } 329 | cpu_exec_exit(cpu); 330 | rcu_read_unlock(); 331 | return ret; 332 | } 333 | ``` 334 | 335 | 输出体量较多, 但是顺序是符合预期的: 336 | 337 | ```shell 338 | caught rdtsc in translate.c 339 | translated before exec tb 340 | rdtsc execed 341 | execd after exec tb 342 | the time is 67290627360 343 | ``` 344 | 345 | 346 | 347 | ## cpu定义 348 | 349 | ### 寄存器定义 350 | 351 | `x86`寄存器相关的定义在`target/i386/cpu.h`中, 结构体为`CPUX86State`, 其中包括了所有的寄存器以及其他硬件状态相关的信息变量。由于结构体庞大, 我们只保留一些常用的做简单介绍。从本章开始, 我们也会在相关的内容中体现出我们之前在实现uintr中的工作, 所以一些代码在qemu原版中是没有的, 这部分多余的代码可以视作对qemu做修改的一些例子。因为增加新的硬件特性涉及寄存器, 指令, 操作系统, 用户程序, 编译器等内容, 全流程介绍较为冗余, 不够聚焦, 我们在介绍的过程中就简单穿插修改部分和qemu本身代码的讲解, 如果对我们实现uintr的部分感兴趣, 欢迎查看我们相关的仓库和主页。 352 | 353 | ```c 354 | typedef struct CPUArchState { 355 | /* standard registers */ 356 | target_ulong regs[CPU_NB_REGS]; // 通用寄存器 357 | target_ulong eip; // pc寄存器 358 | target_ulong eflags; /* eflags register. During CPU emulation, CC 359 | flags and DF are set to zero because they are 360 | stored elsewhere */ 361 | 362 | /* emulator internal eflags handling */ 363 | target_ulong cc_dst; 364 | target_ulong cc_src; 365 | target_ulong cc_src2; 366 | uint32_t cc_op; 367 | int32_t df; /* D flag : 1 if D = 0, -1 if D = 1 */ 368 | uint32_t hflags; /* TB flags, see HF_xxx constants. These flags 369 | are known at translation time. */ 370 | uint32_t hflags2; /* various other flags, see HF2_xxx constants. */ 371 | 372 | /* segments */ 373 | SegmentCache segs[6]; /* selector values */ 374 | SegmentCache ldt; 375 | SegmentCache tr; 376 | SegmentCache gdt; /* only base and limit are used */ 377 | SegmentCache idt; /* only base and limit are used */ 378 | 379 | target_ulong cr[5]; /* NOTE: cr1 is unused !!! */ 380 | //.... 381 | #ifdef TARGET_X86_64 382 | target_ulong lstar; 383 | target_ulong cstar; 384 | target_ulong fmask; 385 | target_ulong kernelgsbase; 386 | #endif 387 | // 我们所实现的uintr依赖的寄存器 388 | uint64_t uintr_rr; 389 | uint64_t uintr_handler; 390 | uint64_t uintr_stackadjust; 391 | uint64_t uintr_misc; 392 | uint64_t uintr_pd; 393 | uint64_t uintr_tt; 394 | uint64_t uintr_uif; 395 | 396 | //.... 397 | } CPUX86State; 398 | ``` 399 | 400 | ### 寄存器访问 401 | 402 | 寄存器的访问比较简单, 在给定结构体指针的情况下, 直接通过结构体访问即可 403 | 404 | ```c 405 | void helper_cpuid(CPUX86State *env) 406 | { 407 | uint32_t eax, ebx, ecx, edx; 408 | cpu_svm_check_intercept_param(env, SVM_EXIT_CPUID, 0, GETPC()); 409 | cpu_x86_cpuid(env, (uint32_t)env->regs[R_EAX], (uint32_t)env->regs[R_ECX], 410 | &eax, &ebx, &ecx, &edx); 411 | env->regs[R_EAX] = eax; 412 | env->regs[R_EBX] = ebx; 413 | env->regs[R_ECX] = ecx; 414 | env->regs[R_EDX] = edx; 415 | } 416 | ``` 417 | 418 | 但是对于一些msr, 是基于专门的指令来访问的, 这就需要我们按照指令的翻译流程进行识别和访问。 419 | 420 | 首先对于msr, 每个都有特定的编号, 我们一般在`cpu.h`中找到相关的定义: 421 | 422 | ```c 423 | #define MSR_IA32_SGXLEPUBKEYHASH0 0x8c 424 | #define MSR_IA32_SGXLEPUBKEYHASH1 0x8d 425 | #define MSR_IA32_SGXLEPUBKEYHASH2 0x8e 426 | #define MSR_IA32_SGXLEPUBKEYHASH3 0x8f 427 | // 用户态寄存器编号定义 428 | #define MSR_IA32_UINTR_RR 0x985 429 | #define MSR_IA32_UINTR_HANDLER 0x986 430 | #define MSR_IA32_UINTR_STACKADJUST 0x987 431 | #define MSR_IA32_UINTR_MISC 0x988 432 | #define MSR_IA32_UINTR_PD 0x989 433 | #define MSR_IA32_UINTR_TT 0x98a 434 | ``` 435 | 436 | 对应的访问指令翻译部分在`translate.c`中。我们去掉`gen_`前缀, 直接搜索`helper_rdmsr`, 可以找到相关函数的定义位置。 437 | 438 | ```c 439 | case 0x130: /* wrmsr */ 440 | case 0x132: /* rdmsr */ 441 | if (check_cpl0(s)) { 442 | gen_update_cc_op(s); 443 | gen_jmp_im(s, pc_start - s->cs_base); 444 | if (b & 2) { 445 | gen_helper_rdmsr(cpu_env); 446 | } else { 447 | gen_helper_wrmsr(cpu_env); 448 | gen_jmp_im(s, s->pc - s->cs_base); 449 | gen_eob(s); 450 | } 451 | } 452 | break; 453 | ``` 454 | 455 | 在`rdmsr`和`wrmsr`相关的函数中, 只是应用了简单的`switch`语句, 456 | 457 | ```c 458 | void helper_rdmsr(CPUX86State *env) 459 | { 460 | X86CPU *x86_cpu = env_archcpu(env); 461 | uint64_t val; 462 | 463 | cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 0, GETPC()); 464 | 465 | switch ((uint32_t)env->regs[R_ECX]) { 466 | //...我们实现的读写msr部分 467 | case MSR_IA32_UINTR_HANDLER: 468 | val = env->uintr_handler; 469 | break; 470 | case MSR_IA32_UINTR_STACKADJUST: 471 | val = env->uintr_stackadjust; 472 | break; 473 | case MSR_IA32_UINTR_MISC: 474 | val = env->uintr_misc; 475 | break; 476 | case MSR_IA32_UINTR_PD: 477 | val = env->uintr_pd; 478 | break; 479 | case MSR_IA32_UINTR_TT: 480 | val = env->uintr_tt; 481 | break; 482 | //... 483 | } 484 | 485 | void helper_wrmsr(CPUX86State *env) 486 | { 487 | // if(Debug)qemu_log("wrmsr %hx \n",(uint32_t)env->regs[R_ECX]); 488 | uint64_t val; 489 | CPUState *cs = env_cpu(env); 490 | 491 | cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 1, GETPC()); 492 | //...我们实现的写msr相关的语句 493 | case MSR_IA32_UINTR_RR: 494 | env->uintr_rr = val; 495 | if(val!= 0){ 496 | helper_rrnzero(env); 497 | } 498 | break; 499 | case MSR_IA32_UINTR_HANDLER: 500 | env->uintr_handler = val; 501 | break; 502 | case MSR_IA32_UINTR_STACKADJUST: 503 | env->uintr_stackadjust = val; 504 | break; 505 | case MSR_IA32_UINTR_MISC: 506 | env->uintr_misc = val; 507 | break; 508 | case MSR_IA32_UINTR_PD: 509 | env->uintr_pd = val; 510 | break; 511 | case MSR_IA32_UINTR_TT: 512 | env->uintr_tt = val; 513 | break; 514 | } 515 | ``` 516 | 517 | 518 | 519 | ## 中断处理 520 | 521 | 中断处理主要的内容在`target/i386/tcg/seg_helper.c`中, 在64位情况下执行的路线为: 522 | 523 | ``` 524 | do_interrupt_x86_hardirq->do_interrupt_all->do_interrupt64 525 | ``` 526 | 527 | 这是大多数情况的执行路线, 在其他情况下可能会调用其他的中断处理函数。 528 | 529 | 530 | 531 | ## apic相关 532 | 533 | apic主要是中断控制相关的函数, 用于发核间中断等。这里我们只举一个例子, `sipi`相关的操作。 534 | 535 | 这里我们找到sipi相关的流程线 536 | 537 | ```c 538 | //target/i386/sysemu/seg_helper.c/x86_cpu_exec_interrupt 539 | if(Debug) printf("x86 cpu exec interrupt called sipi \n"); 540 | do_cpu_sipi(cpu); 541 | 542 | //target/i386/helper.c/do_cpu_sipi 543 | void do_cpu_sipi(X86CPU *cpu) 544 | { 545 | apic_sipi(cpu->apic_state); //此处调用的是第一个函数 546 | } 547 | 548 | //hw/intc/apic.c 549 | void apic_sipi(DeviceState *dev) 550 | { 551 | if(Debug)printf("qemu: apic sipi called\n"); 552 | APICCommonState *s = APIC(dev); 553 | 554 | cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_SIPI); 555 | 556 | if (!s->wait_for_sipi) 557 | return; 558 | cpu_x86_load_seg_cache_sipi(s->cpu, s->sipi_vector); 559 | s->wait_for_sipi = 0; 560 | } 561 | ``` 562 | 563 | 可以在linux启动的日志中看到相关的输出: 564 | 565 | ```shell 566 | [ 0.334116] x86: Booting SMP configuration: 567 | x86 cpu exec interrupt called sipi 568 | qemu: apic sipi called 569 | x86 cpu exec interrupt called sipi 570 | qemu: apic sipi called 571 | [ 0.334220] .... node #0, CPUs: 572 | ``` 573 | 574 | 根据以上的文件组织路径, 我们尝试在clear_eoi中构建相同的调用路径, 调用到apic.c中的函数。 575 | 576 | apic中相关的操作在`apic.c`中有相关的定义, 但多是静态函数, 如果要对外暴露接口, 建议新写函数调用现成的函数。 577 | 578 | 还值得注意的是, 新的函数调用前, 需要将相应的函数声明添加到以下函数的位置: 579 | 580 | ```c 581 | //include/hw/i386/apic.h 582 | #ifndef APIC_H 583 | #define APIC_H 584 | /* apic.c */ 585 | void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, 586 | uint8_t vector_num, uint8_t trigger_mode); 587 | int apic_accept_pic_intr(DeviceState *s); 588 | void apic_deliver_pic_intr(DeviceState *s, int level); 589 | void apic_deliver_nmi(DeviceState *d); 590 | int apic_get_interrupt(DeviceState *s); 591 | void apic_reset_irq_delivered(void); 592 | int apic_get_irq_delivered(void); 593 | void cpu_set_apic_base(DeviceState *s, uint64_t val); 594 | uint64_t cpu_get_apic_base(DeviceState *s); 595 | void cpu_set_apic_tpr(DeviceState *s, uint8_t val); 596 | uint8_t cpu_get_apic_tpr(DeviceState *s); 597 | void apic_init_reset(DeviceState *s); 598 | void apic_sipi(DeviceState *s); 599 | void apic_clear_eoi(DeviceState *s); //新添加的对外暴露的接口 600 | int get_apic_id(DeviceState *dev); //新添加的对外暴露的接口 601 | void send_ipi(DeviceState *dev, uint8_t dest, uint8_t nv); // 新添加的对外暴露的接口 602 | void apic_poll_irq(DeviceState *d); 603 | void apic_designate_bsp(DeviceState *d, bool bsp); 604 | int apic_get_highest_priority_irr(DeviceState *dev); 605 | /* pc.c */ 606 | DeviceState *cpu_get_current_apic(void); 607 | #endif 608 | ``` 609 | 610 | -------------------------------------------------------------------------------- /ppt/asm分析.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [toc] 4 | 5 | 6 | 7 | # 对输出进行反汇编分析 8 | 9 | 10 | ```shell 11 | # 添加icount 的版本 12 | Sending IPI from sender thread index:0 13 | [ 5.884737] rdmsrl misc 5 14 | qemu: caught 0xf30fc7 SENDUIPI eip:0x4017bc #在sender_thread print之后第一条指令 15 | -------------- 16 | 17 | 18 | 0x4017bc block size:46 icont:12 19 | [ 5.886608] rdmsrl misc 5 20 | 0x485e45 block size:12 icont: 3 # 在__clock_nanosleep 21 | 0x4017bc block size:92 icont:20 22 | qemu:helper senduipi called receive regidx:240, uipiindex: 0 23 | qemu: data of uitt valid:1 user_vec:0 UPID address 0xffff9cf1c2b32ac0 24 | qemu: content of upid: status:0x0 nv:0xec ndst:0x100 0x0000000000000000 25 | qemu: data write back in upid: status:0x1 nv:0xec ndst:0x100 0x0000000000000001 26 | 0x450050 block size:46 icont:12 # 在syscall 27 | 0x450050 block size:92 icont:20 28 | [ 5.888169] rdmsrl misc 5 29 | [ 5.888670x485e45 block size:26 icont:10 30 | !!! interrupt 2 intno:236 31 | recognize uintr 32 | the physical address of APIC 0x10 the EOI content: 0xf000ff53f000ff53 33 | rrnzero called handler: 0x4016f5 rr: 0x1 34 | qemu:origin exp 0x7ffd801e2860 eip 0x485e45 eflags: 0x202 35 | qemu:move statck 0x7ffd801e27e0 36 | qemu:after align statck 0x7ffd801e27e0 37 | qemu:push finish now esp is: 0x7ffd801e27c0qemu: eip: 0x4016f5 38 | 2] uintr_unregister_sender called 39 | [ 5.891230] send: unregister sender uintrfd 3 for task=79 ret 0 40 | 0x44f36f block size: 2 icont: 1 # 在__libc_write 41 | 0x475649 block size: 8 icont: 2 # __pthread_disable_asynccancel 42 | 0x401734 block size: 1 icont: 1 # uintr_handler 中写函数的后一条指令(将uintr_received改为1) 43 | -------------- 44 | 45 | 46 | qemu:caught 0xf30f01ec UIRET # 注意! pc: 0x401759 已经进入sender_thread 47 | before: pc_start: 0x401755 sc_base:0 pc: 0x401759 pc.next:0x401755 rip:0x401734 48 | helper uiret called, now eip: 0x401734 # eip 还在uintr_handler (将uintr_received改为1) 49 | qemu: now esp is: 0x7ffd801e2760 50 | qemu:poped values:uirrv:0x1 rip:0x485e45 eflags:0x202 rsp:0x7ffd801e2860 51 | pc_start: 0x401755 sc_base:0 pc: 0x401759 rip:0x485e45 # 希望返回的eip值在nanosleep中 52 | ------------- 53 | 54 | 55 | [ 5.889107] rdmsrl misc 5 56 | [ 5.893860] debug! 57 | [ 5.894780] rdmsrl 1 58 | qemu:wrmsr misc 0x0000000000000000 59 | qemu:wrmsr tt 0x0000000000000000 60 | 0x45006d block size:14 icont: 6 # syscall 61 | 0x4017ea block size: 8 icont: 2 # sender_thread 62 | 0x41801a block size: 7 icont: 3 # jmp 417ed3 start_thread中 63 | 0x417ed3 block size: 5 icont: 1 # start_thread中 417 到 418 都是 64 | 0x417eec block size:19 icont: 3 65 | 0x417a00 block size: 5 icont: 1 66 | 0x417b43 block size:22 icont: 5 67 | 0x417ef1 block size: 1 icont: 1 68 | 0x421c40 block size: 5 icont: 1 # __libc_thread_freeres 69 | 0x421c52 block size:18 icont: 4 70 | 0x47b440 block size: 6 icont: 1 #__res_thread_freeres 71 | 0x47db00 block size:10 icont: 3 72 | 0x47db60 block size:38 icont:11 73 | 0x47b44a block size: 9 icont: 6 74 | 0x47b45c block size:18 icont: 5 75 | 0x421c58 block size: 2 icont: 2 # __libc_thread_freeres 76 | 0x4205b0 block size:22 icont: 3 77 | 0x420678 block size:13 icont: 3 78 | 0x421c6e block size: 1 icont: 1 79 | 0x421c84 block size:22 icont: 3 80 | 0x421c84 block size:10 icont: 2 81 | 0x421c84 block size:92 icont:20 82 | 0x421c8e block size:10 icont: 2 83 | 0x48c8d0 block size: 6 icont: 1 # __libc_dlerror_result_free 84 | 0x48c908 block size:21 icont: 6 85 | 0x421c94 block size: 2 icont: 2 86 | 0x421c9e block size:10 icont: 2 87 | 0x4206b0 block size: 9 icont: 2 88 | 0x420738 block size:31 icont: 8 89 | 0x42079c block size:24 icont: 5 90 | 0x417ef6 block size: 5 icont: 4 91 | 0x417f08 block size:18 icont: 3 92 | 0x417f27 block size:31 icont: 6 93 | 0x417f55 block size: 2 icont: 2 94 | 0x417f6e block size:25 icont: 5 95 | 0x417f8b block size:29 icont: 6 96 | 0x417f99 block size:14 icont: 3 97 | 0x417f9e block size: 5 icont: 1 98 | 0x417f9e block size:92 icont:20 99 | 0x417fc7 block size:41 icont:10 100 | 0x41808b block size:12 icont: 2 101 | 0x450150 block size:20 icont: 4 # __madvise 102 | 0x45015b block size: 3 icont: 2 103 | 0x450163 block size: 8 icont: 2 104 | 0x41809f block size: 1 icont: 1 105 | 0x417fd3 block size: 5 icont: 1 106 | 0x417fe5 block size:18 icont: 3 107 | 0x417ffb block size:14 icont: 3 108 | 0x417ffb block size:11 icont: 4 # start_thread 109 | [ 5.889107] sample2[78]: segfault at 7f29aa395640 ip 00007f29aa395640 sp 00007ffd801e28f8 error 15 110 | 0x417ffb block size: 3 icont: 2 111 | 0x417ffb block size:10 icont: 3 112 | [ 5.889107] Code: 00 00 a0 e0 4a 00 00 00 00 00 a0 e6 4a 00 00 00 00 00 a0 ef 4a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <40> 56 39 aa 29 7f 0f 113 | [ 5.889107] recv: Release uintrfd for r_task 78 uvec 0 114 | qemu:wrmsr misc 0x0000000000000000 115 | qemu:wrmsr tt 0x0000000000000000 116 | qemu:wrmsr pd 0x0000000000000000 117 | qemu:wrmsr RR 0x0 118 | qemu:wrmsr stackadjust 0x0 119 | qemu:wrmsr handler 0x0000000000000000 120 | 0x587ead block size: 2 icont: 2 # 可能是shell程序 121 | 0x49596a block size: 2 icont: 2 122 | 0x49596a block size:92 icont:20 123 | 0x5882b6 block size: 8 icont: 2 124 | 0x5882cb block size: 6 icont: 2 125 | 0x5882cb block size: 2 icont: 1 126 | 0x5882cb block size:10 icont: 3 127 | 0x5882cf block size: 4 icont: 2 128 | 0x5882cf block size: 5 icont: 2 129 | 0x5882cf block size:92 icont:20 130 | 0x5882d4 block size: 5 icont: 2 131 | 0x5882d9 block size: 5 icont: 2 132 | 0x5882d9 block size:92 icont:20 133 | 0x452950 block size:12 icont: 3 134 | 0x452a60 block size:29 icont: 7 135 | 0x452a6e block size:14 icont: 3 136 | 0x452a95 block size:10 icont: 2 137 | 0x452a9c block size:63 icont:17 138 | 0x452980 block size:21 icont: 4 139 | 0x42ec80 block size: 5 icont: 1 140 | 0x452985 block size:11 icont: 3 141 | 0x45298a block size: 5 icont: 2 142 | 0x452990 block size: 6 icont: 2 143 | 0x4529a3 block size:19 icont: 5 144 | 0x4e3de0 block size:74 icont:17 145 | 0x422fff block size:27 icont: 6 146 | 0x4233cb block size:63 icont:17 147 | 0x4233d8 block size:13 icont: 3 148 | 0x5882e5 block size:10 icont: 3 149 | 0x5881c6 block size:18 icont: 5 150 | 0x58821c block size:31 icont: 9 151 | 0x448157 block size:92 icont:19 152 | 0x448157 block size:14 icont: 4 153 | 0x448157 block size: 5 icont: 1 154 | 0x448165 block size:14 icont: 4 155 | 0x448173 block size:14 icont: 4 156 | 0x44819a block size:18 icont: 5 157 | 0x588278 block size:16 icont: 4 158 | 0x58827f block size: 7 icont: 2 159 | 0x58829b block size:23 icont: 6 160 | 0x5882f7 block size:10 icont: 4 161 | 0x588356 block size:14 icont: 5 162 | 0x588698 block size: 9 icont: 4 163 | 0x5883e5 block size:31 icont: 7 164 | 0x4b9837 block size: 1 icont: 1 165 | 0x445b2e block size:63 icont:17 166 | 0x445bc8 block size:28 icont: 7 167 | 0x588401 block size:13 icont: 2 168 | 0x588406 block size: 5 icont: 1 169 | 0x448c1c block size:63 icont:17 170 | 0x448c1c block size:48 icont: 9 171 | 0x448c1c block size: 1 icont: 1 172 | 0x448c4c block size:48 icont: 9 173 | 0x5886b7 block size:14 icont: 2 174 | 0x588518 block size: 5 icont: 1 175 | 0x5875d4 block size: 9 icont: 2 176 | 0x5875d8 block size: 4 icont: 2 177 | 0x5875e5 block size: 5 icont: 2 178 | 0x44c1cc block size:63 icont:17 179 | 0x44c043 block size:35 icont: 7 180 | 0x592fe0 block size:20 icont: 3 181 | 0x5e9037 block size:15 icont: 3 182 | 0x44f80b block size:63 icont:17 183 | 0x44f784 block size:13 icont: 2 184 | 0x58c86e block size:63 icont:17 185 | 0x58c885 block size:12 icont: 3 186 | 0x44f80b block size:25 icont: 5 187 | 0x5876b0 block size:12 icont: 3 188 | 0x58ee73 block size:63 icont:17 189 | 0x5ed66e block size:63 icont:17 190 | 0x4bb63a block size:10 icont: 3 191 | 0x5ed74f block size: 8 icont: 2 192 | 0x44d932 block size:37 icont: 8 193 | 0x44de60 block size:10 icont: 2 194 | 0x44de60 block size:11 icont: 3 195 | 0x44de60 block size:92 icont:20 196 | 0x44de6f block size:11 icont: 3 197 | 0x44de7f block size:16 icont: 5 198 | 0x44de91 block size: 6 icont: 2 199 | 0x44affe block size:16 icont: 4 200 | 0x44dea1 block size:17 icont: 5 201 | 0x44df7f block size:18 icont: 4 202 | 0x44df1e block size:18 icont: 4 203 | 0x44d502 block size:18 icont: 4 204 | 0x52f2be block size:21 icont: 9 205 | 0x52f2c9 block size:92 icont:20 206 | 0x4bb460 block size:10 icont: 3 207 | 0x4bb4d4 block size: 1 icont: 1 208 | 0x4bb4fd block size:10 icont: 3 209 | 0x496beb block size:10 icont: 3 210 | 0x531127 block size: 1 icont: 1 211 | 0x4b967b block size:11 icont: 7 212 | 0x4bbb6b block size:10 icont: 3 213 | 0x52eff3 block size: 8 icont: 2 214 | 0x44f80b block size:63 icont:17 215 | 0x44affe block size:13 icont: 2 216 | 0x4b9d42 block size:31 icont:10 217 | 0x496beb block size:10 icont: 3 218 | 0x445992 block size: 1 icont: 1 219 | 0x4459a1 block size:92 icont:20 220 | 0x42e6f6 block size:20 icont: 3 221 | 0x4b9837 block size: 1 icont: 1 222 | QEMU: Terminated 223 | ``` 224 | 225 | 226 | 227 | 228 | 229 | ## 一次比较好的结果 230 | 231 | ```shell 232 | 233 | -------------- 234 | qemu:caught 0xf30f01ec UIRET 235 | before: pc_start: 0x401764 sc_base:0 pc: 0x401768 pc.next:0x401764 rip:0x401743 236 | helper uiret called, now eip: 0x401743 237 | qemu: now esp is: 0x7ffec0070290 238 | qemu:poped values:uirrv:0x1 rip:0x485e85 eflags:0x202 rsp:0x7ffec0070390 239 | pc_start: 0x401764 sc_base:0 pc: 0x401768 rip:0x485e85 240 | ------------- 241 | 242 | 243 | [ 11.615969] uintr_register_sender called 244 | qemu:wrmsr tt 0xffff9d63413ac001 245 | [ 11.615969] rdmsrl 2 246 | qemu:wrmsr misc 0x000000ec00000100 247 | [ 11.615969] send: register sender task=78 flags 0 ret(uipi_id)=0 248 | [ 11.615969] rdmsrl misc 5 249 | [ 11.615969] rdmsrl misc 5 250 | qemu:helper senduipi called receive regidx:240, uipiindex: 0 251 | qemu: data of uitt valid:1 user_vec:0 UPID address 0xffff9d6342c26cc0 252 | qemu: content of upid: status:0x0 nv:0xec ndst:0x0 0x0000000000000000 253 | qemu: data write back in upid: status:0x1 nv:0xec ndst:0x0 0x0000000000000001 254 | [ 11.615969] uintr_unregister_sender called 255 | [ 11.615969] send: unregister sender uintrfd 3 for task=78 ret 0 256 | - - - - user: now in the handler function 257 | - - - - Sending IPI from sender thread index:0 258 | [ 11.615969] debug! 259 | [ 11.629945] rdmsrl 1 260 | qemu:wrmsr misc 0x0000000000000000 261 | qemu:wrmsr tt 0x0000000000000000 262 | [ 11.615969] rdmsrl 1 263 | qemu:wrmsr misc 0x000000ec00000000 264 | qemu:wrmsr tt 0x0000000000000000 265 | [ 11.615969] rdmsrl misc 5 266 | [ 11.615969] rdmsrl misc 5 267 | - - - - user receive uintr, joining thread 268 | [ 11.615969] traps: sample[78] general protection fault ip:401907 sp:7ffec0070400 error:0 in sample[401000+a4000] 269 | [ 11.658274] recv: Release uintrfd for r_task 78 uvec 0 270 | qemu:wrmsr misc 0x0000000000000000 271 | qemu:wrmsr tt 0x0000000000000000 272 | qemu:wrmsr pd 0x0000000000000000 273 | qemu:wrmsr RR 0x0 274 | qemu:wrmsr stackadjust 0x0 275 | qemu:wrmsr handler 0x0000000000000000 276 | [ 11.666518] sample (78) used greatest stack depth: 14624 bytes left 277 | Segmentation fault 278 | / # [ 32.669644] rcu: INFO: rcu_sched detected stalls on CPUs/tasks: 279 | ``` 280 | 281 | 282 | 283 | 284 | 285 | 捕捉到SENDUIPI之前,执行后面几次的输出如下: 286 | 287 | ``` 288 | [ 304.087150] uintr_register_handler called 289 | qemu:wrmsr handler 0x0000000000401de5 290 | qemu:wrmsr pd 0xffff9c00c38795c0 291 | qemu:wrmsr stackadjust 0x0000000000000080 292 | qemu:rdmsr misc 0x0000000000000000 293 | qemu:wrmsr misc 0x000000ec00000000 294 | [ 304.087617] recv: register handler task=84 flags 0 handler 401de5 ret 0 295 | qemu:rdmsr misc 0x000000ec00000000 296 | qemu:rdmsr misc 0x000000ec00000000 297 | qemu:rdmsr misc 0x000000ec00000000 298 | qemu:rdmsr misc 0x000000ec00000000 299 | regeister returned 0 300 | qemu:rdmsr misc 0x000000ec00000000 301 | qemu:rdmsr misc 0x000000ec00000000 302 | qemu:rdmsr misc 0x000000ec00000000 303 | [ 304.089302] uintr_create_fd called 304 | [ 304.089550] recv: Alloc vector success uintrfd 3 uvec 0 for task=84 305 | qemu:rdmsr misc 0x000000ec00000000 306 | create fd returned 3 qemu:rdmsr misc 0x000000ec00000000 307 | qemu:rdmsr misc 0x000000ec00000000 308 | qemu:rdmsr misc 0x000000ec00000000 309 | qemu:rdmsr misc 0x000000ec00000000 310 | Receiver enabled interrupts 311 | qemu:rdmsr misc 0x000000ec00000000 312 | qemu:rdmsr misc 0x000000ec00000000 313 | qemu:rdmsr misc 0x000000ec00000000 314 | qemu:rdmsr misc 0x000000ec00000000 315 | qemu:rdmsr misc 0x000000ec00000000 316 | qemu:rdmsr misc 0x000000ec00000000 317 | qemu:rdmsr misc 0x000000ec00000000 318 | qemu:rdmsr misc 0x000000ec00000000 319 | qemu:rdmsr misc 0x000000ec00000000 320 | qemu:rdmsr misc 0x000000ec00000000 321 | qemu:rdmsr misc 0x000000ec00000000 322 | [ 304.097607] uintr_register_seqemu:rdmsr misc 0x000000ec00000000 323 | nder called 324 | qemu:rdmsr misc 0x000000ec00000000 325 | qemu:wrmsr tt 0xffff9c00c19d3001 326 | qemu:rdmsr misc 0x0000000000000000 327 | qemu:wrmsr misc 0x0000000000000100 328 | [ 304.1004qemu:rdmsr misc 0x000000ec00000000 329 | 05] send: register qemu:rdmsr misc 0x000000ec00000000 330 | sender task=85 flags 0 ret(uqemu:rdmsr misc 0x000000ec00000000 331 | ipi_id)=0 332 | qemu:rdmsr misc 0x000000ec00000000 333 | qemu:rdmsr misc 0x000000ec00000000 334 | qemu:rdmsr misc 0x000000ec00000000 335 | Sending IPI from sender thread 336 | qemu:rdmsr misc 0x000000ec00000000 337 | qemu:rdmsr misc 0x000000ec00000000 338 | qemu:rdmsr misc 0x000000ec00000000 339 | qemu:rdmsr misc 0x000000ec00000000 340 | [ 304.104475] traps: uipi_samplqemu:rdmsr misc 0x000000ec00000000 341 | e[85] trap invalid opcode ip:401eb7 sqemu:rdmsr misc 0x000000ec00000000 342 | p:7f6056609d90 error:0 in uipi_sample[401000+af000] 343 | qemu:rdmsr misc 0x000000ec00000000 344 | qemu:wrmsr misc 0x0000000000000000 345 | qemu:wrmsr tt 0x0000000000000000 346 | qemu:wrmsr pd 0x0000000000000000 347 | qemu:wrmsr RR 0x0000000000000000 348 | qemu:wrmsr stackadjust 0x0000000000000000 349 | qemu:wrmsr handler 0x0000000000000000 350 | [ 304.113500] recv: Release uintrfd for r_task 84 uvec 0 351 | qemu:wrmsr misc 0x0000000000000000 352 | qemu:wrmsr tt 0x0000000000000000 353 | qemu:wrmsr pd 0x0000000000000000 354 | qemu:wrmsr RR 0x0000000000000000 355 | qemu:wrmsr stackadjust 0x0000000000000000 356 | qemu:wrmsr handler 0x0000000000000000 357 | Illegal instruction 358 | ``` 359 | 360 | 361 | 362 | 363 | 364 | ``` 365 | 在senduipi后设置答应eip 366 | -------------- 367 | qemu: caught 0xf30fc7 SENDUIPI eip:0x4017bc 368 | -------------- 369 | 370 | 371 | qemu:helper senduipi called receive regidx:240, uipiindex: 0 372 | qemu: data of uitt valid:1 user_vec:0 UPID address 0xffff9febc2710b80 373 | qemu: content of upid: status:0x2 nv:0xec ndst:0x0 0x0000000000000000 374 | qemu: data write back in upid: status:0x2 nv:0xec ndst:0x0 0x0000000000000001 375 | 6.174750] rdmsrl misc 5 376 | 0x485e45 377 | !!! interrupt 2 intno:236 378 | recognize uintr 379 | the physical address of APIC 0x10 the EOI content: 0xf000ff53f000ff53 380 | rrnzero called handler: 0x4016f5 rr: 0x1 381 | qemu:origin exp 0x7fff285ec040 eip 0x485e45 eflags: 0x202 382 | qemu:move statck 0x7fff285ebfc0 383 | qemu:after align statck 0x7fff285ebfc0 384 | qemu:push finish now esp is: 0x7fff285ebfa0qemu: eip: 0x4016f5 385 | [ 6.175069] uintr_unregister_sender called 386 | [ 6.174921] rdmsrl misc 5 387 | 0x44f36f 388 | 0x475649 389 | 0x401734 390 | -------------- 391 | 392 | 393 | qemu:caught 0xf30f01ec UIRET 394 | before: pc_start: 0x401755 sc_base:0 pc: 0x401759 pc.next:0x401755 rip:0x401734 395 | helper uiret called, now eip: 0x401734 396 | qemu: now esp is: 0x7fff285ebf40 397 | qemu:poped values:uirrv:0x1 rip:0x485e45 eflags:0x202 rsp:0x7fff285ec040 398 | pc_start: 0x401755 sc_base:0 pc: 0x401759 rip:0x485e45 399 | ------------- 400 | 401 | 402 | -- User Interrupt handler -- 403 | [ 6.180153] send: unregister sender uintrfd 3 for task=78 ret 0 404 | [ 6.181136] debug! 405 | [ 6.182027] rdmsrl 1 406 | qemu:wrmsr misc 0x0000000000000000 407 | qemu:wrmsr tt 0x0000000000000000 408 | 0x45006d 409 | 0x4017ea 410 | 0x41801a 411 | 0x417ed3 412 | 0x417eec 413 | 0x417a00 414 | 0x417b43 415 | 0x417ef1 416 | 0x417ef1 417 | 0x417ef1 418 | 0x421c40 419 | 0x421c52 420 | 0x47b440 421 | 0x47db00 422 | 0x47db00 423 | 0x47db60 424 | 0x47db60 425 | 0x47db60 426 | 0x47b44a 427 | 0x47b45c 428 | 0x421c58 429 | 0x4205b0 430 | 0x420678 431 | 0x421c6e 432 | 0x421c6e 433 | 0x421c84 434 | 0x421c8e 435 | 0x48c8d0 436 | 0x48c908 437 | 0x421c94 438 | 0x421c94 439 | 0x421c9e 440 | 0x4206b0 441 | 0x420738 442 | 0x42079c 443 | 0x42079c 444 | 0x42079c 445 | 0x417ef6 446 | 0x417f08 447 | 0x417f27 448 | 0x417f55 449 | 0x417f6e 450 | 0x417f6e 451 | 0x417f8b 452 | 0x417f99 453 | 0x417f9e 454 | 0x417fc7 455 | 0x41808b 456 | 0x41808b 457 | 0x450150 458 | [ 6.174921] sample2[77]: segfault at 7f4b15d06640 ip 00007f4b15d06640 sp 00007fff285ec0d8 error 15 459 | 0x45015b 460 | 0x450163 461 | 0x41809f 462 | 0x417fd3 463 | 0x417fe5 464 | 0x417ffb 465 | [ 6.174921] Code: 00 00 a0 e0 4a 00 00 00 00 00 a0 e6 4a 00 00 00 00 00 a0 ef 4a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <40> 66 d0 15 4b 7f 0f 466 | [ 6.174921] recv: Release uintrfd for r_task 77 uvec 0 467 | qemu:wrmsr misc 0x0000000000000000 468 | qemu:wrmsr tt 0x0000000000000000 469 | qemu:wrmsr pd 0x0000000000000000 470 | qemu:wrmsr RR 0x0 471 | qemu:wrmsr stackadjust 0x0 472 | qemu:wrmsr handler 0x0000000000000000 473 | 0x587ead 474 | [ 0x49596a 475 | 0x5882b6 476 | 0x5882cb 477 | 0x5882cb 478 | 0x5882cb 479 | 0x5882cf 480 | 0x5882d4 481 | 0x5882d9 482 | 0x452950 483 | 0x452a60 484 | 0x452a6e 485 | 0x452a95 486 | 0x452a9c 487 | 0x452980 488 | 0x42ec80 489 | 0x452985 490 | 0x45298a 491 | 0x452990 492 | 0x4529a3 493 | 6.263346] sample2 (77) used greatest stack depth: 14512 bytes left 494 | 0x4e3de0 495 | 0x422fff 496 | 0x4233cb 497 | 0x4233cb 498 | 0x4233cb 499 | 0x4233d8 500 | 0x5882e5 501 | 0x5881c6 502 | 0x5881c6 503 | 0x58821c 504 | 0x448157 505 | 0x448165 506 | 0x448173 507 | 0x44819a 508 | 0x588278 509 | 0x58827f 510 | 0x58829b 511 | 0x5882f7 512 | 0x588356 513 | 0x588698 514 | 0x588698 515 | 0x588698 516 | 0x5883e5 517 | 0x4b9837 518 | Segmentation fault 519 | 0x4b9837 520 | 0x4b9837 521 | 0x4b9837 522 | 0x445b2e 523 | 0x445bc8 524 | 0x588401 525 | 0x588406 526 | 0x448c1c 527 | 0x448c4c 528 | 0x5886b7 529 | 0x588518 530 | 0x5875d4 531 | 0x5875d8 532 | 0x5875e5 533 | 0x592f92 534 | 0x592f92 535 | 0x44c1cc 536 | 0x44c043 537 | 0x592fe0 538 | 0x5e9037 539 | 0x44f80b 540 | 0x44f784 541 | 0x58c86e 542 | 0x58c885 543 | 0x44f80b 544 | 0x5876b0 545 | 0x58ee73 546 | 0x5ed66e 547 | 0x4bb63a 548 | 0x5ed74f 549 | 0x44d932 550 | 0x44de60 551 | 0x44de6f 552 | 0x44de6f 553 | 0x44de6f 554 | 0x44de7f 555 | 0x44de91 556 | 0x44affe 557 | 0x44dea1 558 | 0x44df7f 559 | 0x44df1e 560 | 0x44d502 561 | 0x4bb460 562 | 0x4bb4d4 563 | 0x4bb4fd 564 | 0x496beb 565 | 0x531127 566 | 0x4b967b 567 | 0x4bbb6b 568 | 0x52eff3 569 | 0x44f80b 570 | 0x44affe 571 | 0x4b9d42 572 | 0x496beb 573 | 0x42e6f6 574 | 0x4b9837 575 | / # 0x4b9837 576 | 0x4b9837 577 | 0x4b9837 578 | ``` 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | ```shell 587 | # 只改了eip,同样senduipi called 之后输出各个块的cp 588 | Sending IPI from sen 589 | 590 | -------------- 591 | qemu: caught 0xf30fc7 SENDUIPI eip:0x4017bc 592 | -------------- 593 | 594 | 595 | qemu:helper senduipi called receive regidx:240, uipiindex: 0 596 | qemu: data of uitt valid:1 user_vec:0 UPID address 0xffffa20a82c1c1c0 597 | qemu: content of upid: status:0x0 nv:0xec ndst:0x100 0x0000000000000000 598 | qemu: data write back in upid: status:0x1 nv:0xec ndst:0x100 0x0000000000000001 599 | der thread i[ 8.789172] uintr_unregister_sender called 600 | [ 8.793096] rdmsrl misc 5 601 | 0x485e45 block size:26 icont:10 602 | !!! interrupt 2 intno:236 603 | recognize uintr 604 | the physical address of APIC 0x10 the EOI content: 0xf000ff53f000ff53 605 | rrnzero called handler: 0x4016f5 rr: 0x1 606 | qemu:origin exp 0x7fff9ed2ca50 eip 0x485e45 eflags: 0x202 607 | qemu:move statck 0x7fff9ed2c9d0 608 | qemu:after align statck 0x7fff9ed2c9d0 609 | qemu:push finish now esp is: 0x7fff9ed2c9b0qemu: eip: 0x4016f5 610 | [ 8.798471] send: unregister sender uintrfd 3 for task=78 ret 0 611 | [ 8.800438] debug! 612 | [ 8.793096] rdmsrl misc 5 613 | 0x44f36f block size:12 icont: 3 614 | 0x475649 block size: 8 icont: 2 615 | 0x401734 block size: 1 icont: 1 616 | -------------- 617 | 618 | 619 | qemu:caught 0xf30f01ec UIRET # 注意! pc: 0x401759 已经进入sender_thread 620 | before: pc_start: 0x401755 sc_base:0 pc: 0x401759 pc.next:0x401755 rip:0x401734 621 | helper uiret called, now eip: 0x401734 # __libc_write后 622 | qemu: now esp is: 0x7fff9ed2c950 # 回去的地方在nanosleep 623 | qemu:poped values:uirrv:0x1 rip:0x485e45 eflags:0x202 rsp:0x7fff9ed2ca50 624 | pc_start: 0x401755 sc_base:0 pc: 0x401759 rip:0x485e45 625 | ------------- 626 | 627 | 628 | [ 8.793096] uintr_register_sender called 629 | qemu:wrmsr tt 0xffffa20a82625001 630 | qemu:wrmsr misc 0x000000ec00000100 631 | 0x45006d block size: 2 icont: 1 632 | 0x44f36f block size: 1 icont: 1 633 | 0x4178e8 block size: 8 icont: 2 634 | 0x41b720 block size: 5 icont: 1 635 | 0x41b73e block size:30 icont: 5 636 | 0x41b746 block size: 8 icont: 3 637 | qemu:helper senduipi called receive regidx:240, uipiindex: 0 638 | qemu: data of uitt valid:1 user_vec:0 UPID address 0xffffa20a82c1c1c0 639 | qemu: content of upid: status:0x0 nv:0xec ndst:0x100 0x0000000000000000 640 | qemu: data write back in upid: status:0x1 nv:0xec ndst:0x100 0x0000000000000001 641 | qemu:wrmsr misc 0x000000ec00000000 642 | qemu:wrmsr tt 0x0000000000000000 643 | [ 8.801274] rdmsrl 1 644 | [ 8.793096] rdmsrl 2 645 | [ 8.793096] send: register sender task=77 flags 0 ret(uipi_id)=0 646 | qemu:wrmsr misc 0x0000000000000000 647 | qemu:wrmsr tt 0x0000000000000000 648 | 0x45006d block size: 1 icont: 1 # syscall 649 | [ 8.793096] rdmsrl misc 5 650 | [ 8.793096] rdmsrl misc 5 651 | [ 8.793096] uintr_unregister_sender called 652 | [ 8.793096] send: unregister sender u0x45006d block size:92 icont:20 653 | 0x4017ea block size: 8 icont: 2 # sender_thread unrigester 654 | int0x41801a block size: 7 icont: 3 # 417ed3 655 | r0x417ed3 block size: 5 icont: 1 656 | f0x417eec block size:19 icont: 3 657 | d 0x417a00 block size: 5 icont: 1 658 | 3 0x417b43 block size:22 icont: 5 659 | 0x417ef1 block size: 1 icont: 1 660 | 0x421c40 block size: 5 icont: 1 661 | for task=77 ret 0 662 | 0x47b440 block size: 6 icont: 1 663 | [ 0x47b440 block size:92 icont:20 664 | 0x47db60 block size:38 icont:11 665 | 0x47b44a block size: 9 icont: 6 666 | 8.793096] debug! 667 | 0x47b45c block size:18 icont: 5 668 | [ 8.793096] rdmsrl 1 669 | 0x4205b0 block size:22 icont: 3 670 | [ 8.7930960x420678 block size:13 icont: 3 671 | 0x421c6e block size: 1 icont: 1 672 | ]0x421c84 block size:22 icont: 3 673 | 0x421c84 block size:10 icont: 2 674 | 0x421c84 block size:92 icont:20 675 | 0x421c8e block size:10 icont: 2 676 | rdmsrl misc 5 677 | 0x48c908 block size:21 icont: 6 678 | 0x421c94 block size: 2 icont: 2 679 | 0x45006d block size:12 icont: 3 680 | 0x4018e9 block size: 8 icont: 2 681 | 0x421c9e block size:10 icont: 2 682 | 0x4206b0 block size: 9 icont: 2 683 | 0x420738 block size:31 icont: 8 684 | 0x42079c block size:24 icont: 5 685 | 0x417ef6 block size: 5 icont: 4 686 | 0x417f08 block size:18 icont: 3 687 | 0x417f27 block size:31 icont: 6 688 | 0x417f55 block size: 2 icont: 2 689 | 0x417f6e block size:25 icont: 5 690 | 0x417f6e block size:29 icont: 6 691 | 0x417f6e block size:92 icont:20 692 | 0x417f8b block size:29 icont: 6 693 | 0x417f99 block size:14 icont: 3 694 | 0x417f9e block size: 5 icont: 1 695 | 0x417fc7 block size:41 icont:10 696 | 0x41808b block size:12 icont: 2 697 | 0x450150 block size:20 icont: 4 698 | 0x45015b block size: 3 icont: 2 699 | 0x450163 block size: 8 icont: 2 700 | 0x41809f block size: 1 icont: 1 701 | 0x417fd3 block size: 5 icont: 1 702 | 0x417fe5 block size:18 icont: 3 703 | 0x417ffb block size:14 icont: 3 # sleep 结束的第一条指令 ip:4018e9 704 | [ 8.793096] traps: sample2[77] general protection fault ip:4018e9 sp:7fff9ed2cac0 error:0 in sample2[401000+a4000] 705 | [ 8.793096] recv: Release uintrfd for r_task 77 uvec 0 706 | qemu:wrmsr misc 0x0000000000000000 707 | qemu:wrmsr tt 0x0000000000000000 708 | qemu:wrmsr pd 0x0000000000000000 709 | qemu:wrmsr RR 0x0 710 | qemu:wrmsr stackadjust 0x0 711 | qemu:wrmsr handler 0x0000000000000000 712 | [ 8.879583] sample2 (77) used greatest stack depth: 14456 bytes left 713 | 0x587ead block size: 2 icont: 2 714 | 0x587ead block size:92 icont:20 715 | 0x49596a block size: 2 icont: 2 716 | 0x49596a block size:10 icont: 3 717 | 0x5882b6 block size: 8 icont: 2 718 | 0x5882cb block size: 6 icont: 2 719 | 0x5882cb block size:92 icont:20 720 | 0x5882cf block size: 4 icont: 2 721 | 0x5882d4 block size: 5 icont: 2 722 | 0x5882d4 block size:11 icont: 7 723 | 0x5882d9 block size: 5 icont: 2 724 | 0x452950 block size:12 icont: 3 725 | 0x452a60 block size:29 icont: 7 726 | 0x452a6e block size:14 icont: 3 727 | 0x452a95 block size:10 icont: 2 728 | 0x452a9c block size:63 icont:17 729 | 0x452980 block size:21 icont: 4 730 | 0x42ec80 block size: 5 icont: 1 731 | 0x452985 block size:11 icont: 3 732 | 0x45298a block size: 5 icont: 2 733 | 0x452990 block size: 6 icont: 2 734 | 0x4529a3 block size:19 icont: 5 735 | 0x4e3de0 block size:63 icont:17 736 | 0x422fff block size:27 icont: 6 737 | 0x4233cb block size:63 icont:17 738 | 0x4233d8 block size:13 icont: 3 739 | 0x5882e5 block size:10 icont: 3 740 | 0x5881c6 block size:18 icont: 5 741 | 0x58821c block size:31 icont: 9 742 | 0x448157 block size:92 icont:19 743 | 0x448165 block size:14 icont: 4 744 | 0x448173 block size:14 icont: 4 745 | 0x44819a block size:18 icont: 5 746 | 0x588278 block size:16 icont: 4 747 | 0x58827f block size: 7 icont: 2 748 | 0x58829b block size:23 icont: 6 749 | 0x5882f7 block size:10 icont: 4 750 | 0x588356 block size:14 icont: 5 751 | 0x588698 block size: 9 icont: 4 752 | 0x5883e5 block size:31 icont: 7 753 | 0x4b9837 block size: 1 icont: 1 754 | 0x445b2e block size:63 icont:17 755 | 0x445bc8 block size:28 icont: 7 756 | 0x588401 block size:13 icont: 2 757 | 0x588406 block size: 5 icont: 1 758 | 0x448c1c block size:63 icont:17 759 | 0x448c4c block size:48 icont: 9 760 | 0x5886b7 block size:14 icont: 2 761 | 0x588518 block size: 5 icont: 1 762 | 0x5875d4 block size: 9 icont: 2 763 | 0x5875d8 block size: 4 icont: 2 764 | 0x5875e5 block size: 5 icont: 2 765 | 0x44c1cc block size:63 icont:17 766 | 0x44c043 block size:35 icont: 7 767 | 0x592fe0 block size:20 icont: 3 768 | 0x5e9037 block size:15 icont: 3 769 | 0x44f80b block size:63 icont:17 770 | 0x44f784 block size:13 icont: 2 771 | 0x58c86e block size:63 icont:17 772 | 0x58c885 block size:12 icont: 3 773 | 0x44f80b block size:63 icont:17 774 | 0x5876b0 block size:63 icont:17 775 | 0x58ee73 block size:63 icont:17 776 | 0x5ed66e block size:63 icont:17 777 | 0x4bb63a block size:10 icont: 3 778 | 0x5ed74f block size: 8 icont: 2 779 | 0x44d932 block size:37 icont: 8 780 | 0x44de60 block size:10 icont: 2 781 | 0x44de6f block size:11 icont: 3 782 | 0x44de7f block size:16 icont: 5 783 | 0x44de91 block size: 6 icont: 2 784 | 0x44affe block size:16 icont: 4 785 | 0x44dea1 block size:17 icont: 5 786 | 0x44df7f block size:18 icont: 4 787 | 0x44df1e block size:18 icont: 4 788 | 0x44d502 block size:18 icont: 4 789 | 0x4bb460 block size:10 icont: 3 790 | 0x4bb4d4 block size: 1 icont: 1 791 | 0x4bb4fd block size:10 icont: 3 792 | 0x496beb block size:10 icont: 3 793 | 0x531127 block size: 1 icont: 1 794 | 0x4b967b block size:11 icont: 7 795 | 0x4bbb6b block size:10 icont: 3 796 | 0x52eff3 block size: 8 icont: 2 797 | 0x44f80b block size:63 icont:17 798 | 0x44affe block size:13 icont: 2 799 | 0x4b9d42 block size:31 icont:10 800 | 0x496beb block size:10 icont: 3 801 | 0x42e6f6 block size:20 icont: 3 802 | 0x4b9837 block size: 1 icont: 1 803 | QEMU: Terminated 804 | ``` 805 | 806 | 807 | 808 | 809 | 810 | --------------------------------------------------------------------------------