2

鸿蒙轻内核A核源码分析进程管理之三

 2 years ago
source link: https://os.51cto.com/article/705431.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

9375a8a77ecd5b00b32353fecfc524ff548c5b.png

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://ost.51cto.com​

本文会继续分析进程和任务管理模块。本文中所涉及的源码,以OpenHarmony LiteOS-A内核为例,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_a 获取。如果涉及开发板,则默认以hispark_taurus为例。

本文记录下进程相关的初始化函数,如OsSystemProcessCreate、OsProcessInit、OsProcessCreateInit、OsUserInitProcess、OsDeInitPCB、OsUserInitProcessStart等。

1、LiteOS-A内核进程创建初始化通用函数

先看看一些内部函数,不管是初始化用户态进程还是内核态进程,都会使用这些函数,包含进程控制块初始化函数OsInitPCB、进程控制块初始化恢复函数OsDeInitPCB

(1) 进程控制块初始化函数OsInitPCB

进程控制块初始化函数OsInitPCB需要3个参数,第一个参数processCB是进程块指针,第二个参数为进程模式mode,分为内核态进程OS_KERNEL_MODE和用户态进程OS_USER_MODE。第三个参数用于设置进程名称。返回值为初始化成功LOS_OK还是失败LOS_ENOMEM。看下代码,⑴处设置进程控制块的信息,用户态进程还是内核态进程,进程状态设置为初始化状态,线程组编号设置为无效值,设置为默认掩码,定时器编号设置为无效值。⑵处初始化进程的双向链表。如果系统配置支持虚拟内存,则执行⑶判断初始化的进程是否为用户态进程,如果是用户态进程,则创建虚拟地址空间,如果创建失败,则把进程状态设置为未使用状态,然后返回错误码。⑷处表示如果是内核态进程,则指定进程的内核进程虚拟地址空间。有关虚拟地址空间的信息,请参考之前的系列文章。

如果执行CPUP特性,则执行⑸处代码,则为CPUP结构体申请内存空间。⑹处,如果开启了LOSCFG_SECURITY_VID,则V初始化ID映射链表。⑺处,如果开启了安全能力LOSCFG_SECURITY_CAPABILITY,则进行相应的初始化。⑻处为进程设置一个名称。

    STATIC UINT32 OsInitPCB(LosProcessCB *processCB, UINT32 mode, const CHAR *name)
    {
⑴      processCB->processMode = mode;
        processCB->processStatus = OS_PROCESS_STATUS_INIT;
        processCB->parentProcessID = OS_INVALID_VALUE;
        processCB->threadGroupID = OS_INVALID_VALUE;
        processCB->umask = OS_PROCESS_DEFAULT_UMASK;
        processCB->timerID = (timer_t)(UINTPTR)MAX_INVALID_TIMER_VID;
⑵      LOS_ListInit(&processCB->threadSiblingList);
        LOS_ListInit(&processCB->childrenList);
        LOS_ListInit(&processCB->exitChildList);
        LOS_ListInit(&(processCB->waitList));
    #ifdef LOSCFG_KERNEL_VM
⑶      if (OsProcessIsUserMode(processCB)) {
            processCB->vmSpace = OsCreateUserVmSpace();
            if (processCB->vmSpace == NULL) {
                processCB->processStatus = OS_PROCESS_FLAG_UNUSED;
                return LOS_ENOMEM;
            }
        } else {
⑷          processCB->vmSpace = LOS_GetKVmSpace();
        }
    #endif
    #ifdef LOSCFG_KERNEL_CPUP
⑸      processCB->processCpup = (OsCpupBase *)LOS_MemAlloc(m_aucSysMem1, sizeof(OsCpupBase));
        if (processCB->processCpup == NULL) {
            return LOS_ENOMEM;
        }
        (VOID)memset_s(processCB->processCpup, sizeof(OsCpupBase), 0, sizeof(OsCpupBase));
    #endif
    #ifdef LOSCFG_SECURITY_VID
⑹      status_t status = VidMapListInit(processCB);
        if (status != LOS_OK) {
            return LOS_ENOMEM;
        }
    #endif
⑺  #ifdef LOSCFG_SECURITY_CAPABILITY
        OsInitCapability(processCB);
    #endif
⑻      if (OsSetProcessName(processCB, name) != LOS_OK) {
            return LOS_ENOMEM;
        }
        return LOS_OK;
    }

