Análisis del código fuente del kernel de Hongmeng (gestión de tareas / subprocesos)

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. Ver todos los artículos de esta serie en el
análisis del código fuente del sistema de oscuridad (Lista)


Este artículo analiza el código fuente de la gestión de tareas / subprocesos en detalle: los_task.c

Tabla de contenido

Prefacio

1. Cómo entender la tarea

1. ¿Cómo describe el documento oficial los hilos?

2. Ejecute el comando de tarea

3. ¿Cómo se ve la tarea?

En segundo lugar, cómo administrar la tarea

1. ¿Qué es un grupo de tareas?

2. ¿Qué pasa con la cola lista?

3. ¿Qué pasa con la pila de tareas?

4. Inicialización de la pila de tareas

Tres, conjunto de funciones de tareas

1. Escenarios de uso y funciones

2. El proceso de creación de tareas

3. ¿Cuáles son los contenidos importantes de la parte Task / Thread que no se han mencionado?

Para obtener más información, haga clic en Análisis de código fuente del sistema Hongmeng (catálogo general)


Prefacio

En el kernel de Hongmeng, una tarea puede entenderse como un hilo en un sentido amplio


1. Cómo entender la tarea

1. ¿Cómo describe el documento oficial los hilos?

Conceptos básicos
Desde la perspectiva del sistema, un hilo es la unidad de operación más pequeña que compite por los recursos del sistema. Los subprocesos pueden usar o esperar recursos del sistema, como CPU y espacio de memoria, y ejecutarse independientemente de otros subprocesos.

Los subprocesos en cada proceso de Hongmeng Kernel se ejecutan de forma independiente y se programan de forma independiente, y la programación de subprocesos en el proceso actual no se ve afectada por otros subprocesos en el proceso.

Los subprocesos en el kernel de Hongmeng adoptan un mecanismo de programación preventiva y admiten la programación por turnos por intervalos de tiempo y la programación FIFO.

Los subprocesos del kernel de Hongmeng tienen un total de 32 prioridades (0-31), la prioridad más alta es 0 y la prioridad más baja es 31.

El subproceso de alta prioridad en el proceso actual puede adelantarse al subproceso de baja prioridad en el proceso actual, y el subproceso de baja prioridad en el proceso actual solo se puede programar después de que el subproceso de alta prioridad en el proceso actual se bloquee o finalice.

Descripción del estado del hilo:

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

Listo (Listo): el hilo está en la lista de listos, esperando la programación de la CPU.

En ejecución: el hilo se está ejecutando.

Bloqueado: el hilo está bloqueado y suspendido. Los estados bloqueados incluyen: pendiente (debido a bloqueos, eventos, semáforos, etc.), suspensión (pendiente activo), retraso (bloqueo retrasado), tiempo de espera (debido a bloqueos, eventos, tiempo de semáforo, etc. espera de tiempo extra).

Exit (Exit): el hilo finaliza, esperando que el hilo principal recupere sus recursos de bloque de control.

Figura 1 Diagrama esquemático de la migración del estado de subprocesos.
Inserte la descripción de la imagen aquí
Tenga en cuenta que el documento oficial habla de subprocesos y no menciona tareas. Sin embargo, hay muchos códigos de tareas en el código fuente del kernel y muy pocos códigos de subprocesos. ¿Qué está pasando?
De hecho, en el kernel de Hongmeng, las tareas son subprocesos. Los principiantes pueden entenderlo de esta manera, pero todavía hay diferencias entre los dos. De lo contrario, ¿por qué deberían describirse en dos palabras?
¿Cuál es la diferencia? Es la diferencia en la gestión, la tarea es el concepto de nivel de programación y el hilo es el concepto de nivel de proceso. Así como la misma persona tiene diferentes identidades en diferentes sistemas de gestión, un hombre puede ser un niño, un padre, un esposo o un programador, con diferentes perspectivas y funciones.

Cómo demostrarlo es una cosa, sigue mirando hacia abajo.

2. Ejecute el comando de tarea

El resultado de la ejecución del comando de tarea Hongmeng:
Inserte la descripción de la imagen aquí

El comando de tarea descubre el estado de ejecución de cada tarea en el ciclo de vida, su espacio de memoria en ejecución, prioridad, intervalo de tiempo, función de ejecución de entrada, ID de proceso, estado y otra información, lo cual es muy complicado. Esta información compleja necesita una estructura para ser transportada. Y esta estructura es LosTaskCB (bloque de control de tareas)

