目录


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 项目下使用如下命令:

cd BiscuitOS
make linux-5.0-i386_defconfig
make menuconfig

  [*] Package  --->
      [*] BiscuitOS Demo Code  --->
          [*] Kernel Demo Code on BiscuitOS  ---> 

make
cd BiscuitOS/output/linux-5.0-i386/package/BiscuitOS-kernel-0.0.1/

开发者使用如下命令部署所需的环境,正如上图,选择 “Kernel Demo Code on BiscuitOS” 选项,保存并退出配置,接着使用 make 进行部署,部署成功之后,进入部署的目录, 接着获取 Demo 源码使用如下命令:

cd BiscuitOS/output/linux-5.0-i386/package/BiscuitOS-kernel-0.0.1/
make download
tree

通过上面的命令之后,BiscuitOS 会自动部署一个 Demo 程序,其中 BiscuitOS-kernel-0.0.1 目录下的 main.c 函数就是源码的位置,同级的 Makefile 就是源码编译描述。Demo 中的 main.c 源码很简单,如下图:

Demo 源码很简单,就是在 “device_initcall” 阶段调用 “BiscuitOS_init()” 函数,并打印 “Hello BiscuitOS on kernel.” 字符串。开发者使用如下命令进行源码编译,并在 BiscuitOS 上运行:

make
make install
make pack
make run

从内核启动 log 中看到打印的 “Hello BiscuitOS on kernel”, 至此 Demo 程序演示完毕,接下来讲解开发者如何将 “独立代码” 添加到 BiscuitOS 机制。 开发者可以将自己的 “独立程序” 替换 Demo 程序中的 main.c 函数,并提供对应的 Makefile 文件,例如开发者的独立程序包含了一个源文件 “usage.c”, 那么可以基于 Demo 程序,将 main.c 函数移除并添加 “usage.c” 文件到该目录下,并修改 Makefile 文件,如下:

obj-y += usage.o

修改完毕之后,就是编译源码和在 BiscuitOS 验证你的独立程序。如果在 BiscuitOS 上验证无误的话,开发者接下来按如下步骤提交独立代码, 并合入 BiscuitOS 项目里。开发者需要发一份邮件给我,按如下内容进行描述:

1. 独立项目的功能
2. 独立代码在什么架构上验证的,内核版本号信息
3. 附件添加对应的源码
3. 开发者的介绍和联系邮箱

开发者准备好如上内容发送至 “buddy.zhang@aliyun.com” 邮箱,并微信通知我。待我审批通过之后合入 BiscuitOS 并进行发布.


编译进内核源码树,任意位置运行

BiscuitOS 支持将独立代码编译进内核,并在指定位置运行。开发者可以使用这种办法将你的 “独立代码” 进行提交,具体使用办法如下. 首先开发者基于 BiscuitOS 项目构建指定架构的环境,这里以 “linux 5.0” i386 架构进行讲解,其他架构类型,开发者参考使用。开发者首先在 BiscuitOS 项目下使用如下命令:

cd BiscuitOS
make linux-5.0-i386_defconfig
make menuconfig

  [*] Package  --->
      [*] BiscuitOS Demo Code  --->
          [*] Kernel Anywhere Demo Code on BiscuitOS  --->

make
cd BiscuitOS/output/linux-5.0-i386/package/BiscuitOS-kernel-any-0.0.1/

开发者使用如下命令部署所需的环境,正如上图,选择 “Kernel Anywhere Demo Code on BiscuitOS” 选项,保存并退出配置,接着使用 make 进行部署,部署成功之后,进入部署的目录, 接着获取 Demo 源码使用如下命令:

cd BiscuitOS/output/linux-5.0-i386/package/BiscuitOS-kernel-any-0.0.1/
make download
tree

通过上面的命令之后,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 上运行:

make
make install
make pack
make run

从内核启动 log 中看到打印的 “Hello BiscuitOS anywhere on kernel”, 至此 Demo 程序演示完毕,接下来讲解开发者如何将 “独立代码” 添加到 BiscuitOS 机制。 开发者可以将自己的 “独立程序” 替换 Demo 程序中的 main.c 函数即可,当函数定义时不能为 static,返回值一定是 int, 参数必须是 void, 例如:

int BiscuitOS_Demo(void) {}

修改完毕之后,就是编译源码和在 BiscuitOS 验证你的独立程序。如果在 BiscuitOS 上验证无误的话,开发者接下来按如下步骤提交独立代码, 并合入 BiscuitOS 项目里。开发者需要发一份邮件给我,按如下内容进行描述:

1. 独立项目的功能
2. 独立代码在什么架构上验证的,内核版本号信息
3. 附件添加对应的源码
3. 开发者的介绍和联系邮箱

开发者准备好如上内容发送至 “buddy.zhang@aliyun.com” 邮箱,并微信通知我。待我审批通过之后合入 BiscuitOS 并进行发布.

独立内核模块研究

BiscuitOS 支持将独立内核模块开发,如果开发者的 “独立代码” 可以通过内核模块方式使用,那么开发者可以使用这种办法将你的 “独立代码” 进行提交,具体使用办法如下. 首先开发者基于 BiscuitOS 项目构建指定架构的环境,这里以 “linux 5.0” i386 架构进行讲解,其他架构类型,开发者参考使用。开发者首先在 BiscuitOS 项目下使用如下命令:

cd BiscuitOS
make linux-5.0-i386_defconfig
make menuconfig

  [*] Package  --->
      [*] BiscuitOS Demo Code  --->
          [*] Module Demo Code on BiscuitOS  --->
          [*] Module Project Demo on BiscuitOS  --->
make

开发者使用如下命令部署所需的环境,正如上图,选择 “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 源码使用如下命令:

cd BiscuitOS/output/linux-5.0-i386/package/BiscuitOS-modules-0.0.1/
make download
tree

通过上面的命令之后,BiscuitOS 会自动部署一个 Demo 程序,其中 BiscuitOS-modules-0.0.1 目录下的 main.c 函数就是源码的位置,同级的 Makefile 就是源码编译描述。Demo 中的 main.c 源码很简单,如下图:

Demo 源码很简单,就是在 KO 加载的时候打印 “Hello modules on BiscuitOS” 字符串。开发者使用如下命令进行源码编译,并在 BiscuitOS 上运行:

make
make install
make pack
make run

BiscuitOS 启动之后,动态加载模块后看到打印相应的字符串, 至此 Demo 程序演示完毕,接下来讲解开发者如何将 “独立代码” 添加到 BiscuitOS 机制。 开发者可以将自己的 “独立程序” 替换 Demo 程序中的 main.c 函数,并提供对应的 Makefile 文件,例如开发者的独立程序包含了一个源文件 “usage.c”, 那么可以基于 Demo 程序,将 main.c 函数移除并添加 “usage.c” 文件到该目录下,并其 Makefile 文件如下:

#
# Module Common
#
# (C) 2020.03.14 BuddyZhang1 <buddy.zhang@aliyun.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.

ifneq ($(KERNELRELEASE),)

## Target
ifeq ("$(origin MODULE_NAME)", "command line")
MODULE_NAME		:= $(MODULE_NAME)
else
MODULE_NAME		:= BiscuitOS_mod
endif
obj-m			:= $(MODULE_NAME).o

## Source Code
$(MODULE_NAME)-m	+= usage.o

## CFlags
ccflags-y		+= -DCONFIG_BISCUITOS_MODULE 
## Header
ccflags-y		+= -I$(PWD)

else

## Parse argument
## Default support ARM32
ifeq ("$(origin BSROOT)", "command line")
BSROOT=$(BSROOT)
else
BSROOT=/xspace/OpenSource/BiscuitOS/BiscuitOS/output/linux-5.0-arm32
endif

ifeq ("$(origin ARCH)", "command line")
ARCH=$(ARCH)
else
ARCH=arm
endif

ifeq ("$(origin CROSS_TOOLS)", "command line")
CROSS_TOOLS=$(CROSS_TOOLS)
else
CROSS_TOOLS=arm-linux-gnueabi
endif

## Don't Edit
KERNELDIR=$(BSROOT)/linux/linux
CROSS_COMPILE_PATH=$(BSROOT)/$(CROSS_TOOLS)/$(CROSS_TOOLS)/bin
CROSS_COMPILE=$(CROSS_COMPILE_PATH)/$(CROSS_TOOLS)-
INCLUDEDIR=$(KERNELDIR)/include
ARCHINCLUDEDIR=$(KERNELDIR)/arch/$(ARCH)/include
INSTALLDIR=$(BSROOT)/rootfs/rootfs/

## X86/X64 Architecture
ifeq ($(ARCH), i386)
CROSS_COMPILE	:=
else ifeq ($(ARCH), x86_64)
CROSS_COMPILE	:=
endif

## Compile
AS		= $(CROSS_COMPILE)as
LD		= $(CROSS_COMPILE)ld
CC		= $(CROSS_COMPILE)gcc
CPP		= $(CC) -E
AR		= $(CROSS_COMPILE)ar
NM		= $(CROSS_COMPILE)nm
STRIP		= $(CROSS_COMPILE)strip
OBJCOPY		= $(CROSS_COMPILE)objcopy
OBJDUMP		= $(CROSS_COMPILE)objdump

# FLAGS
CFLAGS += -I$(INCLUDEDIR) -I$(ARCHINCLUDEDIR)

all: $(OBJS)
	make -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) \
                CROSS_COMPILE=$(CROSS_COMPILE) modules

install:
	make -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) \
		INSTALL_MOD_PATH=$(INSTALLDIR) modules_install

clean:
	@rm -rf *.ko *.o *.mod.o *.mod.c *.symvers *.order \
               .*.o.cmd .tmp_versions *.ko.cmd .*.ko.cmd

endif

开发者可以使用这个 Makefile 模板,只需修改 “usage.o” 对应的内容即可。修改完毕之后,就是编译源码和在 BiscuitOS 验证你的独立程序。如果在 BiscuitOS 上验证无误的话,开发者接下来按如下步骤提交独立代码, 并合入 BiscuitOS 项目里。开发者需要发一份邮件给我,按如下内容进行描述:

1. 独立项目的功能
2. 独立代码在什么架构上验证的,内核版本号信息
3. 附件添加对应的源码
3. 开发者的介绍和联系邮箱

开发者准备好如上内容发送至 “buddy.zhang@aliyun.com” 邮箱,并微信通知我。待我审批通过之后合入 BiscuitOS 并进行发布.


应用程序研究

BiscuitOS 支持独立应用程序开发,如果开发者的 “独立代码” 是一个应用程序,那么开发者可以使用这种办法将你的 “独立代码” 进行提交,具体使用办法如下. 首先开发者基于 BiscuitOS 项目构建指定架构的环境,这里以 “linux 5.0” i386 架构进行讲解,其他架构类型,开发者参考使用。开发者首先在 BiscuitOS 项目下使用如下命令:

cd BiscuitOS
make linux-5.0-i386_defconfig
make menuconfig

  [*] Package  --->
      [*] BiscuitOS Demo Code  --->
          [*] Application Demo Code on BiscuitOS  --->
          [*] Application Project Demo on BiscuitOS  --->
make

开发者使用如下命令部署所需的环境,正如上图,选择 “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 源码使用如下命令:

cd BiscuitOS/output/linux-5.0-i386/package/BiscuitOS-Application-0.0.1/
make download
tree

通过上面的命令之后,BiscuitOS 会自动部署一个 Demo 程序,其中 BiscuitOS-Application-0.0.1 目录下的 main.c 函数就是源码的位置,同级的 Makefile 就是源码编译描述。Demo 中的 main.c 源码很简单,如下图:

Demo 源码很简单,就是应用程序运行的时候打印 “Hello Application Demo on BiscuitOS” 字符串。开发者使用如下命令进行源码编译,并在 BiscuitOS 上运行:

make
make install
make pack
make run

BiscuitOS 启动之后,运行应用程序后看到打印相应的字符串, 至此 Demo 程序演示完毕,接下来讲解开发者如何将 “独立代码” 添加到 BiscuitOS 机制。 开发者可以将自己的 “独立程序” 替换 Demo 程序中的 main.c 函数,并提供对应的 Makefile 文件,例如开发者的独立程序包含了一个源文件 “usage.c”, 那么可以基于 Demo 程序,将 main.c 函数移除并添加 “usage.c” 文件到该目录下,并其 Makefile 文件如下:

#
# Application Project
#
# (C) 2020.02.02 BuddyZhang1 <buddy.zhang@aliyun.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.

## Target
ifeq ("$(origin TARGETA)", "command line")
TARGET			:= $(TARGETA)
else
TARGET			:= BiscuitOS_app
endif

## Source Code
SRC			+= usage.c

## CFlags
LCFLAGS			+= -DCONFIG_BISCUITOS_APP
## Header
LCFLAGS			+= -I./ -I$(PWD)/include

DOT			:= -
## X86/X64 Architecture
ifeq ($(ARCH), i386)
CROSS_COMPILE	=
LCFLAGS		+= -m32
DOT		:=
else ifeq ($(ARCH), x86_64)
CROSS_COMPILE   :=
DOT		:=
endif

# Compile
B_AS		= $(CROSS_COMPILE)$(DOT)as
B_LD		= $(CROSS_COMPILE)$(DOT)ld
B_CC		= $(CROSS_COMPILE)$(DOT)gcc
B_CPP		= $(CC) -E
B_AR		= $(CROSS_COMPILE)$(DOT)ar
B_NM		= $(CROSS_COMPILE)$(DOT)nm
B_STRIP		= $(CROSS_COMPILE)$(DOT)strip
B_OBJCOPY	= $(CROSS_COMPILE)$(DOT)objcopy
B_OBJDUMP	= $(CROSS_COMPILE)$(DOT)objdump

## Install PATH
ifeq ("$(origin INSPATH)", "command line")
INSTALL_PATH		:= $(INSPATH)
else
INSTALL_PATH		:= ./
endif

all:
	$(B_CC) $(LCFLAGS) -o $(TARGET) $(SRC)

install:
	@cp -rfa $(TARGET) $(INSTALL_PATH)

clean:
	@rm -rf $(TARGET) *.o