(2) 进程控制块初始化恢复函数OsDeInitPCB

在创建进程时,会执行该函数,恢复进程控制块信息到初始化之前的状态。⑴处释放进程的资源,包含地址空间、文件、安全能力、定时器等占用的内存。如果存在父进程,则执行⑵从父进程的兄弟列表上删除。如果进程属于进程组,则从进程组中退出。然后执行⑷设置进程状态为退出态,把进程放入待回收链表中。

 STATIC VOID OsDeInitPCB(LosProcessCB *processCB)
    {
        UINT32 intSave;
        ProcessGroup *group = NULL;

        if (processCB == NULL) {
            return;
        }
⑴      OsProcessResourcesToFree(processCB);
        SCHEDULER_LOCK(intSave);
        if (processCB->parentProcessID != OS_INVALID_VALUE) {
⑵          LOS_ListDelete(&processCB->siblingList);
            processCB->parentProcessID = OS_INVALID_VALUE;
        }
⑶      if (processCB->group != NULL) {
            OsExitProcessGroup(processCB, &group);
        }
⑷      processCB->processStatus &= ~OS_PROCESS_STATUS_INIT;
        processCB->processStatus |= OS_PROCESS_FLAG_EXIT;
        LOS_ListHeadInsert(&g_processRecycleList, &processCB->pendList);
        SCHEDULER_UNLOCK(intSave);
        (VOID)LOS_MemFree(m_aucSysMem1, group);
        OsWriteResourceEvent(OS_RESOURCE_EVENT_FREE);
        return;
    }

2、 LiteOS-A内核系统进程创建函数OsSystemProcessCreate

系统进程创建函数OsSystemProcessCreate在文件kernel\common\los_config.c中被调用,在系统启动阶段创建系统进程。该函数又调用OsProcessInit,我们首先看下函数OsProcessInit。

(1) 进程初始化函数OsProcessInit

进程初始化函数OsProcessInit为进程控制块申请内存,初始化进程相关的进程链表。我们看下代码,⑴处获取配置的进程最大数值,然后计算需要的内存大小。⑵处申请内存,初始化申请的内存块。⑶处初始化空闲进程双向链表和待回收进程双向链表。

⑷处初始化每一个进程,社区进程编号、进程状态,然后把每一个进程放到空闲进程链表里。⑸处设置Idle进程编号为0,用户根进程编号为1,系统根进程编号为2,然后执行LOS_ListDelete把这3个进程从阻塞链表上删除。

   STATIC UINT32 OsProcessInit(VOID)
    {
        UINT32 index;
        UINT32 size;
⑴      g_processMaxNum = LOSCFG_BASE_CORE_PROCESS_LIMIT;
        size = g_processMaxNum * sizeof(LosProcessCB);
⑵      g_processCBArray = (LosProcessCB *)LOS_MemAlloc(m_aucSysMem1, size);
        if (g_processCBArray == NULL) {
            return LOS_NOK;
        }
        (VOID)memset_s(g_processCBArray, size, 0, size);
⑶      LOS_ListInit(&g_freeProcess);
        LOS_ListInit(&g_processRecycleList);

⑷      for (index = 0; index < g_processMaxNum; index++) {
            g_processCBArray[index].processID = index;
            g_processCBArray[index].processStatus = OS_PROCESS_FLAG_UNUSED;
            LOS_ListTailInsert(&g_freeProcess, &g_processCBArray[index].pendList);
        }
⑸      g_kernelIdleProcess = 0; /* 0: The idle process ID of the kernel-mode process is fixed at 0 */
        LOS_ListDelete(&OS_PCB_FROM_PID(g_kernelIdleProcess)->pendList);
        g_userInitProcess = 1; /* 1: The root process ID of the user-mode process is fixed at 1 */
        LOS_ListDelete(&OS_PCB_FROM_PID(g_userInitProcess)->pendList);
        g_kernelInitProcess = 2; /* 2: The root process ID of the kernel-mode process is fixed at 2 */
        LOS_ListDelete(&OS_PCB_FROM_PID(g_kernelInitProcess)->pendList);
        return LOS_OK;
    }

