OK6410A 开发板 (八) 18 linux-5.11 OK6410A start_kernel 功能角度 第二阶段之idle进程

idle 进程 相关的打印: 无
idle 进程相关的 函数
	__mmap_switched 中的 ARM( ldmia   r4!, {r0, r1, sp} )	
	sched_init
idle 相关函数
start_kernel
	sched_init
		// 因为 1.__mmap_switched 中的 ARM( ldmia   r4!, {r0, r1, sp} )	
		// 因为 2.__mmap_switched_data 中的 .long   init_thread_union + THREAD_START_SP @ sp

		/*

		include/asm-generic/current.h
			#define current get_current()
			#define get_current() (current_thread_info()->task)

		arch/arm/include/asm/thread_info.h
			current_thread_info
				return (struct thread_info *) (current_stack_pointer & ~(THREAD_SIZE - 1));


		arch/arm/include/asm/percpu.h
			register unsigned long current_stack_pointer asm ("sp");


		 */

		// current 等于 &init_task
		// smp_processor_id() 等于 0 
		init_idle(current, smp_processor_id()); 
			// 用idle制作当前进程(current),并将 sched_class 设置为 idle_sched_class.启动时就能显示为...进程
			
			// DECLARE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);
			// #define cpu_rq(cpu)     (&per_cpu(runqueues, (cpu)))
			struct rq *rq = cpu_rq(cpu);

			// 下面 idle 为 &init_task
			__sched_fork(0, idle);
				// 设置 tcb 中的 进程调度相关的成员
			上锁
				idle->state = TASK_RUNNING;
				rq->idle = idle;
			解锁

			idle->sched_class = &idle_sched_class;


	fork_init
		// create a slab on which task_structs can be allocated
		task_struct_cachep = kmem_cache_create_usercopy("task_struct"
		// 设置最大进程数目
		set_max_threads(MAX_THREADS);
		// 将0号进程的task_t结构中的进程数资源限制的 当前值 设置为系统允许进程的一半
		init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
		// 将0号进程的task_t结构中的进程数资源限制的 最大值 设置为系统允许进程的一半
		// init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
	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);


// 问题 : 从裸机 到 进程0 是什么时候开始,什么时候结束的
	从 stext就开始了
	到 start_kernel->rest_init->schedule_preempt_disabled -> schedule -> __schedule -> context_switch 结束
		// 此时 cpu 就要执行 另一个进程了.
		
// 问题 : 创建进程0 的时候 init_task 是什么时候挂载到 strcuct rq 上的

	早先版本中,idle是参与调度的,所以将其优先级设为最低,当没有其他进程可以运行时,才会调度执行 idle
	而目前的版本中idle并不在运行队列中参与调度,而是在cpu全局运行队列rq中含idle指针,指向idle进程, 在调度器发现运行队列为空的时候运行, 调入运行
	init_idle -> rq->idle = idle;

// 问题 : 创建进程0 的时候 怎么处理 入口函数的? 此时linux裸机已经在跑了,没有入口
	
	idle 进程的建立的 过程 // 其实就是 init_task 结构体初始化的过程
		1.sp 的初始化
			// __mmap_switched -> ARM( ldmia   r4!, {r0, r1, sp} )
			// __mmap_switched_data:
			// .long   init_thread_union + THREAD_START_SP @ sp
			// 在链接过程中确定了 一个值 (该值与 init_thread_union  相关)
			// 将该值 写入 sp 中
			// start_kernel 在 sp 对应的栈 中运行
		2.TCB(init_task&init_thread_info)结构体的建立
			// 静态创建的,不需要创建
			// init_task&init_thread_info 是给 idle(一开始的裸机) 准备的 TCB
		3.TCB(init_task&init_thread_info)结构体的定位
			// idle 找到 为 它 准备的 TCB
			// 根据current 找到该 裸机(后来的idle) 对应的结构体(init_task&init_thread_info)
			// init_task  是 idle 进程的 信息1,供调度器(schedule)选择下一个进程用
			// init_thread_info.cpu_context 是 idle进程的信息2,存储寄存器信息
		4. init_idle:idle 进程的 信息1(init_task) 的初始化
			//供 调度算法 选择 哪个进程 为 下一个进程
		//5. kernel_thread/fork 的时候 建立了 其他(kenel_init) 进程
		6. schedule:idle 进程的 信息2(init_thread_info.cpu_context) 的初始化
			// 当前寄存器信息的保存
			// idle调出 start_kernel->rest_init->schedule_preempt_disabled -> schedule -> __schedule -> context_switch
			// 的时候 , 保存了 idle 的 各个寄存器 到  idle进程的信息2(init_thread_info.cpu_context)
			// 这个过程是调度器的一部分,但是同时也是idle TCB 结构体初始化过程的一部分
		7. 其他进程调用 schedule  , 选中 init_task 为 下一个 进程
			// schedule -> __schedule -> pick_next_task
		8. 将 init_task 对应的 init_thread_info.cpu_context 恢复到寄存器中
			// idle调入 schedule -> __schedule -> context_switch
			// 的时候 , 恢复了 idle进程的信息2(init_thread_info.cpu_context) 到 寄存器
			// 此时,idle 重新执行

进程idle的建立
  • 各个cpu 上0号进程 idle 的建立
创建过程
与cpu 的关系

cpu0 上的 idle进程
	内核裸机程序(在 创建init 进程之前可以这么称呼吧) 在调用 sched_init 之后将自己变成了 0 号内核进程idle
	
	// 这里要明白 裸机 和 进程的区别
	// 进程比裸机 多了 以下内容
	// 1.tcb,且 tcb的状态要设置好,其中 :  调度器要认识该tcb
	// 2.进程代码运行的时候可以随时切出
	// 那么 裸机切换到 进程(进程0)的时候,就需要做这些设置
	// 什么时候能判断 该 idle0 创建成功了
	// 0.current 能够索引当前的 tcb,start_kernel 刚进入时就已经能索引了(和__mmap_switched中赋值sp有关)
	// 1.idle 又创建了一个线程A,idle 放弃cpu ,A开始正常运行,且能和idle进程正常调度
	
	// 这就需要知道 调度器, 调度器 使用的 task_struct 序列
	start_kernel -> sched_init 
		-> init_idle(current, smp_processor_id());
			-> __sched_fork(0, idle);
			-> idle->state = TASK_RUNNING;
	
	0号内核进程 在创建了init和kthreadd进程后, 调用cpu_idle()开始做idle循环
	start_kernel -> arch_call_rest_init -> rest_init
		-> cpu_startup_entry
			-> do_idle
	
	// 问题 : task_struct 结构体在哪里		
	
// bootloader 转接给 kernel 之前 , 只有cpu0 在 跑 流程代码, 其他的cpu1...都是 halt 住的
cpu1 上的 idle 进程
	cpu0 上的 init内核进程在演变成/sbin/init之前,会调用 smp_init(),让cpu1,cpu2 ... 开始启动
	cpu1,cpu2 ... 启动过程中 会调用 如下函数,创建 对应cpu上的 idle进程
	task = copy_process(CLONE_VM, 0, idle_regs(&regs), 0, NULL, NULL, 0); 
	init_idle(task, cpu);
	从而创建 cpu1 上的 idle进程,cpu2 上的 idle进程, ...
	
    cpu0:smp_init
        idle_threads_init
            for_each_possible_cpu idle_init
                fork_idle
                    copy_process
        bringup_nonboot_cpus
            cpu_up
                _cpu_up
                    smp_ops.smp_boot_secondary(cpu, idle); / 即 psci_boot_secondary
                        psci_ops.cpu_on // 对应 0.1 版本的 psci为 psci_0_1_cpu_on
                            psci_0_1_cpu_on
                                __psci_cpu_on
                                    invoke_psci_fn // 调用什么 取决于 set_conduit 的设定值
                                        __invoke_psci_fn_smc
                                            arm_smccc_smc
                                                __arm_smccc_smc
                                                    SMCCC SMCCC_SMC
                                                        __SMC
                                                            0xE1600070 | (((imm4) & 0xF) << 0),
                                                            0xF7F08000 | (((imm4) & 0xF) << 16)


    cpu1:secondary_startup
        __secondary_switched
            secondary_start_kernel
                pr_debug("CPU%u: Booted secondary processor\n", cpu);
                cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
                	do_idle

	
	
	
	
	// 问题 : task_struct 结构体在哪里	
	//  作用是什么,为什么一个cpu上有一个

前两次调度时机
  • 第一次调度时机
start_kernel->rest_init->schedule_preempt_disabled -> schedule -> __schedule -> context_switch

调度到了 kthreadd

  • 第二次调度时机
kthreadd -> schedule -> __schedule -> context_switch
调度到了 kernel_init
idle 流程
  • idle 的流程
当需要调度时 , need_resched()1
cpu_startup_entry
	schedule_preempt_disabled
		schedule
		
当不需要调度时, need_resched()0

cpu_startup_entry
	cpu_idle_loop
		cpuidle_idle_call
			arch_cpu_idle
				cpu_do_idle
					cpu_v7_do_idle
						dsb
						wfi
						ret lr

  • 什么时候设置的 need_resched
TODO
其他
  • thread->cpu_context
kernel_init : r4:0,r5:c0535d3c(kernel_init),pc:c010015c(ret_from_fork)
kthreadd 	: r4:0,r5:c012cc14(kthreadd),pc:c010015c(ret_from_fork)
--- 
idle
rest_init 	: r4:0,r5:0,pc:0
schedule_preempt_disabled -> schedule -> __schedule -> context_switch 中设置了 r4 r5 r6
do_idle 	: r4:c0e55d08(init_stack/init_thread_info/init_thread_union),r5:c0805980(init_task),pc:c0536778(__schedule中)

  • idle 堆栈
Freeing unused kernel memory: 1024K 前的堆栈
[14:57:54]CPU: 0 PID: 0 Comm: swapper Not tainted 5.11.0-00008-g0cf53aa024f-dirty #180
[14:57:54]Hardware name: SMDK6410
[14:57:54][<c0105b60>] (unwind_backtrace) from [<c010477c>] (show_stack+0x10/0x14)
[14:57:54][<c010477c>] (show_stack) from [<c0135434>] (do_idle+0x8/0xd4)
[14:57:54][<c0135434>] (do_idle) from [<c01357d0>] (cpu_startup_entry+0x10/0x14)
[14:57:54][<c01357d0>] (cpu_startup_entry) from [<c07016c8>] (start_kernel+0xbf4/0xe0c)
[14:57:54][<c07016c8>] (start_kernel) from [<00000000>] (0x0)


Freeing unused kernel memory: 1024K 后的堆栈
[14:57:54]CPU: 0 PID: 0 Comm: swapper Not tainted 5.11.0-00008-g0cf53aa024f-dirty #180
[14:57:54]Hardware name: SMDK6410
[14:57:54][<c0105b60>] (unwind_backtrace) from [<c010477c>] (show_stack+0x10/0x14)
[14:57:54][<c010477c>] (show_stack) from [<c0135434>] (do_idle+0x8/0xd4)
[14:57:54][<c0135434>] (do_idle) from [<c01357d0>] (cpu_startup_entry+0x10/0x14)
[14:57:54][<c01357d0>] (cpu_startup_entry) from [<c07016c8>] (start_kernel+0xbf4/0xe0c)

  • kenel_init 堆栈
[14:57:47]CPU: 0 PID: 1 Comm: swapper Not tainted 5.11.0-00008-g0cf53aa024f-dirty #180
[14:57:47]Hardware name: SMDK6410
[14:57:47][<c0105b60>] (unwind_backtrace) from [<c010477c>] (show_stack+0x10/0x14)
[14:57:47][<c010477c>] (show_stack) from [<c015a7f4>] (__clocksource_update_freq_scale+0x8c/0x224)
[14:57:47][<c015a7f4>] (__clocksource_update_freq_scale) from [<c015a9bc>] (__clocksource_register_scale+0x30/0x104)
[14:57:47][<c015a9bc>] (__clocksource_register_scale) from [<c0701954>] (do_one_initcall+0x74/0x18c)
[14:57:47][<c0701954>] (do_one_initcall) from [<c0701bf0>] (kernel_init_freeable+0x130/0x1b0)
[14:57:47][<c0701bf0>] (kernel_init_freeable) from [<c0535c4c>] (kernel_init+0x8/0x11c)
[14:57:47][<c0535c4c>] (kernel_init) from [<c0100170>] (ret_from_fork+0x14/0x24)
[14:57:47]Exception stack(0xc144bfb0 to 0xc144bff8)
[14:57:47]bfa0:                                     00000000 00000000 00000000 00000000
[14:57:47]bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[14:57:47]bfe0: 00000000 00000000 00000000 00000000 00000013 00000000

  • 调度类
有五种调度类:

fair_sched_class,现在较高版本的Linux上也就是CFS(Completely Fair Scheduler),Linux上面主要的调度方式,由CONFIG_FAIR_GROUP_SCHED宏控制
rt_sched_class,由CONFIG_RT_GROUP_SCHED宏控制,实时调度类型。
dl_sched_class,deadline调度类,实时调度中较高级别的调度类型,一般之后在系统紧急情况下会调用;
stop_sched_class,最高优先级的调度类型,属于实时调度类型的一种,在系统终止时会在其上创建进程进入调度。
idle_sched_class,优先级最低,在系统空闲时才会进入该调度类型调度,一般系统中只有一个idle,那就是初始化进程init_task,在初始化完成后它将自己设置为idle进程,并不做更多工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值