开发者可以使用这个 Makefile 模板,只需修改 “usage.c” 对应的内容即可。修改完毕之后,就是编译源码和在 BiscuitOS 验证你的独立程序。如果在 BiscuitOS 上验证无误的话,开发者接下来按如下步骤提交独立代码, 并合入 BiscuitOS 项目里。开发者需要发一份邮件给我,按如下内容进行描述:

1. 独立项目的功能
2. 独立代码在什么架构上验证的,内核版本号信息
3. 附件添加对应的源码
3. 开发者的介绍和联系邮箱

开发者准备好如上内容发送至 “buddy.zhang@aliyun.com” 邮箱,并微信通知我。待我审批通过之后合入 BiscuitOS 并进行发布.


HKC 计划实践


HKC 计划实践 – 独立模块


实践准备

BiscuitOS 支持多种架构、多种内核版本的实践,开发者可以根据需要自行选择,这里以 i386 架构的 linxu 5.0 为例子进行讲解,其余开发者可以在如下链接中进行参考:


实践部署

开发环境部署完毕之后,接下来是使用 BiscuitOS 代码库中的代码,这里以一个 i386 架构的 Demo 做为介绍,其余架构与之类似:

cd BiscuitOS
make linux-5.0-i386_defconfig
make menuconfig

  [*] Package  --->
      [*] BiscuitOS Demo Code  --->
          [*] Module Demo Code on BiscuitOS  --->
          [*] Module Project Demo on BiscuitOS  --->
make

开发者使用如下命令部署所需的环境,正如上图,选择 “Module Demo Code on BiscuitOS” 选项和 “Module Project Demo on BiscuitOS”,保存并退出配置,”Module Demo Code on BiscuitOS” 选项是一个简单的模块 Demo,接着运行 make 进行部署,部署成功之后,进入部署的目录, 接着获取 Demo 源码使用如下命令:

cd BiscuitOS/output/linux-5.0-i386/package/BiscuitOS-modules-0.0.1/
make download
tree

通过上面的命令之后,BiscuitOS 会自动部署一个 Demo 程序,其中 BiscuitOS-modules-0.0.1 目录下的 main.c 函数就是源码的位置,同级的 Makefile 就是源码编译描述。Demo 中的 main.c 源码很简单,如下图:

Demo 源码很简单,就是在 KO 加载的时候打印 “Hello modules on BiscuitOS” 字符串。开发者使用如下命令进行源码编译,并在 BiscuitOS 上运行:

make
make install
make pack
make run

以上便是一个最基础的 “人类知识共同体” 计划实践.


HKC 计划实践 – 内核源码树


实践准备

BiscuitOS 支持多种架构、多种内核版本的实践,开发者可以根据需要自行选择,这里以 i386 架构的 linxu 5.0 为例子进行讲解,其余开发者可以在如下链接中进行参考:


实践部署

开发环境部署完毕之后,接下来是使用 BiscuitOS 代码库中的代码,这里以一个 i386 架构的 Demo 做为介绍,其余架构与之类似:

cd BiscuitOS
make linux-5.0-i386_defconfig
make menuconfig

  [*] Package  --->
      [*] BiscuitOS Demo Code  --->
          [*] Kernel Demo Code on BiscuitOS  --->

make
cd BiscuitOS/output/linux-5.0-i386/package/BiscuitOS-kernel-0.0.1/

开发者使用如下命令部署所需的环境,正如上图,选择 “Kernel Demo Code on BiscuitOS” 选项,保存并退出配置,接着使用 make 进行部署,部署成功之后,进入部署的目录, 接着获取 Demo 源码使用如下命令:

cd BiscuitOS/output/linux-5.0-i386/package/BiscuitOS-kernel-0.0.1/
make download
tree

通过上面的命令之后,BiscuitOS 会自动部署一个 Demo 程序,其中 BiscuitOS-kernel-0.0.1 目录下的 main.c 函数就是源码的位置,同级的 Makefile 就是源码编译描述。Demo 中的 main.c 源码很简单,如下图:

Demo 源码很简单,就是在 “device_initcall” 阶段调用 “BiscuitOS_init()” 函数,并打印 “Hello Biscui tOS on kernel.” 字符串。开发者使用如下命令进行源码编译,并在 BiscuitOS 上运行:

make
make install
make pack
make run

从内核启动 log 中看到打印的 “Hello BiscuitOS on kernel”, 至此 Demo 程序演示完毕.


HKC 计划实践 – 应用程序


实践准备

BiscuitOS 支持多种架构、多种内核版本的实践,开发者可以根据需要自行选择,这里以 i386 架构的 linxu 5.0 为例子进行讲解,其余开发者可以在如下链接中进行参考:


实践部署

开发环境部署完毕之后,接下来是使用 BiscuitOS 代码库中的代码,这里以一个 i386 架构的 Demo 做为介绍,其余架构与之类似:

cd BiscuitOS
make linux-5.0-i386_defconfig
make menuconfig

  [*] Package  --->
      [*] BiscuitOS Demo Code  --->
          [*] Application Demo Code on BiscuitOS  --->
          [*] Application Project Demo on BiscuitOS  --->
make

开发者使用如下命令部署所需的环境,正如上图,选择 “Application Demo Code on BiscuitOS” 选项和 “Application Project Demo on BiscuitOS”,保存并退出配置,”Application Demo Code on BiscuitOS” 选项是一个简单的应用程序 Demo,使用 make 进行部署,部署成功之后,进入部署的目录, 接着获取 Demo 源码使用如下命令:

cd BiscuitOS/output/linux-5.0-i386/package/BiscuitOS-Application-0.0.1/
make download
tree

通过上面的命令之后,BiscuitOS 会自动部署一个 Demo 程序,其中 BiscuitOS-Application-0.0.1 目录下的 main.c 函数就是源码的位置,同级的 Makefile 就是源码编译描述。Demo 中的 main.c 源码很简单,如下图:

Demo 源码很简单,就是应用程序运行的时候打印 “Hello Application Demo on BiscuitOS” 字符串。开发者使用如下命令进行源码编译,并在 BiscuitOS 上运行:

make
make install
make pack
make run

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 中使用配置如下:

[*] Package  --->
    [*] MMU Shrink  --->
        [*] kernel slab shrink Demo  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/kernel-slab-shrink-base-0.0.1

具体实践办法请参考:

通用例程
/*
 * BiscuitOS Kernel BiscuitOS Code
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <linux/init.h>
#include <linux/kernel.h>

/* Shrink interface */
#include <linux/mm.h>
#include <linux/shrinker.h>

extern void drop_slab(void);
unsigned long BiscuitOS_free_pages = 0x1000;
unsigned long BiscuitOS_used_pages = 0x200;

static unsigned long
mmu_shrink_count_bs(struct shrinker *shrink, struct shrink_control *sc)
{
        printk("BiscuitOS Count...\n\n\n");
        return BiscuitOS_used_pages;
}

static unsigned long
mmu_shrink_scan_bs(struct shrinker *shrink, struct shrink_control *sc)
{
        printk("BiscuitOS Scan... \n\n\n");
        return BiscuitOS_free_pages;
}

static struct shrinker mmu_shrinker_bs = {
        .count_objects = mmu_shrink_count_bs,
        .scan_objects  = mmu_shrink_scan_bs,
        .seeks = DEFAULT_SEEKS * 10,
};

/* Shrink */
static int __init BiscuitOS_shrink_init(void)
{
        register_shrinker(&mmu_shrinker_bs);

        return 0;
}

/* kernel entry on initcall */
static int __init BiscuitOS_init(void)
{
        printk("Hello BiscuitOS on kernel.\n");

        drop_slab();

        /* unregister */
        unregister_shrinker(&mmu_shrinker_bs);
        return 0;
}

fs_initcall(BiscuitOS_shrink_init);
device_initcall(BiscuitOS_init);

在例子中,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 的消息,那么内核启动过程中打印如下:

Block layer SCSI generic (bsg) driver version 0.4 loaded (major 251)
io scheduler mq-deadline registered
io scheduler kyber registered
Hello BiscuitOS on kernel.
BiscuitOS Count...


input: Power Button as /devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
ACPI: Power Button [PWRF]
Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
00:05: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A
Non-volatile memory driver v1.3
Linux agpgart interface v0.103

从内核启动的信息来看,当调用 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 中使用配置如下:

[*] Package  --->
    [*] Assembly  --->
        [*] X86/i386/X64 Assembly  --->
            [*] RDMSR  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/rdmsr-x86-0.0.1/

具体实践办法请参考:

通用例程
/*
 * RDMSR [X86/i386/X86_64] -- Read from MSR register.
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

/*
 * Both i386 and x86_64 returns 64-bit value in edx:eax, but gcc's "A"
 * constraint has different meanings. For i386, "A" means exactly
 * edx:eax, while for x86_64 it doesn't mean rdx:rax or edx:eax. Instead
 * it means rax *or* rdx.
 */
#ifdef CONFIG_X86_64
#define DECLARE_ARGS(val, low, high)		unsigned low, high
#define EAX_EDX_VAL(val, low, high)		((low) | ((u64)(high) << 32))
#define EAX_EDX_ARGS(val, low, high)		"a" (low), "d" (high)
#define EAX_EDX_RET(val, low, high)		"=a" (low), "=d" (high)
#else
#define DECLARE_ARGS(val, low, high)		unsigned long long val
#define EAX_EDX_VAL(val, low, high)		(val)
#define EAX_EDX_ARGS(val, low, high)		"A" (val)
#define EAX_EDX_RET(val, low, high)		"=A" (val)
#endif

static inline unsigned long long native_read_msr_bs(unsigned int msr)
{
	DECLARE_ARGS(val, low, high);

	asm volatile("rdmsr"
		    : EAX_EDX_RET(val, low, high)
		    : "c" (msr));
	return EAX_EDX_VAL(val, low, high);
}

#define rdmsr_bs(msr, low, high)			\
do {							\
	u64 __val = native_read_msr_bs((msr));		\
	(void)((low) = (u32)__val);			\
	(void)((high) = (u32)(__val >> 32));		\
} while (0)

#define MSR_IA32_SYSENTER_CS		0x00000174

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
	unsigned int host_cs, junk;

	/* RDMSR */
	rdmsr_bs(MSR_IA32_SYSENTER_CS, host_cs, junk);
	printk("MSR_IA32_SYSENTER_CS: %#lx-%lx\n", host_cs, junk);

	return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Common Device driver on BiscuitOS");

上面的例子很简单,就是通过内嵌汇编的方式在内核模块中调用 RDMSR 指令。值得注意的是,在 X86_64 内嵌汇编中,GCC “A” 标志不代表 “EDX:EAX” 的组合,而在 X86 内嵌汇编中,GCC “A” 标志表示 “EDX:EAX” 的组合,因此使用时应该做区分. 程序运行时的结果如下:

cd /lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # 
/lib/modules/5.0.0/extra # ls
rdmsr-x86-0.0.1.ko
/lib/modules/5.0.0/extra # 
/lib/modules/5.0.0/extra # insmod rdmsr-x86-0.0.1.ko 
rdmsr_x86_0.0.1: loading out-of-tree module taints kernel.
MSR_IA32_SYSENTER_CS: 0x60-0
/lib/modules/5.0.0/extra #

更多 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 中使用配置如下:

[*] Package  --->
    [*] Assembly  --->
        [*] X86/i386/X64 Assembly  --->
            [*] WRMSR  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/wrmsr-x86-0.0.1/

具体实践办法请参考:

通用例程
/*
 * WRMSR [X86/X86_64] -- Write to Model Specific Register
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#define IA32_PQR_ASSOC		0x0c8f

static inline void wrmsr_bs(unsigned msr, unsigned low, unsigned high)
{
	asm volatile ("wrmsr"
		      :
		      : "c" (msr), "a" (low), "d" (high)
		      : "memory");
}

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
	printk("Hello modules on BiscuitOS\n");

	/* WRMSR */
	wrmsr_bs(IA32_PQR_ASSOC, 0, 0);

	return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Common Device driver on BiscuitOS");

上面的例子很简单,就是通过内嵌汇编的方式将 MSR 寄存器的值通过 “c” 写入到 ECX 寄存器中,然后通过 “a” 将 64bit 值的低 32bit 写入到 EAX 寄存器,通过 “d” 将 64bit 值写入到 EDX 寄存器。最终使用 “memory” 标记,以此告诉编译器不要将用到的变量缓存到寄存器,因为这段代码可能会用到内存变量,而这些内存变量会以不可预知的方式发生改变,因此 GCC 插入必要的代码先将缓存到寄存器的变量值写回内存,如果后面又访问这些变量,需要重新访问内存. 上面的实例在 BiscuitOS 运行的结果如下:

cd lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # ls
wrmsr-x86-0.0.1.ko
/lib/modules/5.0.0/extra # 
/lib/modules/5.0.0/extra # insmod wrmsr-x86-0.0.1.ko 
wrmsr_x86_0.0.1: loading out-of-tree module taints kernel.
Hello modules on BiscuitOS
general protection fault: 0000 [#1] SMP
CPU: 1 PID: 1142 Comm: insmod Tainted: G           O      5.0.0 #61
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-0-ga698c8995f-p4
EIP: BiscuitOS_init+0x1b/0x1000 [wrmsr_x86_0.0.1]
Code: Bad RIP value.
EAX: 00000000 EBX: e0a5c000 ECX: 00000c8f EDX: 00000000
ESI: e0a5f000 EDI: cb59c0e0 EBP: cd3c9df8 ESP: cd3c9df4
DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 EFLAGS: 00010246
CR0: 80050033 CR2: e0a5eff1 CR3: 0ef54000 CR4: 001406d0
Call Trace:
 do_one_initcall+0x42/0x191
 ? call_usermodehelper_exec+0x87/0x170
 ? free_pcp_prepare+0x4f/0xa0
 ? _cond_resched+0x17/0x40
 ? kmem_cache_alloc_trace+0x35/0x160
 ? do_init_module+0x21/0x1e4
 do_init_module+0x50/0x1e4
 load_module+0x1d46/0x2280
 ? map_vm_area+0x2c/0x40
 sys_init_module+0x10d/0x150
 do_fast_syscall_32+0x7a/0x1c0
 entry_SYSENTER_32+0x6b/0xbe
EIP: 0xb7f3b871
Code: 8b 98 58 cd ff ff 85 d2 89 c8 74 02 89 0a 5b 5d c3 8b 04 24 c3 8b 14 24 c3 8b 1c6
EAX: ffffffda EBX: 09ab6390 ECX: 00000aec EDX: 0820dbd6
ESI: 00000003 EDI: 00000000 EBP: bf8b064c ESP: bf8b02e0
DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b EFLAGS: 00000246
Modules linked in: wrmsr_x86_0.0.1(O+)
---[ end trace f75e95a374ac04ef ]---
EIP: BiscuitOS_init+0x1b/0x1000 [wrmsr_x86_0.0.1]
Code: Bad RIP value.
EAX: 00000000 EBX: e0a5c000 ECX: 00000c8f EDX: 00000000
ESI: e0a5f000 EDI: cb59c0e0 EBP: cd3c9df8 ESP: dd06049c
DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 EFLAGS: 00010246
CR0: 80050033 CR2: e0a5eff1 CR3: 0ef54000 CR4: 001406d0
insmod (1142) used greatest stack depth: 6600 bytes left
Segmentation fault
/lib/modules/5.0.0/extra # 

更多 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 中使用配置如下:

[*] Package  --->
    [*] Notifier mechanism on Kernel  --->
        [*] Kernel notifier Base Demo  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/kernel-notifier-base-0.0.1

具体实践办法请参考:

通用例程
/*
 * Kernel notifier
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#define BISCUITOS_EVENT_A		0x01
#define BISCUITOS_EVENT_B		0x02
#define BISCUITOS_EVENT_C		0x03

static RAW_NOTIFIER_HEAD(BiscuitOS_chain);

int BiscuitOS_notifier_event(struct notifier_block *nb, 
					unsigned long event, void *v)
{
	switch (event) {
	case BISCUITOS_EVENT_A:
		printk("BiscuitOS notifier event A [%s]\n", (char *)v);
		break;
	case BISCUITOS_EVENT_B:
		printk("BiscuitOS notifier event B [%s]\n", (char *)v);
		break;
	default:
		break;
	}

	return NOTIFY_DONE;
}

static struct notifier_block BiscuitOS_notifier = {
	.notifier_call = BiscuitOS_notifier_event,
};

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
	printk("Hello modules on BiscuitOS\n");

	/* Register notifier */
	raw_notifier_chain_register(&BiscuitOS_chain, &BiscuitOS_notifier);

	/* Notifier */
	raw_notifier_call_chain(&BiscuitOS_chain, 
					BISCUITOS_EVENT_B, "BiscuitOS");
	raw_notifier_call_chain(&BiscuitOS_chain,
					BISCUITOS_EVENT_A, "Buddy");
	raw_notifier_call_chain(&BiscuitOS_chain,
					BISCUITOS_EVENT_C, "Memory");

	return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
	/* Unregister notifier */
	raw_notifier_chain_unregister(&BiscuitOS_chain, &BiscuitOS_notifier);
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Common Device driver on 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 中运行的结果如下:

cd lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # 
/lib/modules/5.0.0/extra # ls
kernel-notifier-base-0.0.1.ko
/lib/modules/5.0.0/extra # insmod kernel-notifier-base-0.0.1.ko 
kernel_notifier_base_0.0.1: loading out-of-tree module taints kernel.
Hello modules on BiscuitOS
BiscuitOS notifier event B [BiscuitOS]
BiscuitOS notifier event A [Buddy]
/lib/modules/5.0.0/extra #

在 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 中使用配置如下:

[*] Package  --->
    [*] CPUMASK mechanism on Kernel  --->
        [*] Kernel cpumask Base Demo  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/kernel-cpumask-base-0.0.1

具体实践办法请参考:

通用例程
/*
 * CPU mask mechanism
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mm.h>

/* CPUmask */
#include <linux/cpumask.h>

/* declear cpumask */
static cpumask_var_t BiscuitOS_cpumask;

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
	int cpu = raw_smp_processor_id();

	printk("BiscuitOS current cpu %d\n", cpu);
	/* alloc cpumask */
	zalloc_cpumask_var(&BiscuitOS_cpumask, GFP_KERNEL);

	/* test and set */
	if (!cpumask_test_cpu(cpu, BiscuitOS_cpumask)) {
		printk("CPUMASK set cpu %d\n", cpu);
		cpumask_set_cpu(cpu, BiscuitOS_cpumask);
	}

	/* test and clear */
	if (cpumask_test_cpu(cpu, BiscuitOS_cpumask)) {
		printk("CPUMASK clear cpu %d\n", cpu);
		cpumask_clear_cpu(cpu, BiscuitOS_cpumask);
	}

	return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
	/* free cpumask */
	free_cpumask_var(BiscuitOS_cpumask);
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Common Device driver on 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 中运行的情况如下:

cd lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # ls
kernel-cpumask-base-0.0.1.ko
/lib/modules/5.0.0/extra # insmod kernel-cpumask-base-0.0.1.ko 
kernel_cpumask_base_0.0.1: loading out-of-tree module taints kernel.
BiscuitOS current cpu 0
CPUMASK set cpu 0
CPUMASK clear cpu 0
/lib/modules/5.0.0/extra #

从运行的结果可以看出,当前 CPU ID 是 0,第一次检测的时候,CPU 没有在 BiscuitOS_cpumask 中置位,那么将其置位。第二次检测的时候,CPU 已经在 BiscuitOS_cpumask 中置位,那么将其清零.


smp_call_function_single

smp_call_function_single() 函数用于在指定的 CPU 上运行指定的函数,其函数原型如下:

/*
 * smp_call_function_single - Run a function on a specific CPU
 * @func: The function to run. This must be fast and non-blocking.
 * @info: An arbitrary pointer to pass to the function.
 * @wait: If true, wait until function has completed on other CPUs.
 *
 * Returns 0 on success, else a negative status code.
 */
int smp_call_function_single(int cpu, smp_call_func_t func, void *info, int wait)

cpu 指定了需要运行的 CPU ID,func 参数则是指向需要执行的函数,info 指向需要向执行函数传入的数据,wait 用于指明是否等待原 CPU 执行完毕之后再去其他 CPU 执行。

BiscuitOS 配置

本实例已经在 Linux 5.0 i386 架构上验证通过, 在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] SMP  --->
        [*] smp_call_function_single  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/smp_call_function_single-0.0.1

具体实践办法请参考:

通用例程
/*
 * Run a function on a specific CPU
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

void BiscuitOS_smp(void *rtn)
{
	int cpu = raw_smp_processor_id();

	printk("SMP running on CPU%d - Default CPU%d\n", cpu, (int)rtn);
}

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
	int cpu = 0;
	int current_cpu = raw_smp_processor_id();

	/* Call function on specific CPU */
	smp_call_function_single(cpu, BiscuitOS_smp, (void *)current_cpu, 0);

	printk("Hello modules on BiscuitOS\n");

	return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Common Device driver on BiscuitOS");

在例子中,函数调用 smp_call_function_single() 函数在指定的 CPU 0 上运行 BiscuitOS_smp() 函数,并向 BiscuitOS_smp() 函数传递了原始 CPU ID。其在 BiscuitOS 上运行的结果如下:

cd lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # ls
smp_call_function_single-0.0.1.ko
/lib/modules/5.0.0/extra # insmod smp_call_function_single-0.0.1.ko 
smp_call_function_single_0.0.1: loading out-of-tree module taints kernel.
SMP running on CPU0 - Default CPU1
Hello modules on BiscuitOS
/lib/modules/5.0.0/extra #

函数运行在 CPU 1 上,然后调用 smp_call_function_single() 函数让 BiscuitOS_smp() 函数运行在 CPU 0 上.


on_each_cpu

on_each_cpu() 函数用于将指定函数在没有 CPU 上运行,其函数原型如下:

/*
 * Call a function on all processors.  May be used during early boot while
 * early_boot_irqs_disabled is set.  Use local_irq_save/restore() instead
 * of local_irq_disable/enable().
 */ 
int on_each_cpu(void (*func) (void *info), void *info, int wait)

func 为指定在每个 CPU 上运行的函数,info 为传入调用函数的数据,wait 参数用于指定是否等待当前 CPU 任务执行完毕之后在执行.

BiscuitOS 配置

本实例已经在 Linux 5.0 i386 架构上验证通过, 在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] SMP  --->
        [*] on_each_cpu  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/on_each_cpu-0.0.1

具体实践办法请参考:

通用例程
/*
 * Call a function on all processors
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

void BiscuitOS_smp(void *info)
{
        int cpu = raw_smp_processor_id();

        printk("Current CPU%d Default CPU%d\n", cpu, (int)info);
}

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
        int cpu = raw_smp_processor_id();

        /* Call function on all processor */
        on_each_cpu(BiscuitOS_smp, (void *)cpu, 0);

        printk("Hello modules on BiscuitOS\n");

        return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Common Device driver on BiscuitOS");

在本例子中,函数定义了一个函数 BiscuitOS_smp,并调用 on_each_cpu() 函数让 BiscuitOS_smp() 函数在所有的 CPU 上运行。其在 BiscuitOS 上运行的情况如下:

cd lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # ls
on_each_cpu-0.0.1.ko
/lib/modules/5.0.0/extra # insmod on_each_cpu-0.0.1.ko 
on_each_cpu_0.0.1: loading out-of-tree module taints kernel.
Current CPU1 Default CPU1
Current CPU0 Default CPU1
Hello modules on BiscuitOS
/lib/modules/5.0.0/extra #

从运行的结果可以看出 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 中使用配置如下:

[*] Package  --->
    [*] Notifier mechanism on Kernel  --->
        [*] Reboot notifier  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/reboot-notifier-0.0.1

具体实践办法请参考:

通用例程
/*
 * Reboot notifier
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

/* reboot notifier */
#include <linux/reboot.h>

static int BiscuitOS_reboot_notifier(struct notifier_block *notifier,
                                                unsigned long val, void *v)
{
        printk("Trigger reboot on BiscuitOS.\n");
        return 0;
}

static struct notifier_block BiscuitOS_reboot = {
        .notifier_call = BiscuitOS_reboot_notifier,
        .priority = 0,
};

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
        printk("Hello modules on BiscuitOS\n");

        /* Register reboot notifier */
        register_reboot_notifier(&BiscuitOS_reboot);

        return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
        unregister_reboot_notifier(&BiscuitOS_reboot);
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Common Device driver on BiscuitOS");

在本例子中,调用 register_reboot_notifier() 函数注册了一个监听事件,当系统发生 REBOOT 事件之后,BiscuitOS_reboot_notifier() 函数就会被执行。当模块卸载的时候,调用 unregister_reboot_notifier() 函数撤销监听事件. 其在 BiscuitOS 上执行情况如下:

cd lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # ls
reboot-notifier-0.0.1.ko
/lib/modules/5.0.0/extra # insmod reboot-notifier-0.0.1.ko 
reboot_notifier_0.0.1: loading out-of-tree module taints kernel.
Hello modules on BiscuitOS

~ # 
~ # reboot
~ # umount: devtmpfs busy - remounted read-only
EXT4-fs (ram0): re-mounted. Opts: (null)
The system is going down NOW!
Sent SIGTERM totelnetd (1129) used greatest stack depth: 6172 bytes left
 all processes
Sent SIGKILL to all processes
Requesting system reboot
sh (1131) used greatest stack depth: 6144 bytes left
Unregister pv shared memory for cpu 1
Unregister pv shared memory for cpu 0
Trigger reboot on BiscuitOS.
reboot: Restarting system
reboot: machine restart

在 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 中使用配置如下:

[*] Package  --->
    [*] Power Manager  --->
        [*] Trigger Suspend Demo  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/suspend-base-0.0.1

具体实践办法请参考:

通用例程
/*
 * Syscore suspend/resume
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/syscore_ops.h>

static int BiscuitOS_suspend(void)
{
        printk("Trigger suspend on BiscuitOS\n");
        return 0;
}

static void BiscuitOS_resume(void)
{
        printk("Trigger resume on BiscuitOS.\n");
}

static struct syscore_ops BiscuitOS_syscore = {
        .suspend = BiscuitOS_suspend,
        .resume  = BiscuitOS_resume,
};

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
        printk("Hello modules on BiscuitOS\n");

        register_syscore_ops(&BiscuitOS_syscore);    

        return 0;
}

/* Module exit entry */ 
static void __exit BiscuitOS_exit(void)
{
        unregister_syscore_ops(&BiscuitOS_syscore);
}   

module_init(BiscuitOS_init); 
module_exit(BiscuitOS_exit);
    
MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Common Device driver on BiscuitOS");

在本例子中,驱动通过调用 register_syscore_ops() 注册了 SUSPEND/RESUME 的监听事件 BiscuitOS_syscore, 并提供两个接口,当系统进行 SUSPEND 状态,那么 BiscuitOS_suspend() 函数则会被调用,如果系统从 SUSPEND 状态恢复为 RESUME 状态,那么 BiscuitOS_resume() 函数则会别调用. 当驱动卸载的时候,会调用 unregister_syscore_ops() 函数撤销监听事件. 本例子在 BiscuitOS 上运行的情况如下:

~ # 
~ # cd /lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # ls
reboot-notifier-0.0.1.ko  suspend-base-0.0.1.ko
/lib/modules/5.0.0/extra # insmod suspend-base-0.0.1.ko
suspend_base_0.0.1: loading out-of-tree module taints kernel.
Hello modules on BiscuitOS
/lib/modules/5.0.0/extra # 
/lib/modules/5.0.0/extra # 
/lib/modules/5.0.0/extra # cd /sys/power/
/sys/power # echo disk > state
PM: hibernation entry
PM: Syncing filesystems ... 
PM: done.
Freezing user space processes ... (elapsed 0.001 seconds) done.
OOM killer disabled.
PM: Marking nosave pages: [mem 0x00000000-0x00000fff]
PM: Marking nosave pages: [mem 0x0009f000-0x000fffff]
PM: Basic memory bitmaps created
PM: Preallocating image memory... done (allocated 47593 pages)
PM: Allocated 190372 kbytes in 0.01 seconds (19037.20 MB/s)
Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done.
printk: Suspending console(s) (use no_console_suspend to debug)
ACPI: Preparing to enter system sleep state S4
PM: Saving platform NVS memory
Disabling non-boot CPUs ...
Unregister pv shared memory for cpu 1
smpboot: CPU 1 is now offline
Trigger suspend on BiscuitOS
PM: Creating hibernation image:
PM: Need to copy 47276 pages
PM: Normal pages needed: 47276 + 1024, available pages: 83650
PM: Hibernation image created (47276 pages copied)
kvm-clock: cpu 0, msr 1ba6b001, primary cpu clock, resume
PM: Restoring platform NVS memory
Trigger resume on BiscuitOS.
Enabling non-boot CPUs ...
x86: Booting SMP configuration:
smpboot: Booting Node 0 Processor 1 APIC 0x1
Initializing CPU#1
kvm-clock: cpu 1, msr 1ba6b041, secondary cpu clock
KVM setup async PF for cpu 1
kvm-stealtime: cpu 1, msr 1f78fa40
 cache: parent cpu1 should not be sleeping
CPU1 is up
ACPI: Waking up from system sleep state S4
PM: Cannot find swap device, try swapon -a
PM: Cannot get swap writer
PM: Basic memory bitmaps freed
OOM killer enabled.
Restarting tasks ... done.
PM: hibernation exit
sh: write error: No such device
/sys/power # ata2.01: NODEV after polling detection
e1000: eth0 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX

当向系统安装完驱动之后,在 /sys/power/ 目录下执行如下命令:

echo disk > /sys/power/state

执行上面的操作之后,将运行状态数据存到硬盘, 然后关机, 唤醒最慢。以上动作便会触发系统进入 SUSPEND 状态,此时触发驱动的 BiscuitOS_suspend() 函数。当有数据写入磁盘时候,系统又由 SUSPEND 状态进入 RESUME 状态,此时触发驱动的 BiscuitOS_resume() 函数。系统支持的 4 中休眠设置,如下:

1. freeze  冻结 I/O 设备, 将它们置于低功耗状态,使处理器进入空闲状态, 唤醒最快, 耗电比其它 standby、mem、disk 方式高
2. standby 除了冻结 I/O 设备外,还会暂停系统,唤醒较快,耗电比其它 mem、disk 方式高
3. mem     将运行状态数据存到内存, 并关闭外设, 进入等待模式, 唤醒较慢, 耗电比 disk 方式高
4. disk    将运行状态数据存到硬盘, 然后关机, 唤醒最慢


Trigger resume notifier

内核 syscore 子系统提供了 register_syscore_ops() 函数,用于向系统的 suspend 注册监听事件,当系统进行 SUSPEND 状态,然后从 SUSPEND 中唤醒为 RESUME 状态,那么调用指定的函数处理 resume 消息.

BiscuitOS 配置

本实例已经在 Linux 5.0 i386 架构上验证通过, 在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] Power Manager  --->
        [*] Trigger Resume Demo  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/resume-base-0.0.1

具体实践办法请参考:

通用例程
/*
 * Syscore suspend/resume
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/syscore_ops.h>

static int BiscuitOS_suspend(void)
{
        printk("Trigger suspend on BiscuitOS\n");
        return 0;
}

static void BiscuitOS_resume(void)
{
        printk("Trigger resume on BiscuitOS.\n");
}

static struct syscore_ops BiscuitOS_syscore = {
        .suspend = BiscuitOS_suspend,
        .resume  = BiscuitOS_resume,
};

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
        printk("Hello modules on BiscuitOS\n");

        register_syscore_ops(&BiscuitOS_syscore);    

        return 0;
}

/* Module exit entry */ 
static void __exit BiscuitOS_exit(void)
{
        unregister_syscore_ops(&BiscuitOS_syscore);
}   

module_init(BiscuitOS_init); 
module_exit(BiscuitOS_exit);
    
MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Common Device driver on BiscuitOS");

在本例子中,驱动通过调用 register_syscore_ops() 注册了 SUSPEND/RESUME 的监听事件 BiscuitOS_syscore, 并提供两个接口,当系统进行 SUSPEND 状态,那么 BiscuitOS_suspend() 函数则会被调用,如果系统从 SUSPEND 状态恢复为 RESUME 状态,那么 BiscuitOS_resume() 函数则会别调用. 当驱动卸载的时候,会调用 unregister_syscore_ops() 函数撤销监听事件. 本例子在 BiscuitOS 上运行的情况如下:

~ # 
~ # cd /lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # ls
reboot-notifier-0.0.1.ko  suspend-base-0.0.1.ko
/lib/modules/5.0.0/extra # insmod suspend-base-0.0.1.ko
suspend_base_0.0.1: loading out-of-tree module taints kernel.
Hello modules on BiscuitOS
/lib/modules/5.0.0/extra # 
/lib/modules/5.0.0/extra # 
/lib/modules/5.0.0/extra # cd /sys/power/
/sys/power # echo disk > state
PM: hibernation entry
PM: Syncing filesystems ... 
PM: done.
Freezing user space processes ... (elapsed 0.001 seconds) done.
OOM killer disabled.
PM: Marking nosave pages: [mem 0x00000000-0x00000fff]
PM: Marking nosave pages: [mem 0x0009f000-0x000fffff]
PM: Basic memory bitmaps created
PM: Preallocating image memory... done (allocated 47593 pages)
PM: Allocated 190372 kbytes in 0.01 seconds (19037.20 MB/s)
Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done.
printk: Suspending console(s) (use no_console_suspend to debug)
ACPI: Preparing to enter system sleep state S4
PM: Saving platform NVS memory
Disabling non-boot CPUs ...
Unregister pv shared memory for cpu 1
smpboot: CPU 1 is now offline
Trigger suspend on BiscuitOS
PM: Creating hibernation image:
PM: Need to copy 47276 pages
PM: Normal pages needed: 47276 + 1024, available pages: 83650
PM: Hibernation image created (47276 pages copied)
kvm-clock: cpu 0, msr 1ba6b001, primary cpu clock, resume
PM: Restoring platform NVS memory
Trigger resume on BiscuitOS.
Enabling non-boot CPUs ...
x86: Booting SMP configuration:
smpboot: Booting Node 0 Processor 1 APIC 0x1
Initializing CPU#1
kvm-clock: cpu 1, msr 1ba6b041, secondary cpu clock
KVM setup async PF for cpu 1
kvm-stealtime: cpu 1, msr 1f78fa40
 cache: parent cpu1 should not be sleeping
CPU1 is up
ACPI: Waking up from system sleep state S4
PM: Cannot find swap device, try swapon -a
PM: Cannot get swap writer
PM: Basic memory bitmaps freed
OOM killer enabled.
Restarting tasks ... done.
PM: hibernation exit
sh: write error: No such device
/sys/power # ata2.01: NODEV after polling detection
e1000: eth0 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX

当向系统安装完驱动之后,在 /sys/power/ 目录下执行如下命令:

echo disk > /sys/power/state

执行上面的操作之后,将运行状态数据存到硬盘, 然后关机, 唤醒最慢。以上动作便会触发系统进入 SUSPEND 状态,此时触发驱动的 BiscuitOS_suspend() 函数。当有数据写入磁盘时候,系统又由 SUSPEND 状态进入 RESUME 状态,此时触发驱动的 BiscuitOS_resume() 函数。系统支持的 4 中休眠设置,如下:

1. freeze  冻结 I/O 设备, 将它们置于低功耗状态,使处理器进入空闲状态, 唤醒最快, 耗电比其它 standby、mem、disk 方式高
2. standby 除了冻结 I/O 设备外,还会暂停系统,唤醒较快,耗电比其它 mem、disk 方式高
3. mem     将运行状态数据存到内存, 并关闭外设, 进入等待模式, 唤醒较慢, 耗电比 disk 方式高
4. disk    将运行状态数据存到硬盘, 然后关机, 唤醒最慢


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 中使用配置如下:

[*] Package  --->
    [*] Assembly  --->
        [*] X86/i386/X64 Assembly  --->
            [*] RDTSC  --->
            [*] RDTSC on Userspace  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/rdtsc-x86-0.0.1
BiscuitOS/output/linux-XXX-YYY/package/rdtsc-app-x86-0.0.1

具体实践办法请参考:

HKC 计划 BiscuitOS 实践框架介绍 (rdtsc-x86-0.0.1)

HKC 计划 BiscuitOS 实践框架介绍 (rdtsc-app-x86-0.0.1)

通用例程(内核篇)
/*
 * RDTSC - Read Time-Stamp Counter [x86]
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

/* mdelay */
#include <linux/delay.h>

/*
 * both i386 and x86_64 returns 64-bit value in edx:eax, but gcc's "A"
 * constraint has different meanings. For i386, "A" means exactly
 * edx:eax, while for x86_64 it doesn't mean rdx:rax or edx:eax. Instead,
 * it means rax *or* rdx.
 */
#ifdef CONFIG_X86_64
#define DECLARE_ARGS(val, low, high)    unsigned low, high
#define EAX_EDX_VAL(val, low, high)     ((low) | ((u64)(high) << 32))
#define EAX_EDX_ARGS(val, low, high)    "a" (low), "d" (high)
#define EAX_EDX_RET(val, low, high)     "=a" (low), "=d" (high)
#else
#define DECLARE_ARGS(val, low, high)    unsigned long long val
#define EAX_EDX_VAL(val, low, high)     (val)
#define EAX_EDX_ARGS(val, low, high)    "A" (val)
#define EAX_EDX_RET(val, low, high)     "=A" (val)
#endif

static __always_inline unsigned long long __native_read_tsc(void)
{
        DECLARE_ARGS(val, low, high);

        asm volatile("rdtsc" : EAX_EDX_RET(val, low, high));

        return EAX_EDX_VAL(val, low, high);
}

#define rdtscl(low)     ((low) = (u32)__native_read_tsc())
#define rdtscll(val)    ((val) = __native_read_tsc())

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
        u64 tsc_u64;
        u32 tsc_low;
        u64 tsc_a, tsc_b, result, mod;

        /* Read TSC 64bit contents */
        rdtscll(tsc_u64);
        printk("TSC 64bit: %#llx\n", tsc_u64);
        /* Read TSC low-order 32bit contents */
        rdtscl(tsc_low);
        printk("TSC low-order 32bit: %#x\n", tsc_low);

        /* CPU frequency */
        rdtscll(tsc_a);
        mdelay(1000);
        rdtscll(tsc_b);
        result = tsc_b - tsc_a;
        /* 64bit div */
        mod = do_div(result, 1 * 1024 * 1024);

        printk("CPU %d.%d MHz\n", result, mod);
        printk("Hello modules on BiscuitOS\n");

        return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Common Device driver on BiscuitOS");

在本例子中,使用内嵌汇编定义了 RDTSC 指令的两个接口函数 “rdtscll()” 和 “rdtscl()”. “rdtscl()” 函数只读取 “Time-stamp Counter” 寄存器的低 32bit 值,而 “rdtscll()” 可以读取 64bit 的值。例子中还通过计算 1s 内 “Time-stamp Counter” 的变化,以此计算 CPU 的频率,但这里使用 do_div() 函数用于处理 64bit 的除法。本例子在 BiscuitOS 中运行的结果如下:

cd lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # ls
rdtsc-x86-0.0.1.ko
/lib/modules/5.0.0/extra # insmod rdtsc-x86-0.0.1.ko 
rdtsc_x86_0.0.1: loading out-of-tree module taints kernel.
TSC 64bit: 0xbe7b729e4
TSC low-order 32bit: 0xe7cb5b2d
CPU 3141.0 MHz
Hello modules on BiscuitOS
/lib/modules/5.0.0/extra # 
/lib/modules/5.0.0/extra # cat /proc/cpuinfo | grep model
model		: 60
model name	: Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz
model		: 60
model name	: Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz
/lib/modules/5.0.0/extra #

加载模块之后,计算出 CPU 的频率是 3141 MHz, 与系统提供的 3.30GHz 有一点相差,但属于正常情况, 因此计算的数值有效.

通用例程(应用程序)
/*
 * RDTSC -- Read Time-Stamp Counter on userspace [x86]
 *
 * (C) 2020.02.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

/*
 * both i386 and x86_64 returns 64-bit value in edx:eax, but gcc's "A"
 * constraint has different meanings. For i386, "A" means exactly
 * edx:eax, while for x86_64 it doesn't mean rdx:rax or edx:eax. Instead,
 * it means rax *or* rdx.
 */
#ifdef CONFIG_X86_64
#define DECLARE_ARGS(val, low, high)    unsigned low, high
#define EAX_EDX_VAL(val, low, high)     ((low) | ((u64)(high) << 32))
#define EAX_EDX_ARGS(val, low, high)    "a" (low), "d" (high)
#define EAX_EDX_RET(val, low, high)     "=a" (low), "=d" (high)
#else
#define DECLARE_ARGS(val, low, high)    unsigned long long val
#define EAX_EDX_VAL(val, low, high)     (val)
#define EAX_EDX_ARGS(val, low, high)    "A" (val)
#define EAX_EDX_RET(val, low, high)     "=A" (val)
#endif

static __always_inline unsigned long long __native_read_tsc(void)
{
        DECLARE_ARGS(val, low, high);

        asm volatile("rdtsc" : EAX_EDX_RET(val, low, high));

        return EAX_EDX_VAL(val, low, high);
}

#define rdtscl(low)     ((low) = (u32)__native_read_tsc())
#define rdtscll(val)    ((val) = __native_read_tsc())

typedef unsigned long long u64;
typedef unsigned int u32;

int main()
{
        u64 tsc_u64;
        u32 tsc_low;
        u64 tsc_a, tsc_b;

        /* Read TSC 64bit contents */
        rdtscll(tsc_u64);
        printf("TSC 64bit: %#llx\n", tsc_u64);
        /* Read TSC low-order 32bit contents */
        rdtscl(tsc_low);
        printf("TSC low-order 32bit: %#x\n", tsc_low);

        /* CPU frequency */
        rdtscll(tsc_a);
        sleep(1);
        rdtscll(tsc_b);

        printf("CPU %lld MHz\n", (tsc_b - tsc_a) / 1000000);
        printf("Hello Application Demo on BiscuitOS.\n");
        return 0;
}

在本例子中,在用户空间定义两个接口用于从 “Time-Stamp Counter” 寄存器中读取当前的时间戳,rdtscl() 函数用于读取 “Time-Stamp Counter” 寄存器的低 32bit 的值,而 rdtscll() 函数则可以读取 “Time-Stamp Counter” 寄存器的 64bit 值。在程序中还通过延时 1s 来计算 CPU 的频率。本例子在 BiscuitOS 的运行情况如下:

~ # 
~ # rdtsc-app-x86-0.0.1 
TSC 64bit: 0x670b3d3e8
TSC low-order 32bit: 0x70c2ce2d
CPU 3293 MHz
Hello Application Demo on BiscuitOS.
~ # cat /proc/cpuinfo | grep model
model		: 60
model name	: Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz
model		: 60
model name	: Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz
~ # 

从计算的结果来看非常接近 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 中使用配置如下:

[*] Package  --->
    [*] Notifier mechanism on Kernel  --->
        -*-   MMU notifier (Kernel Portion)  --->
        [*]   MMU notifier (Userspace Portion)  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/MMU-notifier-0.0.1
BiscuitOS/output/linux-XXX-YYY/package/MMU-userspace-notifier-0.0.1

具体实践办法请参考:

HKC 计划 BiscuitOS 实践框架介绍 (MMU-notifier-0.0.1)

HKC 计划 BiscuitOS 实践框架介绍 (MMU-userspace-notifier-0.0.1)

通用例程 (内核部分)
/*
 * MMU notifier on BiscuitOS
 *
 * (C) 2020.10.06 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/sched/mm.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

/* MMU notifier */
#include <linux/mmu_notifier.h>
/* Current */
#include <linux/sched.h>

/* DD Platform Name */
#define DEV_NAME                "BiscuitOS"

static struct mmu_notifier BiscuitOS_notifier;
static struct mmu_notifier_range BiscuitOS_range;

static void BiscuitOS_mmu_release(struct mmu_notifier *mn,
                                                struct mm_struct *mm)
{
        printk("BiscuitOS notifier: release\n");
}

static int BiscuitOS_mmu_clear_flush_young(struct mmu_notifier *mn,
                struct mm_struct *mm, unsigned long start, unsigned long end)
{
        printk("BiscuitOS notifier: clear_flush_young\n");
        return 0;
}

static int BiscuitOS_mmu_clear_young(struct mmu_notifier *mn,
                struct mm_struct *mm, unsigned long start, unsigned long end)
{
        printk("BiscuitOS notifier: clear_young\n");
        return 0;
}

static int BiscuitOS_mmu_test_young(struct mmu_notifier *mn,
                        struct mm_struct *mm, unsigned long address)
{   
        printk("BiscuitOS notifier: test_young\n");
        return 0;
}

static void BiscuitOS_mmu_change_pte(struct mmu_notifier *mn,
                struct mm_struct *mm, unsigned long address, pte_t pte)
{
        printk("BiscuitOS notifier: change_pte\n");
}

static int BiscuitOS_mmu_invalidate_range_start(struct mmu_notifier *mn,
                                const struct mmu_notifier_range *range)
{
        printk("BiscuitOS notifier: invalidate_range_start.\n");
        return 0;
}

static void BiscuitOS_mmu_invalidate_range_end(struct mmu_notifier *mn,
                                const struct mmu_notifier_range *range)
{
        printk("BiscuitOS notifier: invalidate_range_end.\n");
}

static void BiscuitOS_mmu_invalidate_range(struct mmu_notifier *mn,
                struct mm_struct *mm, unsigned long start, unsigned long end)
{
        printk("BiscuitOS notifier: invalidate_range.\n");
}

static const struct mmu_notifier_ops BiscuitOS_mmu_notifer_ops = {
        .release     = BiscuitOS_mmu_release,
        .clear_young = BiscuitOS_mmu_clear_young,
        .test_young  = BiscuitOS_mmu_test_young,
        .change_pte  = BiscuitOS_mmu_change_pte,
        .clear_flush_young = BiscuitOS_mmu_clear_flush_young,
        .invalidate_range  = BiscuitOS_mmu_invalidate_range,
        .invalidate_range_start = BiscuitOS_mmu_invalidate_range_start,
        .invalidate_range_end   = BiscuitOS_mmu_invalidate_range_end,
};

static int BiscuitOS_mmap(struct file *filp, struct vm_area_struct *vma)
{
        struct mm_struct *mm = filp->private_data;
        pte_t pte;

        /* Trigger invalidate range [range, start, end] */
        mmu_notifier_range_init(&BiscuitOS_range, mm,
                vma->vm_start & PAGE_MASK, vma->vm_end & PAGE_MASK);
        mmu_notifier_invalidate_range_start(&BiscuitOS_range);
        mmu_notifier_invalidate_range_end(&BiscuitOS_range);

        /* Trigger clear_flush_young */
        mmu_notifier_clear_flush_young(mm, vma->vm_start, vma->vm_end);

        /* Trigger clear_young */
        mmu_notifier_clear_young(mm, vma->vm_start, vma->vm_end);

        /* Trigger test_young */
        mmu_notifier_test_young(mm, vma->vm_start);

        /* Trigger change pte */
        mmu_notifier_change_pte(mm, vma->vm_start, pte);

        /* Trigger realease */
        mmu_notifier_release(mm);

        return 0;
}

static int BiscuitOS_open(struct inode *inode, struct file *file)
{
        struct mm_struct *mm = get_task_mm(current);

        file->private_data = mm;
        /* mmu notifier initialize */
        BiscuitOS_notifier.ops = &BiscuitOS_mmu_notifer_ops;
        /* mmu notifier register */
        mmu_notifier_register(&BiscuitOS_notifier, mm);

        return 0;
}

static int BiscuitOS_release(struct inode *inode, struct file *file)
{
        mmu_notifier_unregister(&BiscuitOS_notifier, current->mm);
        return 0;
}

/* file operations */
static struct file_operations BiscuitOS_fops = {
        .owner          = THIS_MODULE,
        .open           = BiscuitOS_open,
        .mmap           = BiscuitOS_mmap,
        .release        = BiscuitOS_release,
};

/* Misc device driver */
static struct miscdevice BiscuitOS_drv = {
        .minor  = MISC_DYNAMIC_MINOR,
        .name   = DEV_NAME,
        .fops   = &BiscuitOS_fops,
};

/* Module initialize entry */
static void __init BiscuitOS_init(void)
{
        /* Register Misc device */
        misc_register(&BiscuitOS_drv);

        printk("Hello modules on BiscuitOS\n");
}

device_initcall(BiscuitOS_init);

在本例子中,例子通过 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 函数.
通用例程 (用户空间部分)
/*
 * BiscuitOS MMU notifier on Userspace
 *
 * (C) 2020.10.06 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

/* PATH for device */
#define DEV_PATH                "/dev/BiscuitOS"

int main()
{
        char *memory;
        int fd;

        /* open device */
        fd = open(DEV_PATH, O_RDWR);
        if (fd < 0) {
                printf("ERROR: Can't open %s\n", DEV_PATH);
                return -1;
        }

        /* mmap */
        memory = mmap(NULL, 0x100000, PROT_READ | PROT_WRITE,
                                                MAP_SHARED, fd, 0);
        if (!memory) {
                printf("ERROR: mmap faied.\n");
                goto out;
        }

        /* un-mmap */
        munmap(memory, 0x100000);

        /* Normal ending */
        close(fd);
        return 0;
out:
        close(fd);
        return -1;
}

在本例子用户空间部分的程序,首先通过 open() 函数打开了 “/dev/BiscuitOS” 节点,然后调用调用 mmap() 函数进行映射操作,映射完毕之后再通过 munmap() 函数接触映射。函数最后调用 close() 关闭节点。本例子纯粹是为内核部分的代码创造独立进程的条件. 两个部分在 BiscuitOS 中运行的情况如下:

~ # MMU-userspace-notifier-0.0.1 
BiscuitOS notifier: invalidate_range_start.
BiscuitOS notifier: invalidate_range.
BiscuitOS notifier: invalidate_range_end.
BiscuitOS notifier: clear_flush_young
BiscuitOS notifier: clear_young
BiscuitOS notifier: test_young
BiscuitOS notifier: change_pte
BiscuitOS notifier: release
~ #

从运行的结果可以看出,指定的消息已经传递成功. 开发者可以利用该机制进行页表操作时候通知进程内的其他功能模块.


Platform Simple Device Driver

这是一个最简单的 Platform 驱动,驱动实现向系统注册一个 Platform 驱动.

BiscuitOS 配置

在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] Platofrm: Device Driver and Application  --->
        [*] Platform Simple Module  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/platform_simple_module-0.0.1

具体实践办法请参考:

通用例程
/*
 * Simple Platform Device Driver
 *
 * (C) 2019.10.01 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>

/* LDD Platform Name */
#define DEV_NAME        "BiscuitOS"

static struct platform_device *pdev;

/* Probe: (LDD) Initialize Device */
static int BiscuitOS_probe(struct platform_device *pdev)
{
        /* Device Probe Procedure */
        printk("BiscuitOS Porbeing...\n");

        return 0;
}

/* Remove: (LDD) Remove Device (Module) */
static int BiscuitOS_remove(struct platform_device *pdev)
{
        /* Device Remove Procedure */
        printk("BiscuitOS Removing...\n");

        return 0;
}

/* Platform Driver Information */
static struct platform_driver BiscuitOS_driver = {
        .probe    = BiscuitOS_probe,
        .remove   = BiscuitOS_remove,
        .driver = {
                .owner  = THIS_MODULE,
                .name   = DEV_NAME,
        },
};

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
        int ret;

        ret = platform_driver_register(&BiscuitOS_driver);
        if (ret) {
                printk("Error: Platform driver register.\n");
                return -EBUSY;
        }

        pdev = platform_device_register_simple(DEV_NAME, 1, NULL, 0);
        if (IS_ERR(pdev)) {
                printk("Error: Platform device register\n");
                return PTR_ERR(pdev);
        }
        return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
        platform_device_unregister(pdev);
        platform_driver_unregister(&BiscuitOS_driver);
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Simple Platform Device Driver");

在本例子中,通过调用 platform_driver_register/platform_device_register_simple 注册了一个 platform 的设备和驱动,其中驱动通过 BiscuitOS_driver 数据进行描述,在驱动描述中提供了驱动的 probe 和 remove 两个方法。当驱动 KO 被加载的时候,系统 Platform 总线就会在查找并调用该驱动的 probe 函数,当驱动卸载的时候,Platform 总线就会调用该驱动的 remove 函数。本实例在 BiscuitOS 的运行情况如下:

cd lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # ls
platform_simple_module-0.0.1.ko
/lib/modules/5.0.0/extra # insmod platform_simple_module-0.0.1.ko 
platform_simple_module_0.0.1: loading out-of-tree module taints kernel.
BiscuitOS Porbeing...
/lib/modules/5.0.0/extra # 
/sys/bus/platform # cd /sys/bus/platform/devices/BiscuitOS.1/
/sys/devices/platform/BiscuitOS.1 # 
/sys/devices/platform/BiscuitOS.1 # ls
driver           modalias         subsystem
driver_override  power            uevent
/sys/devices/platform/BiscuitOS.1 #

当驱动加载的时候,驱动的 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 中使用配置如下:

[*] Package  --->
    [*] Bitmap  --->
        [*] Emulate PID Allocating and Releasing  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/emulate-pid-0.0.1

具体实践办法请参考:

通用例程
/*
 * Bitmap: Emulate PID allocating and Releasing on BiscuitOS
 *
 * (C) 2020.10.11  Meijusan <20741602@qq.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include<linux/slab.h>
#include <linux/err.h>
#include <linux/file.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/mm.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/kthread.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <linux/mutex.h>
#include <linux/gfp.h>
#include <linux/pid.h>
#include <linux/delay.h>
#include <linux/random.h>

#define PID_BITMAP_MAX 4096

/*
 * pid range 0~4096,this is a example
 */

static DECLARE_BITMAP( pid_bitmap_mask, PID_BITMAP_MAX);
static DEFINE_MUTEX(pid_bitmap_lock);

int pid_produce_thread(void *arg)
{
        while (!kthread_should_stop())  {
                int pid;

                mutex_lock(&pid_bitmap_lock);
                if( bitmap_full(pid_bitmap_mask, PID_BITMAP_MAX) ) {
                        mutex_unlock(&pid_bitmap_lock);
                        msleep(1000);
                        continue;
                }

                pid = find_first_zero_bit(pid_bitmap_mask, PID_BITMAP_MAX);
                if ( pid >= PID_BITMAP_MAX) {
                        printk("bitmap is full\n");
                        mutex_unlock(&pid_bitmap_lock);
                        msleep(1000);
                        continue;
                }
                printk("threadid %d create  pid: %d \n",current->pid,  pid);
                set_bit(pid, pid_bitmap_mask);
                mutex_unlock(&pid_bitmap_lock);
        }
        return 0;
}

static int pid_consume_thread(void *dummy)
{
        while (!kthread_should_stop()) {

                /*range 0 ~PID_BITMAP_MAX*/
                int pid = prandom_u32()%(PID_BITMAP_MAX + 1) ;

                mutex_lock(&pid_bitmap_lock);
                clear_bit(pid, pid_bitmap_mask);
                mutex_unlock(&pid_bitmap_lock);
                printk("threadid %d remove  pid: %d \n",current->pid,  pid);
                msleep(1000);
        }
        return 0;
}

static struct task_struct *pid_produce_task = NULL;
static struct task_struct *pid_consume_task = NULL;


