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

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)


Tabla de contenido

¿Con qué frecuencia marco?

¿Dónde configurar la función de devolución de llamada tick?

OsTaskScan ()

Además de la programación de los disparadores de tick, ¿qué otras situaciones desencadenan la programación?


El módulo de gestión de reloj es muy simple, pero existe el segmento de código más importante del kernel, OsTickHandler (). Qué es esto, puede entenderse como una tarea de sincronización de JAVA, pero es un temporizador del kernel del sistema. Dado que Hongmeng está abriendo actualmente el kernel lite os ligero (LOS), la frecuencia de tick no será demasiado alta

Consulte el código para obtener más detalles: los_tick.c      los_timeslice.c

¿Con qué frecuencia marco?

/**
 * @ingroup los_config
 * Number of Ticks in one second
 */
#ifndef LOSCFG_BASE_CORE_TICK_PER_SECOND
#define LOSCFG_BASE_CORE_TICK_PER_SECOND 100 //*kfy 默认每秒100次触发,当然这是可以改的
#endif

100 tics por segundo, es decir, el controlador de interrupción del reloj se llama 100 veces por segundo y la unidad de intervalo de tiempo es 10 ms

¿Cuánto tiempo se asigna al proceso para ejecutar una tarea? La respuesta es 2 intervalos de tiempo, es decir, 20 ms

/**
 * @ingroup los_config
 * Longest execution time of tasks with the same priorities
 */
#ifndef LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT
#define LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT 2
#endif

/**
 * @ingroup los_process
 * Hold the time slice process
 */
#define OS_PROCESS_SCHED_RR_INTERVAL     LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT

Se puede redistribuir cuando se agota, pero solo se dan 2 intervalos de tiempo a la vez. Código detallado: OsSchedResched (VOID)

VOID OsSchedResched(VOID)
{
    LosTaskCB *runTask = NULL;
    LosTaskCB *newTask = NULL;
    LosProcessCB *runProcess = NULL;
    LosProcessCB *newProcess = NULL;

    LOS_ASSERT(LOS_SpinHeld(&g_taskSpin));

    if (!OsPreemptableInSched()) {
        return;
    }

    runTask = OsCurrTaskGet();
    newTask = OsGetTopTask();

    /* always be able to get one task */
    LOS_ASSERT(newTask != NULL);

    if (runTask == newTask) {
        return;
    }

    runTask->taskStatus &= ~OS_TASK_STATUS_RUNNING;
    newTask->taskStatus |= OS_TASK_STATUS_RUNNING;

    runProcess = OS_PCB_FROM_PID(runTask->processID);
    newProcess = OS_PCB_FROM_PID(newTask->processID);

    OsSchedSwitchProcess(runProcess, newProcess);

#if (LOSCFG_KERNEL_SMP == YES)
    /* mask new running task's owner processor */
    runTask->currCpu = OS_TASK_INVALID_CPUID;
    newTask->currCpu = ArchCurrCpuid();
#endif

    (VOID)OsTaskSwitchCheck(runTask, newTask);

#if (LOSCFG_KERNEL_SCHED_STATISTICS == YES)
    OsSchedStatistics(runTask, newTask);
#endif

    if ((newTask->timeSlice == 0) && (newTask->policy == LOS_SCHED_RR)) {
        newTask->timeSlice = LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT; //只给两个时间片
    }

    OsCurrTaskSet((VOID*)newTask);

    if (OsProcessIsUserMode(newProcess)) {
        OsCurrUserTaskSet(newTask->userArea);
    }

    PRINT_TRACE("cpu%d run process name: (%s) pid: %d status: %x threadMap: %x task name: (%s) tid: %d status: %x ->\n"
                "     new process name: (%s) pid: %d status: %x threadMap: %x task name: (%s) tid: %d status: %x!\n",
                ArchCurrCpuid(),
                runProcess->processName, runProcess->processID, runProcess->processStatus,
                runProcess->threadScheduleMap, runTask->taskName,  runTask->taskID, runTask->taskStatus,
                newProcess->processName, newProcess->processID, newProcess->processStatus,
                newProcess->threadScheduleMap, newTask->taskName, newTask->taskID, newTask->taskStatus);

    /* do the task context switch */
    OsTaskSchedule(newTask, runTask);
}

¿Dónde configurar la función de devolución de llamada tick?

Puede ver la inicialización de tick y el registro del programa de servicio de interrupción desde main

// 中断处理函数
VOID OsTickEntry(VOID)
{
    OsTickHandler(); //最最关键函数

    /* clear private timer */
    g_privateTimer->intStatus = 0x01;
}


