openwrt系统启动分析

本文详细描述了从U-Boot的NAND加载环境参数和kernel,再到内核加载过程中的模块加载、init进程启动和preinit脚本执行,最后是procd状态机中inittab的执行,展示了OpenWRT系统的启动流程。
摘要由CSDN通过智能技术生成

如何根据启动时的串口打印信息,从宏观上找到对应的启动逻辑?

一、uboot启动

uboot启动信息:

loading Environment from NAND... OK
In:	seriala40700000	
Out:	seriala40700000	
Err:	seriala40700000	
Net:	No ethernet found.	
Hit any key to stop autoboot: 
Booting from nand ... ...

NAND read: device 0 offset 0x400000,size 0x1000000
	16777216 bytes read: OK 
NAND read: device 0 offset 0x3c0o00,size 0x40000
	262144 bytes read: OK
## Flattened Device Tree blob at 85000000
Booting using the fdt blob at 0x85000000
Loading Device Tree to0000000086ccc000,end oooo000086cd8e17... OK


根据以上串口信息,可以看到,uboot从nand中获取环境参数并启动,在跳过bootdelay后,从nand中读了两个分区,代码中找到对应的启动参数如下,

setenv bootargs noinitrd rootfstype=squashfs,ubifs console=ttyS0,115200n8 rdinit=/sbin/init mem=${kernelmem} boot=nand; \
 nand read ${openwrt_addr_r} firmware 0x1000000; \
 nand read ${fdt_addr_r} device-tree; booti ${kernel_addr_r} - ${fdt_addr_r} ;

可以发现这两个分区正是kernel和device-tree, 这两个分区使用nand命令读取,找到uboot源码中的nand命令的实现(do_nand),U_BOOT_CMD 宏将do_nand注册到uboot命令段(u_boot_list)中,
read操作是将nand中的kernel和device-tree拷贝到DDR中对应的位置
拷贝完成后,进入下一步的内核启动。

二、内核启动

内核启动信息(已删除部分非必要信息)
 

