Análise de código-fonte do kernel Hongmeng (gerenciamento de tarefas / threads)

Nota: Com base na análise de obscuridade de código aberto central, a fonte oficial [ kernel_liteos_a ] documentos oficiais [ docs ] Documento de referência [ Huawei LiteOS ]
autor: Entusiastas do núcleo de Hong Meng, continuarão a estudar o kernel obscuridade, atualizar o blog, portanto, fique atento. O conteúdo representa apenas opiniões pessoais, erros são bem-vindos, todos são bem-vindos para corrigir e melhorar. Ver todos os artigos desta série na
análise de código-fonte do sistema obscuro (lista)


Este artigo analisa o código-fonte do gerenciamento de tarefas / threads em detalhes: los_task.c

índice

Prefácio

1. Como entender a tarefa

1. Como o documento oficial descreve tópicos

2. Execute o comando de tarefa

3. Qual é a aparência da tarefa?

Em segundo lugar, como gerenciar tarefas

1. O que é um pool de tarefas?

2. Qual é o problema com a fila de espera

3. Qual é o problema com a pilha de tarefas

4. Inicialização da pilha de tarefas

Três, conjunto de funções de tarefas

1. Cenários de uso e funções

2. O processo de criação de tarefas

3. Quais são os conteúdos importantes da parte Tarefa / Thread que não foram mencionados?

Para obter mais informações, clique em Hongmeng System Source Code Analysis (Catálogo Geral)


Prefácio

No kernel Hongmeng, uma tarefa pode ser entendida como um thread em um sentido amplo


1. Como entender a tarefa

1. Como o documento oficial descreve tópicos

Conceitos básicos
De uma perspectiva do sistema, um thread é a menor unidade de operação que compete pelos recursos do sistema. Threads podem usar ou aguardar recursos do sistema, como CPU e espaço de memória, e executados independentemente de outros threads.

Os threads em cada processo do Hongmeng Kernel são executados de forma independente e agendados independentemente, e o agendamento de threads no processo atual não é afetado por outros threads no processo.

Os threads no kernel Hongmeng adotam um mecanismo de agendamento preventivo e oferecem suporte ao agendamento round-robin de fração de tempo e ao agendamento FIFO.

Os threads do kernel Hongmeng têm um total de 32 prioridades (0-31), a prioridade mais alta é 0 e a prioridade mais baixa é 31.

O thread de alta prioridade no processo atual pode antecipar o thread de baixa prioridade no processo atual, e o thread de baixa prioridade no processo atual só pode ser agendado depois que o thread de alta prioridade no processo atual for bloqueado ou encerrado.

Descrição do status do tópico:

Inicialização (Init): O thread está sendo criado.

Pronto (Pronto): O thread está na lista de pronto, aguardando o agendamento da CPU.

Em execução: o tópico está em execução.

Bloqueado: o tópico está bloqueado e suspenso. Os estados bloqueados incluem: pend (devido a bloqueios, eventos, semáforos, etc.), suspender (pend ativo), atraso (bloqueio atrasado), pendtime (devido a bloqueios, eventos, tempo do semáforo, etc. espera de horas extras).

Exit (Exit): O thread termina, esperando que o thread pai recupere seus recursos de bloco de controle.

Figura 1 Diagrama esquemático de migração de estado de thread.
Insira a descrição da imagem aqui
Observe que o documento oficial fala sobre threads e não menciona tarefas. No entanto, há muitos códigos de tarefas no código-fonte do kernel e poucos códigos de thread. O que está acontecendo?
Na verdade, no kernel Hongmeng, as tarefas são threads. Iniciantes podem entendê-las dessa maneira, mas ainda há diferenças entre as duas. Caso contrário, por que deveriam ser descritas em duas palavras.
Qual é a diferença? É a diferença no gerenciamento. Tarefa é o conceito de nível de escalonamento e thread é o conceito de nível de processo. Assim como a mesma pessoa tem identidades diferentes em sistemas de gestão diferentes, um homem pode ser uma criança, um pai, um marido ou um programador, com diferentes perspectivas e funções.

