鸿蒙内核源码分析(任务管理篇) | 任务池是如何管理的?

news2024/9/24 7:18:45

任务即线程

在鸿蒙内核中,广义上可理解为一个任务就是一个线程

官方是怎么描述线程的

基本概念
从系统的角度看,线程是竞争系统资源的最小运行单元。线程可以使用或等待CPU、使用内存空间等系统资源,并独立于其它线程运行。

鸿蒙内核每个进程内的线程独立运行、独立调度,当前进程内线程的调度不受其它进程内线程的影响。

鸿蒙内核中的线程采用抢占式调度机制,同时支持时间片轮转调度和FIFO调度方式。

鸿蒙内核的线程一共有32个优先级(0-31),最高优先级为0,最低优先级为31。

当前进程内高优先级的线程可抢占当前进程内低优先级线程,当前进程内低优先级线程必须在当前进程内高优先级线程阻塞或结束后才能得到调度。

线程状态说明:

初始化(Init):该线程正在被创建。

就绪(Ready):该线程在就绪列表中,等待CPU调度。

运行(Running):该线程正在运行。

阻塞(Blocked):该线程被阻塞挂起。Blocked状态包括:pend(因为锁、事件、信号量等阻塞)、suspend(主动pend)、delay(延时阻塞)、pendtime(因为锁、事件、信号量时间等超时等待)。

退出(Exit):该线程运行结束,等待父线程回收其控制块资源。

图 1 线程状态迁移示意图

注意官方文档说的是线程,没有提到task(任务),但内核源码中却有大量 task代码,很少有线程(thread)代码 ,这是怎么回事?
其实在鸿蒙内核中, task就是线程, 初学者完全可以这么理解,但二者还是有区别,否则干嘛要分两个词描述。
会有什么区别?是管理上的区别,task是调度层面的概念,线程是进程层面的概念。 就像同一个人在不同的管理体系中会有不同的身份一样,一个男人既可以是 孩子,爸爸,丈夫,或者程序员,视角不同功能也会不同。

如何证明是一个东西,继续再往下看。

执行task命令

看shell task 命令的执行结果:

task命令 查出每个任务在生命周期内的运行情况,它运行的内存空间,优先级,时间片,入口执行函数,进程ID,状态等等信息,非常的复杂。这么复杂的信息就需要一个结构体来承载。而这个结构体就是 LosTaskCB(任务控制块)

对应张大爷的故事:task就是一个用户的节目清单里的一个节目,用户总清单就是一个进程,所以上面会有很多的节目。

task长得什么样子

说LosTaskCB之前先说下官方文档任务状态对应的 define,可以看出task和线程是一个东西。

#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

LosTaskCB长什么样?抱歉,它确实有点长,但还是要全部贴出全貌。

