Android 筆記-Linux Kernel SMP (Symmetric Multi-Processors) 開機流程解析 Part(3) Linux 多核心啟動流程從rest_init到ker

本文详细解析了Android/Linux系统中SMP(Symmetric Multi-Processors)启动流程,从rest_init到kernel_init,并探讨了CPUIdle、HotPlug机制。在多核心环境下,Linux内核的省电机制仍有优化空间,对于移动设备的省电需求至关重要。文章还介绍了CPU状态的CPU Mask属性和initcall机制,以及启动Secondary处理器、CPU Up/Down流程和cpu_idle函数的工作原理。
摘要由CSDN通过智能技术生成

Android 筆記-Linux Kernel SMP  (Symmetric Multi-Processors) 開機流程解析 Part(3) Linux 多核心啟動流程從rest_init到kernel_init與CPU Idle/HotPlug機制

 

 

hlchou@mail2000.com.tw

by loda.

Loda's Blog

App BizOrz

 

Android/Linux Source Code Tags
App BizOrz 
BizOrz.COM 
BizOrz Blog

            承襲之前的內容,本文會先把resr_init到kernel_init的流程做一個說明. 並針對Secondary處理器Booting,CPU Idle省電機制與CPU Up/Down HotPlug機制做介紹. 而屬於核心 Kthreadd機制,會放到下次的文章中.

 

            Linux Kernel針對CPU Idle與對應的省電機制,已經有滿不錯的框架,只要處理器平台開發者,根據自己的處理器架構,Power Group與對應的周邊適配進來,就可以達到省電最佳化的效益. 尤其,在多核心SMP架構上,Idle的省電機制在Linux Kernel中待改善的部分還非常多,這也是因為每個ARM SMP架構與周邊都會因為平台提供者的設計差異,而不容易標準化(畢竟這不是一家處理器業者獨大的市場). 要達到SMP多核心最佳的省電效益,就必須在基於對核心流程有清楚掌握下,針對要優化的平台來做調整,讓系統可以有更多的機會關閉對應的周邊供電與Clock,才有機會達到最佳化的省電效益.

 

            Android除了TV產品外,多數的應用都是屬於移動裝置,而這些裝置對於省電需求,是非常殷切的. 以目前的平板電腦來說,不同的系統廠商採用同一個雙核心的晶片,最終所設計的產品,在待機時間上,兩者就可以有數倍的差距(mmm,商品名稱就別提了..@_@),其中很大的差異就是對於所採用平台的掌握度是不是足夠高,以及對於系統軟體的投注程度.

 

            由於筆者時間受限,本系列文章會分次刊登,還請見諒.

 

 

 

rest_init

            這函式是Kernel初始化的最後一棒,負責的Bootstrap處理器,也會在這之後進入CPU Idle狀態,並讓系統行程Scheduling正常運作,也是我們了解Kernel時,值得清楚掌握的部份.

 

 

rest_init 流程

說明

rcu_scheduler_starting

實作在檔案kernel/rcutree.c中,

啟動Read-Copy Update,會呼叫num_online_cpus確認目前只有bootstrap處理器在運作,以及呼叫nr_context_switches確認在啟動RCU前,沒有進行過Contex-Switch,最後就是設定rcu_scheduler_active=1啟動RCU機制.

RCU在多核心架構下,不同的行程要讀取同一筆資料內容/結構,可以提供高效率的同步與正確性.

在這之後就可以使用 rcu_read_lock/rcu_read_unlock了.

產生Kernel Thread

kernel_init

Kernel Thread函式 kernel_init實作在檔案init/main.c中,

init Task PID=1,是核心第一個產生的Task.

產生後,會停在函式呼叫wait_for_completion中,等待kthreadd_done Signal,以便往後繼續執行下去.

 

產生Kernel Thread

kthreadd

Kernel Thread函式 kthreadd實作在檔案kernel/kthread.c中,

kthreadd Task PID=2,是核心第二個產生的Task.

 

 

find_task_by_pid_ns

實作在檔案kernel/pid.c中,

呼叫函式find_task_by_pid_ns,並帶入參數 kthreadd的PID 2與PID NameSpace (struct pid_namespace init_pid_ns)取回PID 2的Task Struct.

complete

實作在檔案kernel/sched.c中,

會送出kthreadd_done Signal,讓 kernel_init(也就是 init task)可以往後繼續執行.

init_idle_bootup_task

實作在檔案kernel/sched.c中,

設定目前啟動的Task為IDLE Task. (idle->sched_class = &idle_sched_class), 而struct sched_class idle_sched_class的宣告在檔案kernel/sched_idletask.c中.

