03 startup process of openwrt

Posted by shibobo12 on Mon, 03 Jan 2022 22:51:44 +0100

Reference blog: 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 -

It is obvious that the procd process took over the init process

1 start up process

  • u-boot
    It configures low-level hardware, loads Linux kernel imag and device tree blob, and finally uses kernel cmdline to jump to the Linux kernel image in RAM;
  • Kernel -> Hareware
    Linux Kernel initialization Hareware
  • Kernel -> filesystem
    The root file system will be mounted
  • Kernel -> Init Process (PID 1)
    Kernel initialization process
  • Openwrt -> Preinit
    openwrt initializes the process. Note that this is the Preinit function, not a script
  • Openwrt - > procd, perinit (script)
    procd goes back and calls / etc / RC d
    After preinit initialization is complete. The initialization process is over

2 Preinit

2.1 /etc/preinit

OpenWRT will inject the OpenWRT initialization process preinit into the kernel initialization process list (kernel_init).


At this time, the device will execute / etc/preinit, which is located in package / base files / etc/

#!/bin/sh
# Copyright (C) 2006-2016 OpenWrt.org
# Copyright (C) 2010 Vertical Communications

### When the device executes for the first time, the PREINIT parameter is undefined, so / sbin/init will be executed
### Therefore, / sbin/init is the first initialization process of the device
[ -z "$PREINIT" ] && exec /sbin/init

export PATH="%PATH%"

. /lib/functions.sh
. /lib/functions/preinit.sh
. /lib/functions/system.sh

### boot_hook_init in / lib / functions / Preinit Defined in 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

### Execute all scripts under / 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 is procd / init d/init. C compiled executable.

int
main(int argc, char **argv)
{
    pid_t pid;

    //Open log
    ulog_open(ULOG_KMSG, LOG_DAEMON, "init");

    //Set signal
    sigaction(SIGTERM, &sa_shutdown, NULL);
    sigaction(SIGUSR1, &sa_shutdown, NULL);
    sigaction(SIGUSR2, &sa_shutdown, NULL);

    /*  early
     *   |->early_mounts
     *   |      |-> mount
     *   |      |->early_dev Setting environment variables
     *   |->LOG("Console is alive")
     */
	early();
    
    /*  cmdline
     *      |-> get init_debug Get init_debug level
     */
	cmdline();
    
    /*  watchdog_init
     *      |->LOG("- watchdog -")
     */
	watchdog_init(1);

	pid = fork();
    if (!pid) {
        /*  /sbin/kmodloader
         *      |-> /etc/modules-boot.d Load driver
         */
        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 script
        char *init[] = { "/bin/sh", "/etc/preinit", NULL };
        // procd
        char *plug[] = { "/sbin/procd", "-h", "/etc/hotplug-preinit.json", NULL };
        int fd;

        LOG("- preinit -\n");

        /*  Note that this is a callback function
         */
        plugd_proc.cb = plugd_proc_cb;
        plugd_proc.pid = fork();
        if (!plugd_proc.pid) {
                /*  plug "/sbin/procd", "-h", "/etc/hotplug-preinit.json"
                 *  Execute procd first, and the input parameter is - 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
                 *  Then execute preinit. The input parameter is - 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);
}

Callback function: the difference between a callback function and an ordinary function is that in the callback function, the main program will transfer the callback function to the storage function like a parameter

fork procd process, specifying hotplug Preinit JSON, so hotplug will be executed_ 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':
            /*  Establish netlink communication mechanism, complete kernel interaction and listen for uevent events
             */
            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
        /* State machine processing, the actual effect is as follows
         * [ 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_ After next processing the status, the device will execute to 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;

    // This is where our common self starting scripts are
    // You should know that the files starting with K are stop and the files starting with S are start
    return _rc(&q, "/etc/rc.d", pattern, "*", param);
}

Others' illustrations are quoted here

1. early() is the first function in init. It has four main tasks:

  • early_mounts(): mount /proc, /sysfs, /dev, /tmp;
  • early_env(): Use / usr/sbin: / sbin: / usr/bin: / bin to set the PATH parameter;
  • Initialize / dev/console;
  • Print the first message from init: "the console is a live", as shown above;

2. cmdline() is the second function that reads the kernel boot command line from / proc/cmdline and parses init_debug parameter;
3,watchdog_init() initializes the monitor / dev/watchdog and prints the second message "- Monitor -" as shown above;
4. fork a new thread and let / sbin/kmodloader load / etc / modules boot D / device drivers;
5,uloop_init() initializes uloop, which is an event loop implementation. Later procd and sh /etc/preinit will be managed by uloop;
6. preinit() has four main tasks:

  • Print the third message: "- preinit -", as shown above;
  • fork() a new thread to execute sh /etc/preinit. This will be the second time this initialization script is executed. One is called spawn_ The callback function of procd() will be executed after sh /etc/preinit is completed.
    Note: spawn_procd() will read the system debug level from / TMP / debugllevel and set it to env DBGLVL. It also sets the watchdog fd to env WDTFD. Finally, it forks the real / sbin/procd as a deamon.
  • set env variable PREINIT with setenv ("PREINIT", "1", 1);
  • fork() is a new thread that executes the / sbin/procd program with the parameter - H / etc / hotplug Preinit json.
  • Note: this new thread will pass through uloop_process_add() and a callbakc function are added to uloop as the callback function plugd when / sbin/procd – h is completed_ proc_ cb()

Topics: openwrt