📄 os_cpu_c.c
字号:
/* * uC/OS-II * The Real-Time Kernel * * Linux Port * * File : $Source: /proj/cvs-gfa/Micrium/Software/uCOS-II/Ports/linux-hal/os_cpu_c.c,v $ * By : (c) George Fankhauser, Sensaco Consulting GmbH, * Switzerland, http://www.sensaco.com * Version : $Revision: 1.1.1.1 $ * * Changed by : $Author: gfa $ * $Date: 2003/11/20 10:19:14 $ * * $Log: os_cpu_c.c,v $ * Revision 1.1.1.1 2003/11/20 10:19:14 gfa * check in ucos II for linux * *//** \file * User context switching code derived from Topsy 2.0 port to Linux and Solaris * (see http://www.tik.ee.ethz.ch/~topsy). * * \author George Fankhauser */ #define OS_CPU_GLOBALS#include "includes.h"#include <asm/sigcontext.h> // for context switching#define __USE_GNU#include <ucontext.h>#undef __USE_GNU#include <ucontext.h> // for context switching#include <string.h>#include <unistd.h> // used for syscalls: // ualarm in Threads/unix/TMClock.c // kill in Topsy/unix/SyscallMsg.c // getpid in Topsy/unix/SyscallMsg.c#include <signal.h> // used in Threads/unix/TMHal.c and // Topsy/unix/SyscallMsg.c#include <setjmp.h> // for contextswitches in Threads/unix/TMHal.c#include <sys/select.h>#include <stdio.h> // if we want to use printf.../** TASK CREATION HOOK * * This function is called when a task is created. * Arguments : ptcb is a pointer to the task control block of the task being created. * Note(s) : 1) Interrupts are disabled during this call. */#if OS_CPU_HOOKS_EN > 0void OSTaskCreateHook (OS_TCB *ptcb){ ptcb = ptcb;}#endif/** TASK DELETION HOOK * * This function is called when a task is deleted. * \arg ptcb is a pointer to the task control block of the task being deleted. * Note(s) : 1) Interrupts are disabled during this call. */#if OS_CPU_HOOKS_EN > 0void OSTaskDelHook (OS_TCB *ptcb){ ptcb = ptcb; /* Prevent compiler warning */}#endif/** TASK SWITCH HOOK * * This function is called when a task switch is performed. This allows * you to perform other operations during a context switch. * * Note(s) : 1) Interrupts are disabled during this call. * 2) It is assumed that the global pointer 'OSTCBHighRdy' points to the * TCB of the task that will be 'switched in' (i.e. the highest priority * task) and, 'OSTCBCur' points to the task being switched out (i.e. the * preempted task). */#if (OS_CPU_HOOKS_EN > 0) && (OS_TASK_SW_HOOK_EN > 0)void OSTaskSwHook (void){}#endif/** STATISTIC TASK HOOK * * This function is called every second by uC/OS-II's statistics task. * This allows your application to add functionality to the statistics task. */#if (OS_TASK_STAT_HOOK_EN > 0)void OSTaskStatHook (void){}#endif/** TICK HOOK * * This function is called every tick. * Note(s) : 1) Interrupts may or may not be ENABLED during this call. */#if (OS_CPU_HOOKS_EN > 0) && (OS_TIME_TICK_HOOK_EN > 0)void OSTimeTickHook (void){}#endif/** OS INITIALIZATION HOOK * (BEGINNING) * * This function is called by OSInit() at the beginning of OSInit(). * Note(s) : 1) Interrupts should be disabled during this call. */#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203void OSInitHookBegin (void){ linuxInit(); ///< installs the syscall handler}#endif/** OS INITIALIZATION HOOK * (END) * * This function is called by OSInit() at the end of OSInit(). * Note(s) : 1) Interrupts should be disabled during this call. */#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203void OSInitHookEnd (void){}#endif/** IDLE TASK HOOK * * This function is called by the idle task. This hook has been added to * allow you to do such things as STOP the CPU to conserve power. * Note(s) : 1) Interrupts are enabled during this call. * * On real hardware a power saving function would be selected. * On Linux, we sleep a little to lower the load on the system * or, we block until next signal is delivered by calling select(). * * If either of these methods causes problems just leave the * function empty. */#if OS_CPU_HOOKS_EN > 0 && OS_VERSION >= 251void OSTaskIdleHook (void){ // usleep(50); ///< as done in Topsy select(0, NULL, NULL, NULL, NULL); ///< as done in eCos}#endif/** OSTCBInit() HOOK * * This function is called by OS_TCBInit() after setting up most of the TCB. * \arg ptcb is a pointer to the TCB of the task being created. * Note(s) : 1) Interrupts may or may not be ENABLED during this call. */#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203void OSTCBInitHook (OS_TCB *ptcb){ ptcb = ptcb; }#endif/** INITIALIZE A TASK'S STACK * * This function is called by either OSTaskCreate() or OSTaskCreateExt() to * initialize the stack frame of the task being created. This function is highly * processor specific. * \arg task is a pointer to the task code * \arg pdata is a pointer to a user supplied data area that will be passed to the task * when the task first executes. * \arg ptos is a pointer to the top of stack. It is assumed that 'ptos' points to the * highest valid address on the stack. * \arg opt specifies options that can be used to alter the behavior of OSTaskStkInit(). * \see uCOS_II.H for OS_TASK_OPT_???. * * \return Always returns the location of the new top-of-stack' once the processor * registers have been placed on the stack in the proper order. * * Note(s) : Interrupts are enabled when your task starts executing. You can change * this by setting this SREG to 0x00 instead. In this case, interrupts would be disabled * upon task startup. The application code would be responsible for enabling interrupts * at the beginning of the task code. You will need to modify OSTaskIdle() and * OSTaskStat() so that they enable interrupts. Failure to do this will make your system * crash! */OS_STK* OSTaskStkInit (void (*task)(void* pd), void* pdata, OS_STK* ptos, INT16U opt){ INT32U* stk; ucontext_t uc; INT32U sigsize = 20 + sizeof(uc); opt = opt; /* 'opt' is not used, prevent warning */ getcontext(&uc); stk = (INT32U*)((int)ptos - sigsize); uc.uc_link = NULL; ///< no successor uc.uc_mcontext.gregs[REG_EBP] = (int)stk; uc.uc_stack.ss_sp = (void*)(((int)stk) - (OS_TASK_DEF_STK_SIZE) + sigsize); ///< base address uc.uc_stack.ss_size = OS_TASK_DEF_STK_SIZE - sigsize; makecontext(&uc, (void*)task, 1, pdata); memcpy(stk, &uc, sizeof(uc)); return ((OS_STK *)stk);}/** A new thread is started */void OSStartHighRdy(void) { OSTaskSwHook(); OSRunning = TRUE; // real work goes here... { ucontext_t* ucp; ucp = (struct ucontext*)(OSTCBHighRdy->OSTCBStkPtr); setcontext(ucp); // not reached }}/** called by OSIntExit() * * typically, after signal is handled, a potentially new thread is picked and * resumed */void OSIntCtxSw(void) { // on Linux this is the same as OSCtxSw; no special treats for hw/sw ints OSCtxSw();}/** * This procedure is called whenever a thread sleeps voluntarily, or, a syscall is made and the * the thread is not the highest priority thread anymore after rescheduling. * * This procedure is called via software interrupt (on Linux synthetic targets: signal and * handler). * * \todo setcontext is a user space implementation of calling sigprocmask and then restoring * registers; should try (ugly) hack using sigreturn which is a real syscall */void OSCtxSw(void) { struct ucontext* uc = (struct ucontext*)OSTCBHighRdy->OSTCBStkPtr; // at this point, registers are already saved on stack OSTaskSwHook(); OSTCBCur = OSTCBHighRdy; OSPrioCur = OSPrioHighRdy; //if (uc->uc_mcontext.fpregs == 0) { // fprintf(stderr, "ctx sw: uc->uc_mcontext.fpregs == 0\n"); //uc->uc_mcontext.fpregs = (fpregset_t)0xbffff6cc; //} setcontext(uc);}/** * \arg signo Signal number * \arg info Signal info * \arg uc User context */void OSCtxSwSigHandler(int signo, siginfo_t* info, /*struct ucontext* */ void* uc){ /** Linux specific module variable of current signal context, i.e. interrupted thread */ OSTCBCur->OSTCBStkPtr = uc; //stk; OSCtxSw();} /** \todo maybe wrong for linux port? * adjust sp if nesting == 1 ???? maybe if there is a seperate interrupt stack * linux user space port does not have a special isr stack frame layout */void OSTickISR(void) { ///< register context already saved in sig handler OSIntEnter(); if (OSIntNesting == 1) { //OSTCBCur->OSTCBStkPtr = $SP; //asm("mov %%esp, %0" : "=g"(OSTCBCur->OSTCBStkPtr) : ); } OSTimeTick(); ///< adjusts counters and ready bitmaps OSIntExit(); ///< reschedules if necessary OSIntCtxSw(); ///< restore context and jump to highest prio task}/** * Periodic signal handler that adjusts timers * On Linux this is not very precise but for most applications sufficient * * Pick a value between 10 and 1000 Hz for the timer * * \todo this handler maybe executed during a restore (i.e. setcontext()) * call. This is a problem with the setcontext() user space implementation in * Linux i386. Most SysV and Linux RISC systems don't have this. The tests * below are an indication that this situatiuon has occured; the SIGALRM * is then aborted... * * \todo If this is not fixed by adding a Linux syscall for get/setcontext, a user level * implementation with an 'clock interrupt' lock should be used * * \arg signo Signal number * \arg info Signal info * \arg uc User context */void OSTimeTickSigHandler(int signo, siginfo_t* info, /*struct ucontext* */ void* uc) { if ((((ucontext_t*)uc)->uc_mcontext.gregs[REG_EIP] >= (unsigned int)setcontext) && (((ucontext_t*)uc)->uc_mcontext.gregs[REG_EIP] < (unsigned int)(setcontext + 110))) { //fprintf(stderr, "sig timer: thread interrupted in setcontext\n"); return; } if (((ucontext_t*)uc)->uc_mcontext.fpregs == 0) { //fprintf(stderr, "sig timer: uc->uc_mcontext.fpregs == 0\n"); return; } OSTCBCur->OSTCBStkPtr = uc; //stk; OSTickISR(); // restore context}/** * Kick the periodic clock; this must be done \e after microC/OS-II is initialised, * typically at the end of main() * * Either SIGALRM or SIGVTALRM can be used * * \attention This must be called by the user in the first task */void linuxInitInt(){ ualarm(1000000/OS_TICKS_PER_SEC, 1000000/OS_TICKS_PER_SEC); ///< periodic mode // alternative //setitimer(ITIMER_VIRTUAL, 1000000/OS_TICKS_PER_SEC); ///< SIGVTALRM time spent by the ///< process in User Mode}/** * Setup of Linux specific stuff such as stacks, signals, handlers, mask etc. * * This is called inside the HAL by OSInitHookBegin() */void linuxInit() { struct sigaction act; sigset_t mask; sigemptyset(&mask); act.sa_sigaction = OSTimeTickSigHandler; act.sa_flags = SA_SIGINFO;// | SA_ONSTACK; act.sa_mask = mask; sigaction(SIGALRM, &act, NULL); sigaction(SIGVTALRM, &act, NULL); sigemptyset(&mask); act.sa_sigaction = OSCtxSwSigHandler; act.sa_flags = SA_SIGINFO;// | SA_ONSTACK; act.sa_mask = mask; sigaction(SIGUSR1, &act, NULL);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -