openwrt系统初始化分析

来源:互联网 发布:python 开源题库系统 编辑:程序博客网 时间:2024/06/05 20:08

openwrt固件启动后,进入uboot,加载内核,启动init进程,而init进程包含在procd进程中,启动代码如下:

intmain(int argc, char **argv){    pid_t pid;    sigaction(SIGTERM, &sa_shutdown, NULL);    sigaction(SIGUSR1, &sa_shutdown, NULL);    sigaction(SIGUSR2, &sa_shutdown, NULL);    early();//初始化根文件系统中的需要的文件和设置,early.c    cmdline(); //从proc/cmdline获取命令行启动参数    watchdog_init(1); //初始化watchdog, watchdog.c     pid = fork();    if (!pid) {        char *kmod[] = { "/sbin/kmodloader", "/etc/modules-boot.d/", NULL };        if (debug < 3) {            int fd = open("/dev/null", O_RDWR);            if (fd > -1) {                dup2(fd, STDIN_FILENO);                dup2(fd, STDOUT_FILENO);                dup2(fd, STDERR_FILENO);                if (fd > STDERR_FILENO)                    close(fd);            }        }        execvp(kmod[0], kmod);        ERROR("Failed to start kmodloader\n");        exit(-1);    }    if (pid <= 0)        ERROR("Failed to start kmodloader instance\n");    else        waitpid(pid, NULL, 0);    uloop_init();    preinit(); //执行初始脚本    uloop_run();    return 0;}

preinit()函数中启动了/etc/preinit启动脚本:

voidpreinit(void){    char *init[] = { "/bin/sh", "/etc/preinit", NULL };    char *plug[] = { "/sbin/procd", "-h", "/etc/hotplug-preinit.json", NULL };    LOG("- preinit -\n");    plugd_proc.cb = plugd_proc_cb;    plugd_proc.pid = fork();    if (!plugd_proc.pid) {        execvp(plug[0], plug);         ERROR("Failed to start plugd\n");        exit(-1);    }    if (plugd_proc.pid <= 0) {        ERROR("Failed to start new plugd instance\n");        return;    }    uloop_process_add(&plugd_proc);    setenv("PREINIT", "1", 1);    preinit_proc.cb = spawn_procd;    preinit_proc.pid = fork();    if (!preinit_proc.pid) {        execvp(init[0], init);        ERROR("Failed to start preinit\n");        exit(-1);    }    if (preinit_proc.pid <= 0) {        ERROR("Failed to start new preinit instance\n");        return;    }    uloop_process_add(&preinit_proc);    DEBUG(4, "Launched preinit instance, pid=%d\n", (int) preinit_proc.pid);}

在preinit函数中同时启动procd进程/sbin/procd, procd的执行代码如下:

int main(int argc, char **argv){    int ch;    char *dbglvl = getenv("DBGLVL");    if (dbglvl) {        debug = atoi(dbglvl);        unsetenv("DBGLVL");    }    while ((ch = getopt(argc, argv, "d:s:h:")) != -1) {        switch (ch) {        case 'h':            return hotplug_run(optarg);        case 's':            ubus_socket = optarg;            break;        case 'd':            debug = atoi(optarg);            break;        default:            return usage(argv[0]);        }    }    uloop_init();    procd_signal();    trigger_init();    if (getpid() != 1)        procd_connect_ubus();//ubusd进程存在则连接ubus    else        procd_state_next(); //不存在则,启动ubusd进程    uloop_run();    return 0;}

在函数procd_state_next中调用state_enter 启动ubusd进程

static void state_enter(void){    char ubus_cmd[] = "/sbin/ubusd";    switch (state) {    case STATE_EARLY:        LOG("- early -\n");        watchdog_init(0);        hotplug("/etc/hotplug.json");        procd_coldplug();        break;    case STATE_INIT:        // try to reopen incase the wdt was not available before coldplug        watchdog_init(0);        LOG("- ubus -\n");        procd_connect_ubus();        LOG("- init -\n");        service_init();        service_start_early("ubus", ubus_cmd);        procd_inittab();        procd_inittab_run("respawn");        procd_inittab_run("askconsole");        procd_inittab_run("askfirst");        procd_inittab_run("sysinit");        break;    case STATE_RUNNING:        LOG("- init complete -\n");        break;    case STATE_SHUTDOWN:        LOG("- shutdown -\n");        procd_inittab_run("shutdown");        sync();        break;    case STATE_HALT:        LOG("- reboot -\n");        reboot(reboot_event);        break;    default:        ERROR("Unhandled state %d\n", state);        return;    };}

启动preinit时执行的/etc/preinit脚本,内容如下:

#!/bin/sh# Copyright (C) 2006 OpenWrt.org# Copyright (C) 2010 Vertical Communications[ -z "$PREINIT" ] && exec /sbin/initexport PATH=/bin:/sbin:/usr/bin:/usr/sbinpi_ifname=pi_ip=192.168.1.1pi_broadcast=192.168.1.255pi_netmask=255.255.255.0fs_failsafe_ifname=fs_failsafe_ip=192.168.1.1fs_failsafe_broadcast=192.168.1.255fs_failsafe_netmask=255.255.255.0fs_failsafe_wait_timeout=2pi_suppress_stderr="y"pi_init_suppress_stderr="y"pi_init_path="/bin:/sbin:/usr/bin:/usr/sbin"pi_init_cmd="/sbin/init". /lib/functions.sh. /lib/functions/preinit.sh. /lib/functions/system.shboot_hook_init preinit_essentialboot_hook_init preinit_mainboot_hook_init failsafeboot_hook_init initramfsboot_hook_init preinit_mount_rootfor pi_source_file in /lib/preinit/*; do        . $pi_source_filedoneboot_run_hook preinit_essentialpi_mount_skip_next=falsepi_jffs2_mount_success=falsepi_failsafe_net_message=falseboot_run_hook preinit_main

/etc/preinit脚本是一系列初始化脚本的入口,定义了初始化的各种参数,preinit的一系列脚本放在/lib/preinit/文件夹下:

02_default_set_state         50_indicate_regular_preinit03_preinit_do_ralink.sh      70_initramfs_test10_indicate_failsafe         80_mount_root10_indicate_preinit          81_urandom_seed10_sysinfo                   99_10_failsafe_login30_failsafe_wait             99_10_run_init40_run_failsafe_hook

由于脚本众多,因此openwrt的设计者将这些脚本分成下面几类:

preinit_essentialpreinit_mainfailsafeinitramfspreinit_mount_root

每一类函数按照脚本的开头数字的顺序运行。

preinit执行的最后一个脚本为99_10_run_init,运行

exec env - PATH=$pi_init_path $pi_init_env $pi_init_cmd

pi_init_cmd

pi_init_cmd="/sbin/init"

因此开始运行busybox的init命令

busybox的init命令执行/etc/inittab的脚本,/etc/inittab 内容如下:

::sysinit:/etc/init.d/rcS S boot::shutdown:/etc/init.d/rcS K stoptts/0::askfirst:/bin/ash --loginttyS0::askfirst:/bin/ash --logintty1::askfirst:/bin/ash --login

sysinit为系统初始化运行的 /etc/init.d/rcS S boot脚本
shutdown为系统重启或关机运行的脚本
tty开头的是,如果用户通过串口或者telnet登录,则运行/bin/ash --login
askfirst和respawn相同,只是在运行前提示”Please press Enter to activate
this console.”

当前启动转到运行 /etc/init.d/rcS S boot,/etc/init.d/rcS和preinit类似,rcS也是一系列脚本的入口,其运行/etc/rc.d目录下S开头的的所
有脚本(如果运行rcS K stop,则运行K开头的所有脚本)

K50dropbear S02nvram S40network S50dropbear S96ledK90network S05netconfig S41wmacfixup S50telnet S97watchdogK98boot S10boot S45firewall S60dnsmasq S98sysntpdK99umount S39usb S50cron S95done S99sysctl

上面的脚本文件来自/etc/init.d/,在该文件夹下包含了各种应用程序的初始化脚本,这些脚本通过/etc/rc.common脚本,将init.d的脚
本链接到/etc/rc.d目录下,并且根据 这些脚本中的START和STOP的关键字,添加K${STOP}S${START}的前缀,这样就决定了脚本的先后的运行次序。

openwrt的shell脚本比较复杂,因此看脚本时可以通过添加set -xecho等命令,直接看shell脚本的结果,而不要花太多的时间硬看脚本,主要是理解其主要的意思和设计思路。

原创粉丝点击