μc/OS 任务管理

2.1 任务管理数据结构

任务控制块、任务空闲链表和任务就绪链表、任务优先级指针表、任务堆栈等。

2.1.1 任务控制块

OSTCB结构体包含了任务控制信息。

typedef struct os_tcb {OS_STK          *OSTCBStkPtr;           /* Pointer to current top of stack                         */#if OS_TASK_CREATE_EXT_EN > 0uvoid            *OSTCBExtPtr;           /* Pointer to user definable data for TCB extension        */OS_STK          *OSTCBStkBottom;        /* Pointer to bottom of stack                              */INT32U           OSTCBStkSize;          /* Size of task stack (in number of stack elements)        */INT16U           OSTCBOpt;              /* Task options as passed by OSTaskCreateExt()             */INT16U           OSTCBId;               /* Task ID (0..65535)                                      */
#endifstruct os_tcb   *OSTCBNext;             /* Pointer to next     TCB in the TCB list                 */struct os_tcb   *OSTCBPrev;             /* Pointer to previous TCB in the TCB list                 */#if OS_TASK_CREATE_EXT_EN > 0u
#if defined(OS_TLS_TBL_SIZE) && (OS_TLS_TBL_SIZE > 0u)OS_TLS           OSTCBTLSTbl[OS_TLS_TBL_SIZE];
#endif
#endif#if (OS_EVENT_EN)OS_EVENT        *OSTCBEventPtr;         /* Pointer to          event control block                 */
#endif#if (OS_EVENT_EN) && (OS_EVENT_MULTI_EN > 0u)OS_EVENT       **OSTCBEventMultiPtr;    /* Pointer to multiple event control blocks                */
#endif#if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u)void            *OSTCBMsg;              /* Message received from OSMboxPost() or OSQPost()         */
#endif#if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
#if OS_TASK_DEL_EN > 0uOS_FLAG_NODE    *OSTCBFlagNode;         /* Pointer to event flag node                              */
#endifOS_FLAGS         OSTCBFlagsRdy;         /* Event flags that made task ready to run                 */
#endifINT32U           OSTCBDly;              /* Nbr ticks to delay task or, timeout waiting for event   */INT8U            OSTCBStat;             /* Task      status                                        */INT8U            OSTCBStatPend;         /* Task PEND status                                        */INT8U            OSTCBPrio;             /* Task priority (0 == highest)                            */INT8U            OSTCBX;                /* Bit position in group  corresponding to task priority   */INT8U            OSTCBY;                /* Index into ready table corresponding to task priority   */OS_PRIO          OSTCBBitX;             /* Bit mask to access bit position in ready table          */OS_PRIO          OSTCBBitY;             /* Bit mask to access bit position in ready group          */#if OS_TASK_DEL_EN > 0uINT8U            OSTCBDelReq;           /* Indicates whether a task needs to delete itself         */
#endif#if OS_TASK_PROFILE_EN > 0uINT32U           OSTCBCtxSwCtr;         /* Number of time the task was switched in                 */INT32U           OSTCBCyclesTot;        /* Total number of clock cycles the task has been running  */INT32U           OSTCBCyclesStart;      /* Snapshot of cycle counter at start of task resumption   */OS_STK          *OSTCBStkBase;          /* Pointer to the beginning of the task stack              */INT32U           OSTCBStkUsed;          /* Number of bytes used from the stack                     */
#endif#if OS_TASK_NAME_EN > 0uINT8U           *OSTCBTaskName;
#endif#if OS_TASK_REG_TBL_SIZE > 0uINT32U           OSTCBRegTbl[OS_TASK_REG_TBL_SIZE];
#endif
} OS_TCB;

OSTCBPrio,每个任务有唯一的优先级,用户任务的优先级为0~61,62和63倍统计任务和空闲任务占用。

μc/OS是以结构体数组的形式生成任务控制块的实体。

OS_TCB OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS];

结构体数组OSTCBTbl中元素的个数为最大用户数量+系统任务数。

