首先执行前面的一下汇编代码, C语言的入口是在init/main.c 的 start_kernel 函数,在这个函数中进行时钟,内存,中断等的初始化,然后执行rest_init()函数,其中再执行cpu_idle()函数, 在这会有一个while循环
/* endless idle loop with no priority at all */
while (1) {
idle_notifier_call_chain(IDLE_START);
tick_nohz_idle_enter();
rcu_idle_enter();
while (!need_resched()) {
#ifdef CONFIG_HOTPLUG_CPU
if (cpu_is_offline(smp_processor_id()))
cpu_die();
#endif
/*
* We need to disable interrupts here
* to ensure we don't miss a wakeup call.
*/
local_irq_disable();
#ifdef CONFIG_PL310_ERRATA_769419
wmb();
#endif
if (hlt_counter) {
local_irq_enable();
cpu_relax();
} else if (!need_resched()) {
stop_critical_timings();
if (cpuidle_idle_call())
pm_idle();
start_critical_timings();
/*
* pm_idle functions must always
* return with IRQs enabled.
*/
WARN_ON(irqs_disabled());
} else
local_irq_enable();
}
rcu_idle_exit();
tick_nohz_idle_exit();
idle_notifier_call_chain(IDLE_END);
schedule_preempt_disabled();
}
该循环会不断轮询进程的pcb链表,执行获得cpu控制权的进程.
进程的pcb实际上时struct task_struct的结构体.
系统调用实际的流程如下
1.保存函数的参数到寄存器
2.保存系统调用号到eax
3.执行0x80中断
4.系统调用执行完后,返回值保存在eax中
以下是执行time系统调用的汇编代码
int TimeAsm(int argc, char *argv[])
{
time_t tt;
struct tm *t;
asm volatile(
"mov $0,%%ebx\n\t"
"mov $0xd,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
: "=m" (tt)
);
t = localtime(&tt);
printf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
return 0;
}
cpu一般情况下在执行cpu_idle中的循环,当系统调用发生后,产生中断,执行中断处理也就是执行系统调用,结束后再继续cpu_idle的循环.
创建进程的解析
APP中执行fork,最后内核中会调用sys_clone函数,sys_clone最后会调用do_fork函数.
那么fork为什么能返回两个返回值呢?子进程返回0,父进程返回值大于0.原因是在do_fork中会复制父进程的pcb,然后修改其中的返回值使得两份pcb有不同的返回值.而cpu_idle的轮询是根据pcb进行的,多了一个pcb也就相当于多了一个进程,并且多出来的pcb保存的返回值与原来的不一样,这样在执行完系统调用fork后就表现为fork具有两个返回值.