typedef struct {
    VOID            *stackPointer;      /**< Task stack pointer */ //非用户模式下的栈指针
    UINT16          taskStatus;         /**< Task status */   //各种状态标签,可以拥有多种标签,按位标识
    UINT16          priority;           /**< Task priority */  //任务优先级[0:31],默认是31级
    UINT16          policy;    //任务的调度方式(三种 .. LOS_SCHED_RR )
    UINT16          timeSlice;          /**< Remaining time slice *///剩余时间片
    UINT32          stackSize;          /**< Task stack size */  //非用户模式下栈大小
    UINTPTR         topOfStack;         /**< Task stack top */  //非用户模式下的栈顶 bottom = top + size
    UINT32          taskID;             /**< Task ID */    //任务ID,任务池本质是一个大数组,ID就是数组的索引,默认 < 128
    TSK_ENTRY_FUNC  taskEntry;          /**< Task entrance function */ //任务执行入口函数
    VOID            *joinRetval;        /**< pthread adaption */ //用来存储join线程的返回值
    VOID            *taskSem;           /**< Task-held semaphore */ //task在等哪个信号量
    VOID            *taskMux;           /**< Task-held mutex */  //task在等哪把锁
    VOID            *taskEvent;         /**< Task-held event */  //task在等哪个事件
    UINTPTR         args[4];            /**< Parameter, of which the maximum number is 4 */ //入口函数的参数 例如 main (int argc,char *argv[])
    CHAR            taskName[OS_TCB_NAME_LEN]; /**< Task name */ //任务的名称
    LOS_DL_LIST     pendList;           /**< Task pend node */  //如果任务阻塞时就通过它挂到各种阻塞情况的链表上,比如OsTaskWait时
    LOS_DL_LIST     threadList;         /**< thread list */   //挂到所属进程的线程链表上
    SortLinkList    sortList;           /**< Task sortlink node */ //挂到cpu core 的任务执行链表上
    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 */   //过的优先级,例如 ..01001011 曾经有过 0,1,3,6 优先级
    INT32           errorNo;            /**< Error Num */
    UINT32          signal;             /**< Task signal */ //任务信号类型,(SIGNAL_NONE,SIGNAL_KILL,SIGNAL_SUSPEND,SIGNAL_AFFI)
    sig_cb          sig;    //信号控制块,这里用于进程间通讯的信号,类似于 linux singal模块
#if (LOSCFG_KERNEL_SMP == YES)
    UINT16          currCpu;            /**< CPU core number of this task is running on */ //正在运行此任务的CPU内核号
    UINT16          lastCpu;            /**< CPU core number of this task is running on last time */ //上次运行此任务的CPU内核号
    UINT16          cpuAffiMask;        /**< CPU affinity mask, support up to 16 cores */ //CPU亲和力掩码,最多支持16核,亲和力很重要,多核情况下尽量一个任务在一个CPU核上运行,提高效率
    UINT32          timerCpu;           /**< CPU core number of this task is delayed or pended */ //此任务的CPU内核号被延迟或挂起
#if (LOSCFG_KERNEL_SMP_TASK_SYNC == YES)
    UINT32          syncSignal;         /**< Synchronization for signal handling */ //用于CPU之间 同步信号
#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 *///所属进程ID
    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 */ //等待孩子的PID或GID进程
    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;   //IPC状态
    LOS_DL_LIST     msgListHead;  //消息队列头结点,上面挂的都是任务要读的消息
    BOOL            accessMap[LOSCFG_BASE_CORE_TSK_LIMIT];//访问图,指的是task之间是否能访问的标识,LOSCFG_BASE_CORE_TSK_LIMIT 为任务池总数
#endif
} LosTaskCB;

结构体LosTaskCB内容很多,各代表什么含义?
LosTaskCB相当于任务在内核中的身份证,它反映出每个任务在生命周期内的运行情况。既然是周期就会有状态,要运行就需要内存空间,就需要被内核算法调度,被选中CPU就去执行代码段指令,CPU要执行就需要告诉它从哪里开始执行,因为是多线程,但只有一个CPU就需要不断的切换任务,那执行会被中断,也需要再恢复后继续执行,又如何保证恢复的任务执行不会出错,这些问题都需要说明白。

Task怎么管理

什么是任务池?

前面已经说了任务是内核调度层面的概念,调度算法保证了task有序的执行,调度机制详见其他姊妹篇的介绍。
如此多的任务怎么管理和执行?管理靠任务池和就绪队列,执行靠调度算法。
代码如下(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);//都插入空闲任务列表 
    }//注意:这里挂的是pendList节点,所以取TCB要通过 OS_TCB_FROM_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 就是个任务池,默认创建128个任务,常驻内存,不被释放。
g_losFreeTask是空闲任务链表,想创建任务时来这里申请一个空闲任务,用完了就回收掉,继续给后面的申请使用。
g_taskRecyleList是回收任务链表,专用来回收exit 任务,任务所占资源被确认归还后被彻底删除,就像员工离职一样,得有个离职队列和流程,要归还电脑,邮箱,有没有借钱要还的 等操作。

对应张大爷的故事:用户要来场馆领取表格填节目单,场馆只准备了128张表格,领完就没有了,但是节目表演完了会回收表格,这样多了一张表格就可以给其他人领取了,这128张表格对应鸿蒙内核这就是任务池,简单吧。

就绪队列是怎么回事