2.1.2 空闲链表和就绪链表

μc/OS将任务控制块分成两个链表来管理,即空闲任务链表和就绪任务链表。所谓空闲任务控制块,是指未分配给某个任务的任务控制块,创建一个新任务的前提就是系统中有这样的空闲任务块。就绪链表则是将所有的就绪任务栓在一起,如果有新的任务就绪,就要将其任务控制块从空闲链表中取出,加入到就绪链表中。

OS_TCB *OSTCBFreeList;  //空闲链表
OS_TCB *OSTCBList;      //就绪链表

OSTCBTbl中的任务控制块为空闲链表和就绪链表的节点元素,其中的prev、next指针指向其在OSTCBFreeList或OSTCBList中的下一个或者前一个节点。当创建一个任务的时候,就在空闲链表中remove一个元素,然后为其赋值,再将该元素插入就绪链表。将一个TCB从空闲链表插入到就绪链表时,是从OSTCBFreeList的表头取出一个元素,插入OSTCBList的表头。即,OSTCBFreeList永远指向空闲链表的表头,OSTCBList永远指向就绪链表的表头。

2.1.3 任务优先级指针表

即任务优先级指针数组,用来获某优先级的任务的任务控制块地址。

OS_TCB *OSTCBPrioTbl[OS_LOWEST_PRIO + 1]

任务优先级指针数组的元素为指向OS_TCB的指针。可以快速地根据优先级访问对应任务控制块的内容。

2.1.4 任务堆栈

为了满足任务切换或者响应中断时保存CPU寄存器的内容,以及存储任务私有数据的需要,每个任务都有自己的堆栈。

#define TASK_STK_SIZE 512
typedef unsigned int  OS_STK;
OS_STK TaskStk[OS_MAX_TASKS][TASK_STK_SIZE];

TaskStk中的元素类型为OS_STK,在32位PC系统中为32位无符号数。这里定义了最多用户任务数个用户堆栈,每个用户堆栈的大小为TASK_STK_SIZE。统计任务和空闲任务的堆栈是单独定义的,分别是OSTaskStatStkOSTaskIdleStk。它们作为全局变量定义在ucos_ii.h中。任务堆栈可以由用户自己定义。

OS_EXT  OS_STK            OSTaskStatStk[OS_TASK_STAT_STK_SIZE];      /* Statistics task stack          */
OS_EXT  OS_STK            OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE];      /* Idle task stack                */

2.1.5 任务就绪表和就绪组

查找高优先级的任务,与正在运行的任务的优先级进行比较以确定是否进行任务切换时内核在每个时钟中断都需要做的事情。

typedef unsigned char INT8U;
#define OS_RDY_TBL_SIZE ((OS_LOWEST_PRIO)/8 + 1)
INT8U OSRdyGrp;
INT8U OSRdyTbl[OS_RDY_TBL_SIZE];

OSRdyTbl是每个元素都为8位无符号数的数组,数组中的元素个数是OS_RDY_TBL_SIZE。
每个就绪的任务在就绪表中的对应位置为1,反之为0.只要就绪表OSRdyTbl[n]中有一位不为0,那么就绪组OSRdyGrp对应的位,值为1;只有当OSRdyTbl[n]中的所有位都为0,OSRdyGrp的第n位才为0。

OSRdyTbl为一个数组,假设数组元素数量为8(当最低优先级为63时),每个元素都为一个无符号8位整数,这样就可以把OSRdyTbl看做一个二维数组,行数为8,列数为8。每一行对应8个Task,当任务为就绪时,其在OSRdyTbl中对应的位置为1。
OSRdyTbl的每一行对应于OSRdyGrp的一个位。这一行全为1时对应OSRdyGrp的位为0,否则为1。
如何修改OSRdyGrp以及OSRdyTbl?
关键是要知道某一优先级应该对应OSRdyTbl的哪一行,假设位于n行,就将OSRdyGrp的第n位设置为1。
利用一个查找表OSMapTbl:

INT8U const OSMapTbl[]={0x01,0x02,0x03,0x04,0x08,0x10,0x20,0x40,0x80};

对于优先级prio,将其右移3位,得到其在OSMapTbl中的位置,然后用OSRdyGrp与对应位置元素进行异或操作即可。

序号 值(二进制表示)
0 00000001
1 00000010
2 00000100
3 00001000
4 00010000
5 00100000
6 01000000
7 10000000

比如prio为25,25>>3后,即25/8=3,即将OSRdyGrp的第三位设置为1,OSMapTbl[3]为00001000,异或后就完成了对OSRdyGrp的修改。对OSRdyTbl的修改也是相同的思想,主要是找一个映射关系。

OSRdyGrp |= OSMapTbl[prio>>3];
OSRdyTbl[prio>>3] |= OSMapTbl[prio&0x07];

利用分组管理的方式,可以使OS在查找当前最高优先级时花费的时间是固定的。查找就绪任务中的最高优先级:

y=OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy=(INT8U) ((y<<3) + OSUnMapTbl[OSRdyTbl[y]);

原理:
假设OSRdyGrp中最低为1的位的y号位,决定了在就绪表中哪一行。再接下来是找列号。OSUnMapTbl就是为了这个目的设计的表。
直接查询优先级判定表OSUnMapTbl[n]就可以找到n的二进制表示法中最低为1的位的序号。在这里就是得到OSRdyTbl的元素序号(行号),再使用OSUnMapTbl[OSRdyTbl[y]]就是该行中最低的为1的位的号数,也就是就绪表中的列号。然后y<<3(==y*8)+列号就得到最高优先级。

2.2 任务控制块初始化

2.2.1 代码解析

任务控制块初始化函数OS_TCBInit,当任务被创建时用来初始化任务控制块。

INT8U OSTCBInit(INT8U prio,OS_STK *ptos,OS_TSK *pbos,INT16U id,INT32U stk_size,Void *pext,INT16U opt)
  • prio:被创建任务的优先级
  • ptos:任务堆栈栈顶地址
  • pbos:任务堆栈栈底的地址。
  • id:任务的ID
  • stk_size:堆栈的大小
  • pext:任务控制块的扩展块的地址
  • opt:其他选项

2.2.2 流程分析

OS_TCBInit中,首先查询空闲任务链表OSTCBFreeList,如果有空闲任务控制块,则取出空闲任务控制块,对该TCB中的各个字段完成赋值。注意,这里的OSTCBFreeList是一个全局变量,每当访问一次全局数据时都需要关闭系统中断

OS_ENTER_CRITICAL();
···
critical rigion
···
OS_EXIT_CRITICAL();

随后,调用两个钩子函数,这两个钩子函数允许用户自己填写代码,默认为空。随后对任务优先级指针表和就绪组及就绪表进行相关操作。

2.3 操作系统初始化

操作系统初始化函数OSInit是操作系统在开始运行时,对全局变量、任务控制块、就绪表、事件及消息队列等重要数据结构进行的初始化操作。并创建空闲任务、统计任务等系统任务。

2.3.1 代码解析

void  OSInit (void)
{#if OS_TASK_CREATE_EXT_EN > 0u
#if defined(OS_TLS_TBL_SIZE) && (OS_TLS_TBL_SIZE > 0u)INT8U  err;
#endif
#endifOSInitHookBegin();                                           /* Call port specific initialization code   */OS_InitMisc();                                               /* Initialize miscellaneous variables       */OS_InitRdyList();                                            /* Initialize the Ready List                */OS_InitTCBList();                                            /* Initialize the free list of OS_TCBs      */OS_InitEventList();                                          /* Initialize the free list of OS_EVENTs    */#if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)OS_FlagInit();                                               /* Initialize the event flag structures     */
#endif#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)OS_MemInit();                                                /* Initialize the memory manager            */
#endif#if (OS_Q_EN > 0u) && (OS_MAX_QS > 0u)OS_QInit();                                                  /* Initialize the message queue structures  */
#endif#if OS_TASK_CREATE_EXT_EN > 0u
#id defined(OS_TLS_TBL_SIZE) && (OS_TLS_TBL_SIZE > 0u)OS_TLS_Init(&err);                                           /* Initialize TLS, before creating tasks    */if (err != OS_ERR_NONE) {return;}
#endif
#endifOS_InitTaskIdle();                                           /* Create the Idle Task                     */
#if OS_TASK_STAT_EN > 0uOS_InitTaskStat();                                           /* Create the Statistic Task                */
#endif#if OS_TMR_EN > 0uOSTmr_Init();                                                /* Initialize the Timer Manager             */
#endifOSInitHookEnd();                                             /* Call port specific init. code            */#if OS_DEBUG_EN > 0uOSDebugInit();
#endif
}
}

OS_InitMisc实现对一些杂项全局变量的初始化。
OS_InitRdyList将就绪表和就绪组全部清零,然后对四个重要的任务相关的全局变量进行了初始化,这四个全局变量定义在μcos_ii.h中。

static  void  OS_InitRdyList (void)
{INT8U  i;OSRdyGrp      = 0u;                                    /* Clear the ready list                     */for (i = 0u; i < OS_RDY_TBL_SIZE; i++) {OSRdyTbl[i] = 0u;}OSPrioCur     = 0u;OSPrioHighRdy = 0u;OSTCBHighRdy  = (OS_TCB *)0;OSTCBCur      = (OS_TCB *)0;
}

OS_InitTCBListTCB的两个链表——空闲任务链表和就绪任务链表进行初始化,此时系统中没有就绪用户任务,所以就绪链表为空,空闲链表长度为最长。
OS_InitEventList
OS_FlagInit
OS_MemInit
os_QInit
OS_InitTaskIdle将会创建操作系统的第一个任务——空闲任务。这一部分的解释放在任务创建部分作为例子讲解。
OS_InitTaskStat创建一个统计任务。

2.4 任务的创建

2.4.1 OSTaskCreate代码解析

主要是对任务运行需要的数据结构进行赋值。
task指向任务代码,p_arg是一个指针,指向用来给task任务传值的数据结构,ptos指针指向任务栈顶。prio表示任务优先级。

#if OS_TASK_CREATE_EN > 0u
INT8U  OSTaskCreate (void   (*task)(void *p_arg),void    *p_arg,OS_STK  *ptos,INT8U    prio)
{OS_STK     *psp;INT8U       err;
#if OS_CRITICAL_METHOD == 3u                 /* Allocate storage for CPU status register               */OS_CPU_SR   cpu_sr = 0u;
#endif#ifdef OS_SAFETY_CRITICAL_IEC61508if (OSSafetyCriticalStartFlag == OS_TRUE) {OS_SAFETY_CRITICAL_EXCEPTION();return (OS_ERR_ILLEGAL_CREATE_RUN_TIME);}
#endif#if OS_ARG_CHK_EN > 0uif (prio > OS_LOWEST_PRIO) {             /* Make sure priority is within allowable range           */return (OS_ERR_PRIO_INVALID);}
#endifOS_ENTER_CRITICAL();if (OSIntNesting > 0u) {                 /* Make sure we don't create the task from within an ISR  */OS_EXIT_CRITICAL();return (OS_ERR_TASK_CREATE_ISR);}if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn't already exist at this priority  */OSTCBPrioTbl[prio] = OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ...  *//* ... the same thing until task is created.              */OS_EXIT_CRITICAL();psp = OSTaskStkInit(task, p_arg, ptos, 0u);             /* Initialize the task's stack         */err = OS_TCBInit(prio, psp, (OS_STK *)0, 0u, 0u, (void *)0, 0u);if (err == OS_ERR_NONE) {OS_TRACE_TASK_CREATE(OSTCBPrioTbl[prio]);if (OSRunning == OS_TRUE) {      /* Find highest priority task if multitasking has started */OS_Sched();}} else {OS_TRACE_TASK_CREATE_FAILED(OSTCBPrioTbl[prio]);OS_ENTER_CRITICAL();OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to others                 */OS_EXIT_CRITICAL();}return (err);}OS_EXIT_CRITICAL();return (OS_ERR_PRIO_EXIST);
}
#endif

创建任务时,需要给任务分配堆栈空间。堆栈是在操作系统变量声明中已经分配好了的。因此要将该堆栈空间分配给该任务。执行这个任务的是OSTaskSktInit函数,

OS_STK  *OSTaskStkInit (void  (*task)(void  *pd), void  *p_arg, OS_STK  *ptos, INT16U  opt)
{/*task是函数指针,指向任务代码的地址*//*p_arg:任务代码的参数*//*ptos:任务堆栈栈顶指针*//*opt:初始化选项*/OS_TASK_STK  *p_stk;/* Load stack pointer */p_stk                =  (OS_TASK_STK *)((char *)ptos - sizeof(OS_TASK_STK));p_stk->TaskArgPtr    =  p_arg;p_stk->TaskOpt       =  opt;p_stk->Task          =  task;p_stk->ThreadHandle  =  NULL;p_stk->ThreadID      =  0u;p_stk->TaskState     =  STATE_NONE;p_stk->SignalPtr     =  NULL;p_stk->InitSignalPtr =  NULL;p_stk->Terminate     =  DEF_FALSE;p_stk->OSTCBPtr      =  NULL;return ((OS_STK *)p_stk);
}

这里是用一个OS_TASK_STK结构体来保存任务堆栈的信息

typedef  struct  os_task_stk {void                      *TaskArgPtr;INT16U                     TaskOpt;void                       (*Task)(void*);HANDLE                     ThreadHandle;DWORD                      ThreadID;volatile  OS_TASK_STATE    TaskState;HANDLE                     SignalPtr; /* Task synchronization signal.*/HANDLE                     InitSignalPtr; /* Task created signal.*/CPU_BOOLEAN                Terminate; /* Task terminate flag.*/OS_TCB                    *OSTCBPtr;
} OS_TASK_STK;

在任务堆栈初始化的过程中,任务代码的地址被压入任务堆栈。
在任务堆栈初始化完成以后,OS_TCBInit对任务的TCB进行初始化。
OS_TCBInit函数在前面已经有了分析,主要就是判断是否有空闲TCB,如果有的话就从TCBFreeList中取出一个TCB然后对其中的各个字段进行初始化。在任务创建过程中已经初始化了任务堆栈,所以任务TCB中将会记录任务堆栈的位置等信息。

ptcb->OSTCBStkSize       = stk_size;               /* Store stack size                         */
ptcb->OSTCBStkBottom     = pbos;                   /* Store pointer to bottom of stack         */

2.4.2 OSTaskCreate流程分析

2.4.3 OSTaskCreateExt代码解析

2.5 任务的删除

2.5.1 任务删除代码解析

2.5.2 任务删除流程分析

任务删除代码较为复杂,主要是需要将任务涉及到的方方面面都要进行处理。但总结来说,无外乎先进行参数的检查,然后如果该任务在等待某些事件,就删除等待的标志,之后对TCB中的值进行修改,对就绪表、就绪组、任务优先级指针表、任务就绪链表、空闲任务链表等重要的数据结构进行与创建任务相反的操作。

2.5.3 请求删除任务代码解析

2.6 任务挂起和恢复

任务在创建后,将从睡眠态转移到就绪态,就绪的任务如果调用OSTaskSuspend将会被阻塞,也就是被剥夺CPU使用权,转到阻塞状态。被阻塞的任务不能运行,除非其他任务以该任务的优先级作为参数调用OSTaskResume来恢复它,才能将该任务的状态重新设置为就绪态。

2.7 任务的调度和多任务的启动

2.7.1 任务调度器

在启动多任务以后,每个时钟中断都要执行任务的调度。执行任务调度的函数就是OSTimeTick

void  OSTimeTick (void)
{OS_TCB    *ptcb;BOOLEAN    step;OSTimeTickHook();                                      /*调用用户钩子函数,默认是空函数                     */#if OS_TIME_GET_SET_EN > 0uOS_ENTER_CRITICAL();                                   /* Update the 32-bit tick counter               */OSTime++;OS_EXIT_CRITICAL();
#endifif (OSRunning == OS_TRUE) {#if OS_TICK_STEP_EN > 0uswitch (OSTickStepState) {                         /* Determine whether we need to process a tick  */case OS_TICK_STEP_DIS:                         /* Yes, stepping is disabled                    */step = OS_TRUE;break;case OS_TICK_STEP_WAIT:                        /* No,  waiting for uC/OS-View to set ...       */step = OS_FALSE;                          /*      .. OSTickStepState to OS_TICK_STEP_ONCE */break;case OS_TICK_STEP_ONCE:                        /* Yes, process tick once and wait for next ... */step            = OS_TRUE;                /*      ... step command from uC/OS-View        */OSTickStepState = OS_TICK_STEP_WAIT;break;default:                                       /* Invalid case, correct situation              */step            = OS_TRUE;OSTickStepState = OS_TICK_STEP_DIS;break;}if (step == OS_FALSE) {                            /* Return if waiting for step command           */return;}
#endifptcb = OSTCBList;                                  /* Point at first TCB in TCB list               */while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO) {     /* Go through all TCBs in TCB list              */OS_ENTER_CRITICAL();if (ptcb->OSTCBDly != 0u) {                    /* No, Delayed or waiting for event with TO     */ptcb->OSTCBDly--;                          /* Decrement nbr of ticks to end of delay       */if (ptcb->OSTCBDly == 0u) {                /* Check for timeout                            */if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {ptcb->OSTCBStat  &= (INT8U)~(INT8U)OS_STAT_PEND_ANY;          /* Yes, Clear status flag   */ptcb->OSTCBStatPend = OS_STAT_PEND_TO;                 /* Indicate PEND timeout    */} else {ptcb->OSTCBStatPend = OS_STAT_PEND_OK;}if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {  /* Is task suspended?       */OSRdyGrp               |= ptcb->OSTCBBitY;             /* No,  Make ready          */OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;}}}ptcb = ptcb->OSTCBNext;                        /* Point at next TCB in TCB list                */OS_EXIT_CRITICAL();}}
}

ptcb指向就绪链表表头。就绪任务链表的最后一个元素是空闲任务,当ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO时,说明就绪队列中有其他就绪任务。
OSTimeTick函数做的事就是遍历就绪队列,对延时的任务修改延时时间,然后将延时时间用完的任务设置为就绪。并没有真正执行任务切换。OSTimeTick在每个时钟中断时被调用。

2.7.2 任务切换函数

任务切换函数最重要的两个步骤是:

  1. 保存当前进程的运行上下文
  2. 加载下一个进程的运行上下文

2.7.3 中断中的任务调度

【μc/OS内核分析】 任务管理相关推荐

  1. 鸿蒙OS内核分析|解读鸿蒙源码

    操作系统(Operating System): 操作系统的功能是负责管理各种硬件设备,同时对底层硬件设备进行抽象,为上层软件提供高效的系统接口.操作系统设计和实现的优劣直接决定了系统栈的各个方面,比如 ...

  2. v05.05 鸿蒙内核源码分析(任务管理) | 如何管理任务池 | 百篇博客分析HarmonyOS源码

    曾子曰:"吾日三省吾身:为人谋而不忠乎?与朋友交而不信乎?传不习乎?"<论语>:学而篇 百篇博客系列篇.本篇为: v05.xx 鸿蒙内核源码分析(任务管理篇) | 如何 ...

  3. Zephyr OS 内核篇: 内核链表

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/tidyjiang/article/details/52750485 Zephyr OS 所有的学习笔 ...

  4. Solaris、Mac OS系统日志分析工具

    Solaris.Mac OS系统日志分析工具 本节以PC服务器上常见的几种UNIX系统例如Solaris.Mac OS以及Sco Openserver系统为例如何在它们这些平台下查找系统日志. 一.用 ...

  5. kali2020.3 vm版本内核是多少_Zircon Fuchsia 内核分析 启动(内核初始化)

    相关阅读: Zircon - Fuchsia 内核分析 - 启动(平台初始化) 简介 前面已经介绍了 Zircon 内核启动的汇编代码部分,主要是一些 CPU 的初始化. 现在 prime CPU 已 ...

  6. PG内核分析 Question and Answer

    PG内核分析 Question and Answer PG系统概述 为什么说PG是一种先进的对象-关系数据库系统 因为PG它不仅支持关系数据库的各种功能, 而且还具备类, 继承等对象数据库的特征. 面 ...

  7. Contiki的内核分析-定时器模型

    导读 本文通过分析Contiki的源码,梳理Contiki的定时器模型中一共5个定时器的工作机制和原理. 引入 从本文开始,我们开始探究Contiki的5个定时器模型,遵循从易到难的原则,我们先开始两 ...

  8. T2080 U-BOOT与OS内核移植 准备篇(一)——开发调试环境简介

    T2080 U-BOOT与OS内核移植 准备篇(一)--开发调试环境简介 1.本项目选择的工具 2.几个常用工具简介 2.1 Trace32 2.2 Code Warrior 2.3 QorIQ Li ...

  9. Linux内核实验孟宁,《linux内核分析》实验二:时间片轮转多道程序运行原理

    一.概述 本文通过分析一个简单的时间片轮转多道程序的内核 mykernel,来理解操作系统是如何工作的. mykernel 是孟宁老师的一个开源项目,借助 Linux 内核部分源代码模拟存储程序计算机 ...

最新文章

  1. 剑走偏锋——用css制作一个三角形箭头
  2. JavaScript 中 window.setTimeout() 的详细用法
  3. 如何解决项目中.a文件中的.o冲突
  4. 网络参考模型(Network Reference Model)
  5. 域控dns无法解析域控_域注册商,DNS和托管
  6. windows下安装subversion
  7. 在Linux服务器上配置phpMyAdmin
  8. linux下的常用时间函数总结
  9. 20169212《Linux内核原理及分析》第十二周作业
  10. [C# 线程处理系列]专题四:线程同步
  11. 基于编辑方法的文本生成(上)
  12. 在mysql上发布jbpm4.3
  13. 相关系数的比较与假设检验
  14. 现金流贴现法估值模型
  15. sat数学可以用计算机吗,SAT数学计算器怎么使用?
  16. 红蜻蜓截图工具在win7下保存截图为png失败
  17. 树莓派安装共享打印机HP LaserJet CP1025(foo2zjs)
  18. 2022年虚拟电厂行业研究报
  19. tpch测试mysql_数据库系统TPC-H测试方法及结果分析
  20. 软件测试工程师必备技能——Linux基础知识

热门文章

  1. 实力碾压!荣耀笔记本MagicBook Pro对比新鲜出炉
  2. Python 截取字符函数
  3. 决战云时代--“微服务”连接企业级应用(SaaS)与云计算平台之间最后一公里
  4. java字符串替换括号(中文和英文括号),通过正则表达式替换,如果直接替换中文可以替换,但是英文替换会报错
  5. java中函数和方法区别_java中函数和方法的区别是什么?
  6. 一文带你了解数据库隔离级别和锁之间的关系
  7. 后门发现(yuange)
  8. 关于数据库题:设有一个记录各个球队队员每场比赛进球数的关系模式 R(队员编号,比赛场次,进球数,球队名,队长名)如果规定每个队员只能属于一个球队,每个球队只有一个队长。问题详解
  9. SViva数字化口腔医疗解决方案
  10. 195家十亿美元初创公司,65个数据维度,100张不应错过的图表