03 openwrt的启动过程
引用博客:https://clockworkbird9.wordpress.com/2016/09/
[ 2.824545] VFS: Mounted root (ext4 filesystem) readonly on device 179:1.
[ 2.833446] Freeing unused kernel memory: 244K (84733000 - 84770000)
[ 3.006884] init: Console is alive
[ 3.011436] init: - watchdog -
[ 3.329383] init: - preinit -
[ 6.570976] mount_root: mounting /dev/root
[ 6.579281] EXT4-fs (mmcblk0p1): re-mounted. Opts: (null)
[ 6.596450] procd: - early -
[ 6.599817] procd: - watchdog -
[ 7.301153] procd: - ubus -
[ 7.362047] procd: - init -
可以明显的看到procd
进程接管了init
进程
1 启动流程
u-boot
它配置低级硬件,加载Linux
内核imag
和设备树blob
,最后使用内核cmdline
跳转到RAM
中的Linux
内核映像;Kernel -> Hareware
Linux Kernel
初始化Hareware
Kernel -> filesystem
将挂载根文件系统Kernel -> Init Process (PID 1)
内核初始化进程Openwrt -> Preinit
openwrt
初始化进程,注意这里是Preinit
函数,不是脚本Openwrt -> Procd、perinit(脚本)
procd
回去调用/etc/rc.d
preinit
初始化完成之后。初始化过程就结束了
2 Preinit
2.1 /etc/preinit
OpenWRT
会将OpenWRT
初始化进程preinit
注入到内核初始化进程列表中(kernel_init
)。
此时设备会去执行/etc/preinit
位于package/base-files/etc/
#!/bin/sh
# Copyright (C) 2006-2016 OpenWrt.org
# Copyright (C) 2010 Vertical Communications
### 设备第一次执行到这里PREINIT参数未定义,因此/sbin/init会被执行
### 因此/sbin/init为设备的第一次初始化过程
[ -z "$PREINIT" ] && exec /sbin/init
export PATH="%PATH%"
. /lib/functions.sh
. /lib/functions/preinit.sh
. /lib/functions/system.sh
### boot_hook_init在 /lib/functions/preinit.sh中定义
boot_hook_init preinit_essential
boot_hook_init preinit_main
boot_hook_init failsafe
boot_hook_init initramfs
boot_hook_init preinit_mount_root
### 执行/lib/preinit/下所有脚本
for pi_source_file in /lib/preinit/*; do
. $pi_source_file
done
boot_run_hook preinit_essential
pi_mount_skip_next=false
pi_jffs2_mount_success=false
pi_failsafe_net_message=false
boot_run_hook preinit_main
/sbin/init
是procd/init.d/init.c
编译的可执行文件。
int
main(int argc, char **argv)
{
pid_t pid;
//打开日志
ulog_open(ULOG_KMSG, LOG_DAEMON, "init");
//设置信号
sigaction(SIGTERM, &sa_shutdown, NULL);
sigaction(SIGUSR1, &sa_shutdown, NULL);
sigaction(SIGUSR2, &sa_shutdown, NULL);
/* early
* |->early_mounts
* | |-> mount
* | |->early_dev 设置环境变量
* |->LOG("Console is alive")
*/
early();
/* cmdline
* |-> get init_debug 获取init_debug等级
*/
cmdline();
/* watchdog_init
* |->LOG("- watchdog -")
*/
watchdog_init(1);
pid = fork();
if (!pid) {
/* /sbin/kmodloader
* |-> /etc/modules-boot.d 加载驱动
*/
char *kmod[] = { "/sbin/kmodloader", "/etc/modules-boot.d/", NULL };
if (debug < 3)
patch_stdio("/dev/null");
execvp(kmod[0], kmod);
ERROR("Failed to start kmodloader\n");
exit(-1);
}
if (pid <= 0) {
ERROR("Failed to start kmodloader instance\n");
} else {
int i;
for (i = 0; i < 1200; i++) {
if (waitpid(pid, NULL, WNOHANG) > 0)
break;
usleep(10 * 1000);
watchdog_ping();
}
}
uloop_init();
/* preinit
* |-> LOG("- preinit -")
* |-> fork->procd
* |-> setenv("PREINIT", "1", 1)
* |-> fork->sh /etc/preinit
*/
preinit();
uloop_run();
return 0;
}
initd/preinit.c
void
preinit(void)
{
// perinit脚本
char *init[] = { "/bin/sh", "/etc/preinit", NULL };
// procd
char *plug[] = { "/sbin/procd", "-h", "/etc/hotplug-preinit.json", NULL };
int fd;
LOG("- preinit -\n");
/* 注意这个是回调函数
*/
plugd_proc.cb = plugd_proc_cb;
plugd_proc.pid = fork();
if (!plugd_proc.pid) {
/* plug "/sbin/procd", "-h", "/etc/hotplug-preinit.json"
* 先执行procd 入参为 -h /etc/hotplug-preinit.json
*/
execvp(plug[0], plug);
ERROR("Failed to start plugd: %m\n");
exit(EXIT_FAILURE);
}
if (plugd_proc.pid <= 0) {
ERROR("Failed to start new plugd instance: %m\n");
return;
}
uloop_process_add(&plugd_proc);
setenv("PREINIT", "1", 1);
fd = creat("/tmp/.preinit", 0600);
if (fd < 0)
ERROR("Failed to create sentinel file: %m\n");
else
close(fd);
preinit_proc.cb = spawn_procd;
preinit_proc.pid = fork();
if (!preinit_proc.pid) {
/* init "/bin/sh", "/etc/preinit
* 然后执行preinit 入参为 -h /etc/hotplug-preinit.json
*/
execvp(init[0], init);
ERROR("Failed to start preinit: %m\n");
exit(EXIT_FAILURE);
}
if (preinit_proc.pid <= 0) {
ERROR("Failed to start new preinit instance: %m\n");
return;
}
uloop_process_add(&preinit_proc);
DEBUG(4, "Launched preinit instance, pid=%d\n", (int) preinit_proc.pid);
}
回调函数:回调函数与普通函数的区别在于在回调函数中主程序会把回调函数像参数一样传入库函数
fork procd
进程,指定了hotplug-preinit.json
,所以会执行hotplug_run
int main(int argc, char **argv)
{
int ch;
char *dbglvl = getenv("DBGLVL");
int ulog_channels = ULOG_KMSG;
if (dbglvl) {
debug = atoi(dbglvl);
unsetenv("DBGLVL");
}
while ((ch = getopt(argc, argv, "d:s:h:S")) != -1) {
switch (ch) {
case 'h':
/* 建立netlink通讯机制,完成内核的交互,监听uevent事件
*/
return hotplug_run(optarg);
case 's':
ubus_socket = optarg;
break;
case 'd':
debug = atoi(optarg);
break;
case 'S':
ulog_channels = ULOG_STDIO;
break;
default:
return usage(argv[0]);
}
}
ulog_open(ulog_channels, LOG_DAEMON, "procd");
setsid();
uloop_init();
procd_signal();
if (getpid() != 1)
procd_connect_ubus();
else
/* 状态机处理,实际效果如下
* [ 6.596450] procd: - early -
* [ 6.599817] procd: - watchdog -
* [ 7.301153] procd: - ubus -
* [ 7.362047] procd: - init -
*/
procd_state_next();
uloop_run();
uloop_done();
return 0;
}
procd_state_next
处理完状态之后设备设备会执行到rcS.c
int rcS(char *pattern, char *param, void (*q_empty)(struct runqueue *))
{
runqueue_init(&q);
q.empty_cb = q_empty;
q.max_running_tasks = 1;
// 这里便是我们常见的自启动脚本的地方
// 需要知道的是K开头的文件为stop,S开头的文件为start
return _rc(&q, "/etc/rc.d", pattern, "*", param);
}
这里引用别人的图解
1、early()
是init
中的第一个函数。它有四个主要任务:
early_mounts(): mount /proc, /sysfs, /dev, /tmp
;early_env()
: 使用/usr/sbin:/sbin:/usr/bin:/bin
设置PATH
参数;- 初始化
/dev/console
; - 从
init
打印第一条消息:"控制台是一个活的",如上所示;
2、cmdline()
是第二个函数,它从/proc/cmdline
读取内核引导命令行并解析init_debug
参数;
3、watchdog_init()
初始化监视程序 /dev/watchdog
并打印第二条消息 "- 监视程序 -" 如上所示;
4、fork
一个新线程,让/sbin/kmodloader
加载有关/etc/modules-boot.d/
的设备驱动程序;
5、uloop_init()
初始化uloop
,这是一个事件循环实现。后来的procd
和sh /etc/preinit
将由uloop
管理;
6、preinit()
有四个主要任务:
- 打印第三条消息:
"- preinit -"
,如上所示; fork()
一个新的线程来执行sh /etc/preinit
。这将是第二次执行此初始化脚本。一个名为spawn_procd()
的回调函数将在sh /etc/preinit
完成后执行。
注意:spawn_procd()
将从/tmp/debuglevel
读取系统调试级别,并将其设置为env DBGLVL
。它还将看门狗fd
设置为env WDTFD
。最后,它将分叉真正的/sbin/procd
作为deamon
。set env
变量PREINIT with setenv("PREINIT", "1", 1)
;fork()
一个新的线程,用于执行/sbin/procd
程序,参数为-h /etc/hotplug-preinit.json
。- 注意:这个新线程将通过
uloop_process_add()
以及一个callbakc
函数添加到uloop
中,作为当/sbin/procd – h
完成时,回调函数plugd_proc_cb()
推荐这些技术文章:
🔧openwrt + dnsmasq + anti-AD 两步搞定广告屏蔽
修改 /etc/dnsmasq.conf ,将 anti-AD 提供的配置,全部复制粘贴到最下方保存退出即可。
使用 /etc/init.d/dnsmasq restart 重启 dnsmasq 服务
参考网页:
在openwrt环境下配置dnsmasq
openwrt dnsmasq dns 配置,运行机制初探
...
一、openwrt介绍
OpenWRT(曾用名 LEDE)是一款广泛应用于路由器的嵌入式操作系统。由于其开源,所以在不同硬件上面自己修改源码就可以适配,所以得到了很广泛的使用。
官方地址:https://openwrt.org/
官方论坛:https://forum.openwrt.org/
与Ubuntu的apt-get、Centos的yum类似,Openwrt也有类似的包管理器opkg,所以...
start-gateway-dev.sh
nohup java -jar vipsoft-gateway-1.0.0.jar --spring.profiles.active=dev > startup.log 2>&1 &
stop-gateway-dev.sh
ps -ef | grep "vipsoft-gateway-1.0.0" | grep -v "...
springboot启动之afterpropertiesset方法
1、init-method方法,初始化bean的时候执行,可以针对某个具体的bean进行配置。init-method需要在applicationcontext.xml配置文档中bean的定义里头写明。例如:
这样,当testbean在初始化的时候会执行testbean中定义的init方法。
2、afterpropertiesset方法,初始化bean的时候执行,可以针对某个具体的bean进行...
#命令的位置
CMD="java"
#参数空格隔开
ARGS=" -jar /opt/register-1.0.0.jar"
#命令日志文件
LOG_FILE="./start.log"
#pid文件
PID_FILE="./PID"
start() {
if [ -f $PID_FILE ]; then
echo -n "process has run at "
...
setup_arch -> psci_dt_init
arch/arm64/kernel/setup.c
284void __init __no_sanitize_address setup_arch(char **cmdline_p)
285{
339 340 if (acpi_disabled) 341 u...
当提出这个警告时就需要对boot进行扩容,否则导致安装软件出现问题,比如yum update升级系统时。那这么扩容呢?在网上搜了很多资料实践,但是都不能根据提供的操作一步到位地成功,下面的总结适用于CentOS8,其他环境需待验证。
1、对原/boot挂载进行处理
1). umount /boot
2). mkdir /root/boot_ol...
上一篇: cpu_ops
https://www.cnblogs.com/zhangzhiwei122/p/16090770.html
setup_arch -> smp_init_cpus
arch/arm64/kernel/setup.c
284void __init __no_sanitize_address setup_arch(char **cm...
我在之前的uboot通过NFS挂载ubuntu根文件系统中也有实现过根文件系统的制作,那只是在ubuntu官网已经做好的根文件基础上安装一些自己所需的软解而已。而使用busybox制作根文件系统可以自定义选项,加深自己对linux的系统理解,方便后期学习根文件系统的裁剪。
一、下载busybox制作制作工具
官网下载地址https://busybox.net/,进入“Download Sourc...
文章链接:https://www.dianjilingqu.com/3845.html
本文章来源于网络,版权归原作者所有,如果本站文章侵犯了您的权益,请联系我们删除,联系邮箱:saisai#email.cn,感谢支持理解。