CPU执行速度是很快的,鸿蒙内核默认一个时间片是 10ms, 资源有限,需要在众多任务中来回的切换,所以绝不能让CPU等待任务,CPU就像公司最大的领导,下面很多的部门等领导来审批,吃饭。只有大家等领导,哪有领导等你们的道理,所以工作要提前准备好,每个部门的优先级又不一样,所以每个部门都要有个任务队列,里面放的是领导能直接处理的任务,没准备好的不要放进来,因为这是给CPU提前准备好的粮食!
这就是就绪队列的原理,一共有32个就绪队列,进程和线程都有,因为线程的优先级是默认32个, 每个队列中放同等优先级的task.
还是看源码吧

#define OS_PRIORITY_QUEUE_NUM 32
LITE_OS_SEC_BSS LOS_DL_LIST *g_priQueueList = NULL;//队列链表
LITE_OS_SEC_BSS UINT32 g_priQueueBitmap;//队列位图 UINT32每位代表一个优先级,共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)));//分配32个队列头节点
    if (g_priQueueList == NULL) {
        return LOS_NOK;
    }

    for (priority = 0; priority < OS_PRIORITY_QUEUE_NUM; ++priority) {
        LOS_ListInit(&g_priQueueList[priority]);//队列初始化,前后指针指向自己
    }
    return LOS_OK;
}

注意看g_priQueueList 的内存分配,就是32个LOS_DL_LIST,还记得LOS_DL_LIST的妙用吗,不清楚的去 鸿蒙系统源码分析(总目录)里面翻。

对应张大爷的故事:就是门口那些排队的都是至少有一个节目单是符合表演标准的,资源都到位了,没有的连排队的资格都木有,就慢慢等吧。

任务栈是怎么回事

每个任务都是独立开的,任务之间也相互独立,之间通讯通过IPC,这里的“独立”指的是每个任务都有自己的运行环境 —— 栈空间,称为任务栈,栈空间里保存的信息包含局部变量、寄存器、函数参数、函数返回地址等等
但系统中只有一个CPU,任务又是独立的,调度的本质就是CPU执行一个新task,老task在什么地方被中断谁也不清楚,是随机的。那如何保证老任务被再次调度选中时还能从上次被中断的地方继续玩下去呢?

答案是:任务上下文,CPU内有一堆的寄存器,CPU运行本质的就是这些寄存器的值不断的变化,只要切换时把这些值保存起来,再还原回去就能保证task的连续执行,让用户毫无感知。鸿蒙内核给一个任务执行的时间是 20ms ,也就是说有多任务竞争的情况下,一秒钟内最多要来回切换50次。

对应张大爷的故事:就是碰到节目没有表演完就必须打断的情况下,需要把当时的情况记录下来,比如小朋友在演躲猫猫的游戏,一半不演了,张三正在树上,李四正在厕所躲,都记录下来,下次再回来你们上次在哪就会哪呆着去,就位了继续表演。这样就接上了,观众就木有感觉了。
任务上下文(TaskContext)是怎样的呢?还是直接看源码

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

发现基本都是CPU寄存器的恢复现场值, 具体各寄存器有什么作用大家可以去网上详查,后续也有专门的文章来介绍。这里说其中的三个寄存器 SP, LR, PC

LR
用途有二,一是保存子程序返回地址,当调用BL、BX、BLX等跳转指令时会自动保存返回地址到LR;二是保存异常发生的异常返回地址。

PC(Program Counter)
为程序计数器,用于保存程序的执行地址,在ARM的三级流水线架构中,程序流水线包括取址、译码和执行三个阶段,PC指向的是当前取址的程序地址,所以32位ARM中,译码地址(正在解析还未执行的程序)为PC-4,执行地址(当前正在执行的程序地址)为PC-8, 当突然发生中断的时候,保存的是PC的地址。

SP
每一种异常模式都有其自己独立的r13,它通常指向异常模式所专用的堆栈,当ARM进入异常模式的时候,程序就可以把一般通用寄存器压入堆栈,返回时再出栈,保证了各种模式下程序的状态的完整性。

任务栈初始化

任务栈的初始化就是任务上下文的初始化,因为任务没开始执行,里面除了上下文不会有其他内容,注意上下文存放的位置在栈的底部。初始状态下 sp就是指向的栈底, 栈顶内容永远是 0xCCCCCCCC “烫烫烫烫”,这几个字应该很熟悉吗? 如果不是那几个字了,那说明栈溢出了, 后续篇会详细说明这块,大家也可以自行去看代码,很有意思.

