百篇博客系列篇.本篇为:

任务管理相关篇为:

任务即线程

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

官方是怎么描述线程的

基本概念

从系统的角度看,线程是竞争系统资源的最小运行单元。线程可以使用或等待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和线程是一个东西。

  1. #define OS_TASK_STATUS_INIT 0x0001U
  2. #define OS_TASK_STATUS_READY 0x0002U
  3. #define OS_TASK_STATUS_RUNNING 0x0004U
  4. #define OS_TASK_STATUS_SUSPEND 0x0008U
  5. #define OS_TASK_STATUS_PEND 0x0010U
  6. #define OS_TASK_STATUS_DELAY 0x0020U
  7. #define OS_TASK_STATUS_TIMEOUT 0x0040U
  8. #define OS_TASK_STATUS_PEND_TIME 0x0080U
  9. #define OS_TASK_STATUS_EXIT 0x0100U

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

  1. typedef struct {
  2. VOID *stackPointer; /**< Task stack pointer */ //非用户模式下的栈指针
  3. UINT16 taskStatus; /**< Task status */ //各种状态标签,可以拥有多种标签,按位标识
  4. UINT16 priority; /**< Task priority */ //任务优先级[0:31],默认是31级
  5. UINT16 policy; //任务的调度方式(三种 .. LOS_SCHED_RR )
  6. UINT16 timeSlice; /**< Remaining time slice *///剩余时间片
  7. UINT32 stackSize; /**< Task stack size */ //非用户模式下栈大小
  8. UINTPTR topOfStack; /**< Task stack top */ //非用户模式下的栈顶 bottom = top + size
  9. UINT32 taskID; /**< Task ID */ //任务ID,任务池本质是一个大数组,ID就是数组的索引,默认 < 128
  10. TSK_ENTRY_FUNC taskEntry; /**< Task entrance function */ //任务执行入口函数
  11. VOID *joinRetval; /**< pthread adaption */ //用来存储join线程的返回值
  12. VOID *taskSem; /**< Task-held semaphore */ //task在等哪个信号量
  13. VOID *taskMux; /**< Task-held mutex */ //task在等哪把锁
  14. VOID *taskEvent; /**< Task-held event */ //task在等哪个事件
  15. UINTPTR args[4]; /**< Parameter, of which the maximum number is 4 */ //入口函数的参数 例如 main (int argc,char *argv[])
  16. CHAR taskName[OS_TCB_NAME_LEN]; /**< Task name */ //任务的名称
  17. LOS_DL_LIST pendList; /**< Task pend node */ //如果任务阻塞时就通过它挂到各种阻塞情况的链表上,比如OsTaskWait时
  18. LOS_DL_LIST threadList; /**< thread list */ //挂到所属进程的线程链表上
  19. SortLinkList sortList; /**< Task sortlink node */ //挂到cpu core 的任务执行链表上
  20. UINT32 eventMask; /**< Event mask */ //事件屏蔽
  21. UINT32 eventMode; /**< Event mode */ //事件模式
  22. UINT32 priBitMap; /**< BitMap for recording the change of task priority, //任务在执行过程中优先级会经常变化,这个变量用来记录所有曾经变化
  23. the priority can not be greater than 31 */ //过的优先级,例如 ..01001011 曾经有过 0,1,3,6 优先级
  24. INT32 errorNo; /**< Error Num */
  25. UINT32 signal; /**< Task signal */ //任务信号类型,(SIGNAL_NONE,SIGNAL_KILL,SIGNAL_SUSPEND,SIGNAL_AFFI)
  26. sig_cb sig; //信号控制块,这里用于进程间通讯的信号,类似于 linux singal模块
  27. #if (LOSCFG_KERNEL_SMP == YES)
  28. UINT16 currCpu; /**< CPU core number of this task is running on */ //正在运行此任务的CPU内核号
  29. UINT16 lastCpu; /**< CPU core number of this task is running on last time */ //上次运行此任务的CPU内核号
  30. UINT16 cpuAffiMask; /**< CPU affinity mask, support up to 16 cores */ //CPU亲和力掩码,最多支持16核,亲和力很重要,多核情况下尽量一个任务在一个CPU核上运行,提高效率
  31. UINT32 timerCpu; /**< CPU core number of this task is delayed or pended */ //此任务的CPU内核号被延迟或挂起
  32. #if (LOSCFG_KERNEL_SMP_TASK_SYNC == YES)
  33. UINT32 syncSignal; /**< Synchronization for signal handling */ //用于CPU之间 同步信号
  34. #endif
  35. #if (LOSCFG_KERNEL_SMP_LOCKDEP == YES) //死锁检测开关
  36. LockDep lockDep;
  37. #endif
  38. #if (LOSCFG_KERNEL_SCHED_STATISTICS == YES) //调度统计开关,显然打开这个开关性能会受到影响,鸿蒙默认是关闭的
  39. SchedStat schedStat; /**< Schedule statistics */ //调度统计
  40. #endif
  41. #endif
  42. UINTPTR userArea; //使用区域,由运行时划定,根据运行态不同而不同
  43. UINTPTR userMapBase; //用户模式下的栈底位置
  44. UINT32 userMapSize; /**< user thread stack size ,real size : userMapSize + USER_STACK_MIN_SIZE */
  45. UINT32 processID; /**< Which belong process *///所属进程ID
  46. FutexNode futex; //实现快锁功能
  47. LOS_DL_LIST joinList; /**< join list */ //联结链表,允许任务之间相互释放彼此
  48. LOS_DL_LIST lockList; /**< Hold the lock list */ //拿到了哪些锁链表
  49. UINT32 waitID; /**< Wait for the PID or GID of the child process */ //等待孩子的PID或GID进程
  50. UINT16 waitFlag; /**< The type of child process that is waiting, belonging to a group or parent,
  51. a specific child process, or any child process */
  52. #if (LOSCFG_KERNEL_LITEIPC == YES)
  53. UINT32 ipcStatus; //IPC状态
  54. LOS_DL_LIST msgListHead; //消息队列头结点,上面挂的都是任务要读的消息
  55. BOOL accessMap[LOSCFG_BASE_CORE_TSK_LIMIT];//访问图,指的是task之间是否能访问的标识,LOSCFG_BASE_CORE_TSK_LIMIT 为任务池总数
  56. #endif
  57. } LosTaskCB;

结构体LosTaskCB内容很多,各代表什么含义?

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

Task怎么管理

什么是任务池?

前面已经说了任务是内核调度层面的概念,调度算法保证了task有序的执行,调度机制详见其他姊妹篇的介绍。

如此多的任务怎么管理和执行?管理靠任务池和就绪队列,执行靠调度算法。

代码如下(OsTaskInit):

  1. LITE_OS_SEC_TEXT_INIT UINT32 OsTaskInit(VOID)
  2. {
  3. UINT32 index;
  4. UINT32 ret;
  5. UINT32 size;
  6. g_taskMaxNum = LOSCFG_BASE_CORE_TSK_LIMIT;//任务池中最多默认128个,可谓铁打的任务池流水的线程
  7. size = (g_taskMaxNum + 1) * sizeof(LosTaskCB);//计算需分配内存总大小
  8. /*
  9. * This memory is resident memory and is used to save the system resources
  10. * of task control block and will not be freed.
  11. */
  12. g_taskCBArray = (LosTaskCB *)LOS_MemAlloc(m_aucSysMem0, size);//任务池 常驻内存,不被释放
  13. if (g_taskCBArray == NULL) {
  14. return LOS_ERRNO_TSK_NO_MEMORY;
  15. }
  16. (VOID)memset_s(g_taskCBArray, size, 0, size);
  17. LOS_ListInit(&g_losFreeTask);//空闲任务链表
  18. LOS_ListInit(&g_taskRecyleList);//需回收任务链表
  19. for (index = 0; index < g_taskMaxNum; index++) {
  20. g_taskCBArray[index].taskStatus = OS_TASK_STATUS_UNUSED;
  21. g_taskCBArray[index].taskID = index;//任务ID最大默认127
  22. LOS_ListTailInsert(&g_losFreeTask, &g_taskCBArray[index].pendList);//都插入空闲任务列表
  23. }//注意:这里挂的是pendList节点,所以取TCB要通过 OS_TCB_FROM_PENDLIST 取.
  24. ret = OsPriQueueInit();//创建32个任务优先级队列,即32个双向循环链表
  25. if (ret != LOS_OK) {
  26. return LOS_ERRNO_TSK_NO_MEMORY;
  27. }
  28. /* init sortlink for each core */
  29. for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) {
  30. ret = OsSortLinkInit(&g_percpu[index].taskSortLink);//每个CPU内核都有一个执行任务链表
  31. if (ret != LOS_OK) {
  32. return LOS_ERRNO_TSK_NO_MEMORY;
  33. }
  34. }
  35. return LOS_OK;
  36. }