Uma coisa é provar isso, continue olhando para baixo.

2. Execute o comando de tarefa

O resultado da execução do comando de tarefa Hongmeng:
Insira a descrição da imagem aqui

O comando task descobre o status de execução de cada tarefa no ciclo de vida, seu espaço de memória em execução, prioridade, fração de tempo, função de execução de entrada, ID do processo, status e outras informações, o que é muito complicado. Essas informações complexas precisam de uma estrutura para serem transportadas. E esta estrutura é LosTaskCB (bloco de controle de tarefa)

3. Qual é a aparência da tarefa?

Antes de falarmos sobre LosTaskCB, vamos falar sobre a definição correspondente ao estado da tarefa do documento oficial, pode-se ver que tarefa e thread são a mesma coisa. 

#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

Qual é a aparência do LosTaskCB? Desculpe, está um pouco longo, mas ainda tenho que postar a imagem 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;

A estrutura do LosTaskCB tem muito conteúdo, o que eles significam?
LosTaskCB é equivalente ao ID da tarefa no kernel, que reflete a operação de cada tarefa no ciclo de vida. Por ser um ciclo, há um estado e ele precisa de espaço na memória para ser executado e precisa ser programado pelo algoritmo do kernel. A CPU selecionada executa as instruções do segmento de código. Se a CPU deseja executar, ela precisa dizer onde iniciar a execução, porque é multithread, mas Apenas uma CPU precisa trocar de tarefa constantemente, então a execução será interrompida e a execução deverá ser reiniciada.Como garantir que a execução da tarefa retomada não dê errado? Esses problemas precisam ser esclarecidos.

Em segundo lugar, como gerenciar tarefas

1. O que é um pool de tarefas?