Task函数集

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

使用场景和功能

任务创建后,内核可以执行锁任务调度,解锁任务调度,挂起,恢复,延时等操作,同时也可以设置任务优先级,获取任务优先级。任务结束的时候,则进行当前任务自删除操作。
Huawei LiteOS 系统中的任务管理模块为用户提供下面几种功能。

接口名 | 描述 
LOS_TaskCreateOnly | 创建任务,并使该任务进入suspend状态,并不调度。 
LOS_TaskCreate | 创建任务,并使该任务进入ready状态,并调度。 
LOS_TaskDelete | 删除指定的任务。 
LOS_TaskResume | 恢复挂起的任务。 
LOS_TaskSuspend | 挂起指定的任务。 
LOS_TaskDelay | 任务延时等待。
LOS_TaskYield | 显式放权,调整指定优先级的任务调度顺序。 
LOS_TaskLock | 锁任务调度。
LOS_TaskUnlock | 解锁任务调度。
LOS_CurTaskPriSet | 设置当前任务的优先级。
LOS_TaskPriSet | 设置指定任务的优先级。
LOS_TaskPriGet | 获取指定任务的优先级。
LOS_CurTaskIDGet | 获取当前任务的ID。
LOS_TaskInfoGet | 设置指定任务的优先级。
LOS_TaskPriGet | 获取指定任务的信息。
LOS_TaskStatusGet | 获取指定任务的状态。
LOS_TaskNameGet | 获取指定任务的名称。
LOS_TaskInfoMonitor | 监控所有任务,获取所有任务的信息。
LOS_NextTaskIDGet | 获取即将被调度的任务的ID。

创建任务的过程

创建任务之前先了解另一个结构体 tagTskInitParam

typedef struct tagTskInitParam {//Task的初始化参数
    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         */ //任务cpu亲和力掩码
#endif
    UINT32          uwResved;      /**< It is automatically deleted if set to LOS_TASK_STATUS_DETACHED.
                                        It is unable to be deleted if set to 0\. */ //如果设置为LOS_TASK_STATUS_DETACHED,则自动删除。如果设置为0,则无法删除
    UINT16          consoleID;     /**< The console id of task belongs  */ //任务的控制台id所属
    UINT32          processID; //进程ID
    UserTaskParam   userParam; //在用户态运行时栈参数
} TSK_INIT_PARAM_S;

这些初始化参数是外露的任务初始参数,pfnTaskEntry 对java来说就是你new进程的run(),需要上层使用者提供.
看个例子吧:shell中敲 ping 命令看下它创建的过程

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

发现ping的调度优先级是8,比shell 还高,那shell的是多少?答案是:看源码是 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);
}

关于shell后续会详细介绍,请持续关注。
前置条件了解清楚后,具体看任务是如何一步步创建的,如何和进程绑定,加入调度就绪队列,还是继续看源码

//创建Task
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) {//OS_TASK_FLAG_IDLEFLAG 是属于内核 idle进程专用的
        initParam->processID = OsGetIdleProcessID();//获取空闲进程
    } else if (OsProcessIsUserMode(OsCurrProcessGet())) {//当前进程是否为用户模式
        initParam->processID = OsGetKernelInitProcessID();//不是就取"Kernel"进程
    } else {
        initParam->processID = OsCurrProcessGet()->processID;//获取当前进程 ID赋值
    }
    initParam->uwResved &= ~OS_TASK_FLAG_IDLEFLAG;//不能是 OS_TASK_FLAG_IDLEFLAG
    initParam->uwResved &= ~OS_TASK_FLAG_PTHREAD_JOIN;//不能是 OS_TASK_FLAG_PTHREAD_JOIN
    if (initParam->uwResved & LOS_TASK_STATUS_DETACHED) {//是否设置了自动删除
        initParam->uwResved = OS_TASK_FLAG_DETACHED;//自动删除,注意这里是 = ,也就是说只有 OS_TASK_FLAG_DETACHED 一个标签了
    }

    ret = LOS_TaskCreateOnly(taskID, initParam);//创建一个任务,这是任务创建的实体,前面都只是前期准备工作
    if (ret != LOS_OK) {
        return ret;
    }
    taskCB = OS_TCB_FROM_TID(*taskID);//通过ID拿到task实体

    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) {//当前CPU核处于可调度状态
        LOS_Schedule();//发起调度
    }

    return LOS_OK;
}

