Análisis del código fuente del kernel de Hongmeng (gestión de procesos)

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á estudiando el núcleo de oscuridad, actualizará 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 en detalle el código fuente del mecanismo de programación de tareas: ../kernel/base/sched/sched_sq/los_process.c 

Tabla de contenido

Echemos un vistazo a las instrucciones oficiales.

conceptos básicos

escenas a utilizar

Iniciar análisis formal

Inicialización de la gestión de procesos

El proceso de creación de kernel Kprocess

El proceso de creación de un proceso de modo de usuario

Deja dos pequeñas preguntas para que todos piensen



Echemos un vistazo a las instrucciones oficiales.

conceptos básicos

Desde la perspectiva del sistema, un proceso es una unidad de gestión de recursos . Los procesos pueden usar o esperar recursos del sistema como CPU y espacio de memoria, y ejecutarse independientemente de otros procesos.

El módulo de proceso del kernel de OpenHarmony puede proporcionar a los usuarios múltiples procesos, realizar la conmutación y la comunicación entre procesos y ayudar a los usuarios a administrar los flujos de programas comerciales. De esta forma, los usuarios pueden dedicar más energía a la realización de funciones comerciales.

El proceso en el kernel de OpenHarmony adopta un mecanismo de programación preventiva, admite la programación por turnos por intervalos de tiempo y el mecanismo de programación FIFO.

El proceso del kernel de OpenHarmony tiene un total de 32 prioridades (0-31) y hay 22 (10-31) prioridades configurables para los procesos de usuario. La prioridad más alta es 10 y la prioridad más baja es 31.

Los procesos de alta prioridad pueden adelantarse a los de baja prioridad, y los procesos de baja prioridad solo se pueden programar después de que los procesos de alta prioridad se bloqueen o terminen.

Cada proceso de modo de usuario tiene su propio espacio de proceso independiente, que es invisible entre sí, lo que permite el aislamiento entre procesos.

El proceso de raíz en modo de usuario init es creado por el modo kernel, y otros procesos en modo de usuario son bifurcaciones del proceso init.

Descripción del estado del proceso:

  • Inicialización (Init): se está creando el proceso.

  • Listo: el proceso está en la lista de listos, esperando la programación de la CPU.

  • En ejecución: el proceso se está ejecutando.

  • Pend: El proceso se bloquea y se suspende. Cuando todos los subprocesos de este proceso están bloqueados, el proceso se bloquea y se cuelga.

  • Zombies (Zombies): El proceso finaliza, esperando que el proceso principal recupere sus recursos de bloque de control.

Figura 1  Diagrama esquemático de la transición del estado del proceso

Descripción de la transición del estado del proceso:

  • 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.

  • Listo → En ejecución:

    Después de que se crea el proceso, entra en el estado listo. Cuando ocurre un cambio de proceso, el proceso con la prioridad más alta en la lista lista se ejecuta y entra en el estado de ejecución. Si no hay otros subprocesos en el proceso en estado listo en este momento, el proceso se elimina de la lista lista y solo en el estado en ejecución; si hay otros subprocesos en el proceso en estado listo en este momento, el proceso todavía está en la cola lista. El estado listo y el estado de ejecución del proceso coexisten.

  • Ejecutando → Pend :

    Cuando todos los subprocesos del proceso están en el estado de bloqueo, cuando el último subproceso del proceso pasa al estado de bloqueo, entra en el estado de bloqueo sincrónicamente y luego se produce el cambio de proceso.

  • 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.

  • Listo → Pend:

    Cuando el último subproceso de estado listo en el proceso está en el estado de bloqueo, el proceso se elimina de la lista de listos y el proceso cambia del estado listo al estado de bloqueo.

  • En ejecución → Listo :

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

    1. 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.
    2. 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.
  • Corriendo → Zombis :

    Cuando el subproceso principal o todos los subprocesos del proceso finalizan, el proceso pasa del estado de ejecución al estado zombi, esperando que el proceso principal recupere recursos.

escenas a utilizar

Una vez creado el proceso, el usuario solo puede operar los recursos de su propio espacio de proceso y no puede operar los recursos de otros procesos (excepto los recursos compartidos). El modo de usuario permite suspender, reanudar y retrasar los procesos. Al mismo tiempo, la prioridad de programación de procesos y la estrategia de programación del modo de usuario se pueden configurar para obtener la prioridad de programación de procesos y la estrategia de programación. Cuando el proceso finaliza, el proceso liberará activamente los recursos del proceso retenido, pero los recursos del pid del proceso retenido deben ser reciclados por el proceso principal a través de wait / waitpid o cuando el proceso principal finaliza.

Iniciar análisis formal

Tenga en cuenta que el proceso de palabras en rojo marcado arriba  es una unidad de gestión de recursos, no una unidad de programación ¿Quién es la unidad de programación? Es Task / Thread, mira la definición del estado correspondiente oficial

#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

 El proceso de un proceso desde la creación hasta la muerte debe ser extremadamente complicado en el núcleo, al igual que el nacimiento hasta la desaparición de una empresa. Para facilitar la comprensión del proceso, el autor a menudo hace una analogía, a partir de los ejemplos de la vida, el misterioso núcleo del sistema se exterioriza y se disecciona para que todos lo vean. Una cosa tan complicada debe llevarla una estructura complicada, es decir, LosProcessCB (Process Control Block). El código es muy largo pero hay que sacarlo. La longitud es un poco más larga, ¡paciencia!


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;

Hay dos modos de proceso, el modo kernel y el modo usuario. Se puede pensar que se creará un modo kernel del proceso de mayor prioridad en la función principal, que es  KProcess.

El proceso de llamada es el siguiente

Vea el estado de ejecución de la tarea a través del comando de tarea, puede ver el proceso de KProcess , sabe que es un proceso de kernel por el nombre, se crea cuando se inicia el sistema y puede ver el estado de ejecución del hilo de KProcess en la figura

Por ejemplo: swt_task, oom_task, jffs2_gc_thread, etc.

Tarea de Hongmeng

Inicialización de la gestión de procesos

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 进程组

Permítanme explicar todas las variables en .c primero. Los comentarios son agregados por el autor y hay pocos comentarios sobre el kernel de Hongmeng.

Echemos un vistazo al código

/**
 * @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;
}

El código es muy claro, se crea un pool de procesos, el valor por defecto es 64 procesos, es decir, el sistema puede tener hasta 64 procesos sin cambiar la macro LOSCFG_BASE_CORE_PROCESS_LIMIT, pero se ocupan dos procesos primero, uno en modo usuario y otro en modo kernel, ellos Es el padre del proceso de creación posterior, por lo que se pueden crear como máximo 62 procesos en el exterior y también se borra la lista de bloqueo de tareas de los dos últimos padres del código.

El proceso de creación de kernel Kprocess

Cree un proceso de estado central, que es el número de proceso [2] en el grupo de subprocesos. En el comando de tarea, Kprocess PID = 2, los parámetros son el estado central y la prioridad más alta 0

 El código establece kprocess como el proceso actual y bifurca un KIdle (el proceso inactivo en modo kernel)

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

Lo anterior es el proceso de inicialización del proceso en modo kernel

El proceso de creación de un proceso de modo de usuario

/**
 * @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, &param);
    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;
}

Se encuentra que el proceso de inicio y creación en modo usuario es similar al modo kernel, y la prioridad del proceso padre en modo usuario es 28, muy baja.

Deja dos pequeñas preguntas para que todos piensen

 Hay algún código sobre la memoria en OsUserInitProcess, y el valor no se puede encontrar en el código fuente, como por ejemplo:

    CHAR *userInitTextStart = (CHAR *)&__user_init_entry;
    CHAR *userInitBssStart = (CHAR *)&__user_init_bss;
    CHAR *userInitEnd = (CHAR *)&__user_init_end;

¿Por qué está pasando esto?

Los PID de los otros dos padres son 1 y 2. ¿A dónde fue el proceso número 0 en el grupo de procesos?

Invitamos a todos a responder en el área de comentarios, y analizaremos en detalle más adelante. Se  pueden ver más artículos en el  análisis de código fuente del sistema Hongmeng (Catálogo general)

 

Supongo que te gusta

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