/* Module initialize entry */
static int __init pid_bitmap_demo_init(void)
{
        /* default all pid already exist */
        bitmap_fill(pid_bitmap_mask, PID_BITMAP_MAX);

        pid_produce_task = kthread_run(pid_produce_thread, NULL, "pidconsujme");
        if (IS_ERR(pid_produce_task)) {
                int err = PTR_ERR(pid_produce_task);
                printk("failed to start the kauditd thread (%d)\n", err);
        }

        pid_consume_task = kthread_run(pid_consume_thread, NULL, "testbitmap");
        if (IS_ERR(pid_consume_task)) {
                int err = PTR_ERR(pid_consume_task);
                printk("failed to start the kauditd thread (%d)\n", err);
        }

        return 0;
}

/* Module exit entry */
static void __exit pid_bitmap_demo_exit(void)
{
        if(pid_produce_task)
                kthread_stop(pid_produce_task);
        if(pid_consume_task)
                kthread_stop(pid_consume_task);
}

module_init(pid_bitmap_demo_init);
module_exit(pid_bitmap_demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Meijusan <20741602@qq.com>");
MODULE_DESCRIPTION("Emulate PID allocating and Releasing on BiscuitOS");

独立代码在 BiscuitOS 中运行的结果如下:

~ # cd lrandom: fast init done
~ # cd lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # ls
emulate-pid-0.0.1.ko
/lib/modules/5.0.0/extra # insmod emulate-pid-0.0.1.ko 
emulate_pid_0.0.1: loading out-of-tree module taints kernel.
threadid 1140 remove  pid: 521 
/lib/modules/5.0.0/extra # threadid 1138 create  pid: 521 
threadid 1140 remove  pid: 4073 
threadid 1140 remove  pid: 3235 
threadid 1138 create  pid: 3235 
threadid 1138 create  pid: 4073 
threadid 1140 remove  pid: 960 
threadid 1138 create  pid: 960 
threadid 1140 remove  pid: 1575 
threadid 1138 create  pid: 1575 
threadid 1140 remove  pid: 286 
threadid 1138 create  pid: 286 
threadid 1140 remove  pid: 2280 
threadid 1138 create  pid: 2280 
threadid 1140 remove  pid: 4061 
threadid 1140 remove  pid: 435 
threadid 1138 create  pid: 435 
threadid 1138 create  pid: 4061 
threadid 1140 remove  pid: 2294


LC-trie Protocol

功能说明
1.1 为了方便实验和更好的理解 trie, 本模块从路由 LC-trie 协议, 去除了路由
    规则, trie 的扩展/压缩等复杂模块简化而成
1.2 主要完成一个 32bits 的字典树 (网络搜索参考字典树的逻辑及下文的 "实例图解")
1.3 本模块功能包括: 插入节点, 移除节点和节点查找,节点可以附带具体业务对象数据
1.4 本模块包含基础插入/移除和查找 api, 以及一个使用实例

2. 调试环境信息: linux 5.0.0
3. 运行输出信息: 为了便于理解, 本模块在 init 函数中执行实例程序,并输出人性化
   的调试信息,如下 [运行输出信息]
4. 代码说明

   bits32_trie_new.c:    节点的创建, 内存分配, 本例分配内存统一使用
                         kmalloc/kfree, 如需优化,自行修改
   bits32_trie_debug.c:  调试信息输出, 针对trie结构体进行逐层打印
   bits32_trie_insert.c: 节点插入代码
   bits32_trie_lookup.c: 节点查找代码
   bits32_trie_remove.c: 节点删除代码
   bits32_trie.c:        模块初始化代码及测试实例

代码贡献者: Shaobin shaobin.huang@kernelworker.net

BiscuitOS 配置

在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] Bitmap  --->
        [*] LC-trie Protocol  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/LC-trie-0.0.1

具体实践办法请参考:

通用例程
.
├── LC-trie-0.0.1
│   ├── bits32_trie.c
│   ├── bits32_trie_debug.c
│   ├── bits32_trie_debug.h
│   ├── bits32_trie.h
│   ├── bits32_trie_insert.c
│   ├── bits32_trie_insert.h
│   ├── bits32_trie_lookup.c
│   ├── bits32_trie_lookup.h
│   ├── bits32_trie_new.c
│   ├── bits32_trie_new.h
│   ├── bits32_trie_remove.c
│   ├── bits32_trie_remove.h
│   ├── Kconfig
│   ├── Makefile
│   └── readme.txt
├── Makefile
└── README.md

1 directory, 17 files

该代码在 BiscuitOS 上运行的情况如下:

~ # cd lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # ls
C-trie-0.0.1.ko
/lib/modules/5.0.0/extra # insmod C-trie-0.0.1.ko 
C_trie_0.0.1: loading out-of-tree module taints kernel.
bits32_trie_init:[16 - 4]
~~~~~~~~  start insert  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bits32_trie_init insert leaf[0x12345678] data[0x0]
bits32_trie_init insert leaf[0x87654321] data[0x1]
bits32_trie_init insert leaf[0x11111111] data[0x2]
bits32_trie_init insert leaf[0x22222222] data[0x3]
~~~~~~~~   end insert   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--------------------------------------------------------------
~~~~~~~~   now let's look the trie view ~~~~~~~~~~~~~~~~~
tnode[0x0]:pos[0x1f]bits[0x1]cindex[0x0]
- tnode[0x0]:pos[0x1d]bits[0x1]cindex[0x0]
- - tnode[0x10000000]:pos[0x19]bits[0x1]cindex[0x0]
- - - leaf[0x11111111]:pos[0x0]bits[0x0]data[0x2]cindex[0x0]
- - - leaf[0x12345678]:pos[0x0]bits[0x0]data[0x0]cindex[0x1]
- - leaf[0x22222222]:pos[0x0]bits[0x0]data[0x3]cindex[0x1]
- leaf[0x87654321]:pos[0x0]bits[0x0]data[0x1]cindex[0x1]
~~~~~~~~   trie view end, it's as what you think??  ~~~~~
--------------------------------------------------------------
~~~~~~~~   test lookup  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bits32_trie_init lookup[0x11111111]index[0x2]
bits32_trie_init it seems ok, found leaf->key[0x11111111]
~~~~~~~~  test remove  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bits32_trie_init remove[0x11111111]index[0x2]
bits32_trie_init it seems ok, found no leaf->key[0x11111111] after del
bits32_trie_init remove[12345678]index[0]
bits32_trie_init it seems ok, found no leaf->key[0x12345678] after del
~~~~~~~~  now let's check the trie if it same as what you think  ~~~~
tnode[0x0]:pos[0x1f]bits[0x1]cindex[0x0]
- tnode[0x0]:pos[0x1d]bits[0x1]cindex[0x0]
- - leaf[0x22222222]:pos[0x0]bits[0x0]data[0x3]cindex[0x1]
- leaf[0x87654321]:pos[0x0]bits[0x0]data[0x1]cindex[0x1]
bits32_trie_init finish
insmod (1137) used greatest stack depth: 6300 bytes left
/lib/modules/5.0.0/extra # 

             bit31  ->                      bit0
0x12345678   00010010 00110100 01010110 01111000
0x87654321   10000111 01100101 01000011 00100001
0x11111111   00010001 00010001 00010001 00010001
0x22222222   00100010 00100010 00100010 00100010


                       tp
                       |
                       v
              0x12345678
(00010010 00110100 01010110 01111000)

                                           tp
                                           |
                                           v
                       ------------------ 0x0 --------------------
                       |                                         |
                       v                                         v
              0x12345678                                0x87654321
(00010010 00110100 01010110 01111000)    (10000111 01100101 01000011 00100001)


                                                               tp
                                                               |
                                                               v
                                           ------------------ 0x0 --------------------
                                           |                                         |
                                           v                                         v
                                  0x10000000                                0x87654321
                    (00010000 00000000 00000000 00000000)    (10000111 01100101 01000011 00100001)
                                           |
                                           v
                       -------------------- ----------------------
                       |                                         |
                       v                                         v
              0x12345678                                0x11111111
(00010010 00110100 01010110 01111000)    (00010001 00010001 00010001 00010001)
                                                                                    tp
                                                                                    |
                                                                                    v
                                                                ------------------ 0x0 --------------------
                                                                |                                         |
                                                                v                                         v
                                                               0x0                                   0x87654321
                                                  (00000000 00000000 00000000 00000000)       (10000111 01100101 01000011 00100001)
                                                                |
                                                                v
                                           --------------------  ---------------------
                                           |                                         |
                                           v                                         v
                                     0x10000000                                 0x22222222
                    (00000001 00000000 00000000 00000000)    (00100010 00100010 00100010 00100010)
                                           |
                                           v
                       -------------------- ----------------------
                       |                                         |
                       v                                         v
                   0x11111111                                0x12345678
     (00010001 00010001 00010001 00010001)    (00010010 00110100 01010110 01111000)


Anonymous mmap on Userpsace

代码核心功能是在用户空间通过匿名映射的方式映射一段虚拟机内存使用。

BiscuitOS 配置

在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] System Call  --->
        [*] sys_mmap2  --->
            [*] Anonymous mmap on Userspace  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/BiscuitOS-Anonymous-mmap-Userspace-0.0.1

具体实践办法请参考:

HKC 计划 BiscuitOS 实践框架介绍

通用例程
/*
 * Anonymous mmap from Userspace on BiscuitOS
 *
 * (C) 2020.10.24 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>

/* The size for anonymous mmap */
#define MMAP_SIZE               4096

int main()
{
        char *base;

        /* Anonymous mmap */
        base = mmap(NULL,
                    MMAP_SIZE,
                    PROT_READ | PROT_WRITE,
                    MAP_SHARED | MAP_ANONYMOUS,
                    -1,
                    0);
        if (!base) {
                printf("ERROR: Mmap anonymous failed.\n");
                return -1;
        }

        /* Use */
        sprintf(base, "BiscuitOS");
        printf("=> %s [%#lx]\n", base, (unsigned long)base);

        /* Un-mmap */
        munmap(base, MMAP_SIZE);

        return 0;
}

例子的实现很简单,通过 mmap() 函数映射一段内存,在传入参数的时候,传入 MAP_ANONYMOUS 标志就可以匿名映射一段内存使用。不使用的时候使用 munmap() 函数进行解除映射。例子在 BiscuitOS 中运行的情况如下:

~ # BiscuitOS-Anonymous-mmap-Userspace-0.0.1 
=> BiscuitOS [0xb7d91000]
~ #

BiscuitOS 运行之后,在用户空间直接运行 BiscuitOS-Anonymous-mmap-Userspace-0.0.1 应用程序,运行之后打印一段字符串并打印字符串的地址。结果符合预期.


Anonymous mmap on kernel

本例子用于介绍在内核空间通过匿名映射方式,映射一段可用内存使用。

BiscuitOS 配置

在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] System Call  --->
        [*] sys_mmap2  --->
            [*] Anonymous mmap on Kernel  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/BiscuitOS-Anonymous-mmap-Kernel-0.0.1

具体实践办法请参考:

HKC 计划 BiscuitOS 实践框架介绍

通用例程
/*
 * Anonymous mmap from kernel on BiscuitOS
 *
 * (C) 2020.10.24 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/mman.h>

/* The size for mmaping */
#define MMAP_SIZE               PAGE_SIZE
/* The address fro mamping */
static char *mmap_base;

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
        /* Anonymous mmaping */
        mmap_base = (char *)vm_mmap(
                            NULL,
                            0,
                            MMAP_SIZE,
                            PROT_READ | PROT_WRITE,
                            MAP_SHARED | MAP_ANONYMOUS,
                            0);
        if (IS_ERR((void *)mmap_base)) {
                printk("ERROR: Anonymous mmap failed.\n");
                return -ENOMEM;
        }

        /* Use */
        sprintf(mmap_base, "BiscuitOS");
        printk("=> %s [%#lx]\n", mmap_base, (unsigned long)mmap_base);

        return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
        /* Un-mmap */
        vm_munmap((unsigned long)mmap_base, MMAP_SIZE);
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Anonymous kenrel mmap on BiscuitOS");

本例子在驱动初始化函数中,调用 vm_mmap() 函数并传入 MAP_ANONYMOUS 标志,以此在内核中匿名映射一段内存,并使用这段内存,使用完毕之后调用 vm_munmap() 函数解除这部分内存的映射. 该例子在 BiscuitOS 中运行的结果如下:

/lib/modules/5.0.0/extra # ls
BiscuitOS-Anonymous-mmap-Kernel-0.0.1.ko
/lib/modules/5.0.0/extra # insmod BiscuitOS-Anonymous-mmap-Kernel-0.0.1.ko 
BiscuitOS_Anonymous_mmap_Kernel_0.0.1: loading out-of-tree module taints kernel.
=> BiscuitOS [0xb7f08000]
/lib/modules/5.0.0/extra # 

加载模块之后,模块初始化是调用匿名映射相关的函数,然后在匿名映射的内存上写入一段字符串,并打印这段字符串和匿名映射内存的地址. 结果符合预期.


Anonymous File

本例子用于介绍创建一个匿名文件的流程。例子包含了内核空间和用户空间的实现。

BiscuitOS 配置

在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] VFS (BiscuitOS+)  --->
        -*- Anonymous File (Kernel Part+)  --->
        [*] Anonymous file (Userspace Part+)  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/BiscuitOS-Anonymous-file-userspace-0.0.1
BiscuitOS/output/linux-XXX-YYY/package/BiscuitOS-Anonymous-file-kernel-0.0.1

具体实践办法请参考:

> HKC 计划 BiscuitOS 实践框架介绍

> HKC 计划 BiscuitOS 实践框架介绍

内核部分代码
/*
 * Anonymous file on BiscuitOS
 *
 * (C) 2020.10.06 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/anon_inodes.h>
#include <linux/errno.h>
#include <linux/file.h>

/* DD Platform Name */
#define DEV_NAME                "BiscuitOS"
/* IOCTL CMD */
#define BISCUITOS_IO            0xAE
#define BISCUITOS_SET           _IO(BISCUITOS_IO, 0x00)
#define BISCUITOS_GET           _IO(BISCUITOS_IO, 0x01)

/* File operation for anonymous file */
static struct file_operations BiscuitOS_anonymous_fops;