3. ¿Cómo se ve la tarea?

Antes de hablar de LosTaskCB, hablemos de la definición correspondiente al estado de la tarea del documento oficial, se puede ver que la tarea y el hilo son lo mismo. 

#define OS_TASK_STATUS_INIT         0x0001U
#define OS_TASK_STATUS_READY        0x0002U
#define OS_TASK_STATUS_RUNNING      0x0004U
#define OS_TASK_STATUS_SUSPEND      0x0008U
#define OS_TASK_STATUS_PEND         0x0010U
#define OS_TASK_STATUS_DELAY        0x0020U
#define OS_TASK_STATUS_TIMEOUT      0x0040U
#define OS_TASK_STATUS_PEND_TIME    0x0080U
#define OS_TASK_STATUS_EXIT         0x0100U

¿Qué aspecto tiene LosTaskCB? Lo siento, es un poco largo, pero aún tengo que publicar la imagen completa.

typedef struct {
    VOID            *stackPointer;      /**< Task stack pointer */
    UINT16          taskStatus;         /**< Task status */
    UINT16          priority;           /**< Task priority */
    UINT16          policy;
    UINT16          timeSlice;          /**< Remaining time slice */
    UINT32          stackSize;          /**< Task stack size */
    UINTPTR         topOfStack;         /**< Task stack top */
    UINT32          taskID;             /**< Task ID */
    TSK_ENTRY_FUNC  taskEntry;          /**< Task entrance function */
    VOID            *joinRetval;        /**< pthread adaption */
    VOID            *taskSem;           /**< Task-held semaphore */
    VOID            *taskMux;           /**< Task-held mutex */
    VOID            *taskEvent;         /**< Task-held event */
    UINTPTR         args[4];            /**< Parameter, of which the maximum number is 4 */
    CHAR            taskName[OS_TCB_NAME_LEN]; /**< Task name */
    LOS_DL_LIST     pendList;           /**< Task pend node */
    LOS_DL_LIST     threadList;         /**< thread list */
    SortLinkList    sortList;           /**< Task sortlink node */
    UINT32          eventMask;          /**< Event mask */
    UINT32          eventMode;          /**< Event mode */
    UINT32          priBitMap;          /**< BitMap for recording the change of task priority,
                                             the priority can not be greater than 31 */
    INT32           errorNo;            /**< Error Num */
    UINT32          signal;             /**< Task signal */
    sig_cb          sig;
#if (LOSCFG_KERNEL_SMP == YES)
    UINT16          currCpu;            /**< CPU core number of this task is running on */
    UINT16          lastCpu;            /**< CPU core number of this task is running on last time */
    UINT16          cpuAffiMask;        /**< CPU affinity mask, support up to 16 cores */
    UINT32          timerCpu;           /**< CPU core number of this task is delayed or pended */
#if (LOSCFG_KERNEL_SMP_TASK_SYNC == YES)
    UINT32          syncSignal;         /**< Synchronization for signal handling */
#endif
#if (LOSCFG_KERNEL_SMP_LOCKDEP == YES)
    LockDep         lockDep;
#endif
#if (LOSCFG_KERNEL_SCHED_STATISTICS == YES)
    SchedStat       schedStat;          /**< Schedule statistics */
#endif
#endif
    UINTPTR         userArea;
    UINTPTR         userMapBase;
    UINT32          userMapSize;        /**< user thread stack size ,real size : userMapSize + USER_STACK_MIN_SIZE */
    UINT32          processID;          /**< Which belong process */
    FutexNode       futex;
    LOS_DL_LIST     joinList;           /**< join list */
    LOS_DL_LIST     lockList;           /**< Hold the lock list */
    UINT32          waitID;             /**< Wait for the PID or GID of the child process */
    UINT16          waitFlag;           /**< The type of child process that is waiting, belonging to a group or parent,
                                             a specific child process, or any child process */
#if (LOSCFG_KERNEL_LITEIPC == YES)
    UINT32          ipcStatus;
    LOS_DL_LIST     msgListHead;
    BOOL            accessMap[LOSCFG_BASE_CORE_TSK_LIMIT];
#endif
} LosTaskCB;

