Chinaunix

标题: 自己实现一个RTOS《实时操作系统揭秘》(附源码) [打印本页]

作者: shmilylxx    时间: 2009-01-01 22:11
标题: 自己实现一个RTOS《实时操作系统揭秘》(附源码)
新年伊始,
将自己独立实现的一个比较小的RTOS源码贴上来,
顺便把原理都讲一讲,希望对在这块工作的朋友有些帮助或者启发
大家也给点改进的意见和建议。

本系列文章的标题叫做
《实时操作系统揭秘》


第一篇 前言
很多人对老美发明的操作系统顶礼膜拜,捧到了神的地步,
市面上也充斥着很多有关操作系统的劣质的译作
对其关键部分,大部都语焉不详,隔靴搔痒
让更多的人越看越糊涂
于是操作系统在人们心中更加高深了

其实,操作系统远没有这些人想象的那么神秘
任务切换,内存管理,文件系统,任务间通讯功能,引导程序等模块,
就形成了一个完整的操作系统内核
我们在这里就逐一剥一下操作系统的皮,
把各个模块的原理,以结合代码的形式,抖给大家看看
让大家看清操作系统的一些秘密
对某些模块,系统功能有兴趣的同学,
也可以发邮件给我,unix.lxx@gmail.com
我们一起研究,共同学习...


