OK6410A 开发板 (八) 19 linux-5.11 OK6410A start_kernel 功能角度 第三阶段之init进程

arch_call_rest_init
	rest_init
		pid = kernel_thread(kernel_init, NULL, CLONE_FS);
		pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
		cpu_startup_entry(CPUHP_ONLINE);
  • 内核进程1的创建过程
arm linux 内核源码剖析.pdf P407
  • 内核进程1开始运行的时刻
kthreadd -> schedule -> __schedule -> context_switch
此函数执行后, kernel_init 开始运行
  • 内核进程1应该负责的任务
内核上层建筑的初始化,为应用空间筑基做准备

可以看到 kernel_init 中 也有很多 xxx_init ,这些都是初始化
为什么称其为 上层建筑的初始化, 这些到底 和 第二阶段 有什么区别???

可以看到 kernel_init 中没有创建一个子进程,但是创建了(除去kthreadd外的所有的)内核进程
	在 kernel_init 过程中即使创建了内核进程,内核进程的父进程也不是 init内核进程,而是 kthreadd 内核进程
	实际上 除去 kthreadd 内核进程,其他内核进程都是 kernel_init 调用函数创建的
	
kernel_init 
	kernel_init_freeable
		workqueue_init
			// rescuer_thread 内核线程
			// kworker 内核线程
		smp_init
			// https://blog.csdn.net/u011011827/article/details/116489913
		do_basic_setup
			driver_init // 总线设备驱动模型的初始化
				devtmpfs_init
					// https://blog.csdn.net/u011011827/article/details/112694721
				devices_init
					// sys文件系统根目录 下 devices dev block char 文件夹的创建
				buses_init
					// sys文件系统根目录 下 bus devices/system 文件夹的创建
				classes_init
					// sys文件系统根目录 下 class 文件夹的创建
				firmware_init
					// sys文件系统根目录 下 firmware 文件夹的创建
				of_core_init
					// sys文件系统根目录 下 firmware/devicetree 文件夹的创建
				platform_bus_init
					// platform 总线的注册
			do_initcalls
				for_each_level[0...7] do_initcall_level(level, command_line);
					parse_args(initcall_level_names[level],...) 
						// 第三组(该组为8次,0...7) parse_args 
						// https://blog.csdn.net/u011011827/article/details/116085892
					for_each_initcall_in_level[level]
						do_one_initcall
							rootfs_initcall(populate_rootfs)
								// 挂载 集成式InitRamfs 或 独立式InitRamfs 到 rootfs ?
		console_on_rootfs
			// 生成 标准输入,标准输出,标准错误的文件描述符
			// 进程第一次打开文件,返回0
			// 复制两次,就使打开文件号 0 1 2 都成为同一个连接的代表
			// 0 对应 stdin,1 对应 stdout, 2 对应 stderr
		prepare_namespace
			// image-initrd技术(磁盘或ram)/无initxxx技术(磁盘或nfs) 挂载 最终文件系统
			// 如果是 image-initrd技术(最终挂载磁盘文件系统),那么过程中将会 执行  /initrd.img 中的可执行文件 /linuxrc (/linuxrc 会 挂载 最终的磁盘文件系统)
					
	free_initmem
		// free_initmem 回收 整个 初始化代码段 的内存空间
		// __init_begin -> __init_end
		// 具体包括哪些,请查询 arch/arm/kernel/vmlinux.lds.S 或 arch/arm/kernel/vmlinux.lds
		// __init 修饰的函数 在 __init_begin -> __init_end
		// #define __init      __section(".init.text") __cold  __latent_entropy __noinitretpoline
		// populate_rootfs 就被 __init 修饰了
	system_state = SYSTEM_RUNNING;
	if (ramdisk_execute_command) run_init_process // 针对 initramfs 和 cpio-initrd
	if (execute_command) run_init_process // 针对 image-initrd 和 无 initxxx技术

  • 内核进程1切换用户进程1的时刻
run_init_process 调用 kernel_execve

// x86 的方法
// 1. 1号内核进程伪装成用户进程是通过int指令进入核心态
// 2. 在1号内核进程的核心栈(tss->esp0)压入用户态的SS,ESP,EFLAGS,CS,EIP
// 3. 通过iret返回用户态

// arm 的方法
// 1. 1号内核进程更改自己的 thread_info 为 /linuxrc 的 thread_info(包括pc,sp,cpsr(用户态))  ,以及更改其他的...
// 2. 1号内核进程 退出,退出时调用 schedule -> switch_to 将 /linuxrc的 thread_info(之前为init内核进程的thread_info) 保存
// 3. 某次调度时机,将 1号内核进程调入,调入时 恢复 /linuxrc 的 thread_info , /linuxrc 开始执行