La estructura de LosTaskCB tiene mucho contenido, ¿qué significan?
LosTaskCB es equivalente al ID de la tarea en el kernel, que refleja el funcionamiento de cada tarea en el ciclo de vida. Dado que es un ciclo, hay un estado y necesita espacio de memoria para ejecutarse, y debe ser programado por el algoritmo del kernel. La CPU seleccionada ejecuta las instrucciones del segmento de código. Si la CPU quiere ejecutar, debe indicarle dónde comenzar la ejecución, porque es multiproceso, pero Solo una CPU necesita cambiar constantemente de tareas, luego la ejecución se interrumpirá y la ejecución debe reanudarse. ¿Cómo asegurarse de que la ejecución de la tarea reanudada no salga mal? Estos problemas deben aclararse.

En segundo lugar, cómo administrar la tarea

1. ¿Qué es un grupo de tareas?

Como se mencionó anteriormente, la tarea es el concepto del nivel de programación del kernel. El algoritmo de programación asegura la ejecución ordenada de la tarea. Habrá un capítulo detallado sobre el algoritmo de programación en el seguimiento.
¿Cómo gestionar y ejecutar tantas tareas? La administración se basa en grupos de tareas y colas listas, y la ejecución se basa en algoritmos de programación.
El código es el siguiente (OsTaskInit):

LITE_OS_SEC_TEXT_INIT UINT32 OsTaskInit(VOID)
{
   
    UINT32 index;
    UINT32 ret;
    UINT32 size;

    g_taskMaxNum = LOSCFG_BASE_CORE_TSK_LIMIT;//任务池中最多默认128个,可谓铁打的任务池流水的线程
    size = (g_taskMaxNum + 1) * sizeof(LosTaskCB);
    /* * This memory is resident memory and is used to save the system resources * of task control block and will not be freed. */
    g_taskCBArray = (LosTaskCB *)LOS_MemAlloc(m_aucSysMem0, size);//任务池 常驻内存,不被释放
    if (g_taskCBArray == NULL) {
   
        return LOS_ERRNO_TSK_NO_MEMORY;
    }
    (VOID)memset_s(g_taskCBArray, size, 0, size);

    LOS_ListInit(&g_losFreeTask);//空闲任务链表
    LOS_ListInit(&g_taskRecyleList);//需回收任务链表
    for (index = 0; index < g_taskMaxNum; index++) {
   
        g_taskCBArray[index].taskStatus = OS_TASK_STATUS_UNUSED;
        g_taskCBArray[index].taskID = index;//任务ID最大默认127
        LOS_ListTailInsert(&g_losFreeTask, &g_taskCBArray[index].pendList);//都插入空闲任务链表
    }

    ret = OsPriQueueInit();//创建32个任务优先级队列,即32个双向循环链表
    if (ret != LOS_OK) {
   
        return LOS_ERRNO_TSK_NO_MEMORY;
    }

    /* init sortlink for each core */
    for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) {
   
        ret = OsSortLinkInit(&g_percpu[index].taskSortLink);//每个CPU内核都有一个执行任务链表
        if (ret != LOS_OK) {
   
            return LOS_ERRNO_TSK_NO_MEMORY;
        }
    }
    return LOS_OK;
}

g_taskCBArray es un grupo de tareas, se crean 128 tareas de forma predeterminada, residen en la memoria y no se liberan.
g_losFreeTask es una lista enlazada de tareas inactivas. Cuando desee crear una tarea, venga aquí para solicitar una tarea gratuita. Cuando se agote, se reciclará y se seguirá utilizando para aplicaciones posteriores.
g_taskRecyleList es una lista de tareas de reciclaje, dedicada a las tareas de salida de reciclaje. Los recursos ocupados por las tareas se eliminan por completo una vez confirmadas y devueltas. Al igual que la renuncia de un empleado, debe haber una cola y un proceso de renuncia. La computadora y el buzón deben ser devueltos. Y otras operaciones.

2. ¿Qué pasa con la cola lista?

La velocidad de ejecución de la CPU es muy rápida. El intervalo de tiempo predeterminado del kernel de Hongmeng es de 10 ms. Los recursos son limitados y necesitan alternar entre muchas tareas. Por lo tanto, no se debe permitir que la CPU espere las tareas. La CPU es como el líder más grande de la compañía, muchos departamentos debajo, etc. Los líderes vienen a aprobar y comer. Solo todos están esperando al líder, no hay razón para que el líder espere por usted, por lo que el trabajo debe estar preparado con anticipación, y la prioridad de cada departamento es diferente, por lo que cada departamento debe tener una cola de tareas, que se coloca en el líder puede manejar directamente Tareas, no las ponga si no está listo, ¡porque esto es comida preparada con anticipación para la CPU!
Este es el principio de la cola lista. Hay 32 colas listas, tanto de procesos como de subprocesos, porque la prioridad de los subprocesos es 32 por defecto, y cada cola coloca tareas de la misma prioridad.
Veamos el código fuente .

#define OS_PRIORITY_QUEUE_NUM 32
UINT32 OsPriQueueInit(VOID)
{
   
    UINT32 priority;

    /* system resident resource */
    g_priQueueList = (LOS_DL_LIST *)LOS_MemAlloc(m_aucSysMem0, (OS_PRIORITY_QUEUE_NUM * sizeof(LOS_DL_LIST)));//常驻内存,不被释放
    if (g_priQueueList == NULL) {
   
        return LOS_NOK;
    }

    for (priority = 0; priority < OS_PRIORITY_QUEUE_NUM; ++priority) {
   
        LOS_ListInit(&g_priQueueList[priority]);
    }
    return LOS_OK;
}

Preste atención a la asignación de memoria de g_priQueueList, que es 32 LOS_DL_LIST. ¿Recuerda el efecto mágico de LOS_DL_LIST? Vaya a Análisis de código fuente del sistema Hongmeng (Catálogo general) si no está seguro  .

3. ¿Qué pasa con la pila de tareas?

Cada tarea se abre de forma independiente y las tareas también son independientes entre sí. La comunicación entre ellas se realiza a través de IPC. El "independiente" aquí significa que cada tarea tiene su propio entorno operativo: espacio de pila, llamado pila de tareas, pila La información almacenada en el espacio incluye variables locales, registros, parámetros de función, direcciones de retorno de función, etc.
Sin embargo, solo hay una CPU en el sistema y las tareas son independientes. La esencia de la programación es que la CPU ejecuta una nueva tarea y ¿dónde se interrumpe la tarea anterior? No está claro, es aleatorio. Entonces, ¿cómo garantizar que la tarea anterior pueda continuar ejecutándose desde el lugar donde se interrumpió la última vez cuando se programó nuevamente?

La respuesta es: contexto de la tarea, hay un montón de registros en la CPU. La esencia del funcionamiento de la CPU es que los valores de estos registros cambian constantemente. Siempre que estos valores se guarden al cambiar y luego se restauren, se puede garantizar la ejecución continua de la tarea, dejando al usuario sin Percepción.

Esto es lo mismo que cuando jugábamos al escondite cuando éramos jóvenes. Nuestros padres nos interrumpieron a la mitad del juego y nos llamaron para comer. Seguimos jugando por la tarde. Zhang San, deberías volver al árbol y esconderte, Li Si siguió escondiéndose debajo de la cama, Wang El quinto es atrapar a Zhang San y Li Si continúan atrapando, primero restauran la escena y luego continúan jugando. Esto registra la ubicación de todos y la información del rol es el contexto de la tarea. De hecho, una tarea debe interrumpirse repetidamente. El kernel de Hongmeng da un tiempo de ejecución de la tarea de 20 ms, lo que significa que en el caso de una competencia multitarea, tiene que alternar 50 veces en un segundo como máximo.


¿Qué es el contexto de la tarea (TaskContext)? Todavía mira el código fuente directamente

/* The size of this structure must be smaller than or equal to the size specified by OS_TSK_STACK_ALIGN (16 bytes). */
typedef struct {
   
#if !defined(LOSCFG_ARCH_FPU_DISABLE)
    UINT64 D[FP_REGS_NUM]; /* D0-D31 */
    UINT32 regFPSCR;       /* FPSCR */
    UINT32 regFPEXC;       /* FPEXC */
#endif
    UINT32 resved;          /* It's stack 8 aligned */
    UINT32 regPSR;
    UINT32 R[GEN_REGS_NUM]; /* R0-R12 */
    UINT32 SP;              /* R13 */
    UINT32 LR;              /* R14 */
    UINT32 PC;              /* R15 */
} TaskContext;

Se encuentra que es básicamente el valor del campo de recuperación del registro de la CPU, puedes consultar las funciones específicas de cada registro en Internet, y habrá un artículo especial para presentarlo más adelante. Estos son tres de los registros SP, LR, PC

