注:コアオープンソースのあいまいさの分析に基づいて、公式ソース[ kernel_liteos_a ]公式ドキュメント[ docs ]リファレンスドキュメント[ Huawei LiteOS ]
作者:Hong Mengコア愛好家は、あいまいなカーネルの研究を続け、ブログを更新する予定です。コンテンツは個人的な見解のみを表しており、エラーは歓迎されます。誰でも修正して改善することができます。このシリーズのすべての記事は、Hongmengシステム(一般カタログ)のソースコード分析を表示するために入力します
この記事では、タスクスケジューリングメカニズムのソースコードを詳細に分析します。.. / kernel / base / sched / sched_sq / los_process.c
目次
公式の指示を見てみましょう
基本概念
システムの観点からは、プロセスはリソース管理ユニットです。プロセスは、CPUやメモリスペースなどのシステムリソースを使用または待機し、他のプロセスとは独立して実行できます。
OpenHarmonyカーネルのプロセスモジュールは、ユーザーに複数のプロセスを提供し、プロセス間の切り替えと通信を実現し、ユーザーがビジネスプログラムフローを管理するのに役立ちます。これにより、ユーザーはビジネス機能の実現により多くのエネルギーを費やすことができます。
OpenHarmonyカーネルのプロセスは、プリエンプティブスケジューリングメカニズムを採用し、タイムスライスラウンドロビンスケジューリングおよびFIFOスケジューリングメカニズムをサポートします。
OpenHarmonyカーネルプロセスには合計32の優先度(0〜31)があり、ユーザープロセスには22(10〜31)の構成可能な優先度があります。最高の優先度は10、最低の優先度は31です。
優先度の高いプロセスは優先度の低いプロセスよりも優先され、優先度の低いプロセスは、優先度の高いプロセスがブロックまたは終了された後にのみスケジュールできます。
各ユーザーモードプロセスには、互いに見えない独自の独立したプロセススペースがあり、プロセス間の分離を実現します。
ユーザーモードルートプロセスinitはカーネルモードによって作成され、他のユーザーモードプロセスはinitプロセスからフォークされます。
プロセスステータスの説明:
-
初期化(Init):プロセスを作成しています。
-
準備完了:プロセスは実行可能リストにあり、CPUスケジューリングを待機しています。
-
実行中:プロセスは実行中です。
-
保留:プロセスはブロックされ、中断されます。このプロセスのすべてのスレッドがブロックされると、プロセスはブロックされてハングします。
-
ゾンビ(ゾンビ):プロセスは終了し、親プロセスが制御ブロックリソースを再利用するのを待機します。
プロセス状態遷移の説明:
-
Init→Ready:
プロセスが作成またはフォークされると、プロセス制御ブロックを取得してInit状態になり、プロセスの初期化段階に入ります。プロセスの初期化が完了すると、プロセスはスケジューリングキューに挿入され、プロセスは準備完了状態になります。
-
準備完了→実行中:
プロセスが作成された後、準備完了状態になり、プロセス切り替えが発生すると、準備完了リストで最も優先順位の高いプロセスが実行され、実行状態になります。この時点でプロセス内の他のスレッドが準備完了状態にない場合、プロセスは実行可能リストから削除され、実行中の状態でのみ削除されます。この時点でプロセスに他のスレッドが準備完了状態にある場合、プロセスはまだ準備完了キューにあります。プロセスの準備状態と実行状態が共存しています。
-
実行中→保留:
プロセス内のすべてのスレッドがブロッキング状態にある場合、プロセスの最後のスレッドがブロッキング状態になると、同期的にブロッキング状態に入り、プロセスの切り替えが発生します。
-
保留→準備完了/保留→実行中:
ブロックされたプロセスのいずれかのスレッドが準備完了状態に戻ると、プロセスは準備完了キューに追加され、同期的に準備完了状態に変わります。このときにプロセス切り替えが発生すると、プロセス状態は準備完了状態から実行状態に変わります。
-
準備完了→保留:
プロセス内の最後の準備完了状態スレッドがブロッキング状態になると、プロセスは準備完了リストから削除され、プロセスは準備完了状態からブロッキング状態に変わります。
-
実行中→準備完了:
プロセスが実行状態から準備完了状態に変化する状況は2つあります。
- 優先度の高いプロセスが作成または復元されると、プロセスのスケジューリングが行われます。このとき、実行可能リストで最も優先度の高いプロセスが実行状態になり、元々実行されていたプロセスが実行状態から実行可能状態に変わります。
- プロセスのスケジューリング戦略がSCHED_RRであり、同じ優先順位を持つ別のプロセスが準備完了状態にある場合、プロセスのタイムスライスが使い果たされた後、プロセスは実行状態から準備完了状態に変わり、同じ優先順位を持つ別のプロセスが準備完了状態から変わります。状態が実行状態に変わります。
-
ランニング→ゾンビ:
プロセスのメインスレッドまたはすべてのスレッドが完了すると、プロセスは実行状態からゾンビ状態に変わり、親プロセスがリソースを再利用するのを待ちます。
使用するシーン
プロセスの作成後、ユーザーは自分のプロセススペースのリソースのみを操作でき、他のプロセスのリソース(共有リソースを除く)を操作できません。ユーザーモードでは、プロセスを中断、再開、および遅延できます。同時に、ユーザーモードのプロセススケジューリング優先順位とスケジューリング戦略を設定して、プロセススケジューリング優先順位とスケジューリング戦略を取得できます。プロセスが終了すると、プロセスは保持されているプロセスリソースをアクティブに解放しますが、保持されているプロセスpidリソースは、wait / waitpidまたは親プロセスの終了時に親プロセスによってリサイクルされる必要があります。
正式な分析を開始
上記の赤い単語のプロセスは、リソース管理ユニットであり、スケジューリングユニットではないことに注意してください 。それはタスク/スレッドです、公式の対応する状態の定義を見てください
#define OS_PROCESS_STATUS_INIT 0x0010U
#define OS_PROCESS_STATUS_READY 0x0020U
#define OS_PROCESS_STATUS_RUNNING 0x0040U
#define OS_PROCESS_STATUS_PEND 0x0080U
#define OS_PROCESS_STATUS_ZOMBIES 0x100U
創造から死までのプロセスのプロセスは、企業の誕生から消えるように、カーネルでは非常に複雑でなければなりません。プロセスの理解を容易にするために、著者はしばしば類推を行います。人生の例から、謎のシステムカーネルは外部化され、誰もが見られるように解剖されます。そのような複雑なものは、複雑な構造、つまりLosProcessCB(プロセス制御ブロック)によって運ばれる必要があります。コードは非常に長くなりますが、取り出す必要があります。長さは少し長くなりますので、ご注意ください!
LITE_OS_SEC_BSS LosProcessCB *g_runProcess[LOSCFG_KERNEL_CORE_NUM]; //用一个指针数组记录进程运行,LOSCFG_KERNEL_CORE_NUM 为 CPU的核数
LITE_OS_SEC_BSS LosProcessCB *g_processCBArray = NULL;//进程数组,最大进程数为 64个
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_freeProcess;//记录空闲进程链表
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_processRecyleList;//记录回收的进程列表
typedef struct ProcessCB {
CHAR processName[OS_PCB_NAME_LEN]; /**< Process name */
UINT32 processID; /**< process ID = leader thread ID */
UINT16 processStatus; /**< [15:4] process Status; [3:0] The number of threads currently
running in the process */
UINT16 priority; /**< process priority */
UINT16 policy; /**< process policy */
UINT16 timeSlice; /**< Remaining time slice */
UINT16 consoleID; /**< The console id of task belongs */
UINT16 processMode; /**< Kernel Mode:0; User Mode:1; */
UINT32 parentProcessID; /**< Parent process ID */
UINT32 exitCode; /**< process exit status */
LOS_DL_LIST pendList; /**< Block list to which the process belongs */
LOS_DL_LIST childrenList; /**< my children process list */
LOS_DL_LIST exitChildList; /**< my exit children process list */
LOS_DL_LIST siblingList; /**< linkage in my parent's children list */
ProcessGroup *group; /**< Process group to which a process belongs */
LOS_DL_LIST subordinateGroupList; /**< linkage in my group list */
UINT32 threadGroupID; /**< Which thread group , is the main thread ID of the process */
UINT32 threadScheduleMap; /**< The scheduling bitmap table for the thread group of the
process */
LOS_DL_LIST threadSiblingList; /**< List of threads under this process */
LOS_DL_LIST threadPriQueueList[OS_PRIORITY_QUEUE_NUM]; /**< The process's thread group schedules the
priority hash table */
volatile UINT32 threadNumber; /**< Number of threads alive under this process */
UINT32 threadCount; /**< Total number of threads created under this process */
LOS_DL_LIST waitList; /**< The process holds the waitLits to support wait/waitpid */
#if (LOSCFG_KERNEL_SMP == YES)
UINT32 timerCpu; /**< CPU core number of this task is delayed or pended */
#endif
UINTPTR sigHandler; /**< signal handler */
sigset_t sigShare; /**< signal share bit */
#if (LOSCFG_KERNEL_LITEIPC == YES)
ProcIpcInfo ipcInfo; /**< memory pool for lite ipc */
#endif
LosVmSpace *vmSpace; /**< VMM space for processes */
#ifdef LOSCFG_FS_VFS
struct files_struct *files; /**< Files held by the process */
#endif
timer_t timerID; /**< iTimer */
#ifdef LOSCFG_SECURITY_CAPABILITY
User *user;
UINT32 capability;
#endif
#ifdef LOSCFG_SECURITY_VID
TimerIdMap timerIdMap;
#endif
#ifdef LOSCFG_DRIVERS_TZDRIVER
struct file *execFile; /**< Exec bin of the process */
#endif
mode_t umask;
} LosProcessCB;
プロセスモードにはカーネルモードとユーザーモードの2つがあり、KProcessであるメイン関数に最も優先度の高いプロセスのカーネルモードが作成されると考えられます。
呼び出しプロセスは次のとおりです
タスクコマンドを使用してタスクの実行ステータスを表示します。KProcessプロセスを確認できます。名前でカーネルプロセスであることがわかり、システムの起動時に作成されます。図でKProcessのスレッド実行ステータスを確認できます。
例:swt_task、oom_task、jffs2_gc_threadなど。
プロセス管理の初期化
LITE_OS_SEC_BSS LosProcessCB *g_runProcess[LOSCFG_KERNEL_CORE_NUM];//*kyf CPU内核个数
LITE_OS_SEC_BSS LosProcessCB *g_processCBArray = NULL; //*kyf 进程池
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_freeProcess;//*kyf 空闲状态下可供分配的进程,此时进程白纸一张
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_processRecyleList;//*kyf 需要回收的进程列表
LITE_OS_SEC_BSS UINT32 g_userInitProcess = OS_INVALID_VALUE;//*kyf 用户态的初始进程,用户态下其他进程由它 fork
LITE_OS_SEC_BSS UINT32 g_kernelInitProcess = OS_INVALID_VALUE;//*kyf 内核态初始进程,内核态下其他进程由它 fork
LITE_OS_SEC_BSS UINT32 g_kernelIdleProcess = OS_INVALID_VALUE;
LITE_OS_SEC_BSS UINT32 g_processMaxNum;//*kyf 进程最大数量
LITE_OS_SEC_BSS ProcessGroup *g_processGroup = NULL;//*kyf 进程组
まず、.cのすべての変数について説明します。コメントは作者によって追加され、Hongmengカーネルに関するコメントはほとんどありません。
コードを見てみましょう
/**
* @ingroup los_config
* Maximum supported number of process rather than the number of usable processes.
*/
#ifndef LOSCFG_BASE_CORE_PROCESS_LIMIT
#define LOSCFG_BASE_CORE_PROCESS_LIMIT 64
#endif
LITE_OS_SEC_TEXT_INIT UINT32 OsProcessInit(VOID)
{
UINT32 index;
UINT32 size;
g_processMaxNum = LOSCFG_BASE_CORE_PROCESS_LIMIT;//*kyf 默认是64个
size = g_processMaxNum * sizeof(LosProcessCB);
g_processCBArray = (LosProcessCB *)LOS_MemAlloc(m_aucSysMem1, size);//*kyf 进程池
if (g_processCBArray == NULL) {
return LOS_NOK;
}
(VOID)memset_s(g_processCBArray, size, 0, size);
LOS_ListInit(&g_freeProcess);
LOS_ListInit(&g_processRecyleList);
for (index = 0; index < g_processMaxNum; index++) {
g_processCBArray[index].processID = index;
g_processCBArray[index].processStatus = OS_PROCESS_FLAG_UNUSED;//*kyf 默认都是白纸一张,臣妾干净着呢
LOS_ListTailInsert(&g_freeProcess, &g_processCBArray[index].pendList);//*kyf 可分配链表
}
g_userInitProcess = 1; /* 1: The root process ID of the user-mode process is fixed at 1 */
LOS_ListDelete(&g_processCBArray[g_userInitProcess].pendList);//*kyf 清空pend链表
g_kernelInitProcess = 2; /* 2: The root process ID of the kernel-mode process is fixed at 2 */
LOS_ListDelete(&g_processCBArray[g_kernelInitProcess].pendList);//*kyf 清空pend链表
return LOS_OK;
}
コードは非常に明確で、プロセスプールが作成されます。デフォルトは64プロセスです。つまり、システムはマクロLOSCFG_BASE_CORE_PROCESS_LIMITを変更せずに最大64のプロセスを持つことができますが、最初に2つのプロセスが占有されます。1つはユーザーモード、もう1つはカーネルモードです。これは後続の作成プロセスの親であるため、最大62プロセスを外部で作成でき、コードの最後の2つの親のタスクブロックリストもクリアされます。
カーネルKprocessを作成するプロセス
スレッドプールのプロセス番号[2]であるコア状態のプロセスを作成します。タスクコマンドKprocess PID = 2では、パラメーターはコア状態であり、最高の優先度0です。
コードはkprocessを現在のプロセスとして設定し、KIdle(カーネルモードのアイドルプロセス)をフォークします。
STATIC UINT32 OsCreateIdleProcess(VOID)
{
UINT32 ret;
CHAR *idleName = "Idle";
LosProcessCB *idleProcess = NULL;
Percpu *perCpu = OsPercpuGet();
UINT32 *idleTaskID = &perCpu->idleTaskID;
ret = OsCreateResourceFreeTask();//*kyf 创建一个资源回收任务
if (ret != LOS_OK) {
return ret;
}
ret = LOS_Fork(CLONE_FILES, "KIdle", (TSK_ENTRY_FUNC)OsIdleTask, LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE);
if (ret < 0) {
return LOS_NOK;
}
g_kernelIdleProcess = (UINT32)ret;
idleProcess = OS_PCB_FROM_PID(g_kernelIdleProcess);
*idleTaskID = idleProcess->threadGroupID;
OS_TCB_FROM_TID(*idleTaskID)->taskStatus |= OS_TASK_FLAG_SYSTEM_TASK;
#if (LOSCFG_KERNEL_SMP == YES)
OS_TCB_FROM_TID(*idleTaskID)->cpuAffiMask = CPUID_TO_AFFI_MASK(ArchCurrCpuid());
#endif
(VOID)memset_s(OS_TCB_FROM_TID(*idleTaskID)->taskName, OS_TCB_NAME_LEN, 0, OS_TCB_NAME_LEN);
(VOID)memcpy_s(OS_TCB_FROM_TID(*idleTaskID)->taskName, OS_TCB_NAME_LEN, idleName, strlen(idleName));
return LOS_OK;
}
上記はカーネルモードプロセスの初期化プロセスです
ユーザーモードプロセスを作成するプロセス
/**
* @ingroup los_process
* User state root process default priority
*/
#define OS_PROCESS_USERINIT_PRIORITY 28
LITE_OS_SEC_TEXT_INIT UINT32 OsUserInitProcess(VOID)
{
INT32 ret;
UINT32 size;
TSK_INIT_PARAM_S param = { 0 };
VOID *stack = NULL;
VOID *userText = NULL;
CHAR *userInitTextStart = (CHAR *)&__user_init_entry;
CHAR *userInitBssStart = (CHAR *)&__user_init_bss;
CHAR *userInitEnd = (CHAR *)&__user_init_end;
UINT32 initBssSize = userInitEnd - userInitBssStart;
UINT32 initSize = userInitEnd - userInitTextStart;
LosProcessCB *processCB = OS_PCB_FROM_PID(g_userInitProcess);
ret = OsProcessCreateInit(processCB, OS_USER_MODE, "Init", OS_PROCESS_USERINIT_PRIORITY);
if (ret != LOS_OK) {
return ret;
}
userText = LOS_PhysPagesAllocContiguous(initSize >> PAGE_SHIFT);
if (userText == NULL) {
ret = LOS_NOK;
goto ERROR;
}
(VOID)memcpy_s(userText, initSize, (VOID *)&__user_init_load_addr, initSize);
ret = LOS_VaddrToPaddrMmap(processCB->vmSpace, (VADDR_T)(UINTPTR)userInitTextStart, LOS_PaddrQuery(userText),
initSize, VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE |
VM_MAP_REGION_FLAG_PERM_EXECUTE | VM_MAP_REGION_FLAG_PERM_USER);
if (ret < 0) {
goto ERROR;
}
(VOID)memset_s((VOID *)((UINTPTR)userText + userInitBssStart - userInitTextStart), initBssSize, 0, initBssSize);
stack = OsUserInitStackAlloc(g_userInitProcess, &size);
if (stack == NULL) {
PRINTK("user init process malloc user stack failed!\n");
ret = LOS_NOK;
goto ERROR;
}
param.pfnTaskEntry = (TSK_ENTRY_FUNC)userInitTextStart;
param.userParam.userSP = (UINTPTR)stack + size;
param.userParam.userMapBase = (UINTPTR)stack;
param.userParam.userMapSize = size;
param.uwResved = OS_TASK_FLAG_PTHREAD_JOIN;
ret = OsUserInitProcessStart(g_userInitProcess, ¶m);
if (ret != LOS_OK) {
(VOID)OsUnMMap(processCB->vmSpace, param.userParam.userMapBase, param.userParam.userMapSize);
goto ERROR;
}
return LOS_OK;
ERROR:
(VOID)LOS_PhysPagesFreeContiguous(userText, initSize >> PAGE_SHIFT);
OsDeInitPCB(processCB);
return ret;
}
ユーザーモードの初期化と作成のプロセスはカーネルモードに似ており、ユーザーモードの親プロセスの優先順位は28と非常に低いことがわかります。
誰もが考えられるように、2つの小さな質問を残してください
OsUserInitProcessにはメモリに関するいくつかのコードがあり、次のようにソースコードで値が見つかりません。
CHAR *userInitTextStart = (CHAR *)&__user_init_entry;
CHAR *userInitBssStart = (CHAR *)&__user_init_bss;
CHAR *userInitEnd = (CHAR *)&__user_init_end;
なぜこうなった?
他の2つの親のPIDは1と2です。プロセスプール内のプロセス番号0はどこに行きましたか?
コメント欄で返信してください。詳細は後で分析します。Hongmengシステムのソースコード分析(総合カタログ)で、より多くの記事 を表示できます。