如何移植uCOS-III到Linux操作系统???
关于uCOS-II移植到Linux系统的核心代码
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
基本移植已被映射到Linux信号,并在OS_CPU_C.C中实施;注意在这个移植方案中没有汇编语言文件。一些功能被内联在OS_CPU.H中。换句话说,就是这里只有一个Linux进程(也只有一个线程),以及所有任务的创建和调度都是由microC/OS - II来做的。
在当前任务堆栈进行信号传递。OSTCBStkPtr是指向ucontext_t堆栈结构,供接下去使用。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
•OS_TASK_SW( ):使用Linux系统调用的上下文切换杀()系统调用,它发送信号SIGUSR1给自己。
•OSCtxSw( ):切换任务是做一个中断发生后,一个新的任务变得准备运行,任务自愿放弃CPU的。这个函数被调用的信号SIGUSR1和SIGALRM的处理程序。切换是由setcontext()。
•OSTickISR( ) 是一个标准的执行代码,如在书中所述。它被函数OSTimeTickSigHandler()周期性地调用。
•OSInitHookBegin():这个钩子是用来初始化Linux的信号和句柄。它调用LinuxInit()。
•LinuxInitInt( ) 已被要求用户在最高优先级的用户应用任务中调用。它启动时钟节拍。
•OSTaskIdleHook( ) 是空的,休眠一段时间或直到下一次信号被发生(调用一个select()系统调用)。
•OSTaskStkInit( ) 初始化一个ucontext_t结构的任务堆栈和调整TCBs域。SysV系统调用getcontext()和makecontext( )有助于处理此过程。
•OSStartHighRdy( ) 使用setcontext()恢复先前上下文。由于该功能只能使用一次,应该在未来的调用中被OSCtxSw取代。
•OS_CPU_SR:此类型映射到一个sigset_t。它是用来阻塞和恢复信号。由于Linux移植采用了Critical Method #3,它贯穿整个系统。
•OS_CRITICAL_METHOD:= 3; Linux移植必须记住信号掩码的先前状态。
•OS_ENTER_CRITICAL( ) 是一个宏,通过Linuxsigprocmask()系统调用,阻塞SIGALRM和SIGUSR1信号。
•OS_EXIT_CRITICAL( ) 恢复信号掩码的先前状态。
•OS_TASK_DEF_STK_SIZE:在Linux上的合理大小是2000words。这主要是由于处理堆栈上的信号。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/*
* uC/OS-II
* The Real-Time Kernel
*
* Linux Port
*
* File : $Source: /proj/cvs-gfa/Micrium/Software/uCOS-II/Ports/linux-hal/os_cpu.h,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.h,v $
* Revision 1.1.1.1 2003/11/20 10:19:14 gfa
* check in ucos II for linux
*
*/
/*
* \file
*
* Definition moved here so it can be used in the assembler file OS_CPU_A.ASM.
* See below for the meaning of this define
*
* \author George Fankhauser
*/
#include <unistd.h>
#include <signal.h>
#include <string.h> /// memcpy, memset used in os_task.c !
#ifndef OS_CPU_A /* skip the rest if we're including from the assembler file */
#ifdef OS_CPU_GLOBALS
#define OS_CPU_EXT
#else
#define OS_CPU_EXT extern
#endif
/*
* DATA TYPES (Compiler and architecture specific)
*/
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U; /* Unsigned 8 bit quantity */
typedef signed char INT8S; /* Signed 8 bit quantity */
typedef unsigned short int INT16U; /* Unsigned 16 bit quantity */
typedef signed short int INT16S; /* Signed 16 bit quantity */
typedef unsigned int INT32U; /* Unsigned 32 bit quantity */
typedef signed int INT32S; /* Signed 32 bit quantity */
typedef float FP32; /* Single precision floating point */
typedef INT32U OS_STK; /* Each stack entry is 32 bit wide */
typedef sigset_t OS_CPU_SR; /// the 'status register' corresponds to signals
/*
* used in os_cpu_c
*/
void OSTaskSwHook (void);
/*
* linuxInit does the setup of the signal handler.
*/
void linuxInit(void);
/** linuxInitInt starts periodic interrupts. */
void linuxInitInt(void);
/*
* Macro to block interrupts (on Linux: signals)
*
* Critical method 1 which does not restore the signal state may lead to hanging
* timer interrupts, especially when debugging (i.e. real time is much faster than debug
* time).
*/
#define OS_CRITICAL_METHOD 3
/*
* We add all the virtual interrupt signals to the mask and save the old process
* signal mask to the backup 'status register'.
*/
#define OS_ENTER_CRITICAL() { sigset_t set; \
sigemptyset(&set); \
sigaddset(&set, SIGALRM); \
sigaddset(&set, SIGIO); \
sigprocmask(SIG_SETMASK, &set, &cpu_sr); \
}
/*
* Macro to unblock interrupts
*
* Here we just restore the state that was returned by previous call to sigprocmask
*/
#define OS_EXIT_CRITICAL() { sigprocmask(SIG_SETMASK, &cpu_sr, NULL); \
}
/*
* Stack grows from HIGH to LOW memory on linux x86
*/
#define OS_STK_GROWTH 1
/*
* This macro posts a Linux signal to ourselves; it is returned in the handler with the
* threads context. The SIGUSR1 handler saves the context and then calls OSCtxSw().
*
* (use OSCtxSw() directly when no sw int available)
*/
#define OS_TASK_SW() { kill(getpid(), SIGUSR1); }
#endif //OS_CPU_A
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/*
* 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);
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++