概述
openwrt固件启动后,进入uboot,加载内核,启动init进程,而init进程包含在procd进程中,启动代码如下:
int
main(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 kmodloadern");
exit(-1);
}
if (pid <= 0)
ERROR("Failed to start kmodloader instancen");
else
waitpid(pid, NULL, 0);
uloop_init();
preinit(); //执行初始脚本
uloop_run();
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
preinit()
函数中启动了/etc/preinit
启动脚本:
void
preinit(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 plugdn");
exit(-1);
}
if (plugd_proc.pid <= 0) {
ERROR("Failed to start new plugd instancen");
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 preinitn");
exit(-1);
}
if (preinit_proc.pid <= 0) {
ERROR("Failed to start new preinit instancen");
return;
}
uloop_process_add(&preinit_proc);
DEBUG(4, "Launched preinit instance, pid=%dn", (int) preinit_proc.pid);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
在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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
在函数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 %dn", state);
return;
};
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
启动preinit时执行的/etc/preinit
脚本,内容如下:
#!/bin/sh
# Copyright (C) 2006 OpenWrt.org
# Copyright (C) 2010 Vertical Communications
[ -z "$PREINIT" ] && exec /sbin/init
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
pi_ifname=
pi_ip=192.168.1.1
pi_broadcast=192.168.1.255
pi_netmask=255.255.255.0
fs_failsafe_ifname=
fs_failsafe_ip=192.168.1.1
fs_failsafe_broadcast=192.168.1.255
fs_failsafe_netmask=255.255.255.0
fs_failsafe_wait_timeout=2
pi_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.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
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
/etc/preinit
脚本是一系列初始化脚本的入口,定义了初始化的各种参数,preinit的一系列脚本放在/lib/preinit/
文件夹下:
02_default_set_state 50_indicate_regular_preinit
03_preinit_do_ralink.sh 70_initramfs_test
10_indicate_failsafe 80_mount_root
10_indicate_preinit 81_urandom_seed
10_sysinfo 99_10_failsafe_login
30_failsafe_wait 99_10_run_init
40_run_failsafe_hook
- 1
- 2
- 3
- 4
- 5
- 6
- 7
由于脚本众多,因此openwrt的设计者将这些脚本分成下面几类:
preinit_essential
preinit_main
failsafe
initramfs
preinit_mount_root
- 1
- 2
- 3
- 4
- 5
每一类函数按照脚本的开头数字的顺序运行。
preinit执行的最后一个脚本为99_10_run_init
,运行
exec env - PATH=$pi_init_path $pi_init_env $pi_init_cmd
- 1
pi_init_cmd
为
pi_init_cmd="/sbin/init"
- 1
因此开始运行busybox的init命令
busybox的init命令执行/etc/inittab
的脚本,/etc/inittab
内容如下:
::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K stop
tts/0::askfirst:/bin/ash --login
ttyS0::askfirst:/bin/ash --login
tty1::askfirst:/bin/ash --login
- 1
- 2
- 3
- 4
- 5
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 S96led
K90network S05netconfig S41wmacfixup S50telnet S97watchdog
K98boot S10boot S45firewall S60dnsmasq S98sysntpd
K99umount S39usb S50cron S95done S99sysctl
- 1
- 2
- 3
- 4
上面的脚本文件来自/etc/init.d/
,在该文件夹下包含了各种应用程序的初始化脚本,这些脚本通过/etc/rc.common
脚本,将init.d的脚
本链接到/etc/rc.d
目录下,并且根据 这些脚本中的START和STOP的关键字,添加K${STOP}
和S${START}
的前缀,这样就决定了脚本的先后的运行次序。
openwrt的shell脚本比较复杂,因此看脚本时可以通过添加set -x
和echo
等命令,直接看shell脚本的结果,而不要花太多的时间硬看脚本,主要是理解其主要的意思和设计思路。
博客原文链接:https://blog.csdn.net/viewsky11/article/details/73201162
最后
以上就是凶狠星月为你收集整理的Openwrt 系统初始化分析的全部内容,希望文章能够帮你解决Openwrt 系统初始化分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复