// 由 main 函数调用,注册中断处理函数 OsTickEntry
VOID HalClockInit(VOID)
{
    UINT32 ret;

    ret =  LOS_HwiCreate(PRVTIMER_INT_NUM, 0xa0, 0, OsTickEntry, NULL);
    if (ret != LOS_OK) {
        PRINT_ERR("%s, %d create tick irq failed, ret:0x%x\n", __FUNCTION__, __LINE__, ret);
    }
}

OsTickHandler es un manejador de interrupciones de tick, que completa la verificación del intervalo de tiempo y el escaneo de tareas.

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

#if (LOSCFG_BASE_CORE_SWTMR == YES)
    OsSwtmrScan();
#endif
}
LITE_OS_SEC_TEXT VOID OsTimesliceCheck(VOID)
{
    LosTaskCB *runTask = NULL;
    LosProcessCB *runProcess = OsCurrProcessGet();
    if (runProcess->policy != LOS_SCHED_RR) {
        goto SCHED_TASK;
    }

    if (runProcess->timeSlice != 0) {
        runProcess->timeSlice--;//进程时间片减少一次
        if (runProcess->timeSlice == 0) {
            LOS_Schedule();//进程时间片用完,发起调度
        }
    }

SCHED_TASK:
    runTask = OsCurrTaskGet();
    if (runTask->policy != LOS_SCHED_RR) {
        return;
    }

    if (runTask->timeSlice != 0) {
        runTask->timeSlice--;//对应任务时间片也减少一次
        if (runTask->timeSlice == 0) {
            LOS_Schedule();
        }
    }
}

OsTaskScan ()

OsTaskScan () comprueba constantemente el estado de la tarea y la ejecuta cuando hay una tarea ¡No es exagerado decir que es la fuente de la ejecución ordenada del proceso!

LITE_OS_SEC_TEXT VOID OsTaskScan(VOID)
{
    SortLinkList *sortList = NULL;
    LosTaskCB *taskCB = NULL;
    BOOL needSchedule = FALSE;
    UINT16 tempStatus;
    LOS_DL_LIST *listObject = NULL;
    SortLinkAttribute *taskSortLink = NULL;

    taskSortLink = &OsPercpuGet()->taskSortLink;
    taskSortLink->cursor = (taskSortLink->cursor + 1) & OS_TSK_SORTLINK_MASK;
    listObject = taskSortLink->sortLink + taskSortLink->cursor;

    /*
     * When task is pended with timeout, the task block is on the timeout sortlink
     * (per cpu) and ipc(mutex,sem and etc.)'s block at the same time, it can be waken
     * up by either timeout or corresponding ipc it's waiting.
     *
     * Now synchronize sortlink preocedure is used, therefore the whole task scan needs
     * to be protected, preventing another core from doing sortlink deletion at same time.
     */
    LOS_SpinLock(&g_taskSpin);

    if (LOS_ListEmpty(listObject)) {
        LOS_SpinUnlock(&g_taskSpin);
        return;
    }
    sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
    ROLLNUM_DEC(sortList->idxRollNum);

    while (ROLLNUM(sortList->idxRollNum) == 0) {
        LOS_ListDelete(&sortList->sortLinkNode);
        taskCB = LOS_DL_LIST_ENTRY(sortList, LosTaskCB, sortList);
        taskCB->taskStatus &= ~OS_TASK_STATUS_PEND_TIME;
        tempStatus = taskCB->taskStatus;
        if (tempStatus & OS_TASK_STATUS_PEND) {
            taskCB->taskStatus &= ~OS_TASK_STATUS_PEND;
#if (LOSCFG_KERNEL_LITEIPC == YES)
            taskCB->ipcStatus &= ~IPC_THREAD_STATUS_PEND;
#endif
            taskCB->taskStatus |= OS_TASK_STATUS_TIMEOUT;
            LOS_ListDelete(&taskCB->pendList);
            taskCB->taskSem = NULL;
            taskCB->taskMux = NULL;
        } else {
            taskCB->taskStatus &= ~OS_TASK_STATUS_DELAY;
        }

        if (!(tempStatus & OS_TASK_STATUS_SUSPEND)) {
            OS_TASK_SCHED_QUEUE_ENQUEUE(taskCB, OS_PROCESS_STATUS_PEND);
            needSchedule = TRUE;
        }

        if (LOS_ListEmpty(listObject)) {
            break;
        }

        sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
    }

    LOS_SpinUnlock(&g_taskSpin);

    if (needSchedule != FALSE) {
        LOS_MpSchedule(OS_MP_CPU_ALL);
        LOS_Schedule();
    }
}

Además de la programación de los disparadores de tick, ¿qué otras situaciones desencadenan la programación?

Si lo piensa, deje un mensaje en el área de comentarios. Más artículos de esta serie ingresarán  al análisis del código fuente del sistema Hongmeng (catálogo general)  .

Supongo que te gusta

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