经常有很多小伙伴抱怨说:不知道学习鸿蒙开发哪些技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?

为了能够帮助到大家能够有规划的学习,这里特别整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。

在这里插入图片描述

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

在这里插入图片描述

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

在这里插入图片描述

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

在这里插入图片描述

OpenHarmony 开发环境搭建
图片

《OpenHarmony源码解析》

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……
  • 系统架构分析
  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

图片

OpenHarmony 设备开发学习手册

图片

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2046491.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在Linux中进行supervisor进程守护的安装和配置

supervisor用于守护进程&#xff0c;在进程意外终止后将其重启。 supervisor没有监听内部程序和自动重启的功能。 Python-3.9.5安装 第一步&#xff0c;检查Linux系统是否自带Python。 命令&#xff1a;python --version 第二步&#xff0c;安装依赖包。 命令&#xff1a;…

Java超市收银系统(八、数据导入)

引言 当选择1时&#xff0c;程序读取 “商品信息.xls” 文件&#xff0c;将所有数据存放于product集合中&#xff0c;然后将集合中的所有数据增加到商品表中&#xff0c;增加的时候要检查每条记录的条形码在商品表中是否存在&#xff0c;若存在&#xff0c;则不需要增加到数据库…

tortoisegit下载及其使用流程

下载 官方下载链接&#xff1a;Download – TortoiseGit – Windows Shell Interface to Git 选择适合自己的电脑位数的版本&#xff1a;一般64的兼容32的 按照就不介绍了怎么开心怎么来&#xff0c;本篇暂时为了支持一位粉丝的疑惑 安装的话没有特殊配置暂不介绍&#xff0c…

Dbeaver连接达梦数据库教程(图文版)

本章教程&#xff0c;主要介绍如何用Dbeaver连接国产达梦数据库。 达梦数据库Docker部署教程参考&#xff1a;https://yang-roc.blog.csdn.net/article/details/141158807 一、Dbeaver安装包下载 下载Dbeaver&#xff1a;https://dbeaver.io/ 在这里就不演示安装过程了&#xf…

GPU驱动的大规模静态物件渲染

GPU Driven 的静态物件渲染&#xff0c;听起来很高级&#xff0c;其实具体操作很简单&#xff0c;基础就是直接调用 Graphics.DrawMeshInstancedIndirect 这个 Unity 内置接口就可以了。但我们项目对这个流程做了一些优化&#xff0c;使得支持的实体数量有大幅提升。 这套系统主…

海南云亿商务咨询有限公司引领抖音电商新潮流

在当今这个数字化时代&#xff0c;电商行业如日中天&#xff0c;而抖音作为短视频与社交电商完美融合的典范&#xff0c;正以前所未有的速度改变着人们的购物习惯和消费模式。在这片充满机遇与挑战的蓝海中&#xff0c;海南云亿商务咨询有限公司凭借其敏锐的市场洞察力和专业的…

【算法/学习】:flood算法

✨ 君子坐而论道&#xff0c;少年起而行之 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;算法学习 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &…

鸿蒙交互事件开发01——点击/拖拽/触摸事件

如果你也对鸿蒙开发感兴趣&#xff0c;加入“Harmony自习室”吧&#xff01;扫描下方名片&#xff0c;关注公众号&#xff0c;公众号更新更快&#xff0c;同时也有更多学习资料和技术讨论群。 1 概 述 事件是人机交互的基础&#xff0c;鸿蒙开发中&#xff0c;事件分…

EmguCV学习笔记 VB.Net 2.1 颜色空间和颜色

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV学习笔记目录 Vb.net EmguCV学习笔记目录 C# 笔者的博客网址&#xff1a;VB.Net-CSDN博客 教程相关说明以及如何获得pdf教程…

