Chapter 4 Task Management 1 Objectives To know
Chapter 4: Task Management 1
Objectives To know (and trace the codes of) the services: – – – – – Task prototype Create a task Task stacks Stack checking Delete a task Change a task’s priority Suspend a task Resume a task Query task information 2
Tasks • A task could be periodic or aperiodic. void Your. Task (void *pdata) { for (; ; ) { /* USER CODE */ Call one of u. C/OS-II’s services: OSMbox. Pend(); OSQPend(); OSSem. Pend(); OSTask. Del(OS_PRIO_SELF); OSTask. Suspend(OS_PRIO_SELF); OSTime. Dly(); OSTime. Dly. HMSM(); /* USER CODE */ } } void Your. Task (void *pdata) { /* USER CODE */ OSTask. Del(OS_PRIO_SELF); } A job is created on demand. A task consists of periodically invoked jobs 3
Creating a Task • You must create at least one task before multitasking is started. – Calling OSInit() and OSStat. Init() will implicitly create 2 tasks (it must be done before OSStart()). • An ISR can not create a task. – ? ? ? • Related data structures are created according to the given parameters. – A Task Control Block (TCB). – The stack of the created task. – The priority table. • After a new task is created, the scheduler is called if multitasking is enabled. 4
OSTask. Create() • task: a pointer-to-function points to the entry point of a task (note the syntax). • pdata: a parameter passed to the task. • ptos: a pointer points to the top-of-stack. • prio: task priority. • id: task id, for future extension. • pbos: a pointer points to the bottom-of-stack. • stk_size: the stack size in the number of elements (OS_STK bytes each) 1 word under x 86 • pext: an user-defined extension to the TCB. • opt: the options specified to create the task. 5
OSTask. Create() • Check that the task priority is valid • Set up the task stack OSTask. Stk. Init() • Obtain and initialize OS_TCB from TCB pool OSTCBInit() • Call OSTask. Create. Hook() to extend the functionality • Call OSSched() If the task is created with multitasking started 6
OSTask. Create() INT 8 U OSTask. Create (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT 8 U prio) { OS_STK *psp; INT 8 U err; OS_ENTER_CRITICAL(); if (OSTCBPrio. Tbl[prio] == (OS_TCB *)0) { OSTCBPrio. Tbl[prio] = (OS_TCB *)1; Occupying a priority table slot and re-enable interrupts immediately. OS_EXIT_CRITICAL(); psp = (OS_STK *)OSTask. Stk. Init(task, pdata, ptos, 0); Stack initialization is a hardware-dependant implementation. (Because of the growing direction. . . ) 7
OSTask. Create() err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0); if (err == OS_NO_ERR) { OS_ENTER_CRITICAL(); Create a corresponding TCB OSTask. Ctr++; and connect it with the OS_EXIT_CRITICAL(); priority table. if (OSRunning == TRUE) { OS_Sched(); If the task is created with } multitasking started, the } else { scheduler is called. OS_ENTER_CRITICAL(); OSTCBPrio. Tbl[prio] = (OS_TCB *)0; /*? ? */ OS_EXIT_CRITICAL(); } return (err); } OS_EXIT_CRITICAL(); return (OS_PRIO_EXIST); } 8
OSTask. Create. Ext() • task: a pointer-to-function points to the entry point of a task (note the syntax). • pdata: a parameter passed to the task. • ptos: a pointer points to the top-of-stack. • prio: task priority. • id: task id, for future extension. • pbos: a pointer points to the bottom-of-stack. • stk_size: the stack size in the number of elements (OS_STK bytes each) 1 word under x 86 • pext: an user-defined extension to the TCB. • opt: the options specified to create the task. 10
OSTask. Create. Ext() • More flexibility, at the expense of overhead • The first four arguments are the same as OSTask. Create() – id, assign a unique identifier for the task • • pbos, a pointer that can perform stack checking stk_size, specifies the size of the stack pext, a pointer to extend the OS_TCB opt, specifies whether stack checking is performed 11
OSTask. Create. Ext() The stack is required to be cleared The stack grows toward low address, so the starting address is bos. The stack grows toward high address, so the starting address is tos. 12
OSTask. Create. Ext() 13
Task Stacks • Stack grows either toward high memory address or low memory space under different processors. • Operations over stacks might not be bytewise. – The element size might not be 1 byte (16 bits under x 86). – Defined by a macro OS_STK. 14
Task Stacks OS_STK Task. Stack[TASK_STACK_SIZE]; #if OS_STK_GROWTH == 0 OSTask. Create(task, pdata, &Task. Stack[0], prio); #else OSTask. Create(task, pdata, &Task. Stack[TASK_STACK_SIZE-1], prio); #endif • OS_STK_GROWTH == 0 Stacks grow toward high addresses. • OS_STK_GROWTH == 1 Stacks grow toward low addresses. 15
Task Stacks • The stack must be a contiguous memory space. • Stack space can be statically declared variables or dynamically allocated space (malloc()? ? ? ). 16
Task Stacks • Fragmentation might cause memory allocations (for stacks) failed. 17
Stack Checking • Stack checking intends to determine the maximum run-time usage of stacks. • Computes the amount of free stack space by “walking” from the bottom of the stack until a nonzero value is found 18
To do stack checking • Set OS_TASK_CREATE_EXT to 1 in OS_CFG. h • Create your tasks by using OSTask. Create. Ext() with options OS_TASK_OPT_STK_CHK + OS_TASK_OPT_SRK_CLR and give the tasks reasonably large stacks. • Call OSTask. Stk. Chk() to determine the stack usage of a certain task. • Reduce the stack size if possible, once you think you had run enough simulations. 19
Stack Checking 20
For either stack growing direction… Counting from BOS until a non-zero element is encountered. 21
Deleting a Task • Deleting a task means that the data structures (e. g. , TCB) corresponding to the task-to-delete would be removed from main-memory. – The code for the task will not be deleted – Return to the “DORMANT” state • Deleting a task is slightly more complicated than creating it since every resources/objects held by the task must be returned to the operating system. OSTask. Del. Req() 22
Procedures • Argument checking – Prevent from deleting an idle task – Prevent from deleting a task from within an ISR – Verify that the task to be deleted does exist • Remove the task from ready list or other lists • Add a preemption point – Set. OSTCBStat/. OSTCBDly to OS_STAT_RDY/0 – OS_ENTER_CRITICAL(); OS_EXIT_CRITICAL(); • Call OSTask. Del. Hook() • Remove the OS_TCB from the priority table • Call OSSched() 23
INT 8 U OSTask. Del (INT 8 U prio) { OS_EVENT *pevent; OS_FLAG_NODE *pnode; OS_TCB *ptcb; BOOLEAN self; We do not allow to delete a task within ISR’s, because the ISR might currently interrupts that task. if (OSInt. Nesting > 0) { return (OS_TASK_DEL_ISR); } OS_ENTER_CRITICAL(); if (prio == OS_PRIO_SELF) { prio = OSTCBCur->OSTCBPrio; } Clear the corresponding bit of the task-toptcb = OSTCBPrio. Tbl[prio]; delete in the ready list. if (ptcb != (OS_TCB *)0) { if ((OSRdy. Tbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBit. X) == 0 x 00) { OSRdy. Grp &= ~ptcb->OSTCBBit. Y; If the row are all 0’s, then clear the } Rdy. Grp bit also. pevent = ptcb->OSTCBEvent. Ptr; if (pevent != (OS_EVENT *)0) { if ((pevent->OSEvent. Tbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBit. X) == 0) { pevent->OSEvent. Grp &= ~ptcb->OSTCBBit. Y; } Remove the task from the event } control block since we no longer pnode = ptcb->OSTCBFlag. Node; wait for the event. if (pnode != (OS_FLAG_NODE *)0) { OS_Flag. Unlink(pnode); } 24
Prevent tick. ISR from making this task ready when interrupts are re-enabled later. ptcb->OSTCBDly = 0; ptcb->OSTCBStat = OS_STAT_RDY; Prevent this task from being resumed since we if (OSLock. Nesting < 255) { are not in the ready list now (a “ready task can OSLock. Nesting++; not be resumed). } (clear the OS_STATE_SUSPEND bit) OS_EXIT_CRITICAL(); OS_Dummy(); OS_ENTER_CRITICAL(); if (OSLock. Nesting > 0) { Interrupts are re-enabled for a while (note that OSLock. Nesting--; the scheduler is locked). *What does OS_dummy() do? } OSTask. Del. Hook(ptcb); OSTask. Ctr--; OSTCBPrio. Tbl[prio] = (OS_TCB *)0; if (ptcb->OSTCBPrev == (OS_TCB *)0) { ptcb->OSTCBNext->OSTCBPrev = (OS_TCB *)0; OSTCBList = ptcb->OSTCBNext; } else { ptcb->OSTCBPrev->OSTCBNext = ptcb->OSTCBNext; ptcb->OSTCBNext->OSTCBPrev = ptcb->OSTCBPrev; } ptcb->OSTCBNext = OSTCBFree. List; OSTCBFree. List = ptcb; OS_EXIT_CRITICAL(); Remove the task from OS_Sched(); the double linked list return (OS_NO_ERR); Lock the scheduler. } OS_EXIT_CRITICAL(); return (OS_TASK_DEL_ERR); } 25
OSTask. Del. Req() Req • Tell the task that owns memory buffers or semaphore to delete itself • Procedures – If the task’s priority is OS_PRIO_SELF Return the flag of OSTCBDel. Req – Otherwise Set OSTCBDel. Req of the task to OS_TASK_DEL_REQ 26
Example void Requestor. Task(void *pdata) { for (; ; ) { /*application code*/ if (the task “Task. To. Be. Deleted()” needs to be deleted) { while(OSTask. Del. Req(TASK_TO_DEL_PRIO) != OS_TASK_NOT_EXIT) OSTime. Dly(1); } } void Task. To. Be. Deleted(void *pdata) { while(1) { /*application code*/ if (OSTask. Del. Req(OS_PRIO_SELF) == OS_TASK_DEL_REQ) { /*release any owned resources; de-allocated any dynamic memory; */ OSTask. Del(OS_PRIO_SELF); } else { /*application code)*/ } } } Del. Req = 0 TCB (Req. Task) Del. Req = 1 TCB (Task. Del) 27
28
Changing a Task’s Priority • When you create a new task, you assign the task a priority • At run time, you can change this priority dynamically by calling OSTask. Change. Prio • Cannot change the priority of the idle task • INT 8 U OSTask. Change. Prio(INT 8 U old. Prio, INT 8 U new. Prio) – INT 8 U OSTask. Change. Prio(INT 8 U Task. Id, INT 8 U new. Prio) 29
Procedures • Reserve the new priority by OSTCBPrio. Tbl[newprio] = (OS_TCB *) 1; • Remove the task from the priority table • Insert the task into new location of the priority table • Change the OS_TCB of the task • Call OSSched() 30
in ready queue the task is ready to run 31
in waiting queue 32
Suspending a Task • A suspended task can only resumed by calling the OSTask. Resume() function call • If a task being suspended is also waiting for time to expire, the suspension needs to be removed and the time needs to expire in order for the task ready to run. • INT 8 U OSTask. Suspend(INT 8 U prio) • INT 8 U OSTask. Resume(INT 8 U prio) 33
Procedure – OSTask. Suspend() • • Check the input priority Remove the task from the ready list Set the OS_STAT_SUSPEND flag in OS_TCB Call OSSched() 34
INT 8 U OSTask. Suspend (INT 8 U prio) { BOOLEAN self; OS_TCB *ptcb; OS_ENTER_CRITICAL(); /* See if suspending self*/ if (prio == OS_PRIO_SELF) { prio = OSTCBCur->OSTCBPrio; self = TRUE; } else if (prio == OSTCBCur->OSTCBPrio) { self = TRUE; } else { self = FALSE; } ptcb = OSTCBPrio. Tbl[prio]; /* Task to suspend must exist*/ if (ptcb == (OS_TCB *)0) { OS_EXIT_CRITICAL(); return (OS_TASK_SUSPEND_PRIO); } Call OS_Sched()? 35
/* Make task not ready*/ if ((OSRdy. Tbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBit. X) == 0 x 00) { OSRdy. Grp &= ~ptcb->OSTCBBit. Y; } /* Status of task is 'SUSPENDED'*/ ptcb->OSTCBStat |= OS_STAT_SUSPEND; OS_EXIT_CRITICAL(); /* Context switch only if SELF*/ if (self == TRUE) { OS_Sched(); } return (OS_NO_ERR); } 36
Procedure – OSTask. Resume() • Check the input priority • Clear the OS_STAT_SUSPEND bit in the OSTCBStat field • Set OSTCBDly to 0 • Call OSSched() 37
Resuming a Task INT 8 U OSTask. Resume (INT 8 U prio) { OS_TCB *ptcb; OS_ENTER_CRITICAL(); ptcb = OSTCBPrio. Tbl[prio]; /* Task to suspend must exist*/ if (ptcb == (OS_TCB *)0) { OS_EXIT_CRITICAL(); return (OS_TASK_RESUME_PRIO); } 38
/* Task must be suspended */ if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) != 0 x 00) { /* Remove suspension */ if (((ptcb->OSTCBStat &= ~OS_STAT_SUSPEND) == OS_STAT_RDY) && /* Must not be delayed */ (ptcb->OSTCBDly == 0)) { /* Make task ready to run */ OSRdy. Grp |= ptcb->OSTCBBit. Y; OSRdy. Tbl[ptcb->OSTCBY] |= ptcb->OSTCBBit. X; OS_EXIT_CRITICAL(); OS_Sched(); } else { OS_EXIT_CRITICAL(); } return (OS_NO_ERR); } OS_EXIT_CRITICAL(); return (OS_TASK_NOT_SUSPENDED); } 39
Getting Information About a Task • OSTask. Query return a copy of the contents of the desired task’s OS_TCB • To call OSTask. Query, your application must allocate storage for an OS_TCB • Only you this function to SEE what a task is doing – don’t modify the contains (OSTCBNext, OSTCBPrev) 40
INT 8 U OSTask. Query (INT 8 U prio, OS_TCB *pdata) { OS_TCB *ptcb; OS_ENTER_CRITICAL(); if (prio == OS_PRIO_SELF) { prio = OSTCBCur->OSTCBPrio; } ptcb = OSTCBPrio. Tbl[prio]; /* Task to query must exist*/ if (ptcb == (OS_TCB *)0) { OS_EXIT_CRITICAL(); return (OS_PRIO_ERR); } /* Copy TCB into user storage area*/ memcpy(pdata, ptcb, sizeof(OS_TCB)); OS_EXIT_CRITICAL(); return (OS_NO_ERR); } 41
- Slides: 41