今天在调试ko的时候,发现没有在开机的时候进行自动加载,之前只知道在Makefile文件中增加一个配置就可以实现。
AUTOLOAD:=$(call AutoLoad,60,leds-gpio,1)
但是我这边也有注意到,一些ipt相关的ko makefile的写法是:
AUTOLOAD:=$(call AutoProbe,$(notdir $(NFT_BRIDGE-m)))
我这边做一下分析。
编译
在 include/kernel.mk中可以看到定义
version_filter=$(if $(findstring @,$(1)),$(shell $(SCRIPT_DIR)/package-metadata.pl version_filter $(KERNEL_PATCHVER) $(1)),$(1))
# 1: priority (optional)
# 2: module list
# 3: boot flag
define AutoLoad
$(if $(1),$(1),0) $(if $(3),1,0) $(call version_filter,$(2))
endef
# 1: module list
# 2: boot flag
define AutoProbe
$(call AutoLoad,,$(1),$(2))
endef
其实 AutoProbe 就是 AutoLoad,
(
1
)
,
(1),
(1),(2) 的封装。封装的时候省略了 priority。
自己也可以实现:
AUTOLOAD:=$(call AutoLoad,,leds-gpio,1)
# 或者
AUTOLOAD:=$(call AutoLoad,0,leds-gpio,1)
编译配置加载顺序
系统启动加载的时候分为两种方式:
- /sbin/init 中加载
其实这块有点表述不清,应该说是在 /sbin/init 的时候加载的一些系统关键ko,此时没有执行 /etc/init.d/boot 中的操作(挂在文件系统等等)
/sbin/init 阶段加载的模块列表是在 /etc/modules-boot.d/ 目录下
实现的关键是 AutoLoad 的第三个参数(boot flag), 如果设置了这个值为1,则会在boot阶段加载 - 系统启动过程自动加载
在/etc/init.d/boot 中的操作,此时系统文件系统等已经挂载完。
加载的模块列表是在 /etc/modules.d/ 目录下
看一下编译时候是怎么根据配置生成的:
define Package/kmod-$(1)/install
@for mod in $$(call version_filter,$$(FILES)); do \
if grep -q "$$$$$$$${mod##$(LINUX_DIR)/}" "$(LINUX_DIR)/modules.builtin"; then \
echo "NOTICE: module '$$$$$$$$mod' is built-in."; \
elif [ -e $$$$$$$$mod ]; then \
mkdir -p $$(1)/$(MODULES_SUBDIR) ; \
$(CP) -L $$$$$$$$mod $$(1)/$(MODULES_SUBDIR)/ ; \
else \
echo "ERROR: module '$$$$$$$$mod' is missing." >&2; \
exit 1; \
fi; \
done;
$(call ModuleAutoLoad,$(1),$$(1),$(filter-out 0-,$(word 1,$(AUTOLOAD))-),$(filter-out 0,$(word 2,$(AUTOLOAD))),$(sort $(wordlist 3,99,$(AUTOLOAD))))
$(call KernelPackage/$(1)/install,$$(1))
endef
# 1: name
# 2: install prefix
# 3: module priority prefix
# 4: required for boot
# 5: module list
define ModuleAutoLoad
$(if $(5), \
mkdir -p $(2)/etc/modules.d; \
($(foreach mod,$(5), \
echo "$(mod)$(if $(MODPARAMS.$(mod)), $(MODPARAMS.$(mod)),$(if $(MODPARAMS), $(MODPARAMS)))"; )) > $(2)/etc/modules.d/$(3)$(1); \
$(if $(4), \
mkdir -p $(2)/etc/modules-boot.d; \
ln -sf ../modules.d/$(3)$(1) $(2)/etc/modules-boot.d/;))
endef
加载ko
ko加载log信息:
root@MUXI:/# logread | grep kmodloader
Fri Jul 14 12:23:40 2023 user.info kernel: [ 2.129349] kmodloader: loading kernel modules from /etc/modules-boot.d/*
Fri Jul 14 12:23:40 2023 user.info kernel: [ 2.462703] kmodloader: done loading kernel modules from /etc/modules-boot.d/*
Fri Jul 14 12:23:40 2023 user.info kernel: [ 6.045561] kmodloader: loading kernel modules from /etc/modules.d/*
Fri Jul 14 12:23:40 2023 user.info kernel: [ 12.411877] kmodloader: done loading kernel modules from /etc/modules.d/*
/sbin/init 阶段ko加载
引用2: openwrt启动流程(一)
/sbin/init 的源码:
其中就包含了 /sbin/kmodloader /etc/modules-boot.d/
int
main(int argc, char **argv)
{
pid_t pid;
ulog_open(ULOG_KMSG, LOG_DAEMON, "init");
// 挂载了 /proc 、/sys 、/dev 、/tmp …等目录,并设置 PATH 环境变量
early();
// 从cmd line中获取debug log等级
cmdline();
// 开启看门狗
watchdog_init(1);
pid = fork();
if (!pid) {
///etc/modules-boot.d/ 目录下保存了需要开机自动加载的kernel模块信息。
char *kmod[] = { "/sbin/kmodloader", "/etc/modules-boot.d/", NULL };
if (debug < 3)
patch_stdio("/dev/null");
//加载kernel模块
execvp(kmod[0], kmod);
ERROR("Failed to start kmodloader: %m\n");
exit(EXIT_FAILURE);
}
if (pid <= 0) {
ERROR("Failed to start kmodloader instance: %m\n");
} else {
const struct timespec req = {0, 10 * 1000 * 1000};
int i;
for (i = 0; i < 1200; i++) {
if (waitpid(pid, NULL, WNOHANG) > 0)
break;
nanosleep(&req, NULL);
watchdog_ping();
}
}
uloop_init();
// 执行/sbin/procd -h /etc/hotplug-preinit.json, 然后是 /bin/sh /etc/preinit ,并在/etc/preinit 执行结束的回调函数中调用 /sbin/procd
preinit();
uloop_run();
return 0;
}
系统启动阶段ko加载
在 /etc/init.d/boot 文件中
boot() {
[ -f /proc/mounts ] || /sbin/mount_root
[ -f /proc/jffs2_bbc ] && echo "S" > /proc/jffs2_bbc
# ...
# 这个步骤会安装 /etc/modules.d/ 目录下的ko文件
/sbin/kmodloader
# ...
/bin/config_generate
uci_apply_defaults
# temporary hack until configd exists
/sbin/reload_config
}
/sbin/kmodloader 源码
提供一下 /sbin/kmodloader 的源码,作为备注查看
ubox/kmodloader.c
static int main_loader(int argc, char **argv)
{
char *dir = "/etc/modules.d/";
//参数判断,由参数的话加载指定路径下的ko文件,没有参数的话,加载默认路径下的文件
if (argc > 1)
dir = argv[1];
//... 省略部分代码
scan_module_folders();
scan_loaded_modules();
ULOG_INFO("loading kernel modules from %s\n", path);
// ...
for (j = 0; j < gl.gl_pathc; j++) {
FILE *fp = fopen(gl.gl_pathv[j], "r");
while (getline(&mod, &mod_len, fp) > 0) {
//...
m = find_module(get_module_name(mod));
//...
}
}
fail = load_modprobe(true);
// ...
}