目录
-
Assembly
-
Bitmap
-
CPUMASK
-
MMAP
-
MMU
-
Notifier mechanism
-
Platform Device Driver
-
SMP
-
VFS
-
附录
HKC 计划介绍
使用过 BiscuitOS 的开发者应该使用过 BiscuitOS 快速开发框架,开发者只需通过图形化的界面勾选所需的项目,那么 BiscuitOS 可以自动部署对应的项目,并可以在 BiscuitOS 上快速执行项目。设想一下,如果提供一个足够充足的 BiscuitOS 项目库,开发者在开发过程中需要进行某些功能模块的验证或使用,只需简单的配置一下 BiscuitOS,那么 BiscuitOS 就会快速搭建所需项目的开发环境,并支持快速跨平台验证,这样将大大提高一线工程师、学生研究学者、极客爱好者开发进度,因此这里提出了面向程序员的 “人类知识共同体” 计划。
“人类知识共同体” 计划面向每个开发者,提供一套完整的生态逻辑,开发者可以提供自己的 “独立代码” 到 BiscuitOS 代码库里,也可自由使用 biscuitOS 代码库里面的代码,相辅相成,共同促进生态的可持续发展.因此本文为开发者介绍加入和使用 “人类知识共同体” 计划。如果开发者需要使用 “人类知识共同体” 计划,请参考如下:
如果开发者想向 “人类知识共同体” 计划提供 “独立代码”,请参考:
项目捐赠,捐赠资金将捐赠给我的一位患白血病的小学同学. (下图是微信支付和支付宝支付)
HKC 计划使用
“人类知识共同体” 计划基于 BiscuitOS 提供了一套完整的运行机制,开发者可以参与其中,本节用于介绍加入 “人类知识共同体” 计划。正如 “人类知识共同体” 计划介绍,开发者可以将 “独立程序” 提交加入到项目中,以便和其他人分享。首先开发者应该将自己的 “独立程序” 进行分类,目前项目支持的程序分类如下:
- 编译进内核源码树代码
指的是开发者提供的 “独立代码” 必须编译进内核才能使用,譬如内核源码树内使用 “obj-y” 指定的源文件. 其核心通过 initcall 机制进行调用
- 编译进内核源码树,任意位置运行
指的是开发者提供的 “独立代码” 必须编译进内存才能使用,并且需要指明插入内核的位置,这样就可以在内核任意位置运行.
- 独立内核模块代码
指的是开发者提供的 “独立代码” 可以在内核源码数之外进行编译,并通过 KO 的模式使用.
- 独立 Application 代码
指的是开发者提供的 “独立代码” 是一个应用程序.
开发者首先将自己的 “独立程序” 进行分类,如果是 “编译进内核源码树代码”, 那么请点击下面链接:
如果是 “编译进内核源码树”,并在内核指定位置运行,那么请参考如下链接:
如果是 “独立内核模块代码”, 开发者可以参考如下链接:
如果是 “独立 Application 代码”, 开发者可以参考如下链接:
编译进内核源码树研究
BiscuitOS 支持将独立代码编译进内核,开发者可以使用这种办法将你的 “独立代码” 进行提交,具体使用办法如下. 首先开发者基于 BiscuitOS 项目构建指定架构的环境,这里以 “linux 5.0” i386 架构进行讲解,其他架构类型,开发者参考使用。开发者首先在 BiscuitOS 项目下使用如下命令:
开发者使用如下命令部署所需的环境,正如上图,选择 “Kernel Demo Code on BiscuitOS” 选项,保存并退出配置,接着使用 make 进行部署,部署成功之后,进入部署的目录, 接着获取 Demo 源码使用如下命令:
通过上面的命令之后,BiscuitOS 会自动部署一个 Demo 程序,其中 BiscuitOS-kernel-0.0.1 目录下的 main.c 函数就是源码的位置,同级的 Makefile 就是源码编译描述。Demo 中的 main.c 源码很简单,如下图:
Demo 源码很简单,就是在 “device_initcall” 阶段调用 “BiscuitOS_init()” 函数,并打印 “Hello BiscuitOS on kernel.” 字符串。开发者使用如下命令进行源码编译,并在 BiscuitOS 上运行:
从内核启动 log 中看到打印的 “Hello BiscuitOS on kernel”, 至此 Demo 程序演示完毕,接下来讲解开发者如何将 “独立代码” 添加到 BiscuitOS 机制。 开发者可以将自己的 “独立程序” 替换 Demo 程序中的 main.c 函数,并提供对应的 Makefile 文件,例如开发者的独立程序包含了一个源文件 “usage.c”, 那么可以基于 Demo 程序,将 main.c 函数移除并添加 “usage.c” 文件到该目录下,并修改 Makefile 文件,如下:
修改完毕之后,就是编译源码和在 BiscuitOS 验证你的独立程序。如果在 BiscuitOS 上验证无误的话,开发者接下来按如下步骤提交独立代码, 并合入 BiscuitOS 项目里。开发者需要发一份邮件给我,按如下内容进行描述:
开发者准备好如上内容发送至 “buddy.zhang@aliyun.com” 邮箱,并微信通知我。待我审批通过之后合入 BiscuitOS 并进行发布.
编译进内核源码树,任意位置运行
BiscuitOS 支持将独立代码编译进内核,并在指定位置运行。开发者可以使用这种办法将你的 “独立代码” 进行提交,具体使用办法如下. 首先开发者基于 BiscuitOS 项目构建指定架构的环境,这里以 “linux 5.0” i386 架构进行讲解,其他架构类型,开发者参考使用。开发者首先在 BiscuitOS 项目下使用如下命令:
开发者使用如下命令部署所需的环境,正如上图,选择 “Kernel Anywhere Demo Code on BiscuitOS” 选项,保存并退出配置,接着使用 make 进行部署,部署成功之后,进入部署的目录, 接着获取 Demo 源码使用如下命令:
通过上面的命令之后,BiscuitOS 会自动部署一个 Demo 程序,其中 BiscuitOS-kernel-any-0.0.1 目录下的 main.c 函数就是源码的位置,同级的 Makefile 就是源码编译描述。Demo 中的 main.c 源码很简单,如下图:
Demo 源码很简单,定义了一个函数,函数不能是 static,返回值必须是 int,参数必须是 void。函数的功能很简单,打印 “Hello BiscuitOS anywhere on kernel.” 字符串。源码中还有一个比较重要的文件 “BiscuitOS_insert.bs”, 该文件用于描述 main.c 中函数运行的位置,如图:
“[File]” 下一行用于配置函数插入的文件在内核源码树的位置,例如图中插在内核 “init/main.c” 函数里。”[Func]” 下一行的内容用于说明插入在某个函数的前一行,例如上面的例子中插入在 “setup_arch()” 函数的前一行。”[Content]” 下一行用于说明插入的内容,例如上图中,将 main.c 函数的 BiscuitOS_Running() 函数插入到了 “setup_arch()” 函数之前运行. 开发者使用如下命令进行源码编译,并在 BiscuitOS 上运行:
从内核启动 log 中看到打印的 “Hello BiscuitOS anywhere on kernel”, 至此 Demo 程序演示完毕,接下来讲解开发者如何将 “独立代码” 添加到 BiscuitOS 机制。 开发者可以将自己的 “独立程序” 替换 Demo 程序中的 main.c 函数即可,当函数定义时不能为 static,返回值一定是 int, 参数必须是 void, 例如:
修改完毕之后,就是编译源码和在 BiscuitOS 验证你的独立程序。如果在 BiscuitOS 上验证无误的话,开发者接下来按如下步骤提交独立代码, 并合入 BiscuitOS 项目里。开发者需要发一份邮件给我,按如下内容进行描述:
开发者准备好如上内容发送至 “buddy.zhang@aliyun.com” 邮箱,并微信通知我。待我审批通过之后合入 BiscuitOS 并进行发布.
独立内核模块研究
BiscuitOS 支持将独立内核模块开发,如果开发者的 “独立代码” 可以通过内核模块方式使用,那么开发者可以使用这种办法将你的 “独立代码” 进行提交,具体使用办法如下. 首先开发者基于 BiscuitOS 项目构建指定架构的环境,这里以 “linux 5.0” i386 架构进行讲解,其他架构类型,开发者参考使用。开发者首先在 BiscuitOS 项目下使用如下命令:
开发者使用如下命令部署所需的环境,正如上图,选择 “Module Demo Code on BiscuitOS” 选项和 “Module Project Demo on BiscuitOS”,保存并退出配置,”Module Demo Code on BiscuitOS” 选项是一个简单的模块 Demo,如果开发者的 “独立程序” 只是一个简单的功能,那么可以参考这个 Demo 进行部署; 如果开发者的 “独立程序” 是一个复杂多层的项目,那么开发者可以参考 “Module Project Demo on BiscuitOS”,其是一个简单的多层源码 Demo. 接着以 “Module Demo Code on BiscuitOS” Demo 进行讲解,其他 Demo 类似. 使用 make 进行部署,部署成功之后,进入部署的目录, 接着获取 Demo 源码使用如下命令:
通过上面的命令之后,BiscuitOS 会自动部署一个 Demo 程序,其中 BiscuitOS-modules-0.0.1 目录下的 main.c 函数就是源码的位置,同级的 Makefile 就是源码编译描述。Demo 中的 main.c 源码很简单,如下图:
Demo 源码很简单,就是在 KO 加载的时候打印 “Hello modules on BiscuitOS” 字符串。开发者使用如下命令进行源码编译,并在 BiscuitOS 上运行:
BiscuitOS 启动之后,动态加载模块后看到打印相应的字符串, 至此 Demo 程序演示完毕,接下来讲解开发者如何将 “独立代码” 添加到 BiscuitOS 机制。 开发者可以将自己的 “独立程序” 替换 Demo 程序中的 main.c 函数,并提供对应的 Makefile 文件,例如开发者的独立程序包含了一个源文件 “usage.c”, 那么可以基于 Demo 程序,将 main.c 函数移除并添加 “usage.c” 文件到该目录下,并其 Makefile 文件如下:
开发者可以使用这个 Makefile 模板,只需修改 “usage.o” 对应的内容即可。修改完毕之后,就是编译源码和在 BiscuitOS 验证你的独立程序。如果在 BiscuitOS 上验证无误的话,开发者接下来按如下步骤提交独立代码, 并合入 BiscuitOS 项目里。开发者需要发一份邮件给我,按如下内容进行描述:
开发者准备好如上内容发送至 “buddy.zhang@aliyun.com” 邮箱,并微信通知我。待我审批通过之后合入 BiscuitOS 并进行发布.
应用程序研究
BiscuitOS 支持独立应用程序开发,如果开发者的 “独立代码” 是一个应用程序,那么开发者可以使用这种办法将你的 “独立代码” 进行提交,具体使用办法如下. 首先开发者基于 BiscuitOS 项目构建指定架构的环境,这里以 “linux 5.0” i386 架构进行讲解,其他架构类型,开发者参考使用。开发者首先在 BiscuitOS 项目下使用如下命令:
开发者使用如下命令部署所需的环境,正如上图,选择 “Application Demo Code on BiscuitOS” 选项和 “Application Project Demo on BiscuitOS”,保存并退出配置,”Application Demo Code on BiscuitOS” 选项是一个简单的应用程序 Demo,如果开发者的 “独立程序” 只是一个简单的功能,那么可以参考这个 Demo 进行部署; 如果开发者的 “独立程序” 是一个复杂多层的项目,那么开发者可以参考 “Application Project Demo on BiscuitOS”,其是一个简单的多层源码 Demo. 接着以 “Application Demo Code on BiscuitOS” Demo 进行讲解,其他 Demo 类似. 使用 make 进行部署,部署成功之后,进入部署的目录, 接着获取 Demo 源码使用如下命令:
通过上面的命令之后,BiscuitOS 会自动部署一个 Demo 程序,其中 BiscuitOS-Application-0.0.1 目录下的 main.c 函数就是源码的位置,同级的 Makefile 就是源码编译描述。Demo 中的 main.c 源码很简单,如下图:
Demo 源码很简单,就是应用程序运行的时候打印 “Hello Application Demo on BiscuitOS” 字符串。开发者使用如下命令进行源码编译,并在 BiscuitOS 上运行:
BiscuitOS 启动之后,运行应用程序后看到打印相应的字符串, 至此 Demo 程序演示完毕,接下来讲解开发者如何将 “独立代码” 添加到 BiscuitOS 机制。 开发者可以将自己的 “独立程序” 替换 Demo 程序中的 main.c 函数,并提供对应的 Makefile 文件,例如开发者的独立程序包含了一个源文件 “usage.c”, 那么可以基于 Demo 程序,将 main.c 函数移除并添加 “usage.c” 文件到该目录下,并其 Makefile 文件如下:
开发者可以使用这个 Makefile 模板,只需修改 “usage.c” 对应的内容即可。修改完毕之后,就是编译源码和在 BiscuitOS 验证你的独立程序。如果在 BiscuitOS 上验证无误的话,开发者接下来按如下步骤提交独立代码, 并合入 BiscuitOS 项目里。开发者需要发一份邮件给我,按如下内容进行描述:
开发者准备好如上内容发送至 “buddy.zhang@aliyun.com” 邮箱,并微信通知我。待我审批通过之后合入 BiscuitOS 并进行发布.
HKC 计划实践
HKC 计划实践 – 独立模块
实践准备
BiscuitOS 支持多种架构、多种内核版本的实践,开发者可以根据需要自行选择,这里以 i386 架构的 linxu 5.0 为例子进行讲解,其余开发者可以在如下链接中进行参考:
实践部署
开发环境部署完毕之后,接下来是使用 BiscuitOS 代码库中的代码,这里以一个 i386 架构的 Demo 做为介绍,其余架构与之类似:
开发者使用如下命令部署所需的环境,正如上图,选择 “Module Demo Code on BiscuitOS” 选项和 “Module Project Demo on BiscuitOS”,保存并退出配置,”Module Demo Code on BiscuitOS” 选项是一个简单的模块 Demo,接着运行 make 进行部署,部署成功之后,进入部署的目录, 接着获取 Demo 源码使用如下命令:
通过上面的命令之后,BiscuitOS 会自动部署一个 Demo 程序,其中 BiscuitOS-modules-0.0.1 目录下的 main.c 函数就是源码的位置,同级的 Makefile 就是源码编译描述。Demo 中的 main.c 源码很简单,如下图:
Demo 源码很简单,就是在 KO 加载的时候打印 “Hello modules on BiscuitOS” 字符串。开发者使用如下命令进行源码编译,并在 BiscuitOS 上运行:
以上便是一个最基础的 “人类知识共同体” 计划实践.
HKC 计划实践 – 内核源码树
实践准备
BiscuitOS 支持多种架构、多种内核版本的实践,开发者可以根据需要自行选择,这里以 i386 架构的 linxu
5.0 为例子进行讲解,其余开发者可以在如下链接中进行参考:
实践部署
开发环境部署完毕之后,接下来是使用 BiscuitOS 代码库中的代码,这里以一个 i386 架构的 Demo 做为介绍,其余架构与之类似:
开发者使用如下命令部署所需的环境,正如上图,选择 “Kernel Demo Code on BiscuitOS” 选项,保存并退出配置,接着使用 make 进行部署,部署成功之后,进入部署的目录, 接着获取 Demo 源码使用如下命令:
通过上面的命令之后,BiscuitOS 会自动部署一个 Demo 程序,其中 BiscuitOS-kernel-0.0.1 目录下的 main.c 函数就是源码的位置,同级的 Makefile 就是源码编译描述。Demo 中的 main.c 源码很简单,如下图:
Demo 源码很简单,就是在 “device_initcall” 阶段调用 “BiscuitOS_init()” 函数,并打印 “Hello Biscui
tOS on kernel.” 字符串。开发者使用如下命令进行源码编译,并在 BiscuitOS 上运行:
从内核启动 log 中看到打印的 “Hello BiscuitOS on kernel”, 至此 Demo 程序演示完毕.
HKC 计划实践 – 应用程序
实践准备
BiscuitOS 支持多种架构、多种内核版本的实践,开发者可以根据需要自行选择,这里以 i386 架构的 linxu
5.0 为例子进行讲解,其余开发者可以在如下链接中进行参考:
实践部署
开发环境部署完毕之后,接下来是使用 BiscuitOS 代码库中的代码,这里以一个 i386 架构的 Demo 做为介绍,其余架构与之类似:
开发者使用如下命令部署所需的环境,正如上图,选择 “Application Demo Code on BiscuitOS” 选项和 “Application Project Demo on BiscuitOS”,保存并退出配置,”Application Demo Code on BiscuitOS” 选项是一个简单的应用程序 Demo,使用 make 进行部署,部署成功之后,进入部署的目录, 接着获取 Demo 源码使用如下命令:
通过上面的命令之后,BiscuitOS 会自动部署一个 Demo 程序,其中 BiscuitOS-Application-0.0.1 目录下的 main.c 函数就是源码的位置,同级的 Makefile 就是源码编译描述。Demo 中的 main.c 源码很简单,如下图:
Demo 源码很简单,就是应用程序运行的时候打印 “Hello Application Demo on BiscuitOS” 字符串。开发者使用如下命令进行源码编译,并在 BiscuitOS 上运行:
BiscuitOS 启动之后,运行应用程序后看到打印相应的字符串, 至此 Demo 程序演示完毕。
register_shrinker/unregister_shrinker
register_shrinker()/unregister_shrinker() 函数用于向 SLAB 维护的 shrinker_list 链表上注册 “struct shrinker” 节点,当系统调用 drop_slab() 函数收缩 SLAB 内存的时候,系统就会遍历到 register_shrinker() 注册的函数进行数据的统计.
BiscuitOS 配置
本实例已经在 Linux 5.0 i386 架构上验证通过,在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
通用例程
在例子中,BiscuitOS_shrink_init() 会在 “fs_initcall” 阶段调用,而 BiscuitOS_init() 函数则在 “device_initcall” 阶段才调用,因此 BiscuitOS_shrink_init() 函数先执行,而 BiscuitOS_init() 函数后执行。
在 BiscuitOS_shrink_init() 函数中先调用 register_shrinker() 函数向 slab shrink 消息链上注册监听,只要系统 slab 发出 shrink 消息,那么就会调用到 mmu_shrinker_bs 注册的接口函数。在 BiscuitOS_init() 函数中,主动通过 drop_slab() 函数发出 slab shrink 的消息,那么内核启动过程中打印如下:
从内核启动的信息来看,当调用 drop_slab 的时候,mmu_shrinker_bs 接收到了消息,并调用 mmu_shrink_count_bs() 函数执行相关的操作.
RDMSR
RDMSR 指令用于读取一个 64 bit 的 MSR 寄存器. RDMSR 指令通过向 ECX 寄存器写入指定 MSR 寄存器的地址,RDMSR 指令便会将指定 MSR 寄存器的 64 位值存储到 EDX:EAX, 其中 EDX 寄存器存储指定 MSR 寄存器的高 32 bit 的值,而 EAX 寄存器则存储指定 MSR 寄存器的低 32 bit 的值。
BiscuitOS 配置
本实例已经在 Linux 5.0 i386 架构上验证通过,在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
通用例程
上面的例子很简单,就是通过内嵌汇编的方式在内核模块中调用 RDMSR 指令。值得注意的是,在 X86_64 内嵌汇编中,GCC “A” 标志不代表 “EDX:EAX” 的组合,而在 X86 内嵌汇编中,GCC “A” 标志表示 “EDX:EAX” 的组合,因此使用时应该做区分. 程序运行时的结果如下:
更多 RDMSR 请参考 “Intel Development Manual”, 更多 MSR 寄存器请参考手册 “Volume 4: Model-Specific Registers: Chapter 2 Model-Specific Registers (MSRs)”
Intel Development Manual
WRMSR
WRMSR 指令用于将一个 64bit 的值写入指定的 MSR 寄存器中。WRMSR 指令通过将指定寄存器的地址写入 ECX 寄存器中,然后将写入值的高 32bit 写入 EDX 寄存器,然后将写入值的低 32bit 写入 EAX 寄存器中,最后调用 “WRMSR” 指令进行写入操作。
BiscuitOS 配置
本实例已经在 Linux 5.0 i386 架构上验证通过, 在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
通用例程
上面的例子很简单,就是通过内嵌汇编的方式将 MSR 寄存器的值通过 “c” 写入到 ECX 寄存器中,然后通过 “a” 将 64bit 值的低 32bit 写入到 EAX 寄存器,通过 “d” 将 64bit 值写入到 EDX 寄存器。最终使用 “memory” 标记,以此告诉编译器不要将用到的变量缓存到寄存器,因为这段代码可能会用到内存变量,而这些内存变量会以不可预知的方式发生改变,因此 GCC 插入必要的代码先将缓存到寄存器的变量值写回内存,如果后面又访问这些变量,需要重新访问内存. 上面的实例在 BiscuitOS 运行的结果如下:
更多 WRMSR 请参考 “Intel Development Manual”, 更多 MSR 寄存器请参考手册 “Volume 4: Model-Specific Registers: Chapter 2 Model-Specific Registers (MSRs)”
Intel Development Manual
Kernel notifier Demo
本节用于介绍一个在内核中使用通知链的例子, 内核内核通知机制可以在不同子系统之间进行通知,本例子给出一个最简单的例子实现。
BiscuitOS 配置
本实例已经在 Linux 5.0 i386 架构上验证通过, 在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
通用例程
在例子中,函数通过调用 “raw_notifier_chain_register()” 函数注册了一条名为 “BiscuitOS_chain” 的通知链,当接收到通知之后对应的处理函数通过 BiscuitOS_notifier 对应的数据进行处理,其最终对应 “BiscuitOS_notifier_event()” 函数,当接收到通知之后,内核调用该函数,函数内核对不同的消息进行处理,这里定义了两个消息: “BISCUITOS_EVENT_A” 和 “BISCUITOS_EVENT_B”, 当 BiscuitOS_chain 消息链接受到其中一条信息都会进行相应的处理,而对于没有定义的消息,那么则忽略。
内核可以在其他子系统通过调用 “raw_notifier_call_chain()” 向指定消息链发消息,例如本例子中,通过调用该函数一共发送三个信息,并传入指定的内容. 通过上面的代码逻辑构建了一个内核最简单的消息通知机制. 本例子在 BiscuitOS 中运行的结果如下:
在 BiscuitOS 运行模块之后,可以看到 “BiscuitOS_chain” 消息链接受到了 “BISCUITOS_EVENT_A” 和 “BISCUITOS_EVENT_B” 消息,并打印伴随消息传递过来的内容,对于 “BISCUITOS_EVENT_C” 则选择忽略.
Kernel cpumask Demo
CPUMASK 机制用于标记 CPU 的使用情况,在 SMP 系统中,系统在维护某个功能时需要统计指定 CPU 使用某个功能的记录,或者用于标记指定的 CPU 以便得到一个白名单或者黑名单,以此可以通过 MASK 隔离指定的 CPU。内核提供了一套 CPUMASK 机制来实现各种场景下的功能。
BiscuitOS 配置
本实例已经在 Linux 5.0 i386 架构上验证通过, 在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
通用例程
在例子中,首先定义了一个 cpumask_var_t 的变量 BiscuitOS_cpumask, 然后调用 zalloc_cpumask_alloc() 函数为其分配相应的内存,分配完毕之后该 CPUMASK 就可以使用了。这里调用 raw_smp_processor_id() 函数获得当前 CPU ID,然后判断该 CPU 是否已经在 BiscuitOS_cpumask 中置位,如果没有那么调用 cpumask_set_cpu() 将该 CPU 在 BiscuitOS_cpumask 中置位,这样做的目的是以后可以检测针对特定 CPU 的白名单/黑名单. 例子中在检测到对应的 CPU 置位之后,调用 cpumask_clear_cpu() 函数将 CPU 从 BiscuitOS_cpumask 中移除,这样做的目的是以后可以将 CPU 从白名单/黑名单中除名. 该实例在 SMP 4 core 的情况下,BiscuitOS 中运行的情况如下:
从运行的结果可以看出,当前 CPU ID 是 0,第一次检测的时候,CPU 没有在 BiscuitOS_cpumask 中置位,那么将其置位。第二次检测的时候,CPU 已经在 BiscuitOS_cpumask 中置位,那么将其清零.
smp_call_function_single
smp_call_function_single() 函数用于在指定的 CPU 上运行指定的函数,其函数原型如下:
cpu 指定了需要运行的 CPU ID,func 参数则是指向需要执行的函数,info 指向需要向执行函数传入的数据,wait 用于指明是否等待原 CPU 执行完毕之后再去其他 CPU 执行。
BiscuitOS 配置
本实例已经在 Linux 5.0 i386 架构上验证通过, 在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
通用例程
在例子中,函数调用 smp_call_function_single() 函数在指定的 CPU 0 上运行 BiscuitOS_smp() 函数,并向 BiscuitOS_smp() 函数传递了原始 CPU ID。其在 BiscuitOS 上运行的结果如下:
函数运行在 CPU 1 上,然后调用 smp_call_function_single() 函数让 BiscuitOS_smp() 函数运行在 CPU 0 上.
on_each_cpu
on_each_cpu() 函数用于将指定函数在没有 CPU 上运行,其函数原型如下:
func 为指定在每个 CPU 上运行的函数,info 为传入调用函数的数据,wait 参数用于指定是否等待当前 CPU 任务执行完毕之后在执行.
BiscuitOS 配置
本实例已经在 Linux 5.0 i386 架构上验证通过, 在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
通用例程
在本例子中,函数定义了一个函数 BiscuitOS_smp,并调用 on_each_cpu() 函数让 BiscuitOS_smp() 函数在所有的 CPU 上运行。其在 BiscuitOS 上运行的情况如下:
从运行的结果可以看出 BiscuitOS_smp() 函数在所有的 CPU 上都运行了一次.
reboot-notifier
Reboot notifier 用于当系统 reboot 的时候为注册 register_reboot_notifier() 的函数发送 REBOOT 消息。当监听者收到 REBOOT 消息之后就执行对应的函数. Reboot notifier 机制提供了 register_reboot_notifier()/unregister_reboot_notifier() 函数用于注册和撤销 REBOOT 监听事件.
BiscuitOS 配置
本实例已经在 Linux 5.0 i386 架构上验证通过, 在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
通用例程
在本例子中,调用 register_reboot_notifier() 函数注册了一个监听事件,当系统发生 REBOOT 事件之后,BiscuitOS_reboot_notifier() 函数就会被执行。当模块卸载的时候,调用 unregister_reboot_notifier() 函数撤销监听事件. 其在 BiscuitOS 上执行情况如下:
在 BiscuitOS 上安装上模块之后,执行 reboot 命令,系统在准备 reboot 过程中调用了 BiscuitOS_reboot_notifier() 函数,并打印了字符串 “Trigger reboot on BiscuitOS.”.
Trigger suspend notifier
内核 syscore 子系统提供了 register_syscore_ops() 函数,用于向系统的 suspend 注册监听事件,当系统进行 SUSPEND 状态,那么调用指定的函数处理 suspend 消息.
BiscuitOS 配置
本实例已经在 Linux 5.0 i386 架构上验证通过, 在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
通用例程
在本例子中,驱动通过调用 register_syscore_ops() 注册了 SUSPEND/RESUME 的监听事件 BiscuitOS_syscore, 并提供两个接口,当系统进行 SUSPEND 状态,那么 BiscuitOS_suspend() 函数则会被调用,如果系统从 SUSPEND 状态恢复为 RESUME 状态,那么 BiscuitOS_resume() 函数则会别调用. 当驱动卸载的时候,会调用 unregister_syscore_ops() 函数撤销监听事件. 本例子在 BiscuitOS 上运行的情况如下:
当向系统安装完驱动之后,在 /sys/power/ 目录下执行如下命令:
执行上面的操作之后,将运行状态数据存到硬盘, 然后关机, 唤醒最慢。以上动作便会触发系统进入 SUSPEND 状态,此时触发驱动的 BiscuitOS_suspend() 函数。当有数据写入磁盘时候,系统又由 SUSPEND 状态进入 RESUME 状态,此时触发驱动的 BiscuitOS_resume() 函数。系统支持的 4 中休眠设置,如下:
Trigger resume notifier
内核 syscore 子系统提供了 register_syscore_ops() 函数,用于向系统的 suspend 注册监听事件,当系统进行 SUSPEND 状态,然后从 SUSPEND 中唤醒为 RESUME 状态,那么调用指定的函数处理 resume 消息.
BiscuitOS 配置
本实例已经在 Linux 5.0 i386 架构上验证通过, 在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
通用例程
在本例子中,驱动通过调用 register_syscore_ops() 注册了 SUSPEND/RESUME 的监听事件 BiscuitOS_syscore, 并提供两个接口,当系统进行 SUSPEND 状态,那么 BiscuitOS_suspend() 函数则会被调用,如果系统从 SUSPEND 状态恢复为 RESUME 状态,那么 BiscuitOS_resume() 函数则会别调用. 当驱动卸载的时候,会调用 unregister_syscore_ops() 函数撤销监听事件. 本例子在 BiscuitOS 上运行的情况如下:
当向系统安装完驱动之后,在 /sys/power/ 目录下执行如下命令:
执行上面的操作之后,将运行状态数据存到硬盘, 然后关机, 唤醒最慢。以上动作便会触发系统进入 SUSPEND 状态,此时触发驱动的 BiscuitOS_suspend() 函数。当有数据写入磁盘时候,系统又由 SUSPEND 状态进入 RESUME 状态,此时触发驱动的 BiscuitOS_resume() 函数。系统支持的 4 中休眠设置,如下:
RDTSC
RDTSC 指令是 Intel X86 提供的一个用于从 “Time-Stamp Counter” MSR 寄存器中读取当前系统的时间戳. 从 Pentium 开始,很多 80x86 微处理器引入了 TSC 寄存器,它的每个时钟信息 (CLK, CLK 是微处理器中一条用于接收外部振荡器的时钟信号输入线) 到来时加一. 操作系统可以使用 TSC 寄存器来计算 CPU 主频,比如微处理器的主频是 1MHz,那么 TSC 就会在 1s 内增加 1000000。除了计算 CPU 的主频外,还可以通过 TSC 来测试微处理器其他处理单元的运算能力,具体请参考:
RDTSC 指令执行读取寄存器时,处理器将 “Time-Stamp Counter” MSR 寄存器的高 32bit 的值存储在 EDX 寄存器,而低 32bit 值存储在 EAX 寄存器中。
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
HKC 计划 BiscuitOS 实践框架介绍 (rdtsc-x86-0.0.1)
HKC 计划 BiscuitOS 实践框架介绍 (rdtsc-app-x86-0.0.1)
通用例程(内核篇)
在本例子中,使用内嵌汇编定义了 RDTSC 指令的两个接口函数 “rdtscll()” 和 “rdtscl()”. “rdtscl()” 函数只读取 “Time-stamp Counter” 寄存器的低 32bit 值,而 “rdtscll()” 可以读取 64bit 的值。例子中还通过计算 1s 内 “Time-stamp Counter” 的变化,以此计算 CPU 的频率,但这里使用 do_div() 函数用于处理 64bit 的除法。本例子在 BiscuitOS 中运行的结果如下:
加载模块之后,计算出 CPU 的频率是 3141 MHz, 与系统提供的 3.30GHz 有一点相差,但属于正常情况, 因此计算的数值有效.
通用例程(应用程序)
在本例子中,在用户空间定义两个接口用于从 “Time-Stamp Counter” 寄存器中读取当前的时间戳,rdtscl() 函数用于读取 “Time-Stamp Counter” 寄存器的低 32bit 的值,而 rdtscll() 函数则可以读取 “Time-Stamp Counter” 寄存器的 64bit 值。在程序中还通过延时 1s 来计算 CPU 的频率。本例子在 BiscuitOS 的运行情况如下:
从计算的结果来看非常接近 CPUINFO 提供的主频,因此计算有效.
MMU notifier
MMU notifier 机制用于当进程修改页表、或者让页表无效时,用于通知进程的内部这些消息。该机制通过提供很多接口:
- mmu_notifier_register()/mmu_notifier_unregister() 函数注册事件。
- mmu_notifier_invalidate_range_start()/mmu_notifier_invalidate_range_end() 当进程内某段虚拟地址的页表无效时候,可以调用这些函数进行通知
- mmu_notifier_clear_flush_young()/mmu_notifier_clear_young()/mmu_notifier_test_young() 当某些页被修改,可以调用这些函数进行通知
- mmu_notifier_change_pte() 当进程的 PTE 页表改变时,可以调用该函数进行通知。
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
HKC 计划 BiscuitOS 实践框架介绍 (MMU-notifier-0.0.1)
HKC 计划 BiscuitOS 实践框架介绍 (MMU-userspace-notifier-0.0.1)
通用例程 (内核部分)
在本例子中,例子通过 MISC 机制在用于空间创建了 “/dev/BiscuitOS” 节点供用户空间部分的程序使用。用户空间在调用 open() 函数时,例子就会调用 mmu_notifier_register() 注册通知事件,当用户空间调用 close() 函数时,例子就会调用 mmu_notifier_unregister() 释放事件。当用户空间调用 mmap() 的时候,传递了一段虚拟内存地址到该例子的 BiscuitOS_mmap() 函数,此时在该函数中主动触发了以下几个通知:
- mmu_notifier_invalidate_range_start/mmu_notifier_invalidate_range_end 用于触发 BiscuitOS_mmu_invalidate_range_start/BiscuitOS_mmu_invalidate_range_end/BiscuitOS_mmu_invalidate_range 三个函数。
- mmu_notifier_clear_flush_young 触发 BiscuitOS_mmu_clear_flush_young 函数.
- mmu_notifier_clear_young 触发 BiscuitOS_mmu_clear_young 函数.
- mmu_notifier_test_young 触发 BiscuitOS_mmu_test_young 函数.
- mmu_notifier_change_pte 触发 BiscuitOS_mmu_change_pte 函数.
- mmu_notifier_release 触发 BiscuitOS_mmu_release 函数.
通用例程 (用户空间部分)
在本例子用户空间部分的程序,首先通过 open() 函数打开了 “/dev/BiscuitOS” 节点,然后调用调用 mmap() 函数进行映射操作,映射完毕之后再通过 munmap() 函数接触映射。函数最后调用 close() 关闭节点。本例子纯粹是为内核部分的代码创造独立进程的条件. 两个部分在 BiscuitOS 中运行的情况如下:
从运行的结果可以看出,指定的消息已经传递成功. 开发者可以利用该机制进行页表操作时候通知进程内的其他功能模块.
这是一个最简单的 Platform 驱动,驱动实现向系统注册一个 Platform 驱动.
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
通用例程
在本例子中,通过调用 platform_driver_register/platform_device_register_simple 注册了一个 platform 的设备和驱动,其中驱动通过 BiscuitOS_driver 数据进行描述,在驱动描述中提供了驱动的 probe 和 remove 两个方法。当驱动 KO 被加载的时候,系统 Platform 总线就会在查找并调用该驱动的 probe 函数,当驱动卸载的时候,Platform 总线就会调用该驱动的 remove 函数。本实例在 BiscuitOS 的运行情况如下:
当驱动加载的时候,驱动的 probe 函数就会被调用,并且在 /sys/bus/platform/devices 目录下创建了 BiscuitOS.1 的节点,该节点下还包含了与 BiscuitOS.1 设备相关的信息.
Emulate PID Allocating and Releasing
用 4096 位模拟进程的 pid (0~4095), 创建两个线程,一个线程用随机数,选择 0~4095 中的一位进行清零操作,以此模拟进程的退出; 另外一个线程查找 0~4094 位,发现 0 位置位 1,模拟 pid 的申请。
代码共享者: Meijusan 20741602@qq.com
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
通用例程
独立代码在 BiscuitOS 中运行的结果如下:
LC-trie Protocol
功能说明
代码贡献者: Shaobin shaobin.huang@kernelworker.net
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
通用例程
该代码在 BiscuitOS 上运行的情况如下:
Anonymous mmap on Userpsace
代码核心功能是在用户空间通过匿名映射的方式映射一段虚拟机内存使用。
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
HKC 计划 BiscuitOS 实践框架介绍
通用例程
例子的实现很简单,通过 mmap() 函数映射一段内存,在传入参数的时候,传入 MAP_ANONYMOUS 标志就可以匿名映射一段内存使用。不使用的时候使用 munmap() 函数进行解除映射。例子在 BiscuitOS 中运行的情况如下:
BiscuitOS 运行之后,在用户空间直接运行 BiscuitOS-Anonymous-mmap-Userspace-0.0.1 应用程序,运行之后打印一段字符串并打印字符串的地址。结果符合预期.
Anonymous mmap on kernel
本例子用于介绍在内核空间通过匿名映射方式,映射一段可用内存使用。
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
HKC 计划 BiscuitOS 实践框架介绍
通用例程
本例子在驱动初始化函数中,调用 vm_mmap() 函数并传入 MAP_ANONYMOUS 标志,以此在内核中匿名映射一段内存,并使用这段内存,使用完毕之后调用 vm_munmap() 函数解除这部分内存的映射. 该例子在 BiscuitOS 中运行的结果如下:
加载模块之后,模块初始化是调用匿名映射相关的函数,然后在匿名映射的内存上写入一段字符串,并打印这段字符串和匿名映射内存的地址. 结果符合预期.
Anonymous File
本例子用于介绍创建一个匿名文件的流程。例子包含了内核空间和用户空间的实现。
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
> HKC 计划 BiscuitOS 实践框架介绍
> HKC 计划 BiscuitOS 实践框架介绍
内核部分代码
内核部分的代码首先通过调用 get_unused_fd_flags() 获得当前进程一个没有使用的文件句柄,然后再调用 anon_inode_getfile() 函数获得一个匿名的 inode,并将 BiscuitOS_anonymous_fops 文件操作绑定到这个 inode 上面,最后调用 fd_install() 函数将 fd 和 inode 绑定在一起,这样匿名文件就创建完毕.
用户空间代码
用户空间部分的代码比较简单,通过 ioctl() 函数传递 “BISCUITOS_SET” 命令告诉内存创建一个匿名文件,并返回匿名文件的文件句柄. 该例子在 BiscuitOS 完整运行情况如下:
模块安装完毕之后,运行对应的应用程序,此时获得的匿名文件句柄为 4. 文件句柄 0,1,2 分别为标志输入/输出/错误, 句柄 3 为当前打开的文件,那么匿名文件句柄为 4 符合预期.
Kernel Read from Userspace file
实例的作用是内核读取用户空间的文件。
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
HKC 计划 BiscuitOS 实践框架介绍
通用例程
例子的实现很简单,与用户空间文件的操作类似,首先使用 filp_open() 函数打开指定文件,然后调用 get_fs()/set_fs() 设置正确的段权限,最后调用 kernel_read() 函数用户空间文件内存读取到 buffer 里,并使用 printk 将 buffer 里面的内容输出. 操作完毕之后调用 filp_close() 关闭文件并回复 FS 段. 该例子在 BiscuitOS 中运行的情况如下:
BiscuitOS 运行之后,安装对应的模块,模块运行之后将从 “/proc/meminfo” 中读取指定长度的内容,并输出这些内容。运行的结果与预期一致.
Kernel write to Userspace file
本示例的作用是内核向用户空间的文件写入指定的内容.
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
HKC 计划 BiscuitOS 实践框架介绍
通用例程
例子的实现很简单,与用户空间文件的操作类似,首先使用 filp_open() 函数打开指定文件,然后调用 get_fs()/set_fs() 设置正确的段权限,最后调用 kernel_write() 函数将 buffer中的内容写入到用户空间文件,用户空间的文件位于 “/tmp/BiscuitOS.info”. 操作完毕之后调用 filp_close() 关闭文件并回复 FS 段. 该例子在 BiscuitOS 中运行的情况如下:
BiscuitOS 运行之后,将模块进行安装,安装过程中实例将字符串 “BiscuitOS” 写入到用户空间 “/tmp/BiscuitOS.info” 文件中,模块安装完毕之后,查看 “/tmp/BiscuitOS.info” 文件的内容,内容为 “BiscuitOS”, 结果与预期一致.
Memory Hotplug in Code
本实例用于介绍如何在内核中使用代码进行内存的 “热插” 操作. 安装完模块之后会在 “/sys/device/system/memory/” 目录下生成指定的 memoryX 目录,该目录下存储新热插入内存的信息. 在进行热插拔之前,需要配置内核打开热插拔功能,配置如下:
配置完内核之后,再将内核宏 SECTION_SIZE_BITS 设置为 27, 以此表示每个 MEMORY Section 的长度为 128 MiB. 其次如果是 ARM64 架构,那么需要在 arm64_memblock_init() 函数中添加 “memblock_remove(0x50000000, 0x8000000)” 语句将对应的内存区从内核中移除; 如果是 x64 架构,那么需要在 CMDLINE 中添加字段 “memmap=128MiB@0x18000000”. 修改完毕之后重新编译内核.
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
HKC 计划 BiscuitOS 实践框架介绍
通用例程
例子很简单,通过调用 add_memory() 函数将 BISCUITOS_MEMORY_BASE 处长度为 BISCUITOS_MEMORY_SIZE 的物理内存插入到系统内部。如果插入成功,系统会在 “/sys/devices/system/memory/” 目录下创建热插入内存的信息。该例子在 BiscuitOS 中运行情况如下:
BiscuitOS 运行之后,首先查看 IORESOURCE 上的内存设备信息,发现系统此时看不到 0x50000000 到 58000000 的内存信息,并且 “/sys/devices/system/memory/” 目录下并没有 memory10 的目录. 接下来插入模块,查看模块之后可以在 IORESOURCE 下看到 0x50000000 到 58000000 的内存信息,并且 “/sys/devices/system/memory/” 目录可以看到 memory10 的目录。此时查看系统可以物理内存信息,显示为 355284 KiB。接着进入 memory10 目录,查看 state 的信息,显示为 offline,那么表示这块物理内存虽然已经硬件上热插入系统了,但软件上没有,因此将 “online” 字符串发送给 state,让系统将该内存插入。最后查看系统可用内存信息,已经变成了 486356 KiB, 增加的量正好是 128 MiB. 运行的结果符合预期.
Memory Hotplug auto
本实例用于介绍如何在内核中使用代码进行内存的 “热插” 操作. 安装完模块之后会在 “/sys/device/system/memory/” 目录下生成指定的 memoryX 目录,该目录下存储新热插入内存的信息. 在进行热插拔之前,需要配置内核打开热插拔功能,配置如下:
配置完内核之后,再将内核宏 SECTION_SIZE_BITS 设置为 27, 以此表示每个 MEMORY Section 的长度为 128 MiB. 其次如果是 ARM64 架构,那么需要在 arm64_memblock_init() 函数中添加 “memblock_remove(0x50000000, 0x8000000)” 语句将对应的内存区从内核中移除; 如果是 x64 架构,那么需要在 CMDLINE 中添加字段 “memmap=128MiB@0x18000000”. 修改完毕之后重新编译内核.
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
HKC 计划 BiscuitOS 实践框架介绍
通用例程
例子很简单,通过调用 add_memory() 函数将 BISCUITOS_MEMORY_BASE 处长度为 BISCUITOS_MEMORY_SIZE 的物理内存插入到系统内部。如果插入成功,系统会在 “/sys/devices/system/memory/” 目录下创建热插入内存的信息。接着通过内核向用户空间文件写内容的逻辑,向该目录下 state 节点写入 “online” 字符串,以让系统支持软件层面的热插拔动作。该例子在 BiscuitOS 中运行情况如下:
BiscuitOS 运行之后,首先查看 IORESOURCE 上的内存设备信息,发现系统此时看不到 0x50000000 到 58000000 的内存信息,并且 “/sys/devices/system/memory/” 目录下并没有 memory10 的目录. 此时查看系统可以物理内存信息,显示为 355284 KiB。接下来插入模块,查看模块之后可以在 IORESOURCE 下看到 0x50000000 到 58000000 的内存信息,并且 “/sys/devices/system/memory/” 目录可以看到 memory10 的目录。此时查看系统可以物理内存信息,显示为 486356 KiB。接着进入 memory10 目录,查看 state 的信息,显示为 online,那么表示内存已经在模块插入时自动热插拔到系统了。运行的结>果符合预期.
Anonymous Share mmap on Userspace
代码核心功能是在用户空间通过 “共享匿名” 方式映射一段虚拟内存。
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
HKC 计划 BiscuitOS 实践框架介绍
通用例程
例子很精简,首先通过 mmap() 函数进行映射,在映射的时候传入参数 MAP_SHARED 和 MAP_ANONYMOUS 标志就可以映射一段匿名共享的虚拟内存。当对分配的虚拟内存进行读或者写操作的时候,就会触发缺页异常。当不再使用这段虚拟内核时,可以使用 munmap() 函数进行释放. 例子在 BiscuitOS 中运行的情况如下:
BiscuitOS 运行之后,在用户空间直接运行 BiscuitOS-anonymous-share-mmap-userspace-default 引用程序即可,运行之后打印虚拟内存的地址以及虚拟内存的值.
Anonymous Private mmap on Userspace
代码核心功能是在用户空间通过 “私有匿名” 方式映射一段虚拟内存。
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
HKC 计划 BiscuitOS 实践框架介绍
通用例程
例子很精简,首先通过 mmap() 函数进行映射,在映射的时候传入参数 MAP_PRIVATE 和 MAP_ANONYMOUS 标志就可以映射一段匿名私有的虚拟内存。当对分配的虚拟内存进行读或者写操作的时候,就会触发缺页异常。当不再使用这段虚拟内核时,可以使用 munmap() 函数进行释放. 例子在 BiscuitOS 中运行的情况如下:
BiscuitOS 运行之后,在用户空间直接运行 BiscuitOS-anonymous-private-mmap-userspace-default 引用程序即可,运行之后打印虚拟内存的地址以及虚拟内存的值.
File Share mmap on Userspace
代码核心功能是在用户空间通过 “共享” 方式将文件映射到一段虚拟内存上。
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
HKC 计划 BiscuitOS 实践框架介绍
通用例程
例子很精简,首先通过 open() 函数打开一个文件,然后通过 mmap() 函数进行映射,在映射的时候传入参数 MAP_SHARED 标志, 并传入打开文件的 fd,这样就可以将文件以 “共享” 方式映射到虚拟内存。当对分配的虚拟内存进行读或者写操作的时候,就会触发缺页异常,这个时候就会建立虚拟内存到文件的映射关系。当不再使用这段虚拟内核时,可以使用 munmap() 函数进行释放,最后关闭打开的文件。例子在 BiscuitOS 中运行的情况如下:
BiscuitOS 运行之后,直接运行 RunBiscuitOS.sh 脚本,脚本首先在当前目录下创建一个文件并写入 “BiscuitOS” 内容,然后在用户空间直接运行 BiscuitOS-file-share-mmap-userspace-default 调用程序,最后使用 hexdump 工具对虚拟内存写入的值是否与文件中的内容一致。通过实践发现结果与预期一致。
Hello BiscuitOS Application
功能介绍
BiscuitOS 配置
在 BiscuitOS 中使用配置如下:
具体实践办法请参考:
HKC 计划 BiscuitOS 实践框架介绍
通用例程
HKC 贡献者名单
BuddyZhang1 buddy.zhang@aliyun.com
Meijusan 20741602@qq.com
Shaobin shaobin.huang@kernelworker.net