/* Create anonymous file */
static int BiscuitOS_anonymous_file(void)
{
        struct file *file;
        int fd;

        /* Get an unused fd */
        fd = get_unused_fd_flags(O_CLOEXEC);
        if (fd < 0) {
                printk("ERROR[%d]: Get unused fd failed.\n", fd);
                return fd;
        }

        /* Create anonymous file */
        file = anon_inode_getfile("BiscuitOS-anonymous",
                                &BiscuitOS_anonymous_fops, NULL, O_RDWR);
        if (IS_ERR(file)) {
                printk("ERROR[%ld]: Create anonymous file failed.\n",
                                                PTR_ERR(file));
                put_unused_fd(fd);
                return PTR_ERR(file);
        }

        /* Bind fd and file */
        fd_install(fd, file);
        return fd;
}

/* ioctl */
static long BiscuitOS_ioctl(struct file *filp,
                        unsigned int ioctl, unsigned long arg)
{
        switch (ioctl) {
        case BISCUITOS_SET:
                return BiscuitOS_anonymous_file();
        case BISCUITOS_GET:
                printk("IOCTL: BISCUITOS_GET.\n");
                break;
        default:
                break;
        }
        return 0;
}

/* file operations */
static struct file_operations BiscuitOS_fops = {
        .owner          = THIS_MODULE,
        .unlocked_ioctl = BiscuitOS_ioctl,
};

/* Misc device driver */
static struct miscdevice BiscuitOS_drv = {
        .minor  = MISC_DYNAMIC_MINOR,
        .name   = DEV_NAME,
        .fops   = &BiscuitOS_fops,
};

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
        /* Register Misc device */
        misc_register(&BiscuitOS_drv);
        return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
        /* Un-Register Misc device */
        misc_deregister(&BiscuitOS_drv);
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("BiscuitOS Anonymous file");

内核部分的代码首先通过调用 get_unused_fd_flags() 获得当前进程一个没有使用的文件句柄,然后再调用 anon_inode_getfile() 函数获得一个匿名的 inode,并将 BiscuitOS_anonymous_fops 文件操作绑定到这个 inode 上面,最后调用 fd_install() 函数将 fd 和 inode 绑定在一起,这样匿名文件就创建完毕.

用户空间代码
/*
 * Anonymous file on BiscuitOS (Userspace Part+)
 *
 * (C) 2020.10.24 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <linux/ioctl.h>
#include <sys/ioctl.h>
#include <sys/types.h>

/* PATH for devnode */
#define BISCUITOS_NODE          "/dev/BiscuitOS"
/* IOCTL CMD */
#define BISCUITOS_IO            0xAE
#define BISCUITOS_SET           _IO(BISCUITOS_IO, 0x00)
#define BISCUITOS_GET           _IO(BISCUITOS_IO, 0x01)

int main()
{
        int anonymous_fd;
        int fd;

        /* Open BiscuitOS node */
        fd = open(BISCUITOS_NODE, O_RDWR);
        if (fd < 0) {
                printf("ERROR[%d]: open %s failed.\n", fd, BISCUITOS_NODE);
                return fd;
        }

        /* Create Anonymous file by ioctl() */
        anonymous_fd = ioctl(fd, BISCUITOS_SET, (unsigned long)0);
        if (anonymous_fd < 0) {
                printf("ERROR[%d]: Anonymous file failed.\n", anonymous_fd);
                return anonymous_fd;
        }

        /* Anonymous fd */
        printf("Anonymous file-FD: %d\n", anonymous_fd);

        /* close */
        close(fd);

        return 0;
}

用户空间部分的代码比较简单,通过 ioctl() 函数传递 “BISCUITOS_SET” 命令告诉内存创建一个匿名文件,并返回匿名文件的文件句柄. 该例子在 BiscuitOS 完整运行情况如下:

~ # 
~ # cd lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # ls
BiscuitOS-Anonymous-file-kernel-0.0.1.ko
/lib/modules/5.0.0/extra # insmod BiscuitOS-Anonymous-file-kernel-0.0.1.ko 
BiscuitOS_Anonymous_file_kernel_0.0.1: loading out-of-tree module taints kernel.
/lib/modules/5.0.0/extra # 
/lib/modules/5.0.0/extra # BiscuitOS-Anonymous-file-userspace-0.0.1 
Anonymous file-FD: 4
/lib/modules/5.0.0/extra # 
/lib/modules/5.0.0/extra #

模块安装完毕之后,运行对应的应用程序,此时获得的匿名文件句柄为 4. 文件句柄 0,1,2 分别为标志输入/输出/错误, 句柄 3 为当前打开的文件,那么匿名文件句柄为 4 符合预期.


Kernel Read from Userspace file

实例的作用是内核读取用户空间的文件。

BiscuitOS 配置

在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] VFS (BiscuitOS+)  --->
        [*] Kernel read Userspace file  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/BiscuitOS-Kernel-read-Userspace-file-0.0.1

具体实践办法请参考:

HKC 计划 BiscuitOS 实践框架介绍

通用例程
/*
 * Kernel read Userspace file on BiscuitOS
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <asm/uaccess.h>

/* The path for Userspace file */
#define BISCUITOS_FILE          "/proc/meminfo"
/* Buffer length */
#define MAX_BUFFER_SIZE         256

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
        char buffer[MAX_BUFFER_SIZE];
        struct file *filp = NULL;
        mm_segment_t fs;
        loff_t pos = 0;

        /* Open Userspace file */
        filp = filp_open(BISCUITOS_FILE, O_RDWR, 0644);
        if (IS_ERR(filp)) {
                printk("ERROR[%ld]: Open %s failed.\n", PTR_ERR(filp),
                                                        BISCUITOS_FILE);
                return PTR_ERR(filp);
        }

        fs = get_fs();
        set_fs(KERNEL_DS);

        /* Read from Userspace file */
        pos = 0;
        kernel_read(filp, buffer, sizeof(buffer), &pos);
        printk("BiscuitOS Read Contents:\n%s\n", buffer);

        /* Close Userspace file */
        filp_close(filp, NULL);
        set_fs(fs);

        return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Read Userspace file on BiscuitOS");

例子的实现很简单,与用户空间文件的操作类似,首先使用 filp_open() 函数打开指定文件,然后调用 get_fs()/set_fs() 设置正确的段权限,最后调用 kernel_read() 函数用户空间文件内存读取到 buffer 里,并使用 printk 将 buffer 里面的内容输出. 操作完毕之后调用 filp_close() 关闭文件并回复 FS 段. 该例子在 BiscuitOS 中运行的情况如下:

~ # cd lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # 
/lib/modules/5.0.0/extra # ls
BiscuitOS-Kernel-read-Userspace-file-0.0.1.ko
/lib/modules/5.0.0/extra # insmod BiscuitOS-Kernel-read-Userspace-file-0.0.1.ko 
BiscuitOS_Kernel_read_Userspace_file_0.0.1: loading out-of-tree module taints kernel.
BiscuitOS Read Contents:
MemTotal:         504144 kB
MemFree:          334208 kB
MemAvailable:     331916 kB
Buffers:             112 kB
Cached:             1936 kB
SwapCached:            0 kB
Active:             1864 kB
Inactive:            312 kB
Active(anon):        136 kB

BiscuitOS 运行之后,安装对应的模块,模块运行之后将从 “/proc/meminfo” 中读取指定长度的内容,并输出这些内容。运行的结果与预期一致.


Kernel write to Userspace file

本示例的作用是内核向用户空间的文件写入指定的内容.

BiscuitOS 配置

在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] VFS (BiscuitOS+)  --->
        [*] Kernel write Userspace file  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/BiscuitOS-Kernel-write-Userspace-file-0.0.1

具体实践办法请参考:

HKC 计划 BiscuitOS 实践框架介绍

通用例程
/*
 * Kernel write to Userspace file on BiscuitOS
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <asm/uaccess.h>

/* The path for Userspace file */
#define BISCUITOS_FILE          "/tmp/BiscuitOS.info"
/* Buffer length */
#define MAX_BUFFER_SIZE         256

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
        char buffer[MAX_BUFFER_SIZE];
        struct file *filp = NULL;
        mm_segment_t fs;
        loff_t pos = 0;

        /* Open Userspace file */
        filp = filp_open(BISCUITOS_FILE, O_RDWR | O_CREAT, 0644);
        if (IS_ERR(filp)) {
                printk("ERROR[%ld]: Open %s failed.\n", PTR_ERR(filp),
                                                        BISCUITOS_FILE);
                return PTR_ERR(filp);
        }

        fs = get_fs();
        set_fs(KERNEL_DS);

        /* Write data into Userspace file */
        sprintf(buffer, "BiscuitOS");
        buffer[strlen(buffer)] = '\n';
        pos = 0;
        kernel_write(filp, buffer, strlen(buffer) + 1, &pos);

        /* Close Userspace file */
        filp_close(filp, NULL);
        set_fs(fs);

        return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Write Userspace file on BiscuitOS");

例子的实现很简单,与用户空间文件的操作类似,首先使用 filp_open() 函数打开指定文件,然后调用 get_fs()/set_fs() 设置正确的段权限,最后调用 kernel_write() 函数将 buffer中的内容写入到用户空间文件,用户空间的文件位于 “/tmp/BiscuitOS.info”. 操作完毕之后调用 filp_close() 关闭文件并回复 FS 段. 该例子在 BiscuitOS 中运行的情况如下:

~ # cd lib/modules/5.0.0/extra/
/lib/modules/5.0.0/extra # ls
BiscuitOS-Kernel-write-Userspace-file-0.0.1.ko
/lib/modules/5.0.0/extra # insmod BiscuitOS-Kernel-write-Userspace-file-0.0.1.ko
BiscuitOS_Kernel_write_Userspace_file_0.0.1: loading out-of-tree module taints kernel.
insmod (1184) used greatest stack depth: 6500 bytes left
/lib/modules/5.0.0/extra # 
/lib/modules/5.0.0/extra # cat /tmp/BiscuitOS.info 
BiscuitOS

BiscuitOS 运行之后,将模块进行安装,安装过程中实例将字符串 “BiscuitOS” 写入到用户空间 “/tmp/BiscuitOS.info” 文件中,模块安装完毕之后,查看 “/tmp/BiscuitOS.info” 文件的内容,内容为 “BiscuitOS”, 结果与预期一致.


Memory Hotplug in Code

本实例用于介绍如何在内核中使用代码进行内存的 “热插” 操作. 安装完模块之后会在 “/sys/device/system/memory/” 目录下生成指定的 memoryX 目录,该目录下存储新热插入内存的信息. 在进行热插拔之前,需要配置内核打开热插拔功能,配置如下:

  Memory Management options  --->
        Memory model (Sparse Memory)  --->
    [*] Allow for memory hot-add

配置完内核之后,再将内核宏 SECTION_SIZE_BITS 设置为 27, 以此表示每个 MEMORY Section 的长度为 128 MiB. 其次如果是 ARM64 架构,那么需要在 arm64_memblock_init() 函数中添加 “memblock_remove(0x50000000, 0x8000000)” 语句将对应的内存区从内核中移除; 如果是 x64 架构,那么需要在 CMDLINE 中添加字段 “memmap=128MiB@0x18000000”. 修改完毕之后重新编译内核.

BiscuitOS 配置

在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] MMU (BiscuitOS+)  --->
        [*] Memory Hotplug in C  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/BiscuitOS-Memory-Hotplug-in-C-0.0.1

具体实践办法请参考:

HKC 计划 BiscuitOS 实践框架介绍

通用例程
/*
 * Memory Hotplug in Code on BiscuitOS
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

/*
 * Memory region for Hotplugging.
 * BTW, The default Block size as 0x8000000, not 2 Gig.
 *
 * On Aarch, we reserved a memory region from 0x50000000 to 0x58000000 on
 * MEMBLOCK allocator that invoke "memblock_reserve(0x50000000, 0x8000000)"
 * on "arm64_memblock_init()".
 *
 * On x86_64, we can use "memmap=" on CMDLINE to reserved a memory
 * region. The range of memory region from 0x18000000 to 0x20000000, which 
 * descrbe as "memmap=0x128M$0x5000000".
 */

/* NUMA NODE */
static int nid = 0;

#ifdef __aarch64__
#define BISCUITOS_MEMORY_BASE           0x50000000
#else // X64
#define BISCUITOS_MEMORY_BASE           0x18000000
#endif
#define BISCUITOS_MEMORY_SIZE           0x8000000

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
#ifdef CONFIG_MEMORY_HOTPLUG
        /* Add into /sys/device/system/memory/memoryX */
        add_memory(nid, BISCUITOS_MEMORY_BASE, BISCUITOS_MEMORY_SIZE);
#endif
        printk("Hello BiscuitOS :)\n");
        return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Memory Hotplug in Code on BiscuitOS");

例子很简单,通过调用 add_memory() 函数将 BISCUITOS_MEMORY_BASE 处长度为 BISCUITOS_MEMORY_SIZE 的物理内存插入到系统内部。如果插入成功,系统会在 “/sys/devices/system/memory/” 目录下创建热插入内存的信息。该例子在 BiscuitOS 中运行情况如下:

~ # cd /sys/devices/system/memory/
/sys/devices/system/memory # ls
auto_online_blocks  memory11            power
block_size_bytes    memory8             soft_offline_page
hard_offline_page   memory9             uevent
/sys/devices/system/memory # cat /proc/iomem 
40000000-4fffffff : System RAM
  40080000-41a1ffff : Kernel code
  41a20000-41baffff : reserved
  41bb0000-41e3cfff : Kernel data
  48000000-480fffff : reserved
58000000-5fffffff : System RAM
  5d600000-5d7fffff : reserved
  5d926000-5ddfffff : reserved
  5def7000-5def7fff : reserved
  5def8000-5df25fff : reserved
  5df28000-5df28fff : reserved
  5df29000-5df3bfff : reserved
  5df3c000-5df43fff : reserved
  5df44000-5fffffff : reserved
4010000000-401fffffff : PCI ECAM

