并發(fā)(Concurrent):多個線程在單個核心運行,同一時間只能一個線程運行,內(nèi)核不停切換線程,看起來像同時運行,實際上是線程被高速的切換.
通俗好理解的比喻就是高速單行道,單行道指的是CPU的核數(shù),跑的車就是線程(任務(wù)),進程就是管理車的公司,一個公司可以有很多臺車.并發(fā)和并行跟CPU的核數(shù)有關(guān).車道上同時只能跑一輛車,但因為指揮系統(tǒng)很牛,夠快,在毫秒級內(nèi)就能換車跑,人根本感知不到切換.所以外部的感知會是同時在進行,實現(xiàn)了微觀上的串行,宏觀上的并行.
線程切換的本質(zhì)是CPU要換場地上班,去哪里上班由哪里提供場地,那個場地就是任務(wù)棧,每個任務(wù)棧中保存了上班的各種材料,來了就行立馬干活.那些材料就是任務(wù)上下文.簡單的說就是上次活干到那里了,回來繼續(xù)接著干.上下文由任務(wù)棧自己保存,CPU不管的,它來了只負責任務(wù)交過來的材料,材料顯示去哪里搬磚它就去哪里搬磚.
記住一個單詞就能記住并行并發(fā)的區(qū)別, 發(fā)單,發(fā)單(并發(fā)單行).
理解并行概念
并行(Parallel)每個線程分配給獨立的CPU核心,線程真正的同時運行.
通俗好理解的比喻就是高速多行道,實現(xiàn)了微觀和宏觀上同時進行. 并行當然是快,人多了干活就不那么累,但干活人多了必然會帶來人多的管理問題,會把問題變復(fù)雜,請想想會出現(xiàn)哪些問題?
理解協(xié)程概念
這里說下協(xié)程,例如go語言是有協(xié)程支持的,其實協(xié)程跟內(nèi)核層沒有關(guān)系,是應(yīng)用層的概念.是在線程之上更高層的封裝,用通俗的比喻來說就是在車內(nèi)另外搞了幾條車道玩.其對內(nèi)核來說沒有新東西,內(nèi)核只負責車的調(diào)度,至于車內(nèi)你想怎么弄那是應(yīng)用程序自己的事.本質(zhì)的區(qū)別是CPU根本沒有換地方上班(沒有被調(diào)度),而并發(fā)/并行都是換地方上班了.
內(nèi)核如何描述CPU
typedef struct {
SortLinkAttribute taskSortLink; /* task sort link */ //每個CPU core 都有一個task排序鏈表
SortLinkAttribute swtmrSortLink; /* swtmr sort link */ //每個CPU core 都有一個定時器排序鏈表
UINT32 idleTaskID; /* idle task id */ //空閑任務(wù)ID 見于 OsIdleTaskCreate
UINT32 taskLockCnt; /* task lock flag */ //任務(wù)鎖的數(shù)量,當 > 0 的時候,需要重新調(diào)度了
UINT32 swtmrHandlerQueue; /* software timer timeout queue id */ //軟時鐘超時隊列句柄
UINT32 swtmrTaskID; /* software timer task id */ //軟時鐘任務(wù)ID
UINT32 schedFlag; /* pending scheduler flag */ //調(diào)度標識 INT_NO_RESCH INT_PEND_RESCH
#if (LOSCFG_KERNEL_SMP == YES)
UINT32 excFlag; /* cpu halt or exc flag */ //CPU處于停止或運行的標識
#endif
} Percpu;
Percpu g_percpu[LOSCFG_KERNEL_CORE_NUM];//全局CPU數(shù)組
這是內(nèi)核對CPU的描述,主要是兩個排序鏈表,一個是任務(wù)的排序,一個是定時器的排序.什么意思? 在系列篇中多次提過,任務(wù)是內(nèi)核的調(diào)度單元,注意可不是進程,雖然調(diào)度也需要進程參與,也需要切換進程,切換用戶空間.但調(diào)度的核心是切換任務(wù),每個任務(wù)的代碼指令才是CPU的糧食,它吃的是一條條的指令.每個任務(wù)都必須指定取糧地址(即入口函數(shù)).
另外還有一個東西能提供入口函數(shù),就是定時任務(wù).很重要也很常用,沒它某寶每晚9點的準時秒殺實現(xiàn)不了.在內(nèi)核每個CPU都有自己獨立的任務(wù)和定時器鏈表.
每次Tick的到來,處理函數(shù)會去掃描這兩個鏈表,看有沒有定時器超時的任務(wù)需要執(zhí)行,有則立即執(zhí)行定時任務(wù),定時任務(wù)是所有任務(wù)中優(yōu)先級最高的,0號優(yōu)先級,在系列篇中有專門講定時器任務(wù),可自行翻看.
LOSCFG_KERNEL_SMP
# if (LOSCFG_KERNEL_SMP == YES) # define LOSCFG_KERNEL_CORE_NUM LOSCFG_KERNEL_SMP_CORE_NUM //多核情況下支持的CPU核數(shù) # else # define LOSCFG_KERNEL_CORE_NUM 1 //單核配置 # endif
多CPU核的操作系統(tǒng)有3種處理模式(SMP+AMP+BMP) 鴻蒙實現(xiàn)的是 SMP 的方式
非對稱多處理(Asymmetric multiprocessing,AMP)每個CPU內(nèi)核運行一個獨立的操作系統(tǒng)或同一操作系統(tǒng)的獨立實例(instantiation)。
對稱多處理(Symmetric multiprocessing,SMP)一個操作系統(tǒng)的實例可以同時管理所有CPU內(nèi)核,且應(yīng)用并不綁定某一個內(nèi)核。
混合多處理(Bound multiprocessing,BMP)一個操作系統(tǒng)的實例可以同時管理所有CPU內(nèi)核,但每個應(yīng)用被鎖定于某個指定的核心。
宏LOSCFG_KERNEL_SMP表示對多CPU核的支持,鴻蒙默認是打開LOSCFG_KERNEL_SMP的。
多CPU核支持
鴻蒙內(nèi)核對CPU的操作見于 los_mp.c ,因文件不大,這里把代碼都貼出來了.
#if (LOSCFG_KERNEL_SMP == YES)
//給參數(shù)CPU發(fā)送調(diào)度信號
VOID LOS_MpSchedule(UINT32 target)//target每位對應(yīng)CPU core
{
UINT32 cpuid = ArchCurrCpuid();
target &= ~(1U << cpuid);//獲取除了自身之外的其他CPU
HalIrqSendIpi(target, LOS_MP_IPI_SCHEDULE);//向目標CPU發(fā)送調(diào)度信號,核間中斷(Inter-Processor Interrupts),IPI
}
//硬中斷喚醒處理函數(shù)
VOID OsMpWakeHandler(VOID)
{
/* generic wakeup ipi, do nothing */
}
//硬中斷調(diào)度處理函數(shù)
VOID OsMpScheduleHandler(VOID)
{//將調(diào)度標志設(shè)置為與喚醒功能不同,這樣就可以在硬中斷結(jié)束時觸發(fā)調(diào)度程序。
/*
* set schedule flag to differ from wake function,
* so that the scheduler can be triggered at the end of irq.
*/
OsPercpuGet()->schedFlag = INT_PEND_RESCH;//給當前Cpu貼上調(diào)度標簽
}
//硬中斷暫停處理函數(shù)
VOID OsMpHaltHandler(VOID)
{
(VOID)LOS_IntLock();
OsPercpuGet()->excFlag = CPU_HALT;//讓當前Cpu停止工作
while (1) {}//陷入空循環(huán),也就是空閑狀態(tài)
}
//MP定時器處理函數(shù), 遞歸檢查所有可用任務(wù)
VOID OsMpCollectTasks(VOID)
{
LosTaskCB *taskCB = NULL;
UINT32 taskID = 0;
UINT32 ret;
/* recursive checking all the available task */
for (; taskID <= g_taskMaxNum; taskID++) { //遞歸檢查所有可用任務(wù)
taskCB = &g_taskCBArray[taskID];
if (OsTaskIsUnused(taskCB) || OsTaskIsRunning(taskCB)) {
continue;
}
/* 雖然任務(wù)狀態(tài)不是原子的,但此檢查可能成功,但無法完成刪除,此刪除將在下次運行之前處理
* though task status is not atomic, this check may success but not accomplish
* the deletion; this deletion will be handled until the next run.
*/
if (taskCB->signal & SIGNAL_KILL) {//任務(wù)收到被干掉信號
ret = LOS_TaskDelete(taskID);//干掉任務(wù),回歸任務(wù)池
if (ret != LOS_OK) {
PRINT_WARN("GC collect task failed err:0x%x\n", ret);
}
}
}
}
//MP(multiprocessing) 多核處理器初始化
UINT32 OsMpInit(VOID)
{
UINT16 swtmrId;
(VOID)LOS_SwtmrCreate(OS_MP_GC_PERIOD, LOS_SWTMR_MODE_PERIOD, //創(chuàng)建一個周期性,持續(xù)時間為 100個tick的定時器
(SWTMR_PROC_FUNC)OsMpCollectTasks, &swtmrId, 0);//OsMpCollectTasks為超時回調(diào)函數(shù)
(VOID)LOS_SwtmrStart(swtmrId);//開始定時任務(wù)
return LOS_OK;
}
#endif
代碼一一都加上了注解,這里再一一說明下:
1.OsMpInit
多CPU核的初始化, 多核情況下每個CPU都有各自的編號, 內(nèi)核有分成主次CPU, 0號默認為主CPU, OsMain()由主CPU執(zhí)行,被匯編代碼調(diào)用. 初始化只開了個定時任務(wù),只干一件事就是回收不用的任務(wù).回收的條件是任務(wù)是否收到了被干掉的信號. 例如shell命令 kill 9 14 ,意思是干掉14號線程的信號,這個信號會被線程保存起來. 可以選擇自殺也可以等著被殺. 這里要注意,鴻蒙有兩種情況下任務(wù)不能被干掉, 一種是系統(tǒng)任務(wù)不能被干掉的, 第二種是正在運行狀態(tài)的任務(wù).
2.次級CPU的初始化
同樣由匯編代碼調(diào)用,通過以下函數(shù)執(zhí)行,完成每個CPU核的初始化
//次級CPU初始化,本函數(shù)執(zhí)行的次數(shù)由次級CPU的個數(shù)決定. 例如:在四核情況下,會被執(zhí)行3次, 0號通常被定義為主CPU 執(zhí)行main
LITE_OS_SEC_TEXT_INIT VOID secondary_cpu_start(VOID)
{
#if (LOSCFG_KERNEL_SMP == YES)
UINT32 cpuid = ArchCurrCpuid();
OsArchMmuInitPerCPU();//每個CPU都需要初始化MMU
OsCurrTaskSet(OsGetMainTask());//設(shè)置CPU的當前任務(wù)
/* increase cpu counter */
LOS_AtomicInc(&g_ncpu); //統(tǒng)計CPU的數(shù)量
/* store each core's hwid */
CPU_MAP_SET(cpuid, OsHwIDGet());//存儲每個CPU的 hwid
HalIrqInitPercpu(); //CPU硬件中斷初始化
OsCurrProcessSet(OS_PCB_FROM_PID(OsGetKernelInitProcessID())); //設(shè)置內(nèi)核進程為CPU進程
OsSwtmrInit(); //定時任務(wù)初始化,每個CPU維護自己的定時器隊列
OsIdleTaskCreate(); //創(chuàng)建空閑任務(wù),每個CPU維護自己的任務(wù)隊列
OsStart(); //本CPU正式啟動在內(nèi)核層的工作
while (1) {
__asm volatile("wfi");//wait for Interrupt 等待中斷,即下一次中斷發(fā)生前都在此hold住不干活
}//類似的還有 WFE: wait for Events 等待事件,即下一次事件發(fā)生前都在此hold住不干活
#endif
}
可以看出次級CPU有哪些初始化步驟:
初始化MMU,OsArchMmuInitPerCPU
設(shè)置當前任務(wù) OsCurrTaskSet
初始化硬件中斷 HalIrqInitPercpu
初始化定時器隊列 OsSwtmrInit
創(chuàng)建空任務(wù) OsIdleTaskCreate, 外面沒有任務(wù)的時CPU就待在這個空任務(wù)里自己轉(zhuǎn)圈圈.
開始自己的工作流程 OsStart,正式開始工作,跑任務(wù)
多CPU核還有哪些問題?
CPU之間搶資源的情況要怎么處理?
CPU之間通訊(也叫核間通訊)怎么解決?
如果確保兩個CPU不會同時執(zhí)行同一個任務(wù)?
匯編代碼如何實現(xiàn)對各CPU的調(diào)動
請前往系列篇或直接前往內(nèi)核注解代碼查看.這里不再做說明.
編輯:hfy
-
cpu
+關(guān)注
關(guān)注
68文章
11250瀏覽量
223845 -
線程
+關(guān)注
關(guān)注
0文章
508瀏覽量
20796 -
鴻蒙系統(tǒng)
+關(guān)注
關(guān)注
183文章
2642瀏覽量
69597
發(fā)布評論請先 登錄
鴻蒙內(nèi)核源碼Task/線程技術(shù)分析
【HarmonyOS】鴻蒙內(nèi)核源碼分析(調(diào)度機制篇)
鴻蒙內(nèi)核源碼分析:用通俗易懂的語言告訴你鴻蒙內(nèi)核發(fā)生了什么?
鴻蒙內(nèi)核源碼分析(源碼注釋篇):給HarmonyOS源碼逐行加上中文注釋
鴻蒙內(nèi)核源碼分析(內(nèi)存概念篇) :手眼通天的虛擬內(nèi)存
鴻蒙內(nèi)核源碼分析(內(nèi)存概念篇) :手眼通天的虛擬內(nèi)存
鴻蒙內(nèi)核源碼分析(必讀篇):用故事說內(nèi)核
鴻蒙內(nèi)核源碼分析(調(diào)度機制篇):Task是如何被調(diào)度執(zhí)行的
鴻蒙內(nèi)核源碼分析(必讀篇)
鴻蒙內(nèi)核源碼分析:鴻蒙內(nèi)核的每段匯編代碼解析
鴻蒙內(nèi)核源碼分析: 虛擬內(nèi)存和物理內(nèi)存是怎么管理的
鴻蒙內(nèi)核源碼分析:進程是內(nèi)核的資源管理單元
鴻蒙內(nèi)核源碼分析 :內(nèi)核最重要結(jié)構(gòu)體
鴻蒙內(nèi)核源碼分析之任何管理多個CPU?
評論