Análisis del código fuente del kernel de Hongmeng (mecanismo de programación)

Nota: Basado en el análisis de oscuridad del núcleo de código abierto, la fuente oficial [ kernel_liteos_a ] documentos oficiales [ docs ] Documento de referencia [ Huawei LiteOS ]
autor: entusiastas del núcleo de Hong Meng, continuarán estudiando el núcleo de oscuridad, actualizarán el blog, así que estad atentos. El contenido solo representa opiniones personales, los errores son bienvenidos, todos pueden corregir y mejorar. Todos los artículos de esta serie ingresan para ver el  análisis del código fuente del sistema Hongmeng (catálogo general)


Este artículo analiza el código fuente del mecanismo de programación de tareas en detalle: ../kernel/base/sched/sched_sq/los_sched.c

Tabla de contenido

Se recomienda leer primero

¿Por qué tienes que aprender tantos conceptos para aprender una cosa?

Diagrama de transición de estados de procesos e hilos

¿Quién activará el trabajo de programación?

¿Qué le dice el código fuente sobre el proceso de programación?

Por favor, comprenda la función más hermosa del kernel, OsGetTopTask ()




Se recomienda leer primero

Se recomienda leer otros artículos de esta serie antes de leer e ingresar al análisis del código fuente del sistema Hongmeng (catálogo general)

Para comprender el mecanismo de programación de tareas de este artículo.

¿Por qué tienes que aprender tantos conceptos para aprender una cosa?

En el kernel de Hongmeng, Tarea y subproceso pueden entenderse como una cosa en un sentido amplio, pero definitivamente habrá diferencias en un sentido estricto. La diferencia radica en los diferentes sistemas de gestión. La tarea es un concepto a nivel de programación y el subproceso es un concepto a nivel de proceso. Por ejemplo, la primera función en la función main (), OsSetMainTask (); es establecer la tarea de inicio, pero nada ha comenzado todavía y el proceso de Kprocess no se ha creado. ¿Cómo pueden haber subprocesos que todos entiendan en el sentido general? El seguimiento en sentido estricto se explica por Hongmeng Kernel Source Code Analysis (Startup Process). No sé si tienes este tipo de experiencia. En el proceso de aprender algo, tienes que entrar en contacto con muchos conceptos nuevos, especialmente en la ecología de Java / Android. Hay muchos ladrones de conceptos y muchos estudiantes están atrapados en el concepto y no pueden salir. La pregunta es ¿por qué se necesitan tantos conceptos?

Tome un ejemplo para comprender:

Si vas a Shenzhen para una entrevista, el jefe te pregunta de dónde eres. Dirías que eres de Jiangxi, Hunan ... pero no Zhang Quandan del segundo grupo de Zhangjiacun, así que quién se atrevería a preguntarte. Pero si participas en una reunión del municipio y alguien te hace la misma pregunta, no dirás que eres del noreste de Nata, sino que dirás Zhang Quandan del segundo grupo de Zhangjiacun. ¿Lo entiendes? Zhang Quandan sigue siendo el mismo Zhang Quandan, pero debido a que la escena ha cambiado, su declaración debe cambiarse en consecuencia, de lo contrario no podrá charlar felizmente.

Ese diseño de programa viene de la vida y pertenece a la vida. La comprensión de todos los programas es utilizar escenas de la vida para comparar y comprender mejor los conceptos. ¿Qué dices?

En el nivel de programación del kernel, digamos tarea, la tarea es la unidad de programación del kernel, y la programación gira en torno a ella.

Diagrama de transición de estados de procesos e hilos

Primero veamos desde qué canales se genera la tarea:

Hay muchos canales, que pueden ser un comando del shell o creados por el kernel, y más como un hilo creado por todos los que escriben el programa de aplicación.

El contenido de la programación ya está ahí, entonces, ¿cómo se pueden programar de manera ordenada? Respuesta: Hay 32 colas listas para procesos y subprocesos, cada una con 32. ¿Por qué hay 32? El artículo de análisis de código fuente del sistema Hongmeng (catálogo general) tiene instrucciones detalladas, revíselo usted mismo.

Este diagrama esquemático de la migración del estado del proceso debe entenderse. Consulte el documento oficial para la migración del estado del subproceso. No se enumerará uno por uno y ocupará demasiado espacio.

