proc.c
Go to the documentation of this file.
00001 00088 #include "proc_p.h" 00089 #include "proc.h" 00090 00091 #include "cfg/cfg_proc.h" 00092 #define LOG_LEVEL KERN_LOG_LEVEL 00093 #define LOG_FORMAT KERN_LOG_FORMAT 00094 #include <cfg/log.h> 00095 00096 #include "cfg/cfg_monitor.h" 00097 #include <cfg/macros.h> // ROUND_UP2 00098 #include <cfg/module.h> 00099 #include <cfg/depend.h> // CONFIG_DEPEND() 00100 00101 #include <cpu/irq.h> 00102 #include <cpu/types.h> 00103 #include <cpu/attr.h> 00104 #include <cpu/frame.h> 00105 00106 #if CONFIG_KERN_HEAP 00107 #include <struct/heap.h> 00108 #endif 00109 00110 #include <string.h> /* memset() */ 00111 00112 #define PROC_SIZE_WORDS (ROUND_UP2(sizeof(Process), sizeof(cpu_stack_t)) / sizeof(cpu_stack_t)) 00113 00114 /* 00115 * The scheduer tracks ready processes by enqueuing them in the 00116 * ready list. 00117 * 00118 * \note Access to the list must occur while interrupts are disabled. 00119 */ 00120 REGISTER List proc_ready_list; 00121 00122 /* 00123 * Holds a pointer to the TCB of the currently running process. 00124 * 00125 * \note User applications should use proc_current() to retrieve this value. 00126 */ 00127 REGISTER Process *current_process; 00128 00130 static struct Process main_process; 00131 00132 #if CONFIG_KERN_HEAP 00133 00137 static HEAP_DEFINE_BUF(heap_buf, CONFIG_KERN_HEAP_SIZE); 00138 static Heap proc_heap; 00139 00140 /* 00141 * Keep track of zombie processes (processes that are exiting and need to 00142 * release some resources). 00143 * 00144 * \note Access to the list must occur while kernel preemption is disabled. 00145 */ 00146 static List zombie_list; 00147 00148 #endif /* CONFIG_KERN_HEAP */ 00149 00150 /* 00151 * Check if the process context switch can be performed directly by the 00152 * architecture-dependent asm_switch_context() or if it must be delayed 00153 * because we're in the middle of an ISR. 00154 * 00155 * Return true if asm_switch_context() can be executed, false 00156 * otherwise. 00157 * 00158 * NOTE: if an architecture does not implement IRQ_RUNNING() this function 00159 * always returns true. 00160 */ 00161 #define CONTEXT_SWITCH_FROM_ISR() (!IRQ_RUNNING()) 00162 00163 /* 00164 * Save context of old process and switch to new process. 00165 */ 00166 static void proc_context_switch(Process *next, Process *prev) 00167 { 00168 cpu_stack_t *dummy; 00169 00170 if (UNLIKELY(next == prev)) 00171 return; 00172 /* 00173 * If there is no old process, we save the old stack pointer into a 00174 * dummy variable that we ignore. In fact, this happens only when the 00175 * old process has just exited. 00176 */ 00177 asm_switch_context(&next->stack, prev ? &prev->stack : &dummy); 00178 } 00179 00180 static void proc_initStruct(Process *proc) 00181 { 00182 /* Avoid warning for unused argument. */ 00183 (void)proc; 00184 00185 #if CONFIG_KERN_SIGNALS 00186 proc->sig.recv = 0; 00187 proc->sig.wait = 0; 00188 #endif 00189 00190 #if CONFIG_KERN_HEAP 00191 proc->flags = 0; 00192 #endif 00193 00194 #if CONFIG_KERN_PRI 00195 proc->link.pri = 0; 00196 00197 # if CONFIG_KERN_PRI_INHERIT 00198 proc->orig_pri = proc->inh_link.pri = proc->link.pri; 00199 proc->inh_blocked_by = NULL; 00200 LIST_INIT(&proc->inh_list); 00201 # endif 00202 #endif 00203 } 00204 00205 MOD_DEFINE(proc); 00206 00207 void proc_init(void) 00208 { 00209 LIST_INIT(&proc_ready_list); 00210 00211 #if CONFIG_KERN_HEAP 00212 LIST_INIT(&zombie_list); 00213 heap_init(&proc_heap, heap_buf, sizeof(heap_buf)); 00214 #endif 00215 /* 00216 * We "promote" the current context into a real process. The only thing we have 00217 * to do is create a PCB and make it current. We don't need to setup the stack 00218 * pointer because it will be written the first time we switch to another process. 00219 */ 00220 proc_initStruct(&main_process); 00221 current_process = &main_process; 00222 00223 #if CONFIG_KERN_MONITOR 00224 monitor_init(); 00225 monitor_add(current_process, "main"); 00226 #endif 00227 MOD_INIT(proc); 00228 } 00229 00230 00231 #if CONFIG_KERN_HEAP 00232 00237 static void proc_freeZombies(void) 00238 { 00239 Process *proc; 00240 00241 while (1) 00242 { 00243 PROC_ATOMIC(proc = (Process *)list_remHead(&zombie_list)); 00244 if (proc == NULL) 00245 return; 00246 00247 if (proc->flags & PF_FREESTACK) 00248 { 00249 PROC_ATOMIC(heap_freemem(&proc_heap, proc->stack_base, 00250 proc->stack_size + PROC_SIZE_WORDS * sizeof(cpu_stack_t))); 00251 } 00252 } 00253 } 00254 00258 static void proc_addZombie(Process *proc) 00259 { 00260 Node *node; 00261 #if CONFIG_KERN_PREEMPT 00262 ASSERT(!proc_preemptAllowed()); 00263 #endif 00264 00265 #if CONFIG_KERN_PRI 00266 node = &(proc)->link.link; 00267 #else 00268 node = &(proc)->link; 00269 #endif 00270 LIST_ASSERT_VALID(&zombie_list); 00271 ADDTAIL(&zombie_list, node); 00272 } 00273 00274 #endif /* CONFIG_KERN_HEAP */ 00275 00290 struct Process *proc_new_with_name(UNUSED_ARG(const char *, name), void (*entry)(void), iptr_t data, size_t stack_size, cpu_stack_t *stack_base) 00291 { 00292 Process *proc; 00293 LOG_INFO("name=%s", name); 00294 #if CONFIG_KERN_HEAP 00295 bool free_stack = false; 00296 00297 /* 00298 * Free up resources of a zombie process. 00299 * 00300 * We're implementing a kind of lazy garbage collector here for 00301 * efficiency reasons: we can avoid to introduce overhead into another 00302 * kernel task dedicated to free up resources (e.g., idle) and we're 00303 * not introducing any overhead into the scheduler after a context 00304 * switch (that would be *very* bad, because the scheduler runs with 00305 * IRQ disabled). 00306 * 00307 * In this way we are able to release the memory of the zombie tasks 00308 * without disabling IRQs and without introducing any significant 00309 * overhead in any other kernel task. 00310 */ 00311 proc_freeZombies(); 00312 00313 /* Did the caller provide a stack for us? */ 00314 if (!stack_base) 00315 { 00316 /* Did the caller specify the desired stack size? */ 00317 if (!stack_size) 00318 stack_size = KERN_MINSTACKSIZE; 00319 00320 /* Allocate stack dinamically */ 00321 PROC_ATOMIC(stack_base = 00322 (cpu_stack_t *)heap_allocmem(&proc_heap, stack_size)); 00323 if (stack_base == NULL) 00324 return NULL; 00325 00326 free_stack = true; 00327 } 00328 00329 #else // CONFIG_KERN_HEAP 00330 00331 /* Stack must have been provided by the user */ 00332 ASSERT2(IS_VALID_PTR(stack_base), "Invalid stack pointer. Did you forget to \ 00333 enable CONFIG_KERN_HEAP?"); 00334 ASSERT2(stack_size, "Stack size cannot be 0."); 00335 00336 #endif // CONFIG_KERN_HEAP 00337 00338 #if CONFIG_KERN_MONITOR 00339 /* 00340 * Fill-in the stack with a special marker to help debugging. 00341 * On 64bit platforms, CONFIG_KERN_STACKFILLCODE is larger 00342 * than an int, so the (int) cast is required to silence the 00343 * warning for truncating its size. 00344 */ 00345 memset(stack_base, (int)CONFIG_KERN_STACKFILLCODE, stack_size); 00346 #endif 00347 00348 /* Initialize the process control block */ 00349 if (CPU_STACK_GROWS_UPWARD) 00350 { 00351 proc = (Process *)stack_base; 00352 proc->stack = stack_base + PROC_SIZE_WORDS; 00353 // On some architecture stack should be aligned, so we do it. 00354 proc->stack = (cpu_stack_t *)((uintptr_t)proc->stack + (sizeof(cpu_aligned_stack_t) - ((uintptr_t)proc->stack % sizeof(cpu_aligned_stack_t)))); 00355 if (CPU_SP_ON_EMPTY_SLOT) 00356 proc->stack++; 00357 } 00358 else 00359 { 00360 proc = (Process *)(stack_base + stack_size / sizeof(cpu_stack_t) - PROC_SIZE_WORDS); 00361 // On some architecture stack should be aligned, so we do it. 00362 proc->stack = (cpu_stack_t *)((uintptr_t)proc - ((uintptr_t)proc % sizeof(cpu_aligned_stack_t))); 00363 if (CPU_SP_ON_EMPTY_SLOT) 00364 proc->stack--; 00365 } 00366 /* Ensure stack is aligned */ 00367 ASSERT((uintptr_t)proc->stack % sizeof(cpu_aligned_stack_t) == 0); 00368 00369 stack_size -= PROC_SIZE_WORDS * sizeof(cpu_stack_t); 00370 proc_initStruct(proc); 00371 proc->user_data = data; 00372 00373 #if CONFIG_KERN_HEAP | CONFIG_KERN_MONITOR 00374 proc->stack_base = stack_base; 00375 proc->stack_size = stack_size; 00376 #if CONFIG_KERN_HEAP 00377 if (free_stack) 00378 proc->flags |= PF_FREESTACK; 00379 #endif 00380 #endif 00381 proc->user_entry = entry; 00382 CPU_CREATE_NEW_STACK(proc->stack); 00383 00384 #if CONFIG_KERN_MONITOR 00385 monitor_add(proc, name); 00386 #endif 00387 00388 /* Add to ready list */ 00389 ATOMIC(SCHED_ENQUEUE(proc)); 00390 00391 return proc; 00392 } 00393 00399 const char *proc_name(struct Process *proc) 00400 { 00401 #if CONFIG_KERN_MONITOR 00402 return proc ? proc->monitor.name : "<NULL>"; 00403 #else 00404 (void)proc; 00405 return "---"; 00406 #endif 00407 } 00408 00410 const char *proc_currentName(void) 00411 { 00412 return proc_name(proc_current()); 00413 } 00414 00416 void proc_rename(struct Process *proc, const char *name) 00417 { 00418 #if CONFIG_KERN_MONITOR 00419 monitor_rename(proc, name); 00420 #else 00421 (void)proc; (void)name; 00422 #endif 00423 } 00424 00425 00426 #if CONFIG_KERN_PRI 00427 00446 void proc_setPri(struct Process *proc, int pri) 00447 { 00448 #if CONFIG_KERN_PRI_INHERIT 00449 int new_pri; 00450 00451 /* 00452 * Whatever it will happen below, this is the new 00453 * original priority of the process, i.e., the priority 00454 * it has without taking inheritance under account. 00455 */ 00456 proc->orig_pri = pri; 00457 00458 /* If not changing anything we can just leave */ 00459 if ((new_pri = __prio_proc(proc)) == proc->link.pri) 00460 return; 00461 00462 /* 00463 * Actual process priority is the highest among its 00464 * own priority and the one of the top-priority 00465 * process that it is blocking (returned by 00466 * __prio_proc()). 00467 */ 00468 proc->link.pri = new_pri; 00469 #else 00470 if (proc->link.pri == pri) 00471 return; 00472 00473 proc->link.pri = pri; 00474 #endif // CONFIG_KERN_PRI_INHERIT 00475 00476 if (proc != current_process) 00477 ATOMIC(sched_reenqueue(proc)); 00478 } 00479 #endif // CONFIG_KERN_PRI 00480 00481 INLINE void proc_run(void) 00482 { 00483 void (*entry)(void) = current_process->user_entry; 00484 00485 LOG_INFO("New process starting at %p", entry); 00486 entry(); 00487 } 00488 00492 void proc_entry(void) 00493 { 00494 /* 00495 * Return from a context switch assumes interrupts are disabled, so 00496 * we need to explicitly re-enable them as soon as possible. 00497 */ 00498 IRQ_ENABLE; 00499 /* Call the actual process's entry point */ 00500 proc_run(); 00501 proc_exit(); 00502 } 00503 00507 void proc_exit(void) 00508 { 00509 LOG_INFO("%p:%s", current_process, proc_currentName()); 00510 00511 #if CONFIG_KERN_MONITOR 00512 monitor_remove(current_process); 00513 #endif 00514 00515 proc_forbid(); 00516 #if CONFIG_KERN_HEAP 00517 /* 00518 * Set the task as zombie, its resources will be freed in proc_new() in 00519 * a lazy way, when another process will be created. 00520 */ 00521 proc_addZombie(current_process); 00522 #endif 00523 current_process = NULL; 00524 proc_permit(); 00525 00526 proc_switch(); 00527 00528 /* never reached */ 00529 ASSERT(0); 00530 } 00531 00535 static void proc_schedule(void) 00536 { 00537 Process *old_process = current_process; 00538 00539 IRQ_ASSERT_DISABLED(); 00540 00541 /* Poll on the ready queue for the first ready process */ 00542 LIST_ASSERT_VALID(&proc_ready_list); 00543 while (!(current_process = (struct Process *)list_remHead(&proc_ready_list))) 00544 { 00545 /* 00546 * Make sure we physically reenable interrupts here, no matter what 00547 * the current task status is. This is important because if we 00548 * are idle-spinning, we must allow interrupts, otherwise no 00549 * process will ever wake up. 00550 * 00551 * During idle-spinning, an interrupt can occur and it may 00552 * modify \p proc_ready_list. To ensure that compiler reload this 00553 * variable every while cycle we call CPU_MEMORY_BARRIER. 00554 * The memory barrier ensure that all variables used in this context 00555 * are reloaded. 00556 * \todo If there was a way to write sig_wait() so that it does not 00557 * disable interrupts while waiting, there would not be any 00558 * reason to do this. 00559 */ 00560 IRQ_ENABLE; 00561 CPU_IDLE; 00562 MEMORY_BARRIER; 00563 IRQ_DISABLE; 00564 } 00565 if (CONTEXT_SWITCH_FROM_ISR()) 00566 proc_context_switch(current_process, old_process); 00567 /* This RET resumes the execution on the new process */ 00568 LOG_INFO("resuming %p:%s\n", current_process, proc_currentName()); 00569 } 00570 00571 #if CONFIG_KERN_PREEMPT 00572 /* Global preemption nesting counter */ 00573 cpu_atomic_t preempt_count; 00574 00575 /* 00576 * The time sharing interval: when a process is scheduled on a CPU it gets an 00577 * amount of CONFIG_KERN_QUANTUM clock ticks. When these ticks expires and 00578 * preemption is enabled a new process is selected to run. 00579 */ 00580 int _proc_quantum; 00581 00585 bool proc_needPreempt(void) 00586 { 00587 if (UNLIKELY(current_process == NULL)) 00588 return false; 00589 if (!proc_preemptAllowed()) 00590 return false; 00591 if (LIST_EMPTY(&proc_ready_list)) 00592 return false; 00593 return preempt_quantum() ? prio_next() > prio_curr() : 00594 prio_next() >= prio_curr(); 00595 } 00596 00600 void proc_preempt(void) 00601 { 00602 IRQ_ASSERT_DISABLED(); 00603 ASSERT(current_process); 00604 00605 /* Perform the kernel preemption */ 00606 LOG_INFO("preempting %p:%s\n", current_process, proc_currentName()); 00607 /* We are inside a IRQ context, so ATOMIC is not needed here */ 00608 SCHED_ENQUEUE(current_process); 00609 preempt_reset_quantum(); 00610 proc_schedule(); 00611 } 00612 #endif /* CONFIG_KERN_PREEMPT */ 00613 00614 /* Immediately switch to a particular process */ 00615 static void proc_switchTo(Process *proc) 00616 { 00617 Process *old_process = current_process; 00618 00619 SCHED_ENQUEUE(current_process); 00620 preempt_reset_quantum(); 00621 current_process = proc; 00622 proc_context_switch(current_process, old_process); 00623 } 00624 00633 void proc_switch(void) 00634 { 00635 ASSERT(proc_preemptAllowed()); 00636 ATOMIC( 00637 preempt_reset_quantum(); 00638 proc_schedule(); 00639 ); 00640 } 00641 00645 void proc_wakeup(Process *proc) 00646 { 00647 ASSERT(proc_preemptAllowed()); 00648 ASSERT(current_process); 00649 IRQ_ASSERT_DISABLED(); 00650 00651 if (prio_proc(proc) >= prio_curr()) 00652 proc_switchTo(proc); 00653 else 00654 SCHED_ENQUEUE_HEAD(proc); 00655 } 00656 00660 void proc_yield(void) 00661 { 00662 Process *proc; 00663 00664 /* 00665 * Voluntary preemption while preemption is disabled is considered 00666 * illegal, as not very useful in practice. 00667 * 00668 * ASSERT if it happens. 00669 */ 00670 ASSERT(proc_preemptAllowed()); 00671 IRQ_ASSERT_ENABLED(); 00672 00673 IRQ_DISABLE; 00674 proc = (struct Process *)list_remHead(&proc_ready_list); 00675 if (proc) 00676 proc_switchTo(proc); 00677 IRQ_ENABLE; 00678 }
![(please configure the [header_logo] section in trac.ini)](/chrome/site/bertos_logo.png)