(2)进程创建初始化函数OsProcessCreateInit

该函数用于创建进程时的一些初始化操作,主要是文件系统、安全能力、进程组等等。需要3个参数,分别是进程控制块指针,标记用户态还是内核态进程的flags,进程名称name。首先执行⑴调用OsInitPCB()对进程控制块进行初始化,然后,如果配置支持了虚拟文件系统VFS,则执行⑵为进程分别文件。⑶处根据进程号创建进程组。如果支持安全能力,则执行⑷创建用户。如果初始化失败,会执行⑸恢复进程控制块到初始化之前的状态。

  STATIC UINT32 OsProcessCreateInit(LosProcessCB *processCB, UINT32 flags, const CHAR *name)
    {
        ProcessGroup *group = NULL;
⑴      UINT32 ret = OsInitPCB(processCB, flags, name);
        if (ret != LOS_OK) {
            goto EXIT;
        }
    #ifdef LOSCFG_FS_VFS
⑵      processCB->files = alloc_files();
        if (processCB->files == NULL) {
            ret = LOS_ENOMEM;
            goto EXIT;
        }
    #endif
⑶      group = OsCreateProcessGroup(processCB->processID);
        if (group == NULL) {
            ret = LOS_ENOMEM;
            goto EXIT;
        }
    #ifdef LOSCFG_SECURITY_CAPABILITY
⑷      processCB->user = OsCreateUser(0, 0, 1);
        if (processCB->user == NULL) {
            ret = LOS_ENOMEM;
            goto EXIT;
        }
    #endif
        return LOS_OK;
    EXIT:
⑸      OsDeInitPCB(processCB);
        return ret;
    }

(3) 系统进程创建函数OsSystemProcessCreate

接下来,我们看看系统进程创建函数OsSystemProcessCreate的源代码。⑴处开始先后调用OsProcessInit、OsProcessCreateInit初始化内核态根进程,⑵处进程的状态不再是初始化状态。⑶处从内核进程获取全局进程组指针g_processGroup,然后初始化进程组的双向链表。这样看来,所有的进程组都会挂载到内核进程的进程组节点上。⑷处初始化内核空闲进程,内核空闲进程的父进程也是内核态根进程,插入到进程组链表,设置空闲进程的状态不再是初始化状态。执行⑸创建空闲任务,然后设置空闲进程的线程组编号为空闲线程编号。

  LITE_OS_SEC_TEXT_INIT UINT32 OsSystemProcessCreate(VOID)
    {
⑴      UINT32 ret = OsProcessInit();
        if (ret != LOS_OK) {
            return ret;
        }
        LosProcessCB *kerInitProcess = OS_PCB_FROM_PID(g_kernelInitProcess);
        ret = OsProcessCreateInit(kerInitProcess, OS_KERNEL_MODE, "KProcess");
        if (ret != LOS_OK) {
            return ret;
        }
⑵      kerInitProcess->processStatus &= ~OS_PROCESS_STATUS_INIT;
⑶      g_processGroup = kerInitProcess->group;
        LOS_ListInit(&g_processGroup->groupList);

⑷      LosProcessCB *idleProcess = OS_PCB_FROM_PID(g_kernelIdleProcess);
        ret = OsInitPCB(idleProcess, OS_KERNEL_MODE, "KIdle");
        if (ret != LOS_OK) {
            return ret;
        }
        idleProcess->parentProcessID = kerInitProcess->processID;
        LOS_ListTailInsert(&kerInitProcess->childrenList, &idleProcess->siblingList);
        idleProcess->group = kerInitProcess->group;
        LOS_ListTailInsert(&kerInitProcess->group->processList, &idleProcess->subordinateGroupList);
    #ifdef LOSCFG_SECURITY_CAPABILITY
        idleProcess->user = kerInitProcess->user;
    #endif
    #ifdef LOSCFG_FS_VFS
        idleProcess->files = kerInitProcess->files;
    #endif
        idleProcess->processStatus &= ~OS_PROCESS_STATUS_INIT;
⑸      ret = OsIdleTaskCreate();
        if (ret != LOS_OK) {
            return ret;
        }
        idleProcess->threadGroupID = OsGetIdleTaskId();
        return LOS_OK;
    }

