Linux arm啟動c語言部分詳解第四講(from setup_per_cpu_areas();)
Written by leeming
上面的setup_arch花了我們大量的篇幅,現在我們要繼續往前推進了。
注:黑色為主線,藍色為函數的一級展開,紅色是注意重要的地方。
//因為我們沒有定義CONFIG_SMP,所以這兩個函數都為空
setup_per_cpu_areas();
smp_prepare_boot_cpu();
/*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
//和進程初始化有關的函數,進程是任何操作系統的一個大點,因此這部分內容還是很多的,我這次主要是講解c語言的啟動,所以這部分暫時會比較淺的涉及,以后有機會也詳細談到
sched_init();
{
runqueue_t *rq;
int i, j, k;
for_each_cpu(i) {
prio_array_t *array;
//獲取每個cpu的運行隊列結構體runqurere_t
rq = cpu_rq(i);
spin_lock_init(&rq->lock);
rq->nr_running = 0;//該隊列中可運行的進程數
//prio_array_t *active, *expired, arrays[2];
rq->active = rq->arrays;
rq->expired = rq->arrays + 1;
rq->best_expired_prio = MAX_PRIO;
/*此處刪除了smp的內容*/
atomic_set(&rq->nr_iowait, 0);
//初始化active和expired隊列位圖,將優先隊列中的0-(MAX_PRIO-1)清0
//將MAX_PRIO對應的置1
for (j = 0; j < 2; j++) {
array = rq->arrays + j;
for (k = 0; k < MAX_PRIO; k++) {
INIT_LIST_HEAD(array->queue + k);
__clear_bit(k, array->bitmap);
}
// delimiter for bitsearch
__set_bit(MAX_PRIO, array->bitmap);
}
}
/*
* The boot idle thread does lazy MMU switching as well:
*/
atomic_inc(&init_mm.mm_count);
//啥都沒做
enter_lazy_tlb(&init_mm, current);
/*
* Make us the idle thread. Technically, schedule() should not be
* called from this thread, however somewhere below it might be,
* but because we are the idle thread, we just pick up running again
* when this runqueue becomes "idle".
*/
//初始化當前進程,也就是idle進程
init_idle(current, smp_processor_id());
}
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
//禁止搶占,原因如上
preempt_disable();
build_all_zonelists();
//處理器熱插拔時的失控函數,類似變頻時相應的驅動模塊做出的反應,顯然嵌入式中不可能用到
page_alloc_init();
//打印啟動參數,也就是我們再setup_arch中獲得的參數,這里只是打印,對參數的分析就在printk下面
printk(KERN_NOTICE "Kernel command line: %s\n", saved_command_line);
//再次分析參數,之前在setup_arch里面也做了一次,但那次只是得到我們的內存信息
parse_early_param();
parse_args("Booting kernel", command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
sort_main_extable();
//將中斷向量表所在的區域(鏈接的時候的位置不可能是0xffff0000)的內容搬運至0xffff0000;將中斷處理部分的代碼搬運至0xffff0200處。
trap_init();
rcu_init();
init_IRQ();
{
struct irqdesc *desc;
int irq;
#ifdef CONFIG_SMP
bad_irq_desc.affinity = CPU_MASK_ALL;
bad_irq_desc.cpu = smp_processor_id();
#endif
//NR_IRQS在我們的4020中定義為32個中斷,arm本身最多支持128個
for (irq = 0, desc = irq_desc; irq < NR_IRQS; irq++, desc++) {
//將這些中斷初始化為bad_irq_desc
*desc = bad_irq_desc;
INIT_LIST_HEAD(&desc->pend);
}
//init_arch_irq是一個全局的函數指針,它初始化的時候是一個空函數
//但是在setup_arch中把它指向了我們4020的函數,init_arch_irq = mdesc->init_irq;
//也就是在arch/arm/mach-sep4020/irq.c中的sep4020_init_irq,這里重新對我們所有的中斷進行初始化
init_arch_irq();
}
pidhash_init();
init_timers();
{
//這個函數就是timers_nb這個結構體的call函數
timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
(void *)(long)smp_processor_id());
//這個是用的機制和cpufreq的機制是一樣的,通過notifier_chain_register(&cpu_chain, nb)注冊的;
//只不過這里的鏈是cpu_chain,而cpufreq是其他的鏈
register_cpu_notifier(&timers_nb);
//設置軟中斷行動函數描述結構變量softirq_vec[=1](系統定時器)的設置
//也就是設置timer定時器到期之后的處理函數
open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL);
}
//其中函數hrtimers_init()和高精度時鐘相關
hrtimers_init();
//和init_timers最后部分是softirq類似,只不過在那里是初始化=1的時候;
//在softirq_init中是初始化=6, =0的情況,對于整個軟中斷來說有以下幾種情況
/*enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
TASKLET_SOFTIRQ
};*/
softirq_init();
//調用arch/arm/kernel/time.c中的time_init;它首先會檢查system_timer這個全局結構體的偏移是否為空
//system_timer和我們之前在init_IRQ中提到的init_arch_irq類似,也是在setup_arch中賦值的
//system_timer = mdesc->timer;所以之前一直強調setup_arch是一個非常重要的函數,和我們處理器的移植緊密相關的
time_init();
至此,雖然start_kernel的函數只分析了一小部分,但是和平臺和處理器相關的部分已經基本完畢,相信看完了這幾講,你會清楚的知道對于arch/arm/mach-sep4020中的那些文件為什么要那么寫,是不是可以優化(肯定可以),知其然,知其所以然。