ret_from_fork
	kernel_init
		run_init_process
			kernel_execve
				bprm_execve
					do_open_execat
					exec_binprm
						search_binary_handler
							fmt->load_binary/load_elf_binary
								// regs 内容
								// {uregs = {0 <repeats 16 times>, 19, 0}} // 19 -> svc mode
								START_THREAD/start_thread // 此时将 内核init进程 的 task_struct 改写 为 /linuxrc 的 task_struct
								// regs 内容
								// {uregs = {0 <repeats 13 times>, 3200687888, 0, 119976, 48, 0}}

								//			uregs[0]-uregs[12] 		ARM_sp 	  ARM_lr ARM_pc ARM_cpsr(user mode)
								// {uregs = {0x0 <repeats 13 times>, 0xbec69f10, 0x0, 0x1d4a8, 0x30, 0x0}}
		if (!ret) return 0;
	get_thread_info tsk
	ret_slow_syscall
		disable_irq
		work_pending
			do_work_pending
				schedule // 调度出去
					// 调度的时候保存了 ....
					// 调度前该进程还处于内核态
					// 按道理 再次调度回来 还是 init内核进程
					// 但是 init 内核进程在调度之前换了身体(改写了task_struct 和 thread_info),但是身份(PID)没变
					// 调度回来的时候 按照 task_struct 和 thread_info 恢复
					// 调度回来的时候就不是在 do_work_pending -> schedule -> __schedule  -> context_switch -> barrier 了
					// thread_info  中的 cpsr 被初始化为 usermode了
					// 所以 调度回来 就是 用户态的 init进程(/linuxrc)了
					
					// 这里就是 init内核进程 消失的点,但 1号进程对应的 task_struct 还存在
					
					// 可以这么说 , 1号进程对应的 task_struct  是一个贝壳, 
					// 1号进程内核进程是一只老寄居蟹,1号用户进程进程是一只小寄居蟹
					// 老寄居蟹 死了,离开壳体, 小寄居蟹 进入了壳体
					// 而我们分辨寄居蟹 一般是 通过 壳体 , 所以 不管是老寄居蟹(1号内核进程kernel_init)还是小寄居蟹(1号用户进程/linuxrc) 都是 我的那只寄居蟹(1号进程)
					
---
过程中调度了多次.然后调度到了1号进程
---

/linuxrc 第一次被执行的状态是什么?
	取决于 之前保存的值
	//			uregs[0]-uregs[12] 		ARM_sp 	  ARM_lr ARM_pc ARM_cpsr(user mode)
	// {uregs = {0x0 <repeats 13 times>, 0xbec69f10, 0x0, 0x1d4a8, 0x30, 0x0}}
	
	// 某次 schedule -> __schedule  -> context_switch 之后
	// /linuxrc 的环境被准备好了,该设置的页表都设置好了,等等
	// 此时 pc = 0x1d4a8 		// 用户空间的地址
	// 此时 sp = 0xbec69f10 		// 用户空间的地址
	// 此时 cpsr = 0x30
	
	// pc = 0x1d4a8 处 是什么 ???
	// 此时 用户堆栈是什么 ???
	
		
  • 用户进程1开始运行的时刻
用户进程1 的第一条指令
1号内核进程 调用 run_init_process , 再运行下去, 1号内核进程被调出
当1号进程再次被调入,就是 用户进程1开始运行的时刻

  • 用户进程1应该负责的任务
应用空间的筑基

其实最简(不需要lib库)的用户进程
	可以while(1)
	还可以 循环i++
	还可以 系统调用打印一个字符,然后while(1);
但是这样设计的话,linux 之前的初始化就 显得无力了.
所以 用户空间的 init 进程 一般这么设计

int main(void){
	type value;
	service_init();
	while(1){
		value = get_form_user(); 	// 从用户那里得到要求
		serve_user(value); 			// 按照要求做事
	}
	return 0;
}

// 实际上 init 进程的设计 有很多实现
	busybox 的 linuxrc
	sysvinit
	systemd
	...
有多少用户进程
// 除去 init进程linuxrc , 其他用户进程都是 init 或 init 的子进程 创建的

进程ID 所属用户 		 状态  COMMAND进程名
    1 root      1412 S    {linuxrc} init
   52 root      4100 S    /sbin/mdev -df
   64 root     19388 S    /usr/bin/Xorg :0.0 vt01 -s 0 -noreset -allowMouseOpe
   75 root      2352 S    /usr/sbin/dropbear -R
   76 root      3244 S    -bash


linuxrc(1)-+-Xorg(64)
           |-bash(76)
           |-dropbear(75)---pstree(181)
           `-mdev(52)

x86 的1号进程切换
sys_execve
	do_execve
		load_elf_binary()
			do_load_elf_binary()
				do_mmap() 
					// do_mmap完成从文件虚拟空间到内存虚拟空间的映射。
				start_thread(reg,newip,newsp) (processor.h) 
					// start_thread就是要在进程核心栈中的相应位置填入进程用户态的xss,esp and xcs,eip.
					// 最后进程从ret_from_sys_call返回
					// iret指令从核心栈pop出xcs, eip完成特权及指令的转移, pop出 xss,esp,完成堆栈的切换
					// 此时,已经是用户进程了.
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值