Analyse du code source du noyau de Hongmeng (mécanisme de planification)

Remarque: Sur la base de l'analyse de l'obscurité open source de base, la source officielle [ kernel_liteos_a ] documents officiels [ docs ] Document de référence [ Huawei LiteOS ]
auteur: Les passionnés de noyau de Hong Meng continueront d'étudier le noyau d'obscurité, mettre à jour le blog, alors restez à l'écoute. Le contenu ne représente que des vues personnelles, les erreurs sont les bienvenues, tout le monde est invité à corriger et à améliorer. Tous les articles de cette série entrent pour voir l'  analyse du code source du système Hongmeng (catalogue général)


Cet article analyse en détail le code source du mécanisme de planification des tâches: ../kernel/base/sched/sched_sq/los_sched.c

table des matières

Il est recommandé de lire en premier

Pourquoi devez-vous apprendre autant de concepts pour apprendre une chose?

Diagramme de transition d'état des processus et des threads

Qui déclenchera le travail de planification?

Que vous dit le code source sur le processus de planification?

Veuillez comprendre la plus belle fonction du noyau OsGetTopTask ()




Il est recommandé de lire en premier

Il est recommandé de lire les autres articles de cette série avant de lire, et de saisir l' analyse du code source du système Hongmeng (catalogue général)

Afin de comprendre le mécanisme de planification des tâches de cet article.

Pourquoi devez-vous apprendre autant de concepts pour apprendre une chose?

Dans le noyau de Hongmeng, la tâche et le thread peuvent être compris comme une chose au sens large, mais il y aura certainement des différences au sens étroit. La différence réside dans les différents systèmes de gestion. La tâche est un concept au niveau de la planification et le thread est un concept au niveau du processus. Par exemple, la première fonction de la fonction main (), OsSetMainTask (); consiste à définir la tâche de démarrage, mais rien n'a encore commencé et le processus Kprocess n'a pas été créé. Comment peut-il y avoir des threads que tout le monde comprend au sens général. Le suivi au sens strict est expliqué par Hongmeng Kernel Source Code Analysis (Startup Process). Je ne sais pas si vous avez ce genre d'expérience. Pour apprendre quelque chose, vous devez entrer en contact avec de nombreux nouveaux concepts, en particulier dans l'écologie de Java / Android. Il y a beaucoup de voleurs de concepts et de nombreux étudiants sont piégés dans le concept et ne peuvent pas en sortir. La question est de savoir pourquoi tant de concepts sont nécessaires?

Prenons un exemple pour comprendre:

Si vous allez à Shenzhen pour une interview, le patron vous demande d'où venez-vous? Vous diriez que vous êtes du Jiangxi, Hunan ... mais pas Zhang Quandan du deuxième groupe de Zhangjiacun, alors qui oserait vous le demander. Mais si vous participez à une réunion d'un autre canton et que quelqu'un vous pose la même question, vous ne direz pas que vous êtes du nord-est de Nata, mais vous direz Zhang Quandan du deuxième groupe de Zhangjiacun. comprenez vous? Zhang Quandan est toujours le même Zhang Quandan, mais comme la scène a changé, votre déclaration doit être modifiée en conséquence, sinon vous ne pourrez pas discuter avec plaisir.

Cette conception de programme vient de la vie et appartient à la vie. La compréhension de chacun des programmes est d'utiliser des scènes de la vie pour comparer et mieux comprendre les concepts. Que dis-tu?

Au niveau de la planification du noyau, disons tâche, la tâche est l'unité de planification du noyau, et la planification tourne autour d'elle.

Diagramme de transition d'état des processus et des threads

Voyons d'abord de quels canaux la tâche est générée:

Il existe de nombreux canaux, qui peuvent être une commande du shell, ou créés par le noyau, et plus encore un thread créé par tous ceux qui écrivent le programme d'application.