[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x411fd040]
[    0.000000] Linux version 5.x.x
[    0.000000] NUMA: No NUMA configuration found
...
[    0.000000] psci: probing for conduit method from DT.
...
[    0.000000] percpu: Embedded 17 pages/cpu s32576 r8192 d28864 u69632
[    0.109832] pinctrl pinctrl: initialized NVT pinctrl driver
...
[    0.228443] cryptd: max_cpu_qlen set to 1000
[    0.246017] SCSI subsystem initialized
[    0.248824] videodev: Linux video capture interface: v2.00
[    0.260803] Advanced Linux Sound Architecture Driver Initialized.
[    0.265851] clocksource: Switched to clocksource arch_sys_counter
[    0.268969] NET: Registered protocol family 2
[    0.269199] IP idents hash table entries: 4096 (order: 3, 32768 bytes, linear)
[    0.270197] tcp_listen_portaddr_hash hash table entries: 128 (order: 1, 8192 bytes, linear)
[    0.270240] TCP established hash table entries: 2048 (order: 2, 16384 bytes, linear)
[    0.270297] TCP bind hash table entries: 2048 (order: 4, 114688 bytes, linear)
[    0.270448] TCP: Hash tables configured (established 2048 bind 2048)
[    0.270588] UDP hash table entries: 256 (order: 3, 32768 bytes, linear)
[    0.270659] UDP-Lite hash table entries: 256 (order: 3, 32768 bytes, linear)
[    0.271070] NET: Registered protocol family 1
[    0.274660] RPC: Registered named UNIX socket transport module.
[    0.274671] RPC: Registered udp transport module.
[    0.274677] RPC: Registered tcp transport module.
[    0.274682] RPC: Registered tcp NFSv4.1 backchannel transport module.
[    0.289480] Initialise system trusted keyrings
[    0.292459] workingset: timestamp_bits=44 max_order=16 bucket_order=0
[    0.303852] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    0.305383] NFS: Registering the id_resolver key type
[    0.305460] Key type id_resolver registered
[    0.305466] Key type id_legacy registered
[    0.386903] NET: Registered protocol family 38
[    0.386925] Key type asymmetric registered
[    0.440567] nand: device found, Manufacturer ID: 0xef, Chip ID: 0xda
[    0.440580] nand: Winbond W29N02GV
[    0.440586] nand: 256 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64
[    0.440600] attach: page 2048, SMRA size 64, 60
[    0.441382] Bad block table not found for chip 0
[    0.442035] Bad block table not found for chip 0
[    0.442050] Scanning device for bad blocks
[    0.525290] Bad block table written to 0x00000ffe0000, version 0x01
[    0.526384] Bad block table written to 0x00000ffc0000, version 0x01
[    0.526932] 5 fixed-partitions partitions found on MTD device nand0
[    0.526945] Creating 5 MTD partitions on "nand0":
...
[    0.534645] 2 uimage-fw partitions found on MTD device firmware
...
[    0.558965] spi-nor spi1.0: w25q128 (16384 Kbytes)
[    0.559696] 1 fixed-partitions partitions found on MTD device spi1.0
...
[    0.581585] CAN device driver interface
[    0.582513] gmac 40120000.ethernet: IRQ eth_wake_irq not found
[    0.605634] PPP generic driver version 2.4.2
[    0.606086] PPP Deflate Compression module registered
[    0.606094] NET: Registered protocol family 24
[    0.607210] dwc2 40200000.usb: supply vusb_d not found, using dummy regulator
[    0.607564] dwc2 40200000.usb: supply vusb_a not found, using dummy regulator
[    0.815001] dwc2 40200000.usb: Configuration mismatch. dr_mode forced to device
[    0.815098] dwc2 40200000.usb: EPs: 9, dedicated fifos, 3000 entries in SPRAM
[    0.817200] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[    0.817212] ehci-platform: EHCI generic platform driver
...
[    0.985934] i2c /dev entries driver
[    1.008168] rtc-bl5372 0-0032: registered as rtc0
...
[    1.067483] xxxxxx spi0.0: fbtft_property_value: width = 128、
...
[    1.169065] graphics fb0: xxxxxx frame buffer, 128x64, 1 KiB video memory, 1 KiB buffer memory, fps=30, spi0.0 at 500000 Hz, mode : 3
[    3.494913] ubi0: scanning is finished
...
[    3.529200] block ubiblock0_0: created from ubi0:0(rootfs)
[    3.547855] input: gpio_keys_test as /devices/platform/gpio_keys_test/input/input0
[    3.549224] cfg80211: Loading compiled-in X.509 certificates for regulatory database
[    3.567604] cfg80211: Loaded X.509 cert 'sforshee: 00b28ddf47aef9cea7'
[    3.568037] platform regulatory.0: Direct firmware load for regulatory.db failed with error -2
[    3.568055] platform regulatory.0: Falling back to sysfs fallback for: regulatory.db
[    3.592695] VFS: Mounted root (squashfs filesystem) readonly on device 31:8.
[    3.593097] Freeing unused kernel memory: 384K
[    3.605069] Run /sbin/init as init process
[    4.450565] init: Console is alive
[    5.021450] kmodloader: loading kernel modules from /etc/modules-boot.d/*
[    5.748349] kmodloader: done loading kernel modules from /etc/modules-boot.d/*
[    5.757337] init: - preinit -
noinitrd rootfstype=squashfs,ubifs isolcpus=1 console=ttyS0,115200n8 rdinit=/sbin/init mem=248M boot=nand
noinitrd rootfstype=squashfs,ubifs isolcpus=1 console=ttyS0,115200n8 rdinit=/sbin/init mem=248M boot=nand
[    6.931457] UBIFS (ubi0:1): Mounting in unauthenticated mode
...
[    7.070570] mount_root: switching to ubifs overlay
[    7.081731] overlayfs: "xino" feature enabled using 2 upper inode bits.
[    7.094767] urandom-seed: Seeding with /etc/urandom.seed
[    7.250743] procd: - early -
[    8.044287] procd: - ubus -
[    8.448459] procd: - init -

Please press Enter to activate this console.
[    8.773803] kmodloader: loading kernel modules from /etc/modules.d/*
[    8.789284] tun: Universal TUN/TAP device driver, 1.6
[    8.805047] kmodloader: done loading kernel modules from /etc/modules.d/*
[   10.184646] gmac 40120000.ethernet eth0: PHY [stmmac-0:01] driver
...
[   10.259244] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
[   10.407613] device eth0 entered promiscuous mode
[   10.525230] random: ubusd: uninitialized urandom read (4 bytes read)
...
[   15.020644] urngd: v1.0.2 started.
...
[   18.959710] gmac 40130000.ethernet eth1: PHY [stmmac-1:00] driver 
...
[   22.989161] IPv6: ADDRCONF(NETDEV_CHANGE): wlan1: link becomes ready
 


进入内核后,先加载各类驱动,包括网卡,USB,flash,DDR,准备OS运行环境等。
根据以上日志,在系统启动到第3.6秒时( Run /sbin/init as init process),启动了一个init进程。从内核的启动参数中,

noinitrd rootfstype=squashfs,ubifs isolcpus=1 console=ttyS0,115200n8 rdinit=/sbin/init mem=248M boot=nand


可以看到,init进程是系统启动的第一个进程。

三、init分析

在系统中找到init程序的位置(grep 命令),发现init程序是procd包中的一个程序。procd中的init.c文件main函数如下

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);
	sigaction(SIGPWR, &sa_shutdown, NULL);

	if (selinux(argv))
		exit(-1);
	early();
	cmdline();
	watchdog_init(1);

	pid = fork();
	if (!pid) {
		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: %m\n");
		exit(EXIT_FAILURE);
	}
	if (pid <= 0) {
		ERROR("Failed to start kmodloader instance: %m\n");
	} else {
		const struct timespec req = {0, 10 * 1000 * 1000};
		int i;

		for (i = 0; i < 1200; i++) {
			if (waitpid(pid, NULL, WNOHANG) > 0)
				break;
			nanosleep(&req, NULL);
			watchdog_ping();
		}
	}
	uloop_init();
	preinit();
	uloop_run();

	return 0;
}

在子进程中调用kmodloader,加载各种内核模块,父进程等待内核模块加载完毕后,进入preinit函数。preinit相关函数如下(preinit.c)


static void
spawn_procd(struct uloop_process *proc, int ret)
{
	char *wdt_fd = watchdog_fd();
	char *argv[] = { "/sbin/procd", NULL};
	char dbg[2];

	if (plugd_proc.pid > 0)
		kill(plugd_proc.pid, SIGKILL);

	unsetenv("PREINIT");
	unlink("/tmp/.preinit");

	check_sysupgrade();

	DEBUG(2, "Exec to real procd now\n");
	if (wdt_fd)
		setenv("WDTFD", wdt_fd, 1);
	check_dbglvl();
	if (debug > 0) {
		snprintf(dbg, 2, "%d", debug);
		setenv("DBGLVL", dbg, 1);
	}

	execvp(argv[0], argv);
}

static void
plugd_proc_cb(struct uloop_process *proc, int ret)
{
	proc->pid = 0;
}

void
preinit(void)
{
	char *init[] = { "/bin/sh", "/etc/preinit", NULL };
	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) {
		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) {
		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);
}

 可以看到,preinit又在一个子进程中启动脚本/etc/preinit,其将按照顺序启动/lib/preinit/目录下的所有脚本。在preinit创建的另一个进程中,启动procd程序,procd的状态机代码如下 (state.c)

static void state_enter(void)
{
	char ubus_cmd[] = "/sbin/ubusd";
	struct passwd *p;

	switch (state) {
	case STATE_EARLY:
		LOG("- early -\n");
		watchdog_init(0);
		hotplug("/etc/hotplug.json");
		procd_coldplug();
		break;

	case STATE_UBUS:
		// try to reopen incase the wdt was not available before coldplug
		watchdog_init(0);
		set_stdio("console");
		p = getpwnam("ubus");
		if (p) {
			int ret;
			LOG("- ubus -\n");
			mkdir(p->pw_dir, 0755);
			ret = chown(p->pw_dir, p->pw_uid, p->pw_gid);
			if (ret)
				LOG("- ubus - failed to chown(%s)\n", p->pw_dir);
		} else {
			LOG("- ubus (running as root!) -\n");
		}

		procd_connect_ubus();
		service_start_early("ubus", ubus_cmd, p?"ubus":NULL, p?"ubus":NULL);
		break;

	case STATE_INIT:
		LOG("- init -\n");
		procd_inittab();
		procd_inittab_run("respawn");
		procd_inittab_run("askconsole");
		procd_inittab_run("askfirst");
		procd_inittab_run("sysinit");

		// switch to syslog log channel
		ulog_open(ULOG_SYSLOG, LOG_DAEMON, "procd");
		break;

	case STATE_RUNNING:
		LOG("- init complete -\n");
		procd_inittab_run("respawnlate");
		procd_inittab_run("askconsolelate");
		break;

	case STATE_SHUTDOWN:
		/* Redirect output to the console for the users' benefit */
		set_console();
		LOG("- shutdown -\n");
		procd_inittab_run("shutdown");
		sync();
		break;

	case STATE_HALT:
		// To prevent killed processes from interrupting the sleep
		signal(SIGCHLD, SIG_IGN);
		LOG("- SIGTERM processes -\n");
		kill(-1, SIGTERM);
		sync();
		sleep(1);
		LOG("- SIGKILL processes -\n");
		kill(-1, SIGKILL);
		sync();
		sleep(1);
#ifndef DISABLE_INIT
		perform_halt();
#else
		exit(EXIT_SUCCESS);
#endif
		break;

	default:
		ERROR("Unhandled state %d\n", state);
		return;
	};
}

在procd程序运行到STATE_INIT状态时,执行函数procd_inittab以及procd_inittab_run,将会执行/etc/inittab中的内容,inittab如下

::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K shutdown
::askconsole:/usr/libexec/login.sh


第一行将按照rc.d中规定启动的顺序进行执行。至此,openwrt系统完成了启动。
 

Openwrt文件系统在初始化时采用了mini_fo文件系统类型。在启动过程中,Linux内核加载只读的squash文件系统。然后,在/etc/preinit/脚本中,通过一行代码重新挂载整个文件系统为mini_fo文件系统类型,完成文件系统的初始化。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Openwrt 文件系统](https://blog.csdn.net/weixin_31175771/article/details/116715731)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [龙芯1b的openwrt](https://download.csdn.net/download/qq_21850249/86731509)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [openwrt系统初始化分析](https://blog.csdn.net/viewsky11/article/details/73201162)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值