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/initprocd/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,这是一个事件循环实现。后来的procdsh /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介绍
OpenWRT(曾用名 LEDE)是一款广泛应用于路由器的嵌入式操作系统。由于其开源,所以在不同硬件上面自己修改源码就可以适配,所以得到了很广泛的使用。
官方地址:https://openwrt.org/
官方论坛:https://forum.openwrt.org/
与Ubuntu的apt-get、Centos的yum类似,Openwrt也有类似的包管理器opkg,所以...

Sprintboot 项目启动、停止脚本

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进行...

linux启动停止脚本模板

#命令的位置
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 "
...

smp启动-psci模块初始化

 
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...

CentOS8 启动后警告boot空间不足

  当提出这个警告时就需要对boot进行扩容,否则导致安装软件出现问题,比如yum update升级系统时。那这么扩容呢?在网上搜了很多资料实践,但是都不能根据提供的操作一步到位地成功,下面的总结适用于CentOS8,其他环境需待验证。
  1、对原/boot挂载进行处理
    1). umount /boot
        
    2). mkdir /root/boot_ol...

smp启动-smp_init_cpus

上一篇: 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...

通过busybox制作根文件系统详细过程

我在之前的uboot通过NFS挂载ubuntu根文件系统中也有实现过根文件系统的制作,那只是在ubuntu官网已经做好的根文件基础上安装一些自己所需的软解而已。而使用busybox制作根文件系统可以自定义选项,加深自己对linux的系统理解,方便后期学习根文件系统的裁剪。
一、下载busybox制作制作工具
官网下载地址https://busybox.net/,进入“Download Sourc...

文章标题:03 openwrt的启动过程
文章链接:https://www.dianjilingqu.com/3845.html
本文章来源于网络,版权归原作者所有,如果本站文章侵犯了您的权益,请联系我们删除,联系邮箱:saisai#email.cn,感谢支持理解。
THE END
< <上一篇
下一篇>>