LR
tiene dos propósitos. El primero es guardar la dirección de retorno de la subrutina. Cuando se llama a una instrucción de salto como BL, BX, BLX, etc., la dirección de retorno se guarda automáticamente en LR; el segundo es guardar la dirección de retorno de excepción cuando ocurre una excepción.

PC (Program Counter)
es el contador de programa, que se utiliza para guardar la dirección de ejecución del programa. En la arquitectura de canalización de tres etapas de ARM, la canalización del programa incluye tres etapas: direccionamiento, decodificación y ejecución. La PC apunta a la dirección del programa de la dirección actual , Por lo tanto, en ARM de 32 bits, la dirección de decodificación (el programa que se está analizando y aún no se está ejecutando) es PC-4, y la dirección de ejecución (la dirección del programa que se está ejecutando actualmente) es PC-8. Cuando ocurre una interrupción repentina, la PC se guarda la dirección de.


Cada modo de excepción de SP tiene su propio r13 independiente, que generalmente apunta a la pila dedicada al modo de excepción.Cuando ARM entra en el modo de excepción, el programa puede empujar registros de propósito general a la pila y luego abrir la pila cuando regresa. La integridad del estado del programa en varios modos.

4. Inicialización de la pila de tareas

La inicialización de la pila de tareas es la inicialización del contexto de la tarea, debido a que la tarea no ha comenzado a ejecutarse, no habrá otro contenido excepto el contexto. Tenga en cuenta que el contexto se almacena en la parte inferior de la pila.

Tres, conjunto de funciones de tareas

LITE_OS_SEC_TEXT_INIT VOID *OsTaskStackInit(UINT32 taskID, UINT32 stackSize, VOID *topStack, BOOL initFlag)
{
   
    UINT32 index = 1;
    TaskContext *taskContext = NULL;

    if (initFlag == TRUE) {
   
        OsStackInit(topStack, stackSize);
    }
    taskContext = (TaskContext *)(((UINTPTR)topStack + stackSize) - sizeof(TaskContext));//注意看上下文将存放在栈的底部

    /* initialize the task context */
#ifdef LOSCFG_GDB
    taskContext->PC = (UINTPTR)OsTaskEntrySetupLoopFrame;
#else
    taskContext->PC = (UINTPTR)OsTaskEntry;//程序计数器,CPU首次执行task时跑的第一条指令位置
#endif
    taskContext->LR = (UINTPTR)OsTaskExit;  /* LR should be kept, to distinguish it's THUMB or ARM instruction */
    taskContext->resved = 0x0;
    taskContext->R[0] = taskID;             /* R0 */
    taskContext->R[index++] = 0x01010101;   /* R1, 0x01010101 : reg initialed magic word */
    for (; index < GEN_REGS_NUM; index++) {
   //R2 - R12的初始化很有意思,为什么要这么做?
        taskContext->R[index] = taskContext->R[index - 1] + taskContext->R[1]; /* R2 - R12 */
    }

#ifdef LOSCFG_INTERWORK_THUMB // 16位模式
    taskContext->regPSR = PSR_MODE_SVC_THUMB; /* CPSR (Enable IRQ and FIQ interrupts, THUMNB-mode) */
#else
    taskContext->regPSR = PSR_MODE_SVC_ARM;   /* CPSR (Enable IRQ and FIQ interrupts, ARM-mode) */
#endif

#if !defined(LOSCFG_ARCH_FPU_DISABLE)
    /* 0xAAA0000000000000LL : float reg initialed magic word */
    for (index = 0; index < FP_REGS_NUM; index++) {
   
        taskContext->D[index] = 0xAAA0000000000000LL + index; /* D0 - D31 */
    }
    taskContext->regFPSCR = 0;
    taskContext->regFPEXC = FP_EN;
#endif

    return (VOID *)taskContext;
}

1. Escenarios de uso y funciones

Una vez creada la tarea, el kernel puede realizar operaciones como bloquear la programación de tareas, desbloquear la programación de tareas, suspender, reanudar y retrasar. Al mismo tiempo, también puede establecer la prioridad de la tarea y obtener la prioridad de la tarea. Cuando finaliza la tarea, se realiza la operación de eliminación automática de la tarea actual.
El módulo de gestión de tareas del sistema Huawei LiteOS proporciona a los usuarios las siguientes funciones.

