📄 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 > 0
void 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 > 0
void 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 > 203
void 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 > 203
void 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 >= 251
void 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 > 203
void 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 + -