/sys/devices/system/memory # 
/sys/devices/system/memory # insmod /lib/modules/5.0.0/extra/BiscuitOS-Memory-Ho
tplug-in-C-0.0.1.ko 
[   35.980429] BiscuitOS_Memory_Hotplug_in_C_0.0.1: loading out-of-tree module taints kernel.
[   36.019905] Hello BiscuitOS :)
/sys/devices/system/memory # 
/sys/devices/system/memory # ls
auto_online_blocks  memory10            memory9             uevent
block_size_bytes    memory11            power
hard_offline_page   memory8             soft_offline_page
/sys/devices/system/memory # 
/sys/devices/system/memory # cat /proc/iomem 
40000000-4fffffff : System RAM
  40080000-41a1ffff : Kernel code
  41a20000-41baffff : reserved
  41bb0000-41e3cfff : Kernel data
  48000000-480fffff : reserved
50000000-57ffffff : System RAM
58000000-5fffffff : System RAM
/sys/devices/system/memory #
/sys/devices/system/memory/memory11 # cat /proc/meminfo 
MemTotal:         355284 kB
MemFree:          316684 kB
/sys/devices/system/memory # cd memory10/
/sys/devices/system/memory/memory10 # ls
node0        phys_device  power        state        uevent
online       phys_index   removable    subsystem
/sys/devices/system/memory/memory10 # cat state 
offline
/sys/devices/system/memory/memory10 # echo online > state 
/sys/devices/system/memory/memory10 # cat /proc/meminfo 
MemTotal:         486356 kB
MemFree:          447520 kB

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 目录,该目录下存储新热插入内存的信息. 在进行热插拔之前,需要配置内核打开热插拔功能,配置如下:

  Memory Management options  --->
        Memory model (Sparse Memory)  --->
    [*] Allow for memory hot-add

配置完内核之后,再将内核宏 SECTION_SIZE_BITS 设置为 27, 以此表示每个 MEMORY Section 的长度为 128 MiB. 其次如果是 ARM64 架构,那么需要在 arm64_memblock_init() 函数中添加 “memblock_remove(0x50000000, 0x8000000)” 语句将对应的内存区从内核中移除; 如果是 x64 架构,那么需要在 CMDLINE 中添加字段 “memmap=128MiB@0x18000000”. 修改完毕之后重新编译内核.

BiscuitOS 配置

在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] MMU (BiscuitOS+)  --->
        [*] Memory Hotplug auto  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/BiscuitOS-Memory-Hotplug-auto-0.0.1

具体实践办法请参考:

HKC 计划 BiscuitOS 实践框架介绍

通用例程
/*
 * Memory Hotplug in Code on BiscuitOS
 *
 * (C) 2020.10.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <asm/uaccess.h>

/*
 * Memory region for Hotplugging.
 * BTW, The default Block size as 0x8000000, not 2 Gig.
 *
 * On Aarch, we reserved a memory region from 0x50000000 to 0x58000000 on
 * MEMBLOCK allocator that invoke "memblock_reserve(0x50000000, 0x8000000)"
 * on "arm64_memblock_init()".
 *
 * On x86_64, we can use "memmap=" on CMDLINE to reserved a memory
 * region. The range of memory region from 0x5000000 to 0xD000000, which 
 * descrbe as "memmap=0x128M$0x5000000".
 */

/* NUMA NODE */
static int nid = 0;

#ifdef __aarch64__
#define BISCUITOS_MEMORY_BASE   0x50000000
#define BISCUITOS_PATH          "/sys/devices/system/memory/memory10/state"
#else // X64
#define BISCUITOS_MEMORY_BASE   0x18000000
#define BISCUITOS_PATH          "/sys/devices/system/memory/memory3/state"
#endif
#define BISCUITOS_MEMORY_SIZE   0x8000000

/* Module initialize entry */
static int __init BiscuitOS_init(void)
{
        struct file *filp;
        mm_segment_t fs;
        loff_t pos = 0;

#ifdef CONFIG_MEMORY_HOTPLUG
        /* Add into /sys/device/system/memory/memoryX */
        add_memory(nid, BISCUITOS_MEMORY_BASE, BISCUITOS_MEMORY_SIZE);
#endif

        /* open file */
        filp = filp_open(BISCUITOS_PATH, O_RDWR, 0644);
        if (IS_ERR(filp)) {
                printk("ERROR[%ld]: open %s failed.\n", PTR_ERR(filp),
                                                        BISCUITOS_PATH);
                return PTR_ERR(filp);
        }

        fs = get_fs();
        set_fs(KERNEL_DS);

        /* Write */
        pos = 0;
        kernel_write(filp, "online", strlen("online"), &pos);

        /* Close */
        filp_close(filp, NULL);
        set_fs(fs);

        return 0;
}

/* Module exit entry */
static void __exit BiscuitOS_exit(void)
{
#ifdef CONFIG_MEMORY_HOTPLUG
        remove_memory(nid, BISCUITOS_MEMORY_BASE, BISCUITOS_MEMORY_SIZE);
#endif
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("Memory Hotplug in Code on BiscuitOS");

例子很简单,通过调用 add_memory() 函数将 BISCUITOS_MEMORY_BASE 处长度为 BISCUITOS_MEMORY_SIZE 的物理内存插入到系统内部。如果插入成功,系统会在 “/sys/devices/system/memory/” 目录下创建热插入内存的信息。接着通过内核向用户空间文件写内容的逻辑,向该目录下 state 节点写入 “online” 字符串,以让系统支持软件层面的热插拔动作。该例子在 BiscuitOS 中运行情况如下:

~ # 
~ # cd /sys/devices/system/memory/
/sys/devices/system/memory # ls
auto_online_blocks  memory11            power
block_size_bytes    memory8             soft_offline_page
hard_offline_page   memory9             uevent
/sys/devices/system/memory # cat /proc/iomem 
40000000-4fffffff : System RAM
  40080000-41a1ffff : Kernel code
  41a20000-41baffff : reserved
  41bb0000-41e3cfff : Kernel data
  48000000-480fffff : reserved
58000000-5fffffff : System RAM
  5d600000-5d7fffff : reserved
  5d926000-5ddfffff : reserved
  5def7000-5def7fff : reserved
  5def8000-5df25fff : reserved
  5df28000-5df28fff : reserved
  5df29000-5df3bfff : reserved
  5df3c000-5df43fff : reserved
  5df44000-5fffffff : reserved
4010000000-401fffffff : PCI ECAM
/sys/devices/system/memory # cat /proc/meminfo 
MemTotal:         355284 kB
MemFree:          319308 kB
/sys/devices/system/memory # insmod /lib/modules/5.0.0/extra/BiscuitOS-Memory-Ho
tplug-auto-0.0.1.ko
/sys/devices/system/memory # ls
auto_online_blocks  memory10            memory9             uevent
block_size_bytes    memory11            power
hard_offline_page   memory8             soft_offline_page
/sys/devices/system/memory # cat /proc/iomem 
40000000-4fffffff : System RAM
  40080000-41a1ffff : Kernel code
  41a20000-41baffff : reserved
  41bb0000-41e3cfff : Kernel data
  48000000-480fffff : reserved
50000000-57ffffff : System RAM
58000000-5fffffff : System RAM
  5d600000-5d7fffff : reserved
  5d926000-5ddfffff : reserved
  5def7000-5def7fff : reserved
  5def8000-5df25fff : reserved
  5df28000-5df28fff : reserved
  5df29000-5df3bfff : reserved
  5df3c000-5df43fff : reserved
  5df44000-5fffffff : reserved
4010000000-401fffffff : PCI ECAM
/sys/devices/system/memory # cat /proc/meminfo 
MemTotal:         486356 kB
MemFree:          447172 kB
/sys/devices/system/memory # cd memory10/
/sys/devices/system/memory/memory10 # cat state 
online

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 中使用配置如下:

[*] Package  --->
    [*] Memory Mapping Mechanism  --->
        [*]  Anonymous Share mmap on Userspace

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/BiscuitOS-anonymous-share-mmap-userspace-default/

具体实践办法请参考:

HKC 计划 BiscuitOS 实践框架介绍

通用例程
/*
 * Anonymout share mmap on Userspace
 *
 * (C) 2021.04.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>

#define BISCUITOS_MAP_SIZE      4096

int main()
{
        unsigned long *val;
        char *base;

        /* mmap */
        base = (char *)mmap(NULL,
                            BISCUITOS_MAP_SIZE,
                            PROT_READ | PROT_WRITE,
                            MAP_SHARED | MAP_ANONYMOUS,
                            -1,
                            0);
        if (base == MAP_FAILED) {
                printf("ERROR: mmap failed.\n");
                return -ENOMEM;
        }

        val = (unsigned long *)base;
        /* Trigger page fault */
        *val = 88520;
        printf("%#lx => %ld\n", (unsigned long)val, *val);

        /* unmap */
        munmap(base, BISCUITOS_MAP_SIZE);

        return 0;
}

例子很精简,首先通过 mmap() 函数进行映射,在映射的时候传入参数 MAP_SHARED 和 MAP_ANONYMOUS 标志就可以映射一段匿名共享的虚拟内存。当对分配的虚拟内存进行读或者写操作的时候,就会触发缺页异常。当不再使用这段虚拟内核时,可以使用 munmap() 函数进行释放. 例子在 BiscuitOS 中运行的情况如下:

BiscuitOS 运行之后,在用户空间直接运行 BiscuitOS-anonymous-share-mmap-userspace-default 引用程序即可,运行之后打印虚拟内存的地址以及虚拟内存的值.


Anonymous Private mmap on Userspace

代码核心功能是在用户空间通过 “私有匿名” 方式映射一段虚拟内存。

BiscuitOS 配置

在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] Memory Mapping Mechanism  --->
        [*]  Anonymous Private mmap on Userspace

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/BiscuitOS-anonymous-private-mmap-userspace-default/

具体实践办法请参考:

HKC 计划 BiscuitOS 实践框架介绍

通用例程
/*
 * Anonymout private mmap on Userspace
 *
 * (C) 2021.04.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>

#define BISCUITOS_MAP_SIZE      4096

int main()
{
        unsigned long *val;
        char *base;

        /* mmap */
        base = (char *)mmap(NULL,
                            BISCUITOS_MAP_SIZE,
                            PROT_READ | PROT_WRITE,
                            MAP_PRIVATE | MAP_ANONYMOUS,
                            -1,
                            0);
        if (base == MAP_FAILED) {
                printf("ERROR: mmap failed.\n");
                return -ENOMEM;
        }

        val = (unsigned long *)base;
        /* Trigger page fault */
        *val = 88520;
        printf("%#lx => %ld\n", (unsigned long)val, *val);

        /* unmap */
        munmap(base, BISCUITOS_MAP_SIZE);

        return 0;
}

例子很精简,首先通过 mmap() 函数进行映射,在映射的时候传入参数 MAP_PRIVATE 和 MAP_ANONYMOUS 标志就可以映射一段匿名私有的虚拟内存。当对分配的虚拟内存进行读或者写操作的时候,就会触发缺页异常。当不再使用这段虚拟内核时,可以使用 munmap() 函数进行释放. 例子在 BiscuitOS 中运行的情况如下:

BiscuitOS 运行之后,在用户空间直接运行 BiscuitOS-anonymous-private-mmap-userspace-default 引用程序即可,运行之后打印虚拟内存的地址以及虚拟内存的值.


File Share mmap on Userspace

代码核心功能是在用户空间通过 “共享” 方式将文件映射到一段虚拟内存上。

BiscuitOS 配置

在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] Memory Mapping Mechanism  --->
        [*] File Share mmap on Userspace --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/BiscuitOS-file-share-mmap-userspace-default/

具体实践办法请参考:

HKC 计划 BiscuitOS 实践框架介绍

通用例程
/*
 * File Share mmap on Userspace
 *
 * (C) 2021.04.02 BuddyZhang1 <buddy.zhang@aliyun.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>

#define BISCUITOS_FILE_PATH     "/BiscuitOS"
#define BISCUITOS_MAP_SIZE      4096

int main()
{
        unsigned long *val;
        char *base;
        int fd;

        /* Open */
        fd = open(BISCUITOS_FILE_PATH, O_RDWR);
        if (fd < 0) {
                printf("ERROR: Open %s failed.\n", BISCUITOS_FILE_PATH);
                return -EBUSY;
        }

        /* mmap */
        base = (char *)mmap(NULL,
                            BISCUITOS_MAP_SIZE,
                            PROT_READ | PROT_WRITE,
                            MAP_SHARED,
                            fd,
                            0);
        if (base == MAP_FAILED) {
                printf("ERROR: mmap failed.\n");
                close(fd);
                return -ENOMEM;
        }

        val = (unsigned long *)base;
        /* Trigger page fault */
        *val = 88520;
        printf("%#lx => %#lx\n", (unsigned long)val, *val);

        /* unmap */
        munmap(base, BISCUITOS_MAP_SIZE);
        close(fd);

        return 0;
}

例子很精简,首先通过 open() 函数打开一个文件,然后通过 mmap() 函数进行映射,在映射的时候传入参数 MAP_SHARED 标志, 并传入打开文件的 fd,这样就可以将文件以 “共享” 方式映射到虚拟内存。当对分配的虚拟内存进行读或者写操作的时候,就会触发缺页异常,这个时候就会建立虚拟内存到文件的映射关系。当不再使用这段虚拟内核时,可以使用 munmap() 函数进行释放,最后关闭打开的文件。例子在 BiscuitOS 中运行的情况如下:

BiscuitOS 运行之后,直接运行 RunBiscuitOS.sh 脚本,脚本首先在当前目录下创建一个文件并写入 “BiscuitOS” 内容,然后在用户空间直接运行 BiscuitOS-file-share-mmap-userspace-default 调用程序,最后使用 hexdump 工具对虚拟内存写入的值是否与文件中的内容一致。通过实践发现结果与预期一致。


Hello BiscuitOS Application

功能介绍

BiscuitOS 配置

在 BiscuitOS 中使用配置如下:

[*] Package  --->
    [*] Assembly  --->

OUTPUT:
BiscuitOS/output/linux-XXX-YYY/package/

具体实践办法请参考:

HKC 计划 BiscuitOS 实践框架介绍

通用例程


HKC 贡献者名单

BuddyZhang1 buddy.zhang@aliyun.com

Meijusan 20741602@qq.com

Shaobin shaobin.huang@kernelworker.net