(注:
1,这系列文章,
都是按照目前工作中,手头项目的进度
以及涉及到的知识点所写出来的,
是个类笔记的东西,是业余时间的一个作品

2,附注的源码,
是个人的一个小作品,
自己给他取了个不响亮的名字:BenOS
并且该系统在自己的Cortex-M3平台测试通过并且运行自己的应用没有问题

3,BenOS是完全个人独立实现的
没有抄袭任何其他OS的源码


[ 本帖最后由 shmilylxx 于 2009-1-12 23:09 编辑 ]

BenOS.rar

674.29 KB, 下载次数: 5029

基于Cortex-M3平台源码

BenOS实时操作系统解密.rar

12.78 KB, 下载次数: 3925


作者: shmilylxx    时间: 2009-01-01 22:14
第二篇 任务切换,如何才能得到MM的青睐

操作系统最核心的功能就是任务切换

何为任务切换,为啥要任务切换
这个就简单说:
引入操作系统,就是为了让单个CPU可以运行多个任务
但是这些任务是并发运行的,
每个小猪吃一口奶,就让给另一个小猪吃一口
大家轮流来,宏观看起来就像所有的小猪都在同时吃奶一样

任务A正在运行,时间该任务B运行了,
这个时候,操作系统就需要做任务切换
于是,操作系统就保存 任务A目前跑到了哪一步,运行时候,所有的参数是啥子
然后在任务B恢复上次运行到的位置,并且恢复上次停止运行时间点的所有参数
再打任务B一鞭子,任务B就像没停止过一样,开始欢快的跑了。

这些,就是任务切换所需的全部步骤
针对目前我们使用的CORTEX-M3平台而言
任务切换就是,
首先
1,计算当前就绪态任务列表中,优先级最高的任务,
  再判断是否需要切换任务
2,保存当前任务运行相关的寄存器,大概十几个(入栈)
  保存当前任务运行地址(PC指针,其实在1里面保存了)
  保存当前的堆栈指针
3,递减各个任务等待的时间片计数器;
4,其他资源占用情况统计(死锁的解除)
5,然后将堆栈指针指向新任务的堆栈
6,恢复2中保存的所有寄存器和其他数据(出栈)
7,其他系统功能相关的东西
再给出例程代码:
        PUSH {R0-R15} ;(2)
        LDR    R4, =OSTCBCur          ; OSTCBCur->OSTCBStkPtr = SP;
        LDR    R4, [R4]
        STR    SP, [R4]                ;

        LDR    R6, =OSTCBHighRdy  ; SP=OSTCBHighRdy->OSTCBStkPtr
        LDR    SP, [R6]

        POP {R0-R15}            ; (6)
        BX      LR                      ; RET,此时堆栈恢复了,寄存器恢复了,PC指针也恢复了,
                                        ; 状态也恢复了,旧任务就可以继续欢乐的执行了。

注:这里的堆栈指针都是另外赋值操作的,
    绝对不能使用出栈入栈的方式保存恢复
    因为出栈入栈操作会自动修改堆栈指针...

这里主要是(2),(6)的代码,并且没有考虑模式切换和堆栈指针切换,
其他步骤,一般实现都是在这切换之前进行。

任务切换在很多时候都会进行,常说的任务切换就是在时间定时中断时候进行,
芯片(或者由外部时钟)会产生一个定时中断,可以设置每秒多少次,
就是在这个中断处理函数中进行任务切换。
其他的任务切换,发生在,等待消息,任务延时,或者手动强制任务切换等等时候
这些不同的切换时机都有些小差别,不过本质上是一样的。

够简单吧?
其实实际上还有一些幕后工作并没有列出,
比如,内核要维护两个链表,其一是就绪态任务的表,其二是等待态任务的表
每个TICK中断的时候,等待态表中的每个等待TICKS计数器就减一,
当计数为0时,将其任务移动到就绪态表中来。
还是有其他一些繁琐的工作都要做,任务切换锁定检测,死锁检测,优先级反转的避免等;
并且死锁和优先级反转问题在实时系统中是个很深刻的课题,有兴趣的不妨自己做一做。
另外,指出一点:是依据时间片的操作系统,还是据优先级的抢占式操作系统,
还是混合多种切换算法的操作系统的根本区别就是其切换算法,
先进先出,后进先出,最短等待时间,最短平均等待时间,等等算法在这里都有用武之地!

下面是自己的一个实现:

/*
*时钟中断函数
*/
void SysTick_Handler()
{
        INT32        index;
        TCB                *pTCB;
        INT8U        flagFirstTask=0;
        INT_Stat  stat = 0;

        stat = BenOS_INT_Save();

        if (BenOSScheLock != 0)
        {
                BenOS_INT_Restore(stat);
                return;
        }

        /*在时钟中断中,必须将所有时延都--*/
        for (index = 0;index < TaskNUM;index++)
        {
                pTCB = BenOSTCBTable+index;
                /*该任务在睡眠状态,而当前的调用是在时钟中断中*/
                if (pTCB->TCBDelay > 0)
                {
                        pTCB->TCBDelay--;
                }
                else
                {
                        if (flagFirstTask==0)
                        {
                                BenOSNewTCB = pTCB;
                                flagFirstTask = 1;
                        }
                }
               
        }

        if (BenOSNewTCB != BenOSCurTCB)
        {
                OSIntCtxSw();
        }
        BenOS_INT_Restore(stat);
}

/*
*在非中断中 调度新的任务 并且切换
*/
void TaskSche()
{
        INT_Stat  stat = 0;

        stat = BenOS_INT_Save();
        if (BenOSScheLock != 0)
        {
                BenOS_INT_Restore(stat);
                return;
        }               

        if (BenOSNewTCB != BenOSCurTCB)
        {
                OSIntCtxSw();
        }
        BenOS_INT_Restore(stat);
}

__asm void OSCtxSw()
{
        LDR    R4, =NVIC_INT_CTRL   
        LDR    R5, =NVIC_PENDSVSET
        STR    R5, [R4]          ;激活PENDSVC中断,开始切换任务
        BX      LR
        NOP
}


__asm void PendSV_Handler()
{
        MRS    R0, PSP                ; PSP is process stack pointer

        SUB    R0, R0, #0x20          ; save remaining regs r4-11 on process stack
        STM    R0, {R4-R11}

        LDR    R4, =BenOSCurTCB        ; OSTCBCur->OSTCBStkPtr = SP;
        LDR    R4, [R4]
        STR    R0, [R4]                ; R0 is SP of process being switched out

        LDR    R4, =BenOSCurTCB        ; BenOSCurTCB  = BenOSNewTCB;
        LDR    R6, =BenOSNewTCB
        LDR    R6, [R6]
        STR    R6, [R4]

        LDR    R0, [R6]                ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
        LDM    R0, {R4-R11}            ; restore r4-11 from new process stack
        ADD    R0, R0, #0x20
        MSR    PSP, R0                ; load PSP with new process SP
        ORR    LR, LR, #0x04          ; ensure exception return uses process stack
        BX      LR                      ; exception return will restore remaining context
        NOP
}

事实上,这里只是实现的一部分,在后面展示的源码中,会有其他的部分。
作者: shmilylxx    时间: 2009-01-01 22:15
第三篇 世界的起源 任务如何启动

其实绝大部分操作系统的实现步骤都是这样的:
先压点合适的东西进堆栈----堆栈的初始化
(这步放在任务初始化的时候----切记关任务切换和定时中断)
只要恢复寄存器和其他数据之后
SP指针正确,状态寄存器没异常,
直接将PC指向新任务的第一条指令,不就行了嘛。
看下我们CORTEX-M3平台上的实现:(堆栈生长方向是--,跟X86相反)
/*把堆栈初始化模块用汇编写一遍,但愿性能会高点*/
__asm    STACK_TYPE *TaskStkInit (void (*task),STACK_TYPE *ptos)
{
        PUSH    {R4-R6,LR}
        MOV      R4,R0

        MOV      R0,R1
        MOV      R5,#0x1000000
        STR      R5,[R0,#0]

        SUBS    R0,R0,#4
        STR      R4,[R0,#0]
        
        MVN      R5,#1
        SUBS    R0,R0,#4
        STR      R5,[R0,#0]
        
        MOV      R5,#0x2
        SUBS    R0,R0,#4
        STR      R5,[R0,#0]
        
        MOV      R5,#0x3
        SUBS    R0,R0,#4
        STR      R5,[R0,#0]
        
        MOV      R5,#0x4
        SUBS    R0,R0,#4
        STR      R5,[R0,#0]

        ASRS    R5,R5,#1
        SUBS    R0,R0,#4
        STR      R5,[R0,#0]
        
        SUBS    R0,R0,#4
        STR      R1,[R0,#0]
        
        MOV      R5,#0x5
        SUBS    R0,R0,#4
        STR      R5,[R0,#0]

        MOV      R5,#0x6
        SUBS    R6,R0,#4
        MOV      R0,R6
        STR      R5,[R6,#0]
        
        MOV      R5,#0x7
        SUBS    R0,R0,#4
        STR      R5,[R0,#0]
        
        MOV      R5,#0x8
        SUBS    R0,R0,#4
        STR      R5,[R0,#0]

        MOV      R5,#0x8
        SUBS    R0,R0,#4
        STR      R5,[R0,#0]

        MOV      R5,#0x10
        SUBS    R0,R0,#4
        STR      R5,[R0,#0]

        MOV      R5,#0x11
        SUBS    R0,R0,#4
        STR      R5,[R0,#0]

        MOV      R5,#0x12
        SUBS    R6,R0,#4
        MOV   R0,R6
        STR      R5,[R6,#0]
        POP      {R4-R6,PC}

}启动这个任务,就可以直接使用任务切换源码的后半部分(因为没有任务需要保存)
这样PC就指向了新任务的入口,
新任务可以开始运行啦!
世界就这样形成了...

实际启动是使用SVC中断启动
代码如下:

__asm void StartTask()
{
        LDR    R4, =NVIC_SYSPRI2      ; set the PendSV exception priority
        LDR    R5, =NVIC_PENDSV_PRI
        STR    R5, [R4]

        MOV    R4, #0                  ; set the PSP to 0 for initial context switch call
        MSR    PSP, R4

        LDR    R4, =SYS_TICKS  ;设置时钟节拍频率   
        LDR    R5, =NVIC_SYSTICK_LOAD
        STR    R4, [R5]

        MOV    R4, #0x07      
        LDR    R5, =NVIC_SYSTICK_CTRL
        STR    R4, [R5]

      SVC 0    ;呼叫SVC中断
      NOP
}

/*SVC中断入口*/
__asm void SVC_Handler()
{
      LDR R6,=BenOSCurTCB
        LDR    R0, [R6]                ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
        LDR    R0, [R0]                ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;

        LDM    R0, {R4-R11}            ; restore r4-11 from new process stack
        ADD    R0, R0, #0x20
        MSR    PSP, R0                ; load PSP with new process SP
        ORR    LR, LR, #0x04          ; ensure exception return uses process stack
        BX      LR                      ; exception return will restore remaining context
}
作者: shmilylxx    时间: 2009-01-01 22:17
第四篇 任务延时的实现
这次,我们开始关心一个具体的系统接口的实现
OSTimeDly (INT16U ticks)

其实任务延时的实现已经在前面节拍中断中有所体现
在任务运行的过程中,常常需要任务等待很短的时间,再干活
比如
while(1 )
{
  msg=GetMsg();
  if (!msg)
     OSTimeDly(10);/*等待10个时钟*/
  else
  {
    dosomething();
    OSTimeDly(3);/*等待3个时钟*/
  }
}
那么,任务如何知道自己该等待了?
如何知道自己等待了多久?
如何继续运行?
sleepMS()又隐藏了哪些秘密呢?

实现这些东西的原理是这样的
在每个任务TCB结构中,
都存在一个OSTCBDlyTicks元素
这个元素在每次定时中断切换的时候就减一
当减到0时,这个任务就可以继续运行了
具体的步骤如下:
1,OSTCBDlyTicks赋值为该等待的时钟次数
2,将该任务从就绪表中删除
3,将该任务添加到等待表中
4,在每次定时中断时,将所有等待表中任务的OSTCBDlyTicks减一,
  这样每个任务都知道自己还将等待多久
5,当某个任务的OSTCBDlyTicks减到了0,就将其在等待表中删除,添加到就绪表
这样,这个任务进入到了可运行的列表里面,时机到了,就会运行。
sleepMS的实现,其实很简单:
每个时钟中断的时间是固定的
计算就能得出sleepMS该等待的ticks次数啦
主要代码如下:
sleepMS(int ms)
{
    ticks=ms*OS_TICKS_PER_SEC/1000;
    OSTimeDly(ticks);
}

源码如下:

/*目前设计是最大等待次数为255个时钟片*/
void  BenOSTimeDly (INT32 ticks)
{
        INT_Stat  stat = 0;
        INT32          index;
        TCB                  *pTCB;
       
        stat = BenOS_INT_Save();
        BenOSCurTCB->TCBDelay = ticks;

        /*从当前任务向后遍历,第一最大的优先级就是需要调度进去的任务*/
        for (index = 0/*(BenOSCurTCB- BenOSTCBTable)+1*/; index < TaskNUM;index++)
        {
                pTCB = BenOSTCBTable+index;
                if ((pTCB->TCBDelay == 0) && (pTCB->TaskStat !=BenOS_Task_Pend)          )
                {       
                        BenOSNewTCB = pTCB;

                        break;
                }
        }
        BenOS_INT_Restore(stat);
        TaskSche();       
}
作者: shmilylxx    时间: 2009-01-01 22:17
第五篇,信号量的实现
这里直接使用开关中断作为临界处理,
不值得推荐,但是比抄袭别人的方案要好点,哈哈~

实现很直观,代码告诉你一切!

#include "BenOSSemaphore.h"


/*当前信号量列表*/
SEMAPHORE_TYPE        BenOSSemaphore[MAX_SEM_NUM];                               
/*
* 创建信号量
*/
SEMAPHORE_TYPE*        CreateSemaphore(INT32 conuter)
{
        INT_Stat  stat = 0;
        INT32U         index;

        if (conuter < 0)
        {
                return 0;
        }
       
        stat = BenOS_INT_Save();
        for(index=0;index<MAX_SEM_NUM;index++)
        {       
                if (BenOSSemaphore[index]==-1)
                {
                        BenOSSemaphore[index] = conuter;
                        BenOS_INT_Restore(stat);
                        return &(BenOSSemaphore[index]);
                }
        }
       
        BenOS_INT_Restore(stat);
        return        (SEMAPHORE_TYPE*)NULL;
}

INT8 DeleteSemaphore(SEMAPHORE_TYPE* pSem)
{
        INT_Stat  stat = 0;

        stat = BenOS_INT_Save();

        /*信号量不存在*/
        if (pSem == NULL)
        {
                BenOS_INT_Restore(stat);
                return 0;
        }
        /*当且仅当信号量计数为0的时候,才能释放该信号量*/
        if ((*pSem) != 0)
        {
                BenOS_INT_Restore(stat);
                return 0;
        }
        else
        {
                (*pSem) = (SEMAPHORE_TYPE)-1;
                BenOS_INT_Restore(stat);
                return 1;
        }
}


/*这个是一个不完全精确的实现*/
/*其超时时间不会非常精确*/
INT8 WaitSemaphore(SEMAPHORE_TYPE* pSem,INT32U timeout)
{
        INT32U         index;
        INT32U  stat = 0;
        for (index = 0;index < timeout;index++)
        {
                stat = BenOS_INT_Save();
                if ((*pSem) > 0)
                {
                        (*pSem)--;
                        BenOS_INT_Restore(stat);
                        return 1;/*获取到了信号量*/
                }
                else
                {
                        /*等待一个时间片*/
                        BenOS_INT_Restore(stat);
                        BenOSTimeDly(1);
                }
        }
       
        return 0;
}

/*不等待,立即返回是否信号量能否获取*/
INT8 GetSemaphore(SEMAPHORE_TYPE* pSem)
{
        INT32U  stat = 0;

        stat = BenOS_INT_Save();

        if ((*pSem) > 0)
        {
                (*pSem)--;
                BenOS_INT_Restore(stat);
                return 1;/*获取到了信号量*/
        }
        BenOS_INT_Restore(stat);
        return 0;
}

/*释放一个信号量*/
INT8 PostSemaphore(SEMAPHORE_TYPE* pSem)
{
        INT_Stat  stat = 0;

        stat = BenOS_INT_Save();
        (*pSem)++;
        BenOS_INT_Restore(stat);

        return 1;
}
作者: shmilylxx    时间: 2009-01-01 22:18
第六篇 消息队列的实现

消息队列是嵌入式编程中常用的工具,
实现手段有很多,这里使用最直观,最简单的实现方式。
UCOS和RTX都使用了信号量的队列实现,
我这里也直接使用中断关闭的临界区来实现
(一般商用系统都不会使用这种方式,
以后考虑使用Cortex-M3的一些特殊指令来重新实现,
包括信号量互斥量的实现也在考虑之列)

#include "BenOSQueue.h"

/*用于对于的标记消息队列是否使用,将来考虑用位来标志*/
INT8U        MsgQueueFlag[MAX_QUEUE_NUMBER]={0};

/*实际的所有消息队列*/
EventQ        MsgQueue[MAX_QUEUE_NUMBER];

/*
* 创建消息队列
*/
EventQ*        CreateQueue()
{
        INT_Stat  stat = 0;
        INT32U         index;
       
        stat = BenOS_INT_Save();
        for(index=0;index<MAX_QUEUE_NUMBER;index++)
        {       
                /*该消息队列未被使用*/
                if (MsgQueueFlag[index]==0)
                {
                        /*该队列首尾初始化*/
                        MsgQueue[index].front=0;
                        MsgQueue[index].rear=0;
                        BenOS_INT_Restore(stat);
                        return &(MsgQueue[index]);
                }
        }
       
        BenOS_INT_Restore(stat);
        return        (EventQ*)NULL;
}

INT8 DeleteQueue(EventQ* q)
{
        INT_Stat  stat = 0;
       
        stat = BenOS_INT_Save();

        /*信号量不存在*/
        if (q == NULL)
        {
                BenOS_INT_Restore(stat);
                return 0;
        }
        /*队列指针越界*/
        if ((( q-MsgQueue ) < 0)||(( q-MsgQueue ) > (MAX_QUEUE_NUMBER-1)))
        {
                BenOS_INT_Restore(stat);
                return        0;
        }
       
        /*将标记位置0*/
        MsgQueueFlag[q-MsgQueue] =   (INT8U)0;

        BenOS_INT_Restore(stat);
        return        1;

}

/*发送一个消息*/
INT8 SendMessage(EventQ* q,MSG_TYPE msg)
{
        INT_Stat  stat = 0;
       
        stat = BenOS_INT_Save();
        /*队列不存在*/
        if (q == NULL)
        {
                BenOS_INT_Restore(stat);
                return 0;
        }
        /*队列指针越界*/
        if ((( q-MsgQueue ) < 0)||(( q-MsgQueue ) > (MAX_QUEUE_NUMBER-1)))
        {
                BenOS_INT_Restore(stat);
                return        0;
        }


        /*判断队列是否满*/
        if((q->rear+1)%MAX_MSG_NUMBER==q->front)
        {       
                BenOS_INT_Restore(stat);
                return 0;
        }
        else
        {
                /*加入一个新的数据*/
                q->msgQueue[q->rear]=msg;
                /*将队尾数加1*/
                q->rear=(q->rear+1)%MAX_MSG_NUMBER;
                BenOS_INT_Restore(stat);
                return 1;       
        }
}

MSG_TYPE WaitMessage(EventQ *q, INT32U timeout)
{
        INT32U         index;
        INT32U  stat = 0;
        MSG_TYPE msg;

       
        for (index = 0;index < timeout+1;index++)
        {
                stat = BenOS_INT_Save();

                if (q->front==q->rear)//判断队列是否为空
                {       
                        BenOS_INT_Restore(stat);
                        BenOSTimeDly(1);
                }
                else
                {
                        msg=q->msgQueue[q->front];//提出队首数据
                        q->front=(q->front+1)%MAX_MSG_NUMBER;//改变队首

                        BenOS_INT_Restore(stat);
                        return msg;
                }
        }

        BenOS_INT_Restore(stat);
        return -1        ;
}



MSG_TYPE GetMessage(EventQ *q, INT32U timeout)
{
        MSG_TYPE msg;
        INT32U  stat = 0;

        stat = BenOS_INT_Save();
        if (q->front==q->rear)//判断队列是否为空
        {       
                BenOS_INT_Restore(stat);
                return 0;       
        }
        else
        {
                msg=q->msgQueue[q->front];//提出队首数据
                q->front=(q->front+1)%MAX_MSG_NUMBER;//改变队首

                BenOS_INT_Restore(stat);
                return msg;
        }
}
作者: shmilylxx    时间: 2009-01-01 22:19
第七章 实时性和优先级反转
实时性和相关的优先级反转问题,
在实时领域,是个很关键的问题

首先说多任务,
任务就是让一段“流程”,一般都是一遍又一遍的循环运行(死循环)。
一次“流程”运行一遍之后,常常会等待一段时间,
自己休息休息,也让其他任务也运行一下,
这就是多任务并行。
(大家都有房子住,这才是和谐社会嘛。)

在多任务的系统之中,实时性,就是让当前最高优先级的任务优先运行;
若当前最高优先级的任务不是当前正在运行的任务,那么就要给一个时机(时钟中断),
让高优先级的任务运行,正在运行的(低优先级)任务等下再运行。
这就是实时系统中的抢占调度。

实时操作系统的本质就是,
让当前最高优先级的任务以最快的速度运行!
(如果有同优先级的任务,则大家轮流运行)

由此看来,实时的多任务设计,难度在于:
要保证系统性能满足的需求,
在硬性保证高优先级任务在deadline之前运行完的同时
也要保证低优先级的任务顺利的完成自己的工作。

当然,这里就提出了优先级反转的问题了
典型情况如下:
高优先级的任务A要请求的资源被低优先级任务C所占用,
但是任务C的优先级比任务B的优先级低
于是任务B一直运行,比A低优先级的其他任务也一直能运行,
反而高优先级的任务A不能被运行了。

从实时性上讲,若高优先级在等待一个某个资源,
那么为了保证高优先级任务能顺利运行,
则必须要让当前占用该资源的任务赶紧运行下去,直到把资源释放。
再让高优先级的任务来占用这个资源。

优先级反转在RTOS中是一个很深刻的课题,
目前还没有非常好的解决方案。
在这个问题上,目前业界比较典型的做法是VxWorks的做法
原理如下:
当任务A请求的资源被任务C所占用的时候
则将C的优先级提升到任务A的级别,让占有资源的任务先运行,
这样能在一定程度上解决优先级反转的问题。
但是这样做,事实上破坏了实时系统里面运行优先级的意义...

其他,有些商业RTOS也提出了一些解决方案
比如常见的极限优先级方案:
将使用资源的任务优先级提升到系统最高级别
使得任何使用该资源的任务都能快速通过
但是,对优先级意义的破坏性,比优先级继承方案更大!

要是那位朋友有兴趣和勇气,提出了一个自己的好的解决方案,
那么在实时操作系统的发展史上,定会记录下你的名字。

[ 本帖最后由 shmilylxx 于 2009-1-1 22:22 编辑 ]
作者: c/unix    时间: 2009-01-01 22:28
提示: 作者被禁止或删除 内容自动屏蔽
作者: nhuczp    时间: 2009-01-01 23:01
原来有一本书叫自己动手写操作系统..


支持LZ
作者: eveson    时间: 2009-01-02 01:10
你可以整理出一个电子书
作者: shmilylxx    时间: 2009-01-02 09:18
需要在自己平台移植使用的朋友,
可以直接参考UCOS和FREERTOS的移植,
其实所有的小型RTOS移植方法都大同小异,
根本不需要专门去整。


原帖由 c/unix 于 2009-1-1 22:28 发表
沙发,
......

PS.楼主能否贡献源码和整理个chm文档出来。谢谢。



源码将在近几天的时候放上来
到时候也会顺便整理一份chm文档出来。

[ 本帖最后由 shmilylxx 于 2009-1-2 16:47 编辑 ]
作者: shmilylxx    时间: 2009-01-03 14:01
原帖由 eveson 于 2009-1-2 01:10 发表
你可以整理出一个电子书


已经放出来了,在一楼,欢迎下载~
作者: Sorehead    时间: 2009-01-04 16:57
下来看看,论坛需要楼主这样的人。
作者: dreamice    时间: 2009-01-04 17:19
不太清楚,你这个是不是有点类似于ucos。赞扬LZ的精神
作者: shmilylxx    时间: 2009-01-04 22:36
原帖由 dreamice 于 2009-1-4 17:19 发表
不太清楚,你这个是不是有点类似于ucos。赞扬LZ的精神



1,核心数据结构完全不同
2,调度算法完全不同
3,唯一相同的是几个底层函数,呵呵~
作者: smallstar001    时间: 2009-01-05 09:22
very good
作者: djstava    时间: 2009-01-05 10:04
牛人。
作者: 高峰    时间: 2009-01-05 11:41
牛人
作者: chary8088    时间: 2009-01-05 12:00
LZ不错。。。
还没工程的源码,,,,,可以下载。。。
作者: dreamice    时间: 2009-01-05 13:00
标题: 回复 #16 shmilylxx 的帖子
呵呵,难怪我乍一看有点像ucos
作者: linternt    时间: 2009-01-05 15:18
难能可贵的共享精神和耐心,收藏此贴!
作者: wk9999    时间: 2009-01-05 17:17
好贴,好好学习一下
作者: shmilylxx    时间: 2009-01-05 22:19
附件里面的图片,是BenOS和UCOS的性能比对分析结果

事实上,这里显示的BenOS性能会好一点,
但是在其他测试程序中,是不一定的~
我没有手段作一个全面测试,另外,带工程的源码,会后续考虑放上来~

benos-ucos性能比较.JPG (60.09 KB, 下载次数: 92)

benos-ucos性能比较.JPG

作者: shmilylxx    时间: 2009-01-05 22:36
标题: 内存使用情况分析
1,内存(RAM)使用量统计
硬件系统启动使用:
系统堆栈(系统启动的时候需要使用)                                     80个字节

操作系统内核使用:
任务表                                                        96+16个字节       
                                                                           注:20个字节/任务 * 8(默认任务个数)
                                
信号量                                                        32个字节
                                                        注:4个字节/信号量 * 8(默认信号量个数)

消息队列                                                        54个字节
                                                        注: 默认8条消息/队列 * 3 (默认3个消息队列)
                                                               
空闲任务(Idle)                                                96个字节       
                                                                           注:每个任务最少需要96个字节的堆栈,推荐256个字节
                    
RAM使用统计:
一个原始的系统总共占用内存约为400个字节
其中内核占用内存不到300个字节

(注:根据armlink指示,另外还有96个字节的数据用于LIBSPACE.O文件,
其实是开发环境在为main主函数初始化的时候用到,算到OS的头上还不太合理,呵呵)


2,代码量(包括ROM里面的只读数据)统计
内核                                                        1200 个字节
信号量                                                        300 个字节
消息队列                                                           500 个字节

ROM使用统计
原始的系统代码小于2K
其中内核代码小于1.2K

3,总计:
2K二进制代码,
400个字节内存使用
(以上结论是根据编译结果得出)
作者: chary8088    时间: 2009-01-05 22:41
太好了,LZ真是好人哦
LZ也是牛人
作者: zhujun0513    时间: 2009-01-05 22:45
谢谢楼主的分享!
作者: shmilylxx    时间: 2009-01-07 22:35
以下是使用互斥访问实现的信号量(Thumb2汇编实现)


最近在考虑使用位带(bit band)实现互斥量
有没有朋友对位带实现互斥操作熟悉的?
给我点意见,不胜感谢


__asm I8 BenOSGetSemaphore1(SEMAPHORE_TYPE* pSem)
{
THUMB
REQUIRE8
PRESERVE8

  PUSH {R1,R2,LR}

TryToRead_G
  LDREX  R2, [R0]
  CMP R2,#0
  CBZ   R2,GetFailed_G

ReadSuccess_G     ;Try to get sem
  SUB   R2, #1
  STREX  R1,R2,[R0]
  CMP R1, #0
  BNE  TryToRead_G

GetSuccess_G
  MOV   R0, #1
  POP   {R1,R2,PC}

GetFailed_G
;  STREX  R1,R2,[R0]
  MOV   R0, #0
  POP   {R1,R2,PC}
}

__asm I8 BenOSReleaseSemaphore1(SEMAPHORE_TYPE* pSem)
{
  PUSH {R1,R2,LR}

TryToRead_R
  LDREX  R2, [R0]

ReadSuccess_R     ;Try to get sem
  ADD   R2, #1
  STREX  R1,R2,[R0]
  CMP R1, #0
  BNE   TryToRead_R

GetSuccess_R
  MOV   R0, #1
  POP   {R1,R2,PC}
}

__asm I8 BenOSWaitSemaphore1(SEMAPHORE_TYPE* pSem,I32U timeout)
{
  EXTERN BenOSTimeDly

  PUSH {R1,R2,R3,LR}
  ADD  R1, #1
  
Loop_W
TryToRead_W
  LDREX  R2, [R0]
  CBZ   R2,Wait1Tick_W

ReadSuccess_W     ;Try to get sem
  SUB   R2, #1
  STREX  R3,R2,[R0]
  CMP R3, #0
  BNE  TryToRead_W
  B   GetSuccess_W

Wait1Tick_W
;  STREX  R3,R2,[R0]
  SUB   R1, #1
  CBZ   R1,GetFailed_W
  PUSH  {R0-R3}
  MOV   R0,#1
  LDR   R3,=BenOSTimeDly
  BLX   R3
  POP   {R0-R3}
  B   Loop_W


GetSuccess_W
  MOV   R0, #1
  POP   {R1,R2,R3,PC}

GetFailed_W
  MOV   R0, #0
  POP   {R1,R2,R3,PC}  

}

[ 本帖最后由 shmilylxx 于 2009-1-7 22:38 编辑 ]
作者: chary8088    时间: 2009-01-08 09:39
帮顶
还没接触过嵌入式的,,,有没在XP上运行的虚拟机来运行这个系统
作者: vincentdpan    时间: 2009-01-08 12:48
不错
作者: konghui127    时间: 2009-01-10 14:48
呵呵,正开始学习RTOS,非常感谢搂主的贡献!!
作者: hunterformoney    时间: 2009-01-10 16:24
牛人
作者: shmilylxx    时间: 2009-01-11 22:46
原帖由 konghui127 于 2009-1-10 14:48 发表
呵呵,正开始学习RTOS,非常感谢搂主的贡献!!



源码工程文件在1楼可以下载了~
作者: shmilylxx    时间: 2009-01-11 22:51
原帖由 chary8088 于 2009-1-8 09:39 发表
帮顶
还没接触过嵌入式的,,,有没在XP上运行的虚拟机来运行这个系统


目前没有X86的移植版本,从理论上讲,是完全可行
建议您最好找到《自己动手写操作系统》一书,照着做
那本才是专门针对X86平台的。
作者: leiing    时间: 2009-01-12 01:19
牛人,谢谢分享
作者: chary8088    时间: 2009-01-12 14:58
原帖由 shmilylxx 于 2009-1-11 22:51 发表


目前没有X86的移植版本,从理论上讲,是完全可行
建议您最好找到《自己动手写操作系统》一书,照着做
那本才是专门针对X86平台的。



多谢LZ,,,是非常的感谢
作者: 真菜    时间: 2009-01-12 16:08
想不到论坛里面还有这等厉害的角色,支持!
作者: shmilylxx    时间: 2009-02-03 21:58
最新使用护持访问实现的信号量操作。(汇编实现)


/*当前信号量列表*/
SEMAPHORE_TYPE BenOSSemaphore[MAX_SEM_NUM];   
/*
* 创建信号量
*/
SEMAPHORE_TYPE* BenOSCreateSemaphore(I32 conuter)
{
INT_Stat  stat = 0;
I32U  index;

if (conuter < 0)
{
  return (SEMAPHORE_TYPE*)NULL;
}

stat = BenOS_INT_Save();
for(index=0;index<MAX_SEM_NUM;index++)
{
  if (BenOSSemaphore[index]==-1)
  {
   BenOSSemaphore[index] = conuter;
   BenOS_INT_Restore(stat);
   return &(BenOSSemaphore[index]);
  }
}

BenOS_INT_Restore(stat);
return (SEMAPHORE_TYPE*)NULL;
}

I8 BenOSDeleteSemaphore(SEMAPHORE_TYPE* pSem)
{
INT_Stat  stat = 0;

stat = BenOS_INT_Save();

/*当且仅当信号量计数为0的时候,才能释放该信号量*/
if ((*pSem) != 0)
{
  BenOS_INT_Restore(stat);
  return BenOS_Err;
}
else
{
  (*pSem) = (SEMAPHORE_TYPE)-1;
  BenOS_INT_Restore(stat);
  return BenOS_OK;
}
}
__asm I8 BenOSGetSemaphore(SEMAPHORE_TYPE* pSem)
{
THUMB
PRESERVE8

  PUSH {R1,R2,LR}


TryToRead_G
  LDREX  R2, [R0]
  CLZ   R1,R2
  CBZ   R1,GetFailed_G
  CBZ   R2,GetFailed_G

ReadSuccess_G     ;Try to get sem
  SUB   R2, #1
  STREX  R1,R2,[R0]
  CMP R1, #0
  BNE  TryToRead_G

GetSuccess_G
  MOV   R0, #1
  POP   {R1,R2,PC}

GetFailed_G
;  STREX  R1,R2,[R0]
  MOV   R0, #0
  POP   {R1,R2,PC}
}

__asm I8 BenOSReleaseSemaphore(SEMAPHORE_TYPE* pSem)
{
  PUSH {R1,R2,LR}

TryToRead_R
  LDREX  R2, [R0]

ReadSuccess_R     ;Try to get sem
  ADD   R2, #1
  STREX  R1,R2,[R0]
  CMP R1, #0
  BNE   TryToRead_R

GetSuccess_R
  MOV   R0, #1
  POP   {R1,R2,PC}
}

__asm I8 BenOSWaitSemaphore(SEMAPHORE_TYPE* pSem,I32U timeout)
{
  EXTERN BenOSTimeDly

  PUSH {R1,R2,R3,LR}
  ADD  R1, #1
  
Loop_W
TryToRead_W
  LDREX  R2, [R0]
  CLZ   R3,R2
  CBZ   R3,GetFailed_W
  CBZ   R2,Wait1Tick_W


ReadSuccess_W     ;Try to get sem
  SUB   R2, #1
  STREX  R3,R2,[R0]
  CMP R3, #0
  BNE  TryToRead_W
  B   GetSuccess_W

Wait1Tick_W
;  STREX  R3,R2,[R0]
  SUB   R1, #1
  CBZ   R1,GetFailed_W
  PUSH  {R0-R3}
  MOV   R0,#1
  LDR   R3,=BenOSTimeDly
  BLX   R3
  POP   {R0-R3}
  B   Loop_W

GetSuccess_W
  MOV   R0, #1
  POP   {R1,R2,R3,PC}

GetFailed_W
  MOV   R0, #0
  POP   {R1,R2,R3,PC}  
  NOP
}
作者: which    时间: 2009-02-03 22:04
要移植网络的协议栈.包括如何支持各种硬件才更有意义哦.
作者: jxfengzi    时间: 2009-02-04 09:07
顶。。。
作者: smartlinux    时间: 2009-02-04 09:37
崇拜啊
作者: maxxfire    时间: 2009-02-04 09:40
厉害,,拜读一下 。。
作者: axlrose    时间: 2009-02-04 09:51
又一个强人,支持一下
作者: yijun    时间: 2009-02-04 15:29
很强大,慢慢学习哈~~~
作者: niuyimail    时间: 2009-02-04 20:53
标题: 祝贺你刚刚入门RTOS.
请您发表一些真正的创新和比老外高一酬的技术文章.不要发表这些只是入了门的文章,对国内的技术毫无意义.

例如:
实现一个 相当有效率的内存管理模块!


在OS设计中,内存管理是相当重要也相当难的一个重量级模块,我写一个类linux的OS的任务调度\信号量等 花了个把月,但写内存管理我花了4个月了,还未完成。
不过这次,我设计的这个内存管理我觉得非常好,应该有相当的竞争力,我很有信心。因为我看了linux的内存管理,有了比较。
作者: shmilylxx    时间: 2009-02-05 23:00
标题: 回复 #45 niuyimail 的帖子
您的回复是我收到的新年第一块砖,祝贺您!

1,对于RTOS,真正的重量级模块并不是内存管理,而是实时调度模块,最大中断临界时间;
   这才是是衡量一个RTOS的最重要的指标。

2,在RTOS上面,真正意义上面的创新,大概在十年前就停止了,
   其实在一个领域的创新出现最多的时候是该领域刚刚开始发展的时候;

3,在嵌入式系统之中,很多平台都没有MMU,即使是十分想要(主要是移植应用需要),
   也都会使用内存池的方式(主要是考虑到实时性)

4,LINUX是通用OS,跟RTOS没有太大的可比性。

5,个人对内存管理不是很熟悉,也很期待您开放您的源码,让我也能接触下高手的代码。
作者: Send_linux    时间: 2009-02-05 23:16

有交流才有进步吧,拍砖是交流的一种形式,哈哈
作者: wanas    时间: 2009-07-10 17:00
原帖由 shmilylxx 于 2009-1-4 22:36 发表



1,核心数据结构完全不同
2,调度算法完全不同
3,唯一相同的是几个底层函数,呵呵~


看了楼主的delay的实现,这个和ucosII没什么区别。这个延时模型是最原始的了。
如果任务太多了怎么办?不需要延时的任务也必须参与操作?如果用户需要回调类型的timer怎么扩展?

[ 本帖最后由 wanas 于 2009-7-10 17:08 编辑 ]
作者: anders0913    时间: 2009-07-10 17:39
感慨啊~~
作者: zzr1974418    时间: 2011-01-16 16:28
很好的 。。。。。谢谢!
作者: rain_fish    时间: 2011-01-17 20:37
lz牛
作者: asdf93945    时间: 2011-01-18 13:01
{:3_200:}{:3_200:}{:3_200:}
作者: johntsu    时间: 2011-01-30 15:08

作者: lino310    时间: 2011-01-30 15:34
  膜拜!
作者: uindex    时间: 2011-01-30 16:20
BenOSTimeDly,BenOSCreateSemaphore,BenOSCreateTask,BenOSInit......

楼主功德无量!
作者: bluewaterray    时间: 2011-01-31 12:48
以前看过ucOS,支持下国内高手
作者: zhycncnet    时间: 2011-01-31 13:34
LZ,你是高人。
作者: starzhestarzhe    时间: 2011-01-31 13:41
新年伊始


 又一个新年伊始
作者: terrywea    时间: 2011-02-13 10:53
回复 1# shmilylxx
算不算的上是我们这些人的标签,Cortex—M3,也刚接触,也是因为工作。。
作者: sailings    时间: 2011-02-16 13:36
顶起来......
作者: jackarain    时间: 2012-03-03 03:06
向楼主学习!!!
作者: yulihua49    时间: 2012-03-03 21:40
本帖最后由 yulihua49 于 2012-03-03 21:42 编辑

我认为最难的是虚存管理和文件(磁盘)管理,
IO管理,其中的网络管理,时至今日TCP/IP是必须的吧?
系统在网上BOOT如何?有如无盘工作站。
作者: lilinly225    时间: 2012-03-04 22:39
回复 1# shmilylxx





哈哈哈,牛人啊

   
作者: lilinly225    时间: 2012-03-04 22:44
楼主,支持你, 可以招募人员参加你的项目啊,也许以后就是另外一个Linux了

作者: pitonas    时间: 2012-03-05 00:35
楼主犹如神的地步
作者: gpf626487    时间: 2012-03-05 08:25
路过顶lz

作者: lidonghuizo    时间: 2012-04-27 14:52
LZ 高人  只有回帖支持一下 谢谢
作者: zxc381219    时间: 2012-04-28 00:23
mark 好好看看
作者: d19890104    时间: 2012-05-03 16:59
对汇编比较头疼
作者: n73man001    时间: 2012-05-26 10:29
膜拜大神!向楼主多多学习!
作者: panda421    时间: 2012-05-29 18:39
顶,我们要搞自己的实事的东东
作者: chinesedragon    时间: 2012-05-30 13:07
太牛了~~~~~~~~~`
作者: enjoy-linux    时间: 2012-10-20 22:33
膜拜!
作者: emwoody    时间: 2012-10-23 15:17
看贴不顶,那是不行滴
作者: hansion3406    时间: 2012-11-01 00:58
嘿嘿~奸笑中!
作者: hb255    时间: 2012-11-06 22:15
很好的~~~有空学学
作者: cuibixiong_cu    时间: 2012-11-07 17:37
请问LZ,这个的测试环境是?
作者: qiushui_007    时间: 2012-12-21 16:10
目前, 这个 BenOS 应用的稳定不? 有无继续升级?
作者: yulinxueyuan    时间: 2012-12-25 10:00
牛逼啊  哥们!  人才大大的。
作者: jiangandz    时间: 2012-12-28 11:55
不错! ,感谢楼主,望国人加油,
作者: anders0913    时间: 2013-01-07 16:45


支持,学习。
作者: tacbo    时间: 2013-01-27 11:21
LZ万岁万岁万万岁, 值得借鉴!
作者: wgain    时间: 2013-05-07 17:23
学习了,顶楼主
作者: drfxiaoliuzi    时间: 2013-05-10 08:24
学习了,膜拜。。。{:3_193:}
作者: alayi    时间: 2013-05-31 14:44
支持一个        
作者: benetqq    时间: 2013-06-10 08:20
正儿八经的支持楼主
作者: 546588532    时间: 2013-06-16 10:47
标记一下。楼主犀利。
作者: zpyws    时间: 2013-06-27 23:12
回复 1# shmilylxx


    如果在处理PENDSV中断时,来了一个高优先级的中断并再次触发一个PENDSV请求,系统有没有考虑到这种极端情况?
作者: 群雄逐鹿中原    时间: 2013-06-27 23:28
zpyws 发表于 2013-06-27 23:12
回复 1# shmilylxx


PENDSV,芯片设计时就会考虑到此需求-- 随时可以被更高优先级的中断抢占,
这样可以保证实时性的要求。

中断嵌套,本来就没什么问题。
作者: mcyeah    时间: 2013-07-13 18:53
诶呦   这个厉害了   
作者: ecitapple    时间: 2013-07-15 16:32
大神,膜拜中.........
作者: hpxzw520    时间: 2013-08-12 12:55
多谢分享 学习学习!!
作者: wushuilin    时间: 2013-08-17 10:16
LZ可以改进成双堆栈模式吗?
作者: yourcaptain    时间: 2013-08-17 11:15
赞~~~  可否发送一些大作供品味?  yuan.aiqing@gmail.com

作者: xiaoloudoufu    时间: 2013-08-20 23:18
谢谢楼主!给了很好的参考!
作者: icehill711    时间: 2014-03-08 20:17
楼主高尚
作者: leslielg    时间: 2014-03-10 23:53
看了system tick的处理后面都不用看了,水平欠佳,这都拿的出手?
作者: linwenxi1126    时间: 2014-04-23 10:33
很好!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
作者: linuly    时间: 2015-04-30 11:17
楼主有没有对RTX进行过研究?
作者: samos2011    时间: 2015-08-05 08:40
本帖最后由 samos2011 于 2015-08-05 08:42 编辑

回复 97# leslielg

看样子您是高手,小弟不才,也写了一个,欢迎您评估这个极易使用的符合设计模式的C++版RTOS
www-cnblogs.com/samos2011/


   
作者: zhang_int_int    时间: 2015-08-11 11:03
不得不赞一个啊




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2