g_taskCBArray 就是个任务池,默认创建128个任务,常驻内存,不被释放。

g_losFreeTask是空闲任务链表,想创建任务时来这里申请一个空闲任务,用完了就回收掉,继续给后面的申请使用。

g_taskRecyleList是回收任务链表,专用来回收exit 任务,任务所占资源被确认归还后被彻底删除,就像员工离职一样,得有个离职队列和流程,要归还电脑,邮箱,有没有借钱要还的 等操作。

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

就绪队列是怎么回事

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

这就是就绪队列的原理,一共有32个就绪队列,进程和线程都有,因为线程的优先级是默认32个, 每个队列中放同等优先级的task.

还是看源码吧

  1. #define OS_PRIORITY_QUEUE_NUM 32
  2. LITE_OS_SEC_BSS LOS_DL_LIST *g_priQueueList = NULL;//队列链表
  3. LITE_OS_SEC_BSS UINT32 g_priQueueBitmap;//队列位图 UINT32每位代表一个优先级,共32个优先级
  4. //内部队列初始化
  5. UINT32 OsPriQueueInit(VOID)
  6. {
  7.     UINT32 priority;
  8.     /* system resident resource *///常驻内存
  9.     g_priQueueList = (LOS_DL_LIST *)LOS_MemAlloc(m_aucSysMem0, (OS_PRIORITY_QUEUE_NUM * sizeof(LOS_DL_LIST)));//分配32个队列头节点
  10.     if (g_priQueueList == NULL) {
  11.         return LOS_NOK;
  12.     }
  13.     for (priority = 0; priority < OS_PRIORITY_QUEUE_NUM; ++priority) {
  14.         LOS_ListInit(&g_priQueueList[priority]);//队列初始化,前后指针指向自己
  15.     }
  16.     return LOS_OK;
  17. }

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

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

任务栈是怎么回事

每个任务都是独立开的,任务之间也相互独立,之间通讯通过IPC,这里的“独立”指的是每个任务都有自己的运行环境 —— 栈空间,称为任务栈,栈空间里保存的信息包含局部变量、寄存器、函数参数、函数返回地址等等

但系统中只有一个CPU,任务又是独立的,调度的本质就是CPU执行一个新task,老task在什么地方被中断谁也不清楚,是随机的。那如何保证老任务被再次调度选中时还能从上次被中断的地方继续玩下去呢?

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

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

任务上下文(TaskContext)是怎样的呢?还是直接看源码

  1. /* The size of this structure must be smaller than or equal to the size specified by OS_TSK_STACK_ALIGN (16 bytes). */
  2. typedef struct {
  3. #if !defined(LOSCFG_ARCH_FPU_DISABLE)
  4. UINT64 D[FP_REGS_NUM]; /* D0-D31 */
  5. UINT32 regFPSCR; /* FPSCR */
  6. UINT32 regFPEXC; /* FPEXC */
  7. #endif
  8. UINT32 resved; /* It's stack 8 aligned */
  9. UINT32 regPSR;
  10. UINT32 R[GEN_REGS_NUM]; /* R0-R12 */
  11. UINT32 SP; /* R13 */
  12. UINT32 LR; /* R14 */
  13. UINT32 PC; /* R15 */
  14. } 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函数集

  1. LITE_OS_SEC_TEXT_INIT VOID *OsTaskStackInit(UINT32 taskID, UINT32 stackSize, VOID *topStack, BOOL initFlag)
  2. {
  3. UINT32 index = 1;
  4. TaskContext *taskContext = NULL;
  5. if (initFlag == TRUE) {
  6. OsStackInit(topStack, stackSize);
  7. }
  8. taskContext = (TaskContext *)(((UINTPTR)topStack + stackSize) - sizeof(TaskContext));//注意看上下文将存放在栈的底部
  9. /* initialize the task context */
  10. #ifdef LOSCFG_GDB
  11. taskContext->PC = (UINTPTR)OsTaskEntrySetupLoopFrame;
  12. #else
  13. taskContext->PC = (UINTPTR)OsTaskEntry;//程序计数器,CPU首次执行task时跑的第一条指令位置
  14. #endif
  15. taskContext->LR = (UINTPTR)OsTaskExit; /* LR should be kept, to distinguish it's THUMB or ARM instruction */
  16. taskContext->resved = 0x0;
  17. taskContext->R[0] = taskID; /* R0 */
  18. taskContext->R[index++] = 0x01010101; /* R1, 0x01010101 : reg initialed magic word */
  19. for (; index < GEN_REGS_NUM; index++) {
  20. //R2 - R12的初始化很有意思,为什么要这么做?
  21. taskContext->R[index] = taskContext->R[index - 1] + taskContext->R[1]; /* R2 - R12 */
  22. }
  23. #ifdef LOSCFG_INTERWORK_THUMB // 16位模式
  24. taskContext->regPSR = PSR_MODE_SVC_THUMB; /* CPSR (Enable IRQ and FIQ interrupts, THUMNB-mode) */
  25. #else
  26. taskContext->regPSR = PSR_MODE_SVC_ARM; /* CPSR (Enable IRQ and FIQ interrupts, ARM-mode) */
  27. #endif
  28. #if !defined(LOSCFG_ARCH_FPU_DISABLE)
  29. /* 0xAAA0000000000000LL : float reg initialed magic word */
  30. for (index = 0; index < FP_REGS_NUM; index++) {
  31. taskContext->D[index] = 0xAAA0000000000000LL + index; /* D0 - D31 */
  32. }
  33. taskContext->regFPSCR = 0;
  34. taskContext->regFPEXC = FP_EN;
  35. #endif
  36. return (VOID *)taskContext;
  37. }

使用场景和功能

任务创建后,内核可以执行锁任务调度,解锁任务调度,挂起,恢复,延时等操作,同时也可以设置任务优先级,获取任务优先级。任务结束的时候,则进行当前任务自删除操作。

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

  1. typedef struct tagTskInitParam {//Task的初始化参数
  2. TSK_ENTRY_FUNC pfnTaskEntry; /**< Task entrance function */ //任务的入口函数
  3. UINT16 usTaskPrio; /**< Task priority */ //任务优先级
  4. UINT16 policy; /**< Task policy */ //任务调度方式
  5. UINTPTR auwArgs[4]; /**< Task parameters, of which the maximum number is four */ //入口函数的参数,最多四个
  6. UINT32 uwStackSize; /**< Task stack size */ //任务栈大小
  7. CHAR *pcName; /**< Task name */ //任务名称
  8. #if (LOSCFG_KERNEL_SMP == YES)
  9. UINT16 usCpuAffiMask; /**< Task cpu affinity mask */ //任务cpu亲和力掩码
  10. #endif
  11. UINT32 uwResved; /**< It is automatically deleted if set to LOS_TASK_STATUS_DETACHED.
  12. It is unable to be deleted if set to 0. */ //如果设置为LOS_TASK_STATUS_DETACHED,则自动删除。如果设置为0,则无法删除
  13. UINT16 consoleID; /**< The console id of task belongs */ //任务的控制台id所属
  14. UINT32 processID; //进程ID
  15. UserTaskParam userParam; //在用户态运行时栈参数
  16. } TSK_INIT_PARAM_S;

这些初始化参数是外露的任务初始参数,pfnTaskEntry 对java来说就是你new进程的run(),需要上层使用者提供.

看个例子吧:shell中敲 ping 命令看下它创建的过程

  1. u32_t osShellPing(int argc, const char **argv)
  2. {
  3. int ret;
  4. u32_t i = 0;
  5. u32_t count = 0;
  6. int count_set = 0;
  7. u32_t interval = 1000; /* default ping interval */
  8. u32_t data_len = 48; /* default data length */
  9. ip4_addr_t dst_ipaddr;
  10. TSK_INIT_PARAM_S stPingTask;
  11. // ...省去一些中间代码
  12. /* start one task if ping forever or ping count greater than 60 */
  13. if (count == 0 || count > LWIP_SHELL_CMD_PING_RETRY_TIMES) {
  14. if (ping_taskid > 0) {
  15. PRINTK("Ping task already running and only support one now\n");
  16. return LOS_NOK;
  17. }
  18. stPingTask.pfnTaskEntry = (TSK_ENTRY_FUNC)ping_cmd;//线程的执行函数
  19. stPingTask.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;//0x4000 = 16K
  20. stPingTask.pcName = "ping_task";
  21. stPingTask.usTaskPrio = 8; /* higher than shell 优先级高于10,属于内核态线程*/
  22. stPingTask.uwResved = LOS_TASK_STATUS_DETACHED;
  23. stPingTask.auwArgs[0] = dst_ipaddr.addr; /* network order */
  24. stPingTask.auwArgs[1] = count;
  25. stPingTask.auwArgs[2] = interval;
  26. stPingTask.auwArgs[3] = data_len;
  27. ret = LOS_TaskCreate((UINT32 *)(&ping_taskid), &stPingTask);
  28. }
  29. // ...
  30. return LOS_OK;
  31. ping_error:
  32. lwip_ping_usage();
  33. return LOS_NOK;
  34. }

发现ping的调度优先级是8,比shell 还高,那shell的是多少?答案是:看源码是 9

  1. LITE_OS_SEC_TEXT_MINOR UINT32 ShellTaskInit(ShellCB *shellCB)
  2. {
  3. CHAR *name = NULL;
  4. TSK_INIT_PARAM_S initParam = {
  5. 0};
  6. if (shellCB->consoleID == CONSOLE_SERIAL) {
  7. name = SERIAL_SHELL_TASK_NAME;
  8. } else if (shellCB->consoleID == CONSOLE_TELNET) {
  9. name = TELNET_SHELL_TASK_NAME;
  10. } else {
  11. return LOS_NOK;
  12. }
  13. initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ShellTask;
  14. initParam.usTaskPrio = 9; /* 9:shell task priority */
  15. initParam.auwArgs[0] = (UINTPTR)shellCB;
  16. initParam.uwStackSize = 0x3000;
  17. initParam.pcName = name;
  18. initParam.uwResved = LOS_TASK_STATUS_DETACHED;
  19. (VOID)LOS_EventInit(&shellCB->shellEvent);
  20. return LOS_TaskCreate(&shellCB->shellTaskHandle, &initParam);
  21. }

关于shell后续会详细介绍,请持续关注。

前置条件了解清楚后,具体看任务是如何一步步创建的,如何和进程绑定,加入调度就绪队列,还是继续看源码

  1. //创建Task
  2. LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskCreate(UINT32 *taskID, TSK_INIT_PARAM_S *initParam)
  3. {
  4. UINT32 ret;
  5. UINT32 intSave;
  6. LosTaskCB *taskCB = NULL;
  7. if (initParam == NULL) {
  8. return LOS_ERRNO_TSK_PTR_NULL;
  9. }
  10. if (OS_INT_ACTIVE) {
  11. return LOS_ERRNO_TSK_YIELD_IN_INT;
  12. }
  13. if (initParam->uwResved & OS_TASK_FLAG_IDLEFLAG) {//OS_TASK_FLAG_IDLEFLAG 是属于内核 idle进程专用的
  14. initParam->processID = OsGetIdleProcessID();//获取空闲进程
  15. } else if (OsProcessIsUserMode(OsCurrProcessGet())) {//当前进程是否为用户模式
  16. initParam->processID = OsGetKernelInitProcessID();//不是就取"Kernel"进程
  17. } else {
  18. initParam->processID = OsCurrProcessGet()->processID;//获取当前进程 ID赋值
  19. }
  20. initParam->uwResved &= ~OS_TASK_FLAG_IDLEFLAG;//不能是 OS_TASK_FLAG_IDLEFLAG
  21. initParam->uwResved &= ~OS_TASK_FLAG_PTHREAD_JOIN;//不能是 OS_TASK_FLAG_PTHREAD_JOIN
  22. if (initParam->uwResved & LOS_TASK_STATUS_DETACHED) {//是否设置了自动删除
  23. initParam->uwResved = OS_TASK_FLAG_DETACHED;//自动删除,注意这里是 = ,也就是说只有 OS_TASK_FLAG_DETACHED 一个标签了
  24. }
  25. ret = LOS_TaskCreateOnly(taskID, initParam);//创建一个任务,这是任务创建的实体,前面都只是前期准备工作
  26. if (ret != LOS_OK) {
  27. return ret;
  28. }
  29. taskCB = OS_TCB_FROM_TID(*taskID);//通过ID拿到task实体
  30. SCHEDULER_LOCK(intSave);
  31. taskCB->taskStatus &= ~OS_TASK_STATUS_INIT;//任务不再是初始化
  32. OS_TASK_SCHED_QUEUE_ENQUEUE(taskCB, 0);//进入调度就绪队列,新任务是直接进入就绪队列的
  33. SCHEDULER_UNLOCK(intSave);
  34. /* in case created task not running on this core,
  35. schedule or not depends on other schedulers status. */
  36. LOS_MpSchedule(OS_MP_CPU_ALL);//如果创建的任务没有在这个核心上运行,是否调度取决于其他调度程序的状态。
  37. if (OS_SCHEDULER_ACTIVE) {//当前CPU核处于可调度状态
  38. LOS_Schedule();//发起调度
  39. }
  40. return LOS_OK;
  41. }

对应张大爷的故事:就是节目单要怎么填,按格式来,从哪里开始演,要多大的空间,王场馆好协调好现场的环境。这里注意 在同一个节目单只要节目没演完,王场馆申请场地的空间就不能给别人用,这个场地空间对应的就是鸿蒙任务的栈空间,除非整个节目单都完了,就回收了。把整个场地干干净净的留给下一个人的节目单来表演。

至此的创建已经完成,已各就各位,源码最后还申请了一次LOS_Schedule();因为鸿蒙的调度方式是抢占式的,如何本次task的任务优先级高于其他就绪队列,那么接下来要执行的任务就是它了!

鸿蒙内核源码分析.总目录

v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 百篇博客分析 | 51.c.h .o

百万汉字注解.百篇博客分析

百万汉字注解 >> 精读鸿蒙源码,中文注解分析, 深挖地基工程,大脑永久记忆,四大码仓每日同步更新< gitee| github| csdn| coding >

百篇博客分析 >> 故事说内核,问答式导读,生活式比喻,表格化说明,图形化展示,主流站点定期更新中< 51cto| csdn| harmony| osc >

关注不迷路.代码即人生

QQ群:790015635 | 入群密码: 666

原创不易,欢迎转载,但请注明出处.

鸿蒙内核源码分析(任务调度篇) | 任务是内核调度的单元 | 百篇博客分析OpenHarmony源码 | v4.05的更多相关文章

  1. 鸿蒙内核源码分析(fork篇) | 一次调用,两次返回 | 百篇博客分析OpenHarmony源码 | v45.03

    百篇博客系列篇.本篇为: v45.xx 鸿蒙内核源码分析(Fork篇) | 一次调用,两次返回 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管理内 ...

  2. 鸿蒙内核源码分析(任务切换篇) | 看汇编如何切换任务 | 百篇博客分析OpenHarmony源码 | v41.03

    百篇博客系列篇.本篇为: v41.xx 鸿蒙内核源码分析(任务切换篇) | 看汇编如何切换任务 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁 ...

  3. 鸿蒙内核源码分析(系统调用篇) | 开发者永远的口头禅 | 百篇博客分析OpenHarmony源码 | v37.03

    百篇博客系列篇.本篇为: v37.xx 鸿蒙内核源码分析(系统调用篇) | 开发者永远的口头禅 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁 ...

  4. 鸿蒙内核源码分析(信号量篇) | 谁在负责解决任务的同步 | 百篇博客分析OpenHarmony源码 | v29.01

    百篇博客系列篇.本篇为: v29.xx 鸿蒙内核源码分析(信号量篇) | 谁在负责解决任务的同步 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当立 ...

  5. 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 百篇博客分析OpenHarmony源码 | v27.02

    百篇博客系列篇.本篇为: v27.xx 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当立贞 ...

  6. 鸿蒙内核源码分析(并发并行篇) | 听过无数遍的两个概念 | 百篇博客分析OpenHarmony源码 | v25.01

    百篇博客系列篇.本篇为: v25.xx 鸿蒙内核源码分析(并发并行篇) | 听过无数遍的两个概念 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度 ...

  7. 鸿蒙内核源码分析(线程概念篇) | 是谁在不停的折腾CPU? | 百篇博客分析OpenHarmony源码 | v21.06

    百篇博客系列篇.本篇为: v21.xx 鸿蒙内核源码分析(线程概念篇) | 是谁在不断的折腾CPU | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调 ...

  8. 鸿蒙内核源码分析(调度故事篇) | 用故事说内核调度 | 百篇博客分析OpenHarmony源码 | v9.07

    百篇博客系列篇.本篇为: v09.xx 鸿蒙内核源码分析(调度故事篇) | 用故事说内核调度过程 | 51.c.h .o 前因后果相关篇为: v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 ...

  9. 鸿蒙内核源码分析(调度机制篇) | 任务是如何被调度执行的 | 百篇博客分析OpenHarmony源码 | v7.07

    百篇博客系列篇.本篇为: v07.xx 鸿蒙内核源码分析(调度机制篇) | 任务是如何被调度执行的 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调 ...

随机推荐

  1. The Programmer's Oath程序员的誓言----鲍勃·马丁大叔(Bob Martin)

    In order to defend and preserve the honor of the profession of computer programmers, I Promise that, ...

  2. Mybatis映射器(二)

    上一篇文章返回是resultType,但其无法定义多的属性,比如typeHandler,级联等.为了支持复杂映射,可以用resultMap属性,先定义resultmap属性: <mapper n ...

  3. mysql复制内容到一张新表

    -- 1.复制表结构及数据到新表 CREATE TABLE 新表 SELECT * FROM 旧表 -- 2.只复制表结构到新表 CREATE TABLE 新表 SELECT * FROM 旧表 WH ...

  4. tomcat中修改Web站点的默认根目录

    转自:http://blog.csdn.net/wzqcongcong/article/details/6387907 想把Tomcat的默认网站根目录修改成自己指定的目录,比如:F:/MyWeb.这 ...

  5. excel快捷键如下:

    ALT+ 空格键,然后按下 X ALT+ 空格键,然后按下 R  首先打开表格,在A1对角用鼠标左键单击,界面会全部选中,然后调整字体大小框里的数字,回车,表格就变大了. 同时按Alt和E,再按L   ...

  6. 怎样去除EXCEL中的重复行

    工具/原料 安装了EXCEL2010的电脑一台 步骤/方法   假如我们的表格中有下图所示的一系列数据,可以看出其中有一些重复.   首先我们选中所有数据.可以先用鼠标点击"A1单元格&qu ...

  7. Asp.NetCore ResposeCache 缓存的使用

    先小结一下: 缓存策略: [ResponseCache(CacheProfileName ="default30")] 直接使用缓存,30秒过期: [ResponseCache(D ...

  8. 约瑟夫环问题详解 (c++)

    问题描述: 已知n个人(以编号0,2,3...n-1分别表示)围坐在一起.从编号为0的人开始报数,数到k的那个人出列:他的下一个人又从1开始报数,数到k的那个人又出列:依此规律重复下去,直到圆桌周围的 ...

  9. awk 命令-随笔

    awk语法: awk [option] 'pattern{action}' file ... awk [参数] '条件{动作}' 文件 ... 解析: 命令: awk 参数: -F "&qu ...

  10. 洛谷P2338 Bessie Slows Down S 题解

    题目 [USACO14JAN]Bessie Slows Down S 题解 这道题其实蛮简单的,不知道为什么难度划到了提高+,个人觉得这难度大概就是普及左右. 具体说说怎么做吧,简单模拟一下即可,始终 ...