Le contenu de la planification est déjà là, alors comment peuvent-ils être planifiés de manière ordonnée? Réponse: Il y a 32 files d'attente de processus et de thread prêtes, chacune avec 32. Pourquoi y en a-t-il 32? L' article Analyse du code source du système Hongmeng (catalogue général) contient des instructions détaillées, veuillez le parcourir vous-même.

Ce diagramme schématique de la migration de l'état des processus doit être compris. Veuillez vous reporter au document officiel pour la migration de l'état des threads. Il ne sera pas répertorié un par un et prendra trop de place.

Notez que les processus et les threads n'ont que des files d'attente prêtes (la liste d'attente des threads bloqués est une liste liée, et le noyau n'utilise pas de files d'attente pour la décrire), mais parce que prêt signifie que le travail est prêt et attend que le processeur s'exécute. Il y a trois situations qui seront ajoutées à la file d'attente prête

 

  • Init → Prêt :

    Lorsqu'un processus est créé ou forké, il passe à l'état Init après avoir obtenu le bloc de contrôle de processus et est en phase d'initialisation du processus. Une fois l'initialisation du processus terminée, le processus est inséré dans la file d'attente de planification et le processus passe à l'état prêt.

  • En attente → Prêt / En attente → En cours d'exécution :

    Lorsqu'un thread du processus bloqué revient à l'état prêt, le processus est ajouté à la file d'attente prêt et passe de manière synchrone à l'état prêt. Si un changement de processus se produit à ce moment, l'état du processus passe de l'état prêt à l'état en cours d'exécution.

  • En cours d'exécution → Prêt :

    Il existe deux situations dans lesquelles le processus passe de l'état en cours d'exécution à l'état prêt:

  • Une fois qu'un processus avec une priorité plus élevée est créé ou restauré, la planification des processus se produit. À ce moment, le processus de priorité la plus élevée dans la liste prête devient l'état en cours d'exécution et le processus en cours d'exécution passe de l'état en cours à l'état prêt.
  • Si la stratégie de planification du processus est SCHED_RR et qu'un autre processus avec la même priorité est à l'état prêt, une fois la tranche de temps du processus épuisée, le processus passe de l'état en cours à l'état prêt, et un autre processus avec la même priorité passe de l'état prêt L'état passe à l'état de fonctionnement.

Qui déclenchera le travail de planification?

La file d'attente prête permet à la tâche d'être en place et l'état d'avancement s'écoule en continu au cours de son cycle de vie. Qu'est-ce qui fait fonctionner la planification et comment est-elle déclenchée?

Les méthodes de déclenchement auxquelles je peux penser sont les quatre suivantes:

  • Tick ​​(gestion de l'horloge), similaire à la tâche de chronométrage de JAVA, se déclenche lorsque le temps est écoulé. La minuterie système est la partie la plus importante du mécanisme de temps du noyau. Elle fournit un mécanisme d'interruption de déclenchement périodique, c'est-à-dire que la minuterie système déclenche automatiquement l'interruption d'horloge à la fréquence HZ (fréquence de tic d'horloge). Lorsqu'une interruption d'horloge se produit, le noyau la gère via le gestionnaire d'interruption d'horloge OsTickHandler. Par défaut, le noyau Hongmeng est déclenché une fois toutes les 10 ms et exécute les fonctions d'interruption suivantes:
/*
 * Description : Tick interruption handler
 */
LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
{
    UINT32 intSave;

    TICK_LOCK(intSave);
    g_tickCount[ArchCurrCpuid()]++;
    TICK_UNLOCK(intSave);

#ifdef LOSCFG_KERNEL_VDSO
    OsUpdateVdsoTimeval();
#endif

#ifdef LOSCFG_KERNEL_TICKLESS
    OsTickIrqFlagSet(OsTicklessFlagGet());
#endif

#if (LOSCFG_BASE_CORE_TICK_HW_TIME == YES)
    HalClockIrqClear(); /* diff from every platform */
#endif

    OsTimesliceCheck();

    OsTaskScan(); /* task timeout scan *///*kyf 任务扫描,发起调度

#if (LOSCFG_BASE_CORE_SWTMR == YES)
    OsSwtmrScan();
#endif
}

Analyser la tâche et appeler le planificateur de tâches

  • La seconde est l'interruption causée par diverses interruptions matérielles et logicielles, comment brancher et débrancher USB, clavier, souris et autres périphériques.
  • Le troisième est l'interruption active du programme, telle que la nécessité de demander d'autres ressources pendant le fonctionnement, et d'abandonner activement le contrôle
  • Le dernier est la planification préventive lancée après la création d'une nouvelle tâche
  • Où s'appliquera la planification? Regardez une image.

Voici le OsCopyProcess () dans la figure ci-dessous, qui est la fonction principale du processus de fork. On peut voir qu'un ordonnancement est appliqué immédiatement après le fork.

LITE_OS_SEC_TEXT INT32 LOS_Fork(UINT32 flags, const CHAR *name, const TSK_ENTRY_FUNC entry, UINT32 stackSize)
{
    UINT32 cloneFlag = CLONE_PARENT | CLONE_THREAD | CLONE_VFORK | CLONE_FILES;

    if (flags & (~cloneFlag)) {
        PRINT_WARN("Clone dont support some flags!\n");
    }

    flags |= CLONE_FILES;
    return OsCopyProcess(cloneFlag & flags, name, (UINTPTR)entry, stackSize);
}

STATIC INT32 OsCopyProcess(UINT32 flags, const CHAR *name, UINTPTR sp, UINT32 size)
{
    UINT32 intSave, ret, processID;
    LosProcessCB *run = OsCurrProcessGet();

    LosProcessCB *child = OsGetFreePCB();
    if (child == NULL) {
        return -LOS_EAGAIN;
    }
    processID = child->processID;

    ret = OsForkInitPCB(flags, child, name, sp, size);
    if (ret != LOS_OK) {
        goto ERROR_INIT;
    }

    ret = OsCopyProcessResources(flags, child, run);
    if (ret != LOS_OK) {
        goto ERROR_TASK;
    }

    ret = OsChildSetProcessGroupAndSched(child, run);
    if (ret != LOS_OK) {
        goto ERROR_TASK;
    }

    LOS_MpSchedule(OS_MP_CPU_ALL);
    if (OS_SCHEDULER_ACTIVE) {
        LOS_Schedule();//*kyf 申请调度
    }

    return processID;

ERROR_TASK:
    SCHEDULER_LOCK(intSave);
    (VOID)OsTaskDeleteUnsafe(OS_TCB_FROM_TID(child->threadGroupID), OS_PRO_EXIT_OK, intSave);
ERROR_INIT:
    OsDeInitPCB(child);
    return -ret;
}

Il s'avère que créer un processus est si simple, c'est vraiment COPIER! 

Que vous dit le code source sur le processus de planification?

Les informations ci-dessus doivent être connues à l'avance. Ensuite, accédez directement au code source pour voir le processus de planification. Le fichier a trois fonctions, principalement celle-ci:

VOID OsSchedResched(VOID)
{
    LOS_ASSERT(LOS_SpinHeld(&g_taskSpin));//*kyf 调度过程要上锁
    newTask = OsGetTopTask(); //*kyf 获取最高优先级任务
    OsSchedSwitchProcess(runProcess, newProcess);//*kyf 切换运行的进程
    (VOID)OsTaskSwitchCheck(runTask, newTask);
    OsCurrTaskSet((VOID*)newTask);//*kyf 设置当前任务
    if (OsProcessIsUserMode(newProcess)) {
        OsCurrUserTaskSet(newTask->userArea);//*kyf 运行空间
    }
    /* do the task context switch */
    OsTaskSchedule(newTask, runTask); //*kyf 切换任务上下文
}

La fonction est un peu longue. L'auteur a laissé les lignes les plus importantes. Il suffit de regarder ces lignes. Le processus est le suivant:

  1.  Le processus d'ordonnancement nécessite un verrou tournant, et aucune interruption n'est autorisée. Oui, il est dit que rien ne peut être interrompu, sinon les conséquences seront trop graves. C'est l'opération des processus de commutation du noyau et des threads.
  2. Rechercher la tâche la plus prioritaire dans la file d'attente prête
  3. Processus de commutation, c'est-à-dire que le processus auquel appartient la tâche / thread est le processus en cours
  4. Définissez-le comme tâche actuelle
  5. Le mode utilisateur doit définir l'espace d'exécution, car l'espace de chaque processus est différent
  6. Le plus important est de changer le contexte de la tâche. Les paramètres sont les nouvelles et anciennes tâches, l'une pour enregistrer la scène et l'autre pour restaurer la scène.

Qu'est-ce que le contexte de tâche? Regardez d' autres articles de l' analyse du code source du système Hongmeng (catalogue général) , il y a des introductions spéciales. Ce que je veux expliquer ici, c'est qu'au niveau du CPU, il ne reconnaît que le contexte de la tâche! Aucun code ne peut être vu ici, car il est lié au processeur, et différents processeurs doivent s'adapter à différents codes d'assemblage, de sorte que ces codes d'assemblage n'apparaîtront pas dans un projet général. Veuillez prêter attention à l'analyse de suivi du code source du noyau Hongmeng (instructions d'assemblage).

Veuillez comprendre la plus belle fonction du noyau OsGetTopTask ()

Enfin, laissez un devoir.Après avoir lu ce que l'auteur pense être la plus belle fonction du noyau, vous comprendrez en quoi consiste la file d'attente prête.

LITE_OS_SEC_TEXT_MINOR LosTaskCB *OsGetTopTask(VOID)
{
    UINT32 priority, processPriority;
    UINT32 bitmap;
    UINT32 processBitmap;
    LosTaskCB *newTask = NULL;
#if (LOSCFG_KERNEL_SMP == YES)
    UINT32 cpuid = ArchCurrCpuid();
#endif
    LosProcessCB *processCB = NULL;
    processBitmap = g_priQueueBitmap;
    while (processBitmap) {
        processPriority = CLZ(processBitmap);
        LOS_DL_LIST_FOR_EACH_ENTRY(processCB, &g_priQueueList[processPriority], LosProcessCB, pendList) {
            bitmap = processCB->threadScheduleMap;
            while (bitmap) {
                priority = CLZ(bitmap);
                LOS_DL_LIST_FOR_EACH_ENTRY(newTask, &processCB->threadPriQueueList[priority], LosTaskCB, pendList) {
#if (LOSCFG_KERNEL_SMP == YES)
                    if (newTask->cpuAffiMask & (1U << cpuid)) {
#endif
                        newTask->taskStatus &= ~OS_TASK_STATUS_READY;
                        OsPriQueueDequeue(processCB->threadPriQueueList,
                                          &processCB->threadScheduleMap,
                                          &newTask->pendList);
                        OsDequeEmptySchedMap(processCB);
                        goto OUT;
#if (LOSCFG_KERNEL_SMP == YES)
                    }
#endif
                }
                bitmap &= ~(1U << (OS_PRIORITY_QUEUE_NUM - priority - 1));
            }
        }
        processBitmap &= ~(1U << (OS_PRIORITY_QUEUE_NUM - processPriority - 1));
    }

OUT:
    return newTask;
}

#ifdef __cplusplus
#if __cplusplus
}

Écrivons beaucoup pour cet article. Bien que le code source du noyau Hongmeng contienne peu de fichiers, la relation est extrêmement compliquée. Il est difficile et heureux de démonter le code source. C'est encore plus douloureux et heureux d'écrire un document et de le partager avec tout le monde. J'aime ça. Exactement comme ça. Merci pour votre soutien!

Je suppose que tu aimes

Origine blog.csdn.net/kuangyufei/article/details/108705968
conseillé
Classement