Como mencionado anteriormente, a tarefa é o conceito do nível de escalonamento do kernel.O algoritmo de escalonamento garante a execução ordenada da tarefa.Há um capítulo detalhado sobre o algoritmo de escalonamento no seguimento.
Como gerenciar e executar tantas tarefas? O gerenciamento depende de pools de tarefas e filas prontas, e a execução depende de algoritmos de agendamento.
O código é o seguinte (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 é um pool de tarefas, 128 tarefas são criadas por padrão, residentes na memória e não liberadas.
g_losFreeTask é uma lista vinculada de tarefas ociosas. Quando você quiser criar uma tarefa, venha aqui para se inscrever para uma tarefa gratuita. Quando ela se esgotar, será reciclada e continuará a ser usada para aplicativos subsequentes.
g_taskRecyleList é uma lista de tarefas de reciclagem, dedicada a tarefas de saída de reciclagem. Os recursos ocupados por tarefas são completamente excluídos após serem confirmados e devolvidos. Assim como a demissão de funcionários, deve haver uma fila e um processo de demissão. O computador e a caixa de correio devem ser devolvidos. E outras operações.

2. Qual é o problema com a fila de espera

A velocidade de execução da CPU é muito rápida. A fração de tempo padrão do kernel Hongmeng é de 10 ms. Os recursos são limitados e precisam ser alternados entre várias tarefas. Portanto, a CPU não pode esperar por tarefas. A CPU é como o maior líder da empresa, muitos departamentos abaixo, etc. Os líderes vêm para aprovar e comer. Só todo mundo está esperando o líder, não há razão para o líder esperar por você, então o trabalho deve ser preparado com antecedência, e a prioridade de cada departamento é diferente, então cada departamento deve ter uma fila de tarefas, que é colocada no líder pode atender diretamente Tarefas, não as coloque se não estiver pronto, pois se trata de comida preparada com antecedência para a CPU!
Este é o princípio da fila pronta. Existem 32 filas prontas, tanto processos quanto threads. Como a prioridade da thread é 32 por padrão, cada fila coloca tarefas com a mesma prioridade.
Vamos ver o código-fonte .

#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 atenção à alocação de memória de g_priQueueList, que é 32 LOS_DL_LIST. Você se lembra do efeito mágico de LOS_DL_LIST? Vá para Hongmeng System Source Code Analysis (Catálogo Geral) se não tiver certeza  .

3. Qual é o problema com a pilha de tarefas

Cada tarefa é aberta de forma independente, e as tarefas também são independentes umas das outras. A comunicação entre elas é por meio de IPC. O "independente" aqui significa que cada tarefa tem seu próprio ambiente operacional - espaço de pilha, chamado pilha de tarefas, pilha As informações armazenadas no espaço incluem variáveis ​​locais, registros, parâmetros de função, endereços de retorno de função, etc.
Porém, há apenas uma CPU no sistema e as tarefas são independentes.A essência do escalonamento é que a CPU executa uma nova tarefa, e onde a tarefa antiga é interrompida? Não está claro, é aleatório. Então, como garantir que a tarefa antiga possa continuar a ser executada a partir do local em que foi interrompida da última vez quando for agendada novamente?

A resposta é: contexto da tarefa, há um monte de registros na CPU. A essência da operação da CPU é que os valores desses registros mudam constantemente. Contanto que esses valores sejam salvos ao alternar e depois restaurados, a execução contínua da tarefa pode ser garantida, deixando o usuário sem Percepção.

É o mesmo que quando brincávamos de esconde-esconde quando éramos jovens. Fomos interrompidos por nossos pais no meio do jogo e chamados de volta para comer. Continuamos a jogar à tarde. Zhang San, você deve voltar para a árvore e se esconder, Li Si continuou a se esconder debaixo da cama, Wang O quinto é pegar Zhang San e Li Si continuar a pegar, primeiro restaurar a cena e depois continuar a jogar. Isso registra a localização de todos e as informações da função são o contexto da tarefa. Na verdade, uma tarefa deve ser interrompida repetidamente.O kernel Hongmeng dá um tempo de execução da tarefa de 20ms, o que significa que, no caso de uma competição multitarefa, ela deve alternar 50 vezes em um segundo no máximo.


Qual é o contexto da tarefa (TaskContext)? Ainda olhe para o código fonte diretamente

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

Verifica-se que é basicamente o valor do campo de recuperação do registro da CPU.Você pode verificar as funções específicas de cada registro na Internet, e haverá um artigo especial para apresentá-lo posteriormente. Aqui estão três dos registros SP, LR, PC

LR
tem duas finalidades. A primeira é salvar o endereço de retorno da sub-rotina. Quando uma instrução de salto, como BL, BX, BLX, etc. é chamada, o endereço de retorno é salvo automaticamente em LR; a segunda é salvar o endereço de retorno da exceção quando ocorre uma exceção.

PC (contador de programa)
é o contador de programa, usado para salvar o endereço de execução do programa. Na arquitetura de pipeline de três estágios do ARM, o pipeline de programa inclui três fases: endereçamento, decodificação e execução. PC aponta para o endereço do programa do endereço atual , Portanto, no ARM de 32 bits, o endereço de decodificação (o programa sendo analisado e ainda não executado) é PC-4 e o endereço de execução (o endereço do programa em execução) é PC-8. Quando ocorre uma interrupção repentina, o PC é salvo o endereço de.


Cada modo de exceção do SP tem seu próprio r13 independente, que geralmente aponta para a pilha dedicada ao modo de exceção. Quando o ARM entra no modo de exceção, o programa pode colocar registradores de uso geral na pilha e, em seguida, colocá-la quando retornar. A integridade do estado do programa em vários modos.

4. Inicialização da pilha de tarefas

A inicialização da pilha de tarefas é a inicialização do contexto da tarefa, porque a tarefa não começou a executar, não haverá nenhum outro conteúdo exceto o contexto.Note que o contexto é armazenado na parte inferior da pilha.

Três, conjunto de funções de tarefas

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. Cenários de uso e funções

Depois que a tarefa é criada, o kernel pode executar operações como bloqueio de agendamento de tarefa, desbloqueio de agendamento de tarefa, suspensão, retomada e atraso.Ao mesmo tempo, ele também pode definir a prioridade da tarefa e obter a prioridade da tarefa. Quando a tarefa termina, a operação de exclusão automática da tarefa atual é executada.
O módulo de gerenciamento de tarefas no sistema Huawei LiteOS oferece aos usuários as seguintes funções.

Classificação de funções Nome da interface descrição
Criação e exclusão de tarefas LOS_TaskCreateOnly Crie uma tarefa e faça com que ela entre no estado suspenso sem agendamento.
  LOS_TaskCreate Crie uma tarefa, faça com que ela entre no estado de pronta e agende-a.
  LOS_TaskDelete Exclua a tarefa especificada.
Controle de status da tarefa LOS_TaskResume Retome as tarefas suspensas.
  LOS_TaskSuspend Suspenda a tarefa especificada.
  LOS_TaskDelay A tarefa está atrasada.
  LOS_TaskYield Descentralização explícita, ajuste a ordem de agendamento de tarefas da prioridade especificada.
Controle de agendamento de tarefas LOS_TaskLock Bloqueie o agendamento de tarefas.
  LOS_TaskUnlock Desbloqueie o agendamento de tarefas.
Controle de prioridade de tarefa LOS_CurTaskPriSet Defina a prioridade da tarefa atual.
  LOS_TaskPriSet Defina a prioridade da tarefa especificada.
  LOS_TaskPriGet Obtenha a prioridade da tarefa especificada.
Aquisição de informações de tarefa LOS_CurTaskIDGet Obtenha o ID da tarefa atual.
  LOS_TaskInfoGet Defina a prioridade da tarefa especificada.
  LOS_TaskPriGet Obtenha informações sobre a tarefa especificada.
  LOS_TaskStatusGet Obtenha o status da tarefa especificada.
  LOS_TaskNameGet Obtenha o nome da tarefa especificada.
  LOS_TaskInfoMonitor Monitore todas as tarefas e obtenha informações sobre todas as tarefas.
  LOS_NextTaskIDGet Obtenha o ID da tarefa a ser agendada.

2. O processo de criação de tarefas

Antes de criar uma tarefa, aprenda sobre outra estrutura 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;

Esses parâmetros de inicialização são os parâmetros iniciais expostos da tarefa e pfnTaskEntry 对java来说就是你new进程的run(),precisam ser fornecidos pelo usuário superior.

Vejamos um exemplo: digite o comando ping no shell para ver o processo de sua criação

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

Verifica-se que a prioridade de agendamento do ping é 8, que é maior do que a do shell.Qual é a prioridade do shell? A resposta é: veja o código-fonte 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);
}

Continue prestando atenção à introdução detalhada do shell no acompanhamento.
Depois de compreender as pré-condições, isso depende de como a tarefa é criada passo a passo, como se vincular ao processo, entrar na fila de agendamento pronto ou continuar a olhar o código-fonte

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

Até agora, a criação foi concluída e eles estão todos no lugar. O código-fonte finalmente aplicado para LOS_Schedule (); como o método de agendamento de Hongmeng é preemptivo, como a prioridade da tarefa desta tarefa é maior do que outras filas prontas, então a tarefa a ser executada a seguir É isso aí!

3. Quais são os conteúdos importantes da parte Tarefa / Thread que não foram mencionados?

Como a memória é alocada, como se comunica entre threads e como a pilha é executada durante a operação. Após a conversa de acompanhamento sobre memória, a parte do IPC será desmontada para análise.

Para obter mais informações, clique em  Hongmeng System Source Code Analysis (Catálogo Geral)

 

Acho que você gosta

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