威胁组织伪造Loom,Mac用户警惕AMOS窃取软件威胁

近期&#xff0c;一个复杂且可能与神秘威胁组织“Crazy Evil”有关联的网络犯罪活动&#xff0c;已将注意力转向了Mac用户群体。该组织利用广受欢迎的屏幕录制工具Loom作为掩护&#xff0c;悄无声息地传播着臭名远扬的AMOS数据窃取软件。Moonlock Lab的安全研究员们揭开了这一阴…

【数据结构与算法 | 图篇】拓扑排序

1. 概念 拓扑排序是是一种针对有向无环图进行的排序方法。它将图中所有顶点排成一个线性序列&#xff0c;使得对于图中的每一条有向边(u, v)&#xff0c;顶点u在序列中都出现在顶点v之前。 适用范围&#xff1a; 拓扑排序只适用于有向无环图。 结果非唯一&#xff1a; 对于…

阿里云ubuntu系统安装mysql8.0

一、安装mysql8.0 1.已安装其他版本的mysql&#xff0c;需要删除 若没有不需要此操作 1 #卸载MySQL5.7版本 2 apt remove -y mysql-client5.7* mysql-community-server5.7* 4 # 卸载5.7的仓库信息 5 dpkg-l | grep mysql | awk iprint $2} | xargs dpkg -P2.更新仓库 apt u…

FASTSPEECH 2论文阅读

FASTSPEECH 2: FAST AND HIGH-QUALITY END-TOEND TEXT TO SPEECH 现状 非自回归模型可以在质量相当的情况下显著快于先前的自回归模型合成模型。但FastSpeech模型训练依赖与自回归教师模型进行时长预测&#xff08;提供更多的信息作为输入&#xff09;和知识蒸馏&#xff08;…

【开端】Java中判断一个对象是否是空内容

一、绪论 在Java中&#xff0c;我们常常使用的到的就是封装&#xff0c;为什么要封装&#xff0c;封装有什么好处。首先在系统开发过程中&#xff0c;其实很多功能和场景都共性的。那么为了避免重复造轮子&#xff0c;我们这时就使用到了封装。封装可以一次造轮子&#xff0c;无…

数据集搜索

1. 数据集和数据集的分类 数据集是一组数据的集合&#xff0c;通常用于机器学习、统计分析、数据挖掘等领域&#xff0c;帮助算法训练、模型验证和评估。可以是各种形式的数据&#xff0c;如表格、图像、机器学习相关的文件等。 根据在机器学习中的应用&#xff0c;数据集可以…

1. MongoDB概念解析

1. 概念解析 在 MongoDB 中基本的概念是文档、集合、数据库。 SQL 术语/概念MongoDB 术语/概念解释/说明databasedatabase数据库tablecollection数据库表/集合rowdocument数据记录行/文档columnfield数据字段/域indexindex索引table joins表连接,MongoDB不支持primary keypri…

1.3 数据库的发展历史与演变

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 工&#x1f497;重&#x1f497;hao&#x1f497;&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题.…

鸿萌数据恢复服务: 如何修复 SQL Server 数据库错误 829?

天津鸿萌科贸发展有限公司从事数据安全服务二十余年&#xff0c;致力于为各领域客户提供专业的数据恢复、数据备份、网络及终端数据安全等解决方案与服务。 同时&#xff0c;鸿萌是众多国际主流数据恢复软件(Stellar、UFS、R-Studio、ReclaiMe Pro 等)的授权代理商&#xff0c…

pandas 笔记crosstab

用来计算两个&#xff08;或更多&#xff09;因子的交叉表&#xff08;即频率表、列联表或透视表&#xff09;。这个功能特别适用于统计分析和数据探索阶段&#xff0c;帮助理解不同变量之间的关系 1 基本用法 pd.crosstab(index, columns, valuesNone, rownamesNone, colnam…

基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(二)---ROS2与UE5进行图像数据传输

前言 本系列教程旨在使用UE5配置一个具备激光雷达深度摄像机的仿真小车&#xff0c;并使用通过跨平台的方式进行ROS2和UE5仿真的通讯&#xff0c;达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础&#xff0c;Nav2相关的学习教程可以参考本人的其他博…