Clasificación de funciones Nombre de la interfaz descripción
Creación y eliminación de tareas LOS_TaskCreateOnly Cree una tarea y haga que la tarea entre en estado de suspensión sin programar.
  LOS_TaskCreate Cree una tarea, haga que la tarea entre en estado listo y prográmela.
  LOS_TaskDelete Elimina la tarea especificada.
Control de estado de la tarea LOS_TaskResume Reanudar las tareas suspendidas.
  LOS_TaskSuspend Suspenda la tarea especificada.
  LOS_TaskDelay La tarea se retrasa.
  LOS_TaskYield Descentralización explícita, ajuste el orden de programación de tareas de la prioridad especificada.
Control de programación de tareas LOS_TaskLock Bloquear la programación de tareas.
  LOS_TaskUnlock Desbloquee la programación de tareas.
Control de prioridad de tareas LOS_CurTaskPriSet Establece la prioridad de la tarea actual.
  LOS_TaskPriSet Establece la prioridad de la tarea especificada.
  LOS_TaskPriGet Obtenga la prioridad de la tarea especificada.
Adquisición de información de tareas LOS_CurTaskIDGet Obtén el ID de la tarea actual.
  LOS_TaskInfoGet Establece la prioridad de la tarea especificada.
  LOS_TaskPriGet Obtén información sobre la tarea especificada.
  LOS_TaskStatusGet Obtén el estado de la tarea especificada.
  LOS_TaskNameGet Obtenga el nombre de la tarea especificada.
  LOS_TaskInfoMonitor Supervise todas las tareas y obtenga información sobre todas las tareas.
  LOS_NextTaskIDGet Obtenga el ID de la tarea que se va a programar.

2. El proceso de creación de tareas

Antes de crear una tarea, aprenda sobre otra estructura tagTskInitParam

typedef struct tagTskInitParam {
   
    TSK_ENTRY_FUNC  pfnTaskEntry;  /**< Task entrance function */
    UINT16          usTaskPrio;    /**< Task priority */
    UINT16          policy;        /**< Task policy */
    UINTPTR         auwArgs[4];    /**< Task parameters, of which the maximum number is four */
    UINT32          uwStackSize;   /**< Task stack size */
    CHAR            *pcName;       /**< Task name */
#if (LOSCFG_KERNEL_SMP == YES)
    UINT16          usCpuAffiMask; /**< Task cpu affinity mask */
#endif
    UINT32          uwResved;      /**< It is automatically deleted if set to LOS_TASK_STATUS_DETACHED. It is unable to be deleted if set to 0. */
    UINT16          consoleID;     /**< The console id of task belongs */
    UINT32          processID;
    UserTaskParam   userParam;
} TSK_INIT_PARAM_S;

Estos parámetros de inicialización son los parámetros iniciales expuestos de la tarea y pfnTaskEntry 对java来说就是你new进程的run(),deben ser proporcionados por el usuario superior.

Veamos un ejemplo: escriba el comando ping en el shell para ver el proceso de su creación

u32_t osShellPing(int argc, const char **argv)
{
   
    int ret;
    u32_t i = 0;
    u32_t count = 0;
    int count_set = 0;
    u32_t interval = 1000; /* default ping interval */
    u32_t data_len = 48; /* default data length */
    ip4_addr_t dst_ipaddr;
    TSK_INIT_PARAM_S stPingTask;
    // ...省去一些中间代码
    /* start one task if ping forever or ping count greater than 60 */
    if (count == 0 || count > LWIP_SHELL_CMD_PING_RETRY_TIMES) {
   
        if (ping_taskid > 0) {
   
            PRINTK("Ping task already running and only support one now\n");
            return LOS_NOK;
        }
        stPingTask.pfnTaskEntry = (TSK_ENTRY_FUNC)ping_cmd;//线程的执行函数
        stPingTask.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;//0x4000 = 16K 
        stPingTask.pcName = "ping_task";
        stPingTask.usTaskPrio = 8; /* higher than shell 优先级高于10,属于内核态线程*/ 
        stPingTask.uwResved = LOS_TASK_STATUS_DETACHED;
        stPingTask.auwArgs[0] = dst_ipaddr.addr; /* network order */
        stPingTask.auwArgs[1] = count;
        stPingTask.auwArgs[2] = interval;
        stPingTask.auwArgs[3] = data_len;
        ret = LOS_TaskCreate((UINT32 *)(&ping_taskid), &stPingTask);
    }
	// ...
    return LOS_OK;
ping_error:
    lwip_ping_usage();
    return LOS_NOK;
}