3. LiteOS-A内核用户进程创建函数OsUserInitProcess

系统启动阶段,OsUserInitProcess启动init进程。该函数在device开发板目录下系统初始化文件中调用,如system_init.c等等。该函数需要开启LOSCFG_KERNEL_DYNLOAD宏,否则函数体内容为空。在阅读函数OsUserInitProcess的源码前,先看看该函数调用的其他函数,如OsLoadUserInit、OsUserInitStackAlloc、OsUserInitProcessStart等等。

(1) 加载用户初始化数据函数OsLoadUserInit

该函数用于加载用户数据。⑴处从链接脚本获取text、bss的开启和结束地址,然后计算bss段、初始化段的大小。⑵处进行一些必要的校验,是否针对内存页对齐,内存段长度是否合理等。然后执行⑶,申请物理内存页,然后把用户初始化数据复制到申请的内存页。⑷处进行虚实映射。如果bss段长度不为0,执行⑸把bss段的内存区域清零。

STATIC UINT32 OsLoadUserInit(LosProcessCB *processCB)
{
    /*              userInitTextStart               -----
     * | user text |
     *
     * | user data |                                initSize
     *              userInitBssStart  ---
     * | user bss  |                  initBssSize
     *              userInitEnd       ---           -----
     */
    errno_t errRet;
    INT32 ret;
⑴  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;
    VOID *userBss = NULL;
    VOID *userText = NULL;
⑵  if ((LOS_Align((UINTPTR)userInitTextStart, PAGE_SIZE) != (UINTPTR)userInitTextStart) ||
        (LOS_Align((UINTPTR)userInitEnd, PAGE_SIZE) != (UINTPTR)userInitEnd)) {
        return LOS_EINVAL;
    }
    if ((initSize == 0) || (initSize <= initBssSize)) {
        return LOS_EINVAL;
    }
⑶  userText = LOS_PhysPagesAllocContiguous(initSize >> PAGE_SHIFT);
    if (userText == NULL) {
        return LOS_NOK;
    }
    errRet = memcpy_s(userText, initSize, (VOID *)&__user_init_load_addr, initSize - initBssSize);
    if (errRet != EOK) {
        PRINT_ERR("Load user init text, data and bss failed! err : %d\n", errRet);
        goto ERROR;
    }
⑷  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_FIXED | VM_MAP_REGION_FLAG_PERM_EXECUTE |
                               VM_MAP_REGION_FLAG_PERM_USER);
    if (ret < 0) {
        PRINT_ERR("Mmap user init text, data and bss failed! err : %d\n", ret);
        goto ERROR;
    }
    /* The User init boot segment may not actually exist */
⑸  if (initBssSize != 0) {
        userBss = (VOID *)((UINTPTR)userText + userInitBssStart - userInitTextStart);
        errRet = memset_s(userBss, initBssSize, 0, initBssSize);
        if (errRet != EOK) {
            PRINT_ERR("memset user init bss failed! err : %d\n", errRet);
            goto ERROR;
        }
    }
    return LOS_OK;
ERROR:
    (VOID)LOS_PhysPagesFreeContiguous(userText, initSize >> PAGE_SHIFT);
    return LOS_NOK;
}

(2) 用户初始化栈函数OsUserInitStackAlloc

该函数需要2个参数,第一个参数为进程控制块,第二个参数为输出参数,用于获取用户任务栈的大小。⑴处用户任务栈大小对页进行对齐,然后申请内存区域,然后执行⑵设置内存区域类型,并社区内存区域标签为栈。然后设置输出参数为任务栈大小,并返回用户栈空间的开始地址。

