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

cyberlabs發表於2011-10-30

如何移植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);   
}

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

相關文章