在Linux下IDLE Task並不佔PID(也可以把它當作是PID 0),每個處理器都會有這樣的IDLE Task,用來在沒有行程排成時,讓處理器掉入執行的.而最基礎的省電機制,也可透過IDLE Task來進行. (包括讓系統可以關閉必要的周邊電源與Clock Gating).

schedule();

實作在檔案kernel/sched.c中,

//preempt_enable_no_resched/preempt_disable();

啟動Linux Kernel Process的排成Context-Switch機制.

cpu_idle();

實作在檔案arch/arm/kernel/process.c中,

這是處理器IDLE Task的主函式,我們會在稍後進一步說明,走到這,屬於Booting到IDLE Task的流程就算是初步結束了.

 

 

 

 

 

           

 

 

kernel_init

 

            實作在檔案 init/main.c中,這是Linux Kernel產生的第一個Tasks,也是User Mode起點init Task的產生者,所有User Space的初始化工作,包括Shell與相關的Script執行,都必須仰賴init Task,簡要說明如下

 

 

kernel_init
初始化函式的流程

說明

wait_for_completion

實作在檔案kernel/sched.c中,

會呼叫函式wait_for_completion,等待Kernel Thread kthreadd (PID=2)產生完畢.

init can run on any cpu

set_cpus_allowed_ptr

實作在檔案kernel/sched.c中,

透過這函式可以設定CPU bitmask,限定Task只能在特定的處理器上運作.而在init中,會設定為cpu_all_mask (= to_cpumask(cpu_all_bits)),

也就是 init Task可以在所有處理器上運作.

cad_pid = task_pid(current);  

呼叫task_pid (以inline實作在檔案include/linux/sched.h中),設定目前current的init Task PID給cad_pid (也就是要用來接收”ctrl-alt-del” Reboot Signal

的Process ID, 如果設定C_A_D=1,就表示可以處理來自”ctrl-alt-del”的動作.).

最後會呼叫函式ctrl_alt_del (in kernel/sys.c),並確認C_A_D是否為1,以便執行kernel_restart流程.

smp_prepare_cpus

實作在檔案arch/arm/kernel/smp.c中,

呼叫函式smp_prepare_cpus時,會以全域變數setup_max_cpus為函式參數max_cpus,用以表示在編譯核心時,設定支援的最大CPU數量.

首先,會透過函式num_possible_cpus(=cpumask_weight(cpu_possible_mask) ,in include/linux/cpumask.h)取得目前系統存在的處理器數量,並呼叫

函式smp_store_cpu_info,把透過calibrate_delay計算的loops_per_jiffy存到目前處理器的cpu_info (宣告為struct cpuinfo_arm *)中.

而Kernel中的全域變數setup_max_cpus初始值為NR_CPUS (參考檔案include/linux/threads.h 會以CONFIG_NR_CPUS 為值),如果編譯時設定的處理器

個數大於運作時取得的處理器個數(if (max_cpus > ncores) ),會以運作時偵測到的處理器個數為主.

呼叫函式percpu_timer_setup,設定目前處理器的Local Timer.

呼叫函式platform_smp_prepare_cpus (in arch/arm/mach-tegra/platsmp.c),依據max_cpus結果,透過函式set_cpu_present (in kernel/cpu.c),設定這些

處理器為present true.

do_pre_smp_initcalls

實作在檔案init/main.c中,

會透過函式do_one_initcall,執行介於Symbol  __initcall_start與__early_initcall_end之間的函式呼叫,

如下為arch/arm/kernel/vmlinux.lds中的Symbol區間內容,

__initcall_start = .; *(.initcallearly.init) __early_initcall_end = .;

以筆者編譯的結果來說,會執行有透過early_initcall 註冊的函式,例如

spawn_ksoftirqd  (in kernel/softirq.c),

init_workqueues (in kernel/workqueue.c),

init_call_single_data (in kernel/smp.c),

cpu_stop_init (in kernel/stop_machine.c),

….etc

smp_init

實作在檔案kernel/smp.c中,

這函式主要是由Bootstrap處理器,進行Active多核心架構下其它的處理器.

如果發生Online的處理器個數(from num_online_cpus)超過在核心編譯時,所設定的最大處理器個數 setup_max_cpus (from NR_CPUS),就會終止流程.

如果該處理器目前屬於Present (也就是存在系統中),但尚未是Online的狀態,就會呼叫函式cpu_up(in kernel/cpu.c)來啟動該處理器.

 

sched_init_smp

實作在檔案kernel/sched.c中,

 

1,呼叫get_online_cpus,如果目前CPU Hotplug Active Write行程是自己,就直接返回.反之就把 cpu_hotplug.refcount加1 (表示多一個Reader)

2,取得Mutex Lock “sched_domains_mutex”

3,呼叫arch_in

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值