STATIC VOID *OsUserInitStackAlloc(LosProcessCB *processCB, UINT32 *size)
{
    LosVmMapRegion *region = NULL;
⑴  UINT32 stackSize = ALIGN(OS_USER_TASK_STACK_SIZE, PAGE_SIZE);
    region = LOS_RegionAlloc(processCB->vmSpace, 0, stackSize,
                             VM_MAP_REGION_FLAG_PERM_USER | VM_MAP_REGION_FLAG_PERM_READ |
                             VM_MAP_REGION_FLAG_PERM_WRITE, 0);
    if (region == NULL) {
        return NULL;
    }
⑵  LOS_SetRegionTypeAnon(region);
    region->regionFlags |= VM_MAP_REGION_FLAG_STACK;
    *size = stackSize;
    return (VOID *)(UINTPTR)region->range.base;
}

(3)用户进程初始化开启函数OsUserInitProcessStart

用户进程初始化开启函数OsUserInitProcessStart用于创建线程,设置调度策略等。⑴处为用户态进程创建个线程,然后为进程设置优先级。⑵处设置进程状态为非初始化状态,然后执行⑶为任务设置调度策略和优先级。

STATIC UINT32 OsUserInitProcessStart(LosProcessCB *processCB, TSK_INIT_PARAM_S *param)
{
    UINT32 intSave;
    INT32 ret;
⑴  UINT32 taskID = OsCreateUserTask(processCB->processID, param);
    if (taskID == OS_INVALID_VALUE) {
        return LOS_NOK;
    }
    ret = LOS_SetProcessPriority(processCB->processID, OS_PROCESS_USERINIT_PRIORITY);
    if (ret != LOS_OK) {
        PRINT_ERR("User init process set priority failed! ERROR:%d \n", ret);
        goto EXIT;
    }
    SCHEDULER_LOCK(intSave);
⑵  processCB->processStatus &= ~OS_PROCESS_STATUS_INIT;
    SCHEDULER_UNLOCK(intSave);
⑶  ret = LOS_SetTaskScheduler(taskID, LOS_SCHED_RR, OS_TASK_PRIORITY_LOWEST);
    if (ret != LOS_OK) {
        PRINT_ERR("User init process set scheduler failed! ERROR:%d \n", ret);
        goto EXIT;
    }
    return LOS_OK;
EXIT:
    (VOID)LOS_TaskDelete(taskID);
    return ret;
}

(4) 用户态进程初始化函数OsUserInitProcess

用户态进程初始化函数OsUserInitProcess完成用户态进程的初始化。⑴处获取用户态根进程,然后调用函数创建用户态根进程,并调用函数OsLoadUserInit加载用户初始化数据。⑵处初始化用户栈,然后设置线程的初始化参数,⑶处完成用户态进程的创建。

LITE_OS_SEC_TEXT_INIT UINT32 OsUserInitProcess(VOID)
{
    UINT32 ret;
    UINT32 size;
    TSK_INIT_PARAM_S param = { 0 };
    VOID *stack = NULL;
⑴  LosProcessCB *processCB = OS_PCB_FROM_PID(g_userInitProcess);
    ret = OsProcessCreateInit(processCB, OS_USER_MODE, "Init");
    if (ret != LOS_OK) {
        return ret;
    }
    ret = OsLoadUserInit(processCB);
    if (ret != LOS_OK) {
        goto ERROR;
    }
⑵  stack = OsUserInitStackAlloc(processCB, &size);
    if (stack == NULL) {
        PRINT_ERR("Alloc user init process user stack failed!\n");
        goto ERROR;
    }
    param.pfnTaskEntry = (TSK_ENTRY_FUNC)(CHAR *)&__user_init_entry;
    param.userParam.userSP = (UINTPTR)stack + size;
    param.userParam.userMapBase = (UINTPTR)stack;
    param.userParam.userMapSize = size;
    param.uwResved = OS_TASK_FLAG_PTHREAD_JOIN;
⑶  ret = OsUserInitProcessStart(processCB, &param);
    if (ret != LOS_OK) {
        (VOID)OsUnMMap(processCB->vmSpace, param.userParam.userMapBase, param.userParam.userMapSize);
        goto ERROR;
    }
    return LOS_OK;
ERROR:
    OsDeInitPCB(processCB);
    return ret;
}

本文介绍了进程管理的内核进程、用户态进程的初始化相关函数。

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://ost.51cto.com​

71eea7105a1cf9982d2996c42d853b97bd50ef.jpg


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK