鸿蒙源码分析(六十五)

任务or线程?

经过分析take_manager代码,我们发现任务的出现常常伴随着线程,任务池的工作和线程的工作原理有一定的相似度,我们今天分析一下这两者的区别。
鸿蒙中任务就是一种线程,二者本质上没有区别,只是在不同的应用环境下有不一样的名字
下面附上任务的定义代码

typedef struct {
    
    
    VOID            *stackPointer;      /**< Task stack pointer */ //内核态栈指针,SP位置,切换任务时先保存上下文并指向TaskContext位置
    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;

代码定义部分很长,该结构体有一定复杂性。

描述

线程是计算资源竞争的最小单元,线程可以使用或等待CPU、使用内存空间等系统资源,并独立于其它线程运行。
鸿蒙内核每个进程内的线程独立运行、独立调度,当前进程内线程的调度不受其它进程内线程的影响。
线程的状态说明:

  • 初始化(Init):该线程正在被创建。
  • 就绪(Ready):该线程在就绪列表中,等待CPU调度。
  • 运行(Running):该线程正在运行。
  • 阻塞(Blocked):该线程被阻塞挂起。Blocked状态包括:pend(因为锁、事件、信号量等阻塞)、suspend(主动pend)、delay(延时阻塞)、pendtime(因为锁、事件、信号量时间等超时等待)。
  • 退出(Exit):该线程运行结束,等待父线程回收其控制块资源。
    在这里插入图片描述
    这里的任务和线程概念已经没什么区别,主要是不同情况下的称谓不同而已。

OpenHarmony LiteOS-M的任务模块可以给用户提供多个任务,实现任务间的切换,帮助用户管理业务程序流程。任务模块具有如下特性:

  • 支持多任务。
  • 一个任务表示一个线程。
  • 抢占式调度机制,高优先级的任务可打断低优先级任务,低优先级任务必须在高优先级任务阻塞或结束后才能得到调度。
  • 相同优先级任务支持时间片轮转调度方式。
  • 共有32个优先级[0-31],最高优先级为0,最低优先级为31。

任务相关概念

任务状态

任务有多种运行状态。系统初始化完成后,创建的任务就可以在系统中竞争一定的资源,由内核进行调度。

任务状态通常分为以下四种:

  • 就绪(Ready):该任务在就绪队列中,只等待CPU。
  • 运行(Running):该任务正在执行。
  • 阻塞(Blocked):该任务不在就绪队列中。包含任务被挂起(suspend状态)、任务被延时(delay状态)、任务正在等待信号量、读写队列或者等待事件等。
  • 退出态(Dead):该任务运行结束,等待系统回收资源。

四种状态的关系
在这里插入图片描述

任务相关属性

任务ID

任务ID,在任务创建时通过参数返回给用户,是任务的重要标识。系统中的ID号是唯一的。用户可以通过任务ID对指定任务进行任务挂起、任务恢复、查询任务名等操作。

任务优先级

优先级表示任务执行的优先顺序。任务的优先级决定了在发生任务切换时即将要执行的任务,就绪队列中最高优先级的任务将得到执行。

任务入口函数

新任务得到调度后将执行的函数。该函数由用户实现,在任务创建时,通过任务创建结构体设置。

任务栈

每个任务都拥有一个独立的栈空间,我们称为任务栈。栈空间里保存的信息包含局部变量、寄存器、函数参数、函数返回地址等。

任务上下文

任务在运行过程中使用的一些资源,如寄存器等,称为任务上下文。当这个任务挂起时,其他任务继续执行,可能会修改寄存器等资源中的值。如果任务切换时没有保存任务上下文,可能会导致任务恢复后出现未知错误。因此在任务切换时会将切出任务的任务上下文信息,保存在自身的任务栈中,以便任务恢复后,从栈空间中恢复挂起时的上下文信息,从而继续执行挂起时被打断的代码。

任务控制块TCB

每个任务都含有一个任务控制块(TCB)。TCB包含了任务上下文栈指针(stack pointer)、任务状态、任务优先级、任务ID、任务名、任务栈大小等信息。TCB可以反映出每个任务运行情况。

任务切换

任务切换包含获取就绪队列中最高优先级任务、切出任务上下文保存、切入任务上下文恢复等动作。

任务运行机制

用户创建任务时,系统会初始化任务栈,预置上下文。此外,系统还会将“任务入口函数”地址放在相应位置。这样在任务第一次启动进入运行态时,将会执行“任务入口函数”。

猜你喜欢

转载自blog.csdn.net/m0_46976252/article/details/120895699