Tenga en cuenta que tanto los procesos como los subprocesos solo tienen colas listas (la lista pendiente de subprocesos bloqueados es una lista vinculada y el kernel no usa colas para describirla), pero porque listo significa que el trabajo está listo y esperando a que se ejecute la CPU. Hay tres situaciones que se agregarán a la cola lista

 

  • Inicio → Listo :

    Cuando se crea un proceso o se bifurca, ingresa al estado de inicio después de obtener el bloque de control del proceso y se encuentra en la etapa de inicialización del proceso. Cuando se completa la inicialización del proceso, el proceso se inserta en la cola de programación y el proceso entra en el estado listo.

  • Pend → Listo / Pend → En ejecución :

    Cuando cualquier subproceso en el proceso bloqueado vuelve al estado listo, el proceso se agrega a la cola lista y cambia de forma síncrona al estado listo. Si se produce un cambio de proceso en este momento, el estado del proceso cambia del estado listo al estado en ejecución.

  • En ejecución → Listo :

    Hay dos situaciones en las que el proceso cambia del estado en ejecución al estado listo:

  • Después de que se crea o restaura un proceso con una prioridad más alta, se produce la programación del proceso. En este momento, el proceso con la prioridad más alta en la lista de listos se convierte en el estado en ejecución y el proceso en ejecución original cambia del estado en ejecución al estado listo.
  • Si la estrategia de programación del proceso es SCHED_RR, y otro proceso con la misma prioridad está en el estado listo, después de que se agote el intervalo de tiempo del proceso, el proceso pasa del estado en ejecución al estado listo y otro proceso con la misma prioridad cambia del estado listo. El estado cambia al estado de ejecución.

¿Quién activará el trabajo de programación?

La cola lista permite que la tarea esté en su lugar y el estado de progreso fluye continuamente durante su ciclo de vida ¿Qué hace que la programación funcione y cómo se activa?

Los métodos de activación que puedo pensar son los siguientes cuatro:

  • Tick ​​(gestión del reloj), similar a la tarea de temporización de JAVA, se activa cuando se acaba el tiempo. El temporizador del sistema es la parte más importante del mecanismo de tiempo del kernel y proporciona un mecanismo de interrupción de activación periódica, es decir, el temporizador del sistema activa automáticamente la interrupción del reloj a la frecuencia de HZ (frecuencia de tic del reloj). Cuando ocurre una interrupción del reloj, el kernel la maneja a través del manejador de interrupciones del reloj OsTickHandler. De forma predeterminada, el kernel de Hongmeng se activa una vez cada 10 ms y ejecuta las siguientes funciones de interrupción:
/*
 * 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
}

Escanee la tarea y llame al programador de tareas

  • La segunda es la interrupción causada por varias interrupciones de hardware y software, cómo conectar y desconectar USB, teclado, mouse y otros periféricos.
  • El tercero es la interrupción activa del programa, como la necesidad de solicitar otros recursos durante la operación y renunciar activamente al control.
  • El último es la programación preventiva iniciada después de crear una nueva tarea
  • ¿Dónde se solicitará la programación? Mira una foto.

Aquí está el OsCopyProcess () en la figura siguiente, que es la función principal del proceso de bifurcación. Se puede ver que se aplica una programación inmediatamente después de la bifurcación.

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;
}

Resulta que crear un proceso es tan simple, ¡realmente es COPIA! 

¿Qué le dice el código fuente sobre el proceso de programación?

Lo anterior es la información que hay que conocer de antemano. A continuación, vaya directamente al código fuente para ver el proceso de programación. El archivo tiene tres funciones, principalmente esta:

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 función es un poco larga. El autor dejó las líneas más importantes. Solo mire estas líneas. El proceso es el siguiente:

  1.  El proceso de programación requiere un bloqueo de giro, y no se permiten interrupciones. Sí, se dice que no se puede interrumpir nada, de lo contrario las consecuencias serán demasiado graves. Esta es la operación de los procesos y subprocesos de conmutación del kernel.
  2. Encuentra la tarea de mayor prioridad en la cola lista
  3. Cambiar de proceso, es decir, el proceso al que pertenece la tarea / hilo es el proceso actual
  4. Establecerlo como tarea actual
  5. El modo de usuario necesita establecer el espacio de ejecución, porque el espacio de cada proceso es diferente
  6. Lo más importante es cambiar el contexto de la tarea, los parámetros son las tareas nuevas y antiguas, una para guardar la escena y la otra para restaurar la escena.

¿Qué es el contexto de la tarea? Mire otros artículos de Análisis de código fuente del sistema Hongmeng (Catálogo general) , hay introducciones especiales. Lo que quiero explicar aquí es que a nivel de CPU, ¡solo reconoce el contexto de la tarea! No se puede ver ningún código aquí, porque está relacionado con la CPU, y las diferentes CPU deben adaptarse a diferentes códigos de ensamblaje, por lo que estos códigos de ensamblaje no aparecerán en un proyecto general. Preste atención al análisis del código fuente del núcleo de Hongmeng de seguimiento (instrucciones de montaje).

Por favor, comprenda la función más hermosa del kernel, OsGetTopTask ()

Finalmente, deje una tarea Después de leer lo que el autor piensa que es la función más hermosa del kernel, comprenderá de qué se trata la cola lista.

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
}

Escribamos mucho para este artículo. Aunque el código fuente del kernel de Hongmeng tiene pocos archivos, la relación es extremadamente complicada. Es difícil y feliz desensamblar el código fuente. Es aún más doloroso y feliz escribir un documento y compartirlo con todos. Me gusta. Justo como este. ¡Gracias por tu apoyo!

Supongo que te gusta

Origin blog.csdn.net/kuangyufei/article/details/108705968
Recomendado
Clasificación