Se encuentra que la prioridad de programación del ping es 8, que es mayor que la del shell ¿Cuál es la prioridad del shell? La respuesta es: ver el código fuente es 9

LITE_OS_SEC_TEXT_MINOR UINT32 ShellTaskInit(ShellCB *shellCB)
{
   
    CHAR *name = NULL;
    TSK_INIT_PARAM_S initParam = {
   0};
    if (shellCB->consoleID == CONSOLE_SERIAL) {
   
        name = SERIAL_SHELL_TASK_NAME;
    } else if (shellCB->consoleID == CONSOLE_TELNET) {
   
        name = TELNET_SHELL_TASK_NAME;
    } else {
   
        return LOS_NOK;
    }
    initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ShellTask;
    initParam.usTaskPrio   = 9; /* 9:shell task priority */
    initParam.auwArgs[0]   = (UINTPTR)shellCB;
    initParam.uwStackSize  = 0x3000;
    initParam.pcName       = name;
    initParam.uwResved     = LOS_TASK_STATUS_DETACHED;
    (VOID)LOS_EventInit(&shellCB->shellEvent);
    return LOS_TaskCreate(&shellCB->shellTaskHandle, &initParam);
}

Continúe prestando atención a la introducción detallada de shell en el seguimiento.
Después de comprender las condiciones previas, depende de cómo se crea la tarea paso a paso, cómo vincularse al proceso, unirse a la cola de programación lista o continuar mirando el código fuente

LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskCreate(UINT32 *taskID, TSK_INIT_PARAM_S *initParam)
{
    UINT32 ret;
    UINT32 intSave;
    LosTaskCB *taskCB = NULL;

    if (initParam == NULL) {
        return LOS_ERRNO_TSK_PTR_NULL;
    }

    if (OS_INT_ACTIVE) {
        return LOS_ERRNO_TSK_YIELD_IN_INT;
    }

    if (initParam->uwResved & OS_TASK_FLAG_IDLEFLAG) {
        initParam->processID = OsGetIdleProcessID();
    } else if (OsProcessIsUserMode(OsCurrProcessGet())) {
        initParam->processID = OsGetKernelInitProcessID();
    } else {
        initParam->processID = OsCurrProcessGet()->processID;
    }
    initParam->uwResved &= ~OS_TASK_FLAG_IDLEFLAG;
    initParam->uwResved &= ~OS_TASK_FLAG_PTHREAD_JOIN;
    if (initParam->uwResved & LOS_TASK_STATUS_DETACHED) {
        initParam->uwResved = OS_TASK_FLAG_DETACHED;
    }

    ret = LOS_TaskCreateOnly(taskID, initParam);
    if (ret != LOS_OK) {
        return ret;
    }
    taskCB = OS_TCB_FROM_TID(*taskID);

    SCHEDULER_LOCK(intSave);
    taskCB->taskStatus &= ~OS_TASK_STATUS_INIT;
    OS_TASK_SCHED_QUEUE_ENQUEUE(taskCB, 0);
    SCHEDULER_UNLOCK(intSave);

    /* in case created task not running on this core,
       schedule or not depends on other schedulers status. */
    LOS_MpSchedule(OS_MP_CPU_ALL);
    if (OS_SCHEDULER_ACTIVE) {
        LOS_Schedule();//*kyf 任务创建完了 申请调度
    }

    return LOS_OK;
}

Hasta ahora, la creación se ha completado y todos están en su lugar. El código fuente finalmente se aplicó para LOS_Schedule (); debido a que el método de programación de Hongmeng es preventivo, la prioridad de esta tarea es mayor que la de otras colas listas, entonces la tarea que se ejecutará a continuación ¡Eso es!

3. ¿Cuáles son los contenidos importantes de la parte Task / Thread que no se han mencionado?

Cómo se asigna la memoria, cómo comunicarse entre subprocesos y cómo se ejecuta la pila durante la operación Después de la charla de seguimiento sobre la memoria, la parte IPC se desmontará para su análisis.

Para obtener más información, haga clic en  Análisis de código fuente del sistema Hongmeng (catálogo general)

 

Supongo que te gusta

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