如何移植uCOS-III到Linux系统 How to Port uCOS-III on Linux OS

如何移植uCOS-III到Linux操作系统???

ex1.png

关于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);   
}

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值