9,linux进程切换
进程切换:
基本概念:
进程上下文:当一个进程在执行时,CPU的所有寄存器中的值、进程的状态以及堆栈中的内容被称为该进程的上下文。当内核需要切换到另一个进程时,它需要保存当前进程的所有状态,即保存当前进程的上下文,运行于进程上下文的进程是可以被抢占的。
硬件上下文:进程恢复执行前必须载入寄存器的一组数据称为硬件上下文
linux内核在进程切换的时候是并不区分进程和线程的~因为切换是针对task,
进程切换的时机:一般是在系统调用或者中断的时候,发生在内核态。
进程切换过程:
进程切换统一发生在schedule()函数中,核心函数是context_switch()函数,代码位置:kernel/kernel/sched/Core.c文件,以ARM为基础讲解linux进程的切换:linux进程切换过程,schedule()函数代码分析:schedule()代码分析 schedule代码分析二
10,idle进程
idle进程的概念:
简单的说idle是一个进程,其pid号为 0。其前身是系统创建的第一个进程,也是唯一一个没有通过fork()产生的进程。在smp系统中,每个处理器单元有独立的一个运行队列,而每个运行队列上又有一个idle进程,即有多少处理器单元,就有多少idle进程。系统的空闲时间,其实就是指idle进程的"运行时间"。idle进程pid==o,也就是init_task.
idle进程的创建:
vmlinux的入口是arch/arm/kernel/head.S,(关注ENTRY(stext)的位置)入口地址可以从kernel/arch/arm/kernel/vmlinux.lds.S得知文件,在该汇编文件中为pid为0的原始进程设置了执行环境,包括先禁止MMU,内核刚开始是用物理地址的,禁止I-Cache和D-Cache,然后读取RO,R1,R2,然后原是进程开始执行start_kernel()完成Linux内核的初始化工作。包括初始化页表,初始化中断向量表,初始化系统时间等。继而调用 fork(),创建第一个用户进程: 创建init进程(pid==1)的函数如下:
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
pid为1的init进程,它会继续完成剩下的初始化工作,然后execve(/sbin/init), 成为系统中的其他所有进程的祖先,pid为0的idle进程在创建了init进程后,pid=0的进程调用 cpu_idle()演变成了idle进程。current_thread_info()->status |= TS_POLLING;init在演变成/sbin/init之前,会执行一部分初始化工作,其中一个就是 smp_prepare_cpus(),初始化SMP处理器,在这过程中会在处理每个从处理器时调用 task = copy_process(CLONE_VM, 0, idle_regs(®s), 0, NULL, NULL, 0); init_idle(task, cpu);
idle进程的运行时机:
idle进程优先级为MAX_PRIO,即最低优先级。早先版本中,idle是参与调度的,所以将其优先级设为最低,当没有其他进程可以运行时,才会调度执行idle,而目前的版本中idle并不在运行队列中参与调度,而是在运行队列结构中含idle指针,指向idle进程,在调度器发现运行队列为空的时候运行,调入运行
idle进程到底做了什么:
idle进程最终调用了cpu_idle()函数
process.c
void cpu_idle(void)
{
......
/* endless idle loop with no priority at all */
while (1) {
......
while (!need_resched()) {
if (cpu_is_offline(smp_processor_id())) {
tick_set_cpu_plugoff_flag(1);
cpu_die(); /* plugoff CPU */
}
......
if (cpuidle_idle_call())
pm_idle(); /* 进入低电 */
}
......
schedule_preempt_disabled(); /* 调用schedule() */
}
}
当嵌入式设备从uboot跳转到kernel的时候,首先运行的是kernel的自解压程序,linux内核在编译后是以压缩文件形式存在,路径是kernel/out/arch/arm/boot/zImage
zImage里的代码和数据是如何存放的,答:根据out/arch/arm/boot/compressed/vmlinux.lds文件,如果要修改代码段和数据段位置也需要修改该链接脚本。
kernel init的基本流程如下:
.stext
start_kernel()
rest_init()
kernel_init()