Hongmeng 커널 소스 코드 분석 (틱 클럭 관리)

참고 : 핵심 오픈 소스 모호성 분석을 기반으로 공식 소스 [ kernel_liteos_a ] 공식 문서 [ 문서 ] 참조 문서 [ Huawei LiteOS ]
작성자 : Hong Meng 핵심 애호가는 모호성 커널, 업데이트 블로그를 계속 연구하므로 계속 지켜봐 주시기 바랍니다. 내용은 개인적인 견해를 나타내며 오류는 환영하며 모든 사람이 수정하고 개선 할 수 있습니다. 이 시리즈의 모든 기사 는 Hongmeng 시스템 (일반 카탈로그)소스 코드 분석 을보기 위해 입력됩니다. 


목차

얼마나 자주 틱합니까?

틱 콜백 기능은 어디에서 설정합니까?

OsTaskScan ()

틱 트리거 스케줄링 외에 어떤 다른 상황이 스케줄링을 트리거합니까?


클럭 관리 모듈은 매우 간단하지만 커널에서 가장 중요한 코드 세그먼트 인 OsTickHandler ()가 있는데, 이것은 JAVA 타이밍 태스크로 이해할 수 있지만 시스템 커널의 타이머입니다. Hongmeng은 현재 경량 커널 라이트 OS (LOS)를 열고 있으므로 틱 빈도가 너무 높지 않습니다.

자세한 내용은 코드를 참조하십시오 : los_tick.c      los_timeslice.c

얼마나 자주 틱합니까?

/**
 * @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 틱, 즉, 클럭 인터럽트 핸들러는 초당 100 번 호출되고 시간 슬라이스 단위는 10ms입니다.

프로세스가 작업을 실행하는 데 얼마나 많은 시간이 할당됩니까? 답은 2 개의 타임 슬라이스, 즉 20ms입니다.

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

다 사용하면 재배포가 가능하지만 한 번에 2 개의 타임 슬라이스 만 주어집니다. 상세 코드 : 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);
}

틱 콜백 기능은 어디에서 설정합니까?

메인에서 틱 초기화 및 인터럽트 서비스 프로그램 등록을 확인할 수 있습니다.

// 中断处理函数
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는 타임 슬라이스 검사 및 작업 스캔을 완료하는 틱 인터럽트 핸들러입니다.

/*
 * 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 ()은 작업의 상태를 지속적으로 확인하고 작업이있을 때 실행하며, 그것이 프로세스의 질서있는 실행의 원천이라고해도 과언이 아닙니다!

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

틱 트리거 스케줄링 외에 어떤 다른 상황이 스케줄링을 트리거합니까?

생각해 보시면 댓글란에 메시지를 남겨주세요.이 시리즈의 더 많은 기사는 Hongmeng 시스템 소스 코드 분석 (일반 카탈로그)에 들어갈 것   입니다.

추천

출처blog.csdn.net/kuangyufei/article/details/108603468