很多朋友要求我将LMOS开源,这个问题我还没想好,不过我可以将一些框架
性的东西公开,这些代码是用于描述框架的。大家可以看看吧。
中断处理框架:
这里我很想骂人了,因为现在大多数理论性的操作系统教程,一开始就讲进
程以及进程相关的算法进而什么互斥啊、同步啊、死锁啊,等等一大堆。我承认
操作系统中进程是很重要,但不是最重要的。也许读者看了半天,终于明白进程
是啥玩意儿了,可是终究不能明白在操作系统内部是如何组织若干进程让CPU去
执行它们又是如何从它们手中夺回CPU,又分给其它进程的。有些教程还在开始
简要的提及了中断,但是并没说为什么要中断,以及中断了该做些啥。以至于读
者还是会感觉是一知半解,当然对付考试还是可以的,呵呵。在我看来一本脱离
了具体硬件平台的操作系统教程,那就是空中楼阁,终究不能让读者彻头彻尾的
了解操作系统的。
我可以告诉大家操作系统中执行最频繁的代码就是中断处理代码,而不是其
它别的。我很难想像一硬件平台不提供中断那会是什么样子。
好了大家看代码吧,我在次申明这些代码只是描述性的,只是和LMOS中大
相近庭,还请大家不要把这些十分糟糕的代码到处复制-粘贴。
中断处理代码:
;************************************************************
;* 底层核心文件Kernel.asm *
;* 彭东 @ 2011.12.01.10.00 *
;************************************************************
.....
ALIGN 16
GxH_hxi_hwint: //这个标号的地址,会被内核的初始化程序写
//入处理器的中断描述表中。
pushad //这是将x86cpu的8个通用寄存器压入当前线程
//(我的系统内核中基本的执行单元是线程,
//也有进程不过它只是描述一个线程的“执行
//所需资源”的“容器”一个进程内可包含多
//个线程)的内核态堆栈 。
push ds
push es
push fs
push gs
//把这4个数据段寄存器也压入堆栈,一句话
//上面这些汇编指令就是为了保存机器状态
//以便下次能重新启动
mov di, ss
mov ds, di
mov es, di
mov gs, di
//把段寄存器设成0号特权级的段的索引,你
//用用户级特权,去访问内核级特权级数据
//或者代码就处理器电路就产生通常是14号
//保护异常。
push %1
//压入当前中断号,为调用GxH_intpt_allocator
//准备参数,C语言调用协议都是
//调用者准备参数和清理堆栈。
call GxH_intpt_allocator
//调用中断总分发器,进入它后就会调用相
//应的中断服务程序了。
add esp,4
//清理堆栈。
pop gs
pop fs
pop es
pop ds
popad //这堆代码, 恢复线程的机器状态。
iretd //中断返回。
............
/**********************************************************
中断总分配器源文件intallocator.c
***********************************************************
彭东 @ 2011.12.21.10.20
**********************************************************/
PUBLIC void GxH_intpt_allocator(u32_t intnumb)
{
u32_t svflags,intflags;
GxH_save_interrupt(&intflags);
//保存当前中断控制器的状态
GxH_irq_mask((u8_t)intnumb);
//关掉当前中断号的中断信号
//线,以免发生中断重入。
GxH_save_flags_sti(&svflags);
//开启处理器的中断,以便可
//以响应其它中断,处理器在
//响应中断时会自动关掉中断.
GxH_do_irq_up_thread(intnumb);
//唤醒(通常情况会执行)当
//前中断服线程这种设计很前
//卫,不管是windows还是linux
//它们中断服务程序都是以函
//数(也就是代码段)的形式
//存在的,也就是说一旦产生
//中断那么中断服务程序必须
//被执行,它们永远占据着系
//统中最高优先权,可能对其
//它线程造成很大的延迟,然
//而这对系统的响应实时性应
//用,是极其不利的,比如说
//核电站的温度监控线程,和
//火箭发动机的控制线程,经
//不起丝毫的等待,不是吗,
//在说了我的内核讲究世法平
//等,没有谁能有特权,每个
//线程调度的优先权,都是动
//态的,被调度的次数越多,
//优先权就会越低,没有谁会
//“撑爆”也没有谁会“饿死”
if(sched_read_sdneedsched()==MD_SD_NEEDSCHED||
sched_read_preempt()==MD_SD_PREEMPT)
{
//这里会检查内核的调度和抢
//占标志,只要有一个其中一
//个条件满足,就会激发调度
//机制从而选取更值得执行的
//线程运行,我的内核是可抢
//占的,只有拥有自旋锁和信
//号量的代码段是不被抢占的
//其它都行。
schedul();
}
GxH_restore_flags_cli(&svflags);
//关掉处理器中断,因为内核
//代码控制路经到这里,说明
//内核的任务已经做完了,准
//备恢复线程(可能是另一个
//线程)的机器状态了,把处
//理器控制权还给用户的应用
//软件比如QQ,KUGOU,哈哈
GxH_restore_interrupt(&intflags);
//打开当前中断号的中断信号线。
return;
}
下面看看LMOS中的进程是如何组织的:
/**********************************************************
初始化进程调度器头文件init_sched_t.h
***********************************************************
彭东 @ 2012.04.01.16.30
**********************************************************/
#ifndef _INIT_SCHED_T_H
#define _INIT_SCHED_T_H
#define MD_SD_SMCHEAD_NR (CPU_MAX_NRS+1)
#define MD_SD_NOTCPUSIDLE (CPU_MAX_NRS+2)
typedef struct s_PER_CPUSCHED
{
spinlock_t pscd_lock;
u32_t pscd_cpuphyid;
u32_t pscd_cpuid;
u32_t pscd_iscpu_idle;
u32_t pscd_abrunflags;
u32_t pscd_td_nr;
u32_t pscd_ps_nr;
atomic_t pscd_needsched;
atomic_t pscd_preempt_idx;
struct s_THREAD* pscd_td_current;
struct s_PROC* pscd_ps_current;
struct s_THREAD* pscd_td_cpuidle;
struct s_PROC* pscd_ps_cpuidle;
list_h_t pscd_a_b_run_hl;
list_h_t pscd_b_a_run_hl;
list_h_t pscd_new_hl;
list_h_t pscd_run_hl;
list_h_t pscd_sleep_hl;//各种状态的进程队列头
list_h_t pscd_block_hl;
list_h_t pscd_wait_hl;
list_h_t pscd_dead_hl;
list_h_t pscd_cach_hl;
//略......
}per_cpusched_data_t;
typedef struct s_SCHED_MCHEAD
{
spinlock_t sd_lock;
list_h_t sd_list;
atomic_t sd_needsched;
atomic_t sd_preempt_idx;
atomic_t sd_allisnidle;//用于判断哪个CPU负载最低
u32_t sd_td_nr;
u32_t sd_ps_nr;
per_cpusched_data_t sd_cpuscheddata[MD_SD_SMCHEAD_NR];
//有多少
//CPU就有多少个;
atomic_t sd_ps_idcount;
atomic_t sd_td_idcount;
//略......
}sched_mchead_t;
//略......
#endif
由于LMOS是支持多CPU的内核,那么就需要对各种全局数据结构进行保护。
下面看看LMOS自旋锁的实现:
/**********************************************************
自旋锁头文件spinlock.h
***********************************************************
彭东 @ 2012.01.15.16.00
**********************************************************/
#ifndef _SPINLOCK_H
#define _SPINLOCK_H
KLINE void x86_spin_lock_init(spinlock_t * lock)
{
lock->lock = 0;
}
KLINE void x86_spin_lock(spinlock_t * lock)
{
__asm__ __volatile__ (
"1: \n\t"
"lock; xchg %0, %1 \n\t"
"cmpl $0, %0 \n\t"
"jnz 2f \n\t"
".section .spinlock.text,""\"ax\"""\n\t"// 重新定义一个代码段
//所以jnz 2f下面并不是
"2: \n\t" //cmpl $0,%1 事实上
//下面的代码不会常常执行,
"cmpl $0, %1 \n\t" //这是为了不在cpu指令
//高速缓存中填充无用代码
"jne 2b \n\t" //要知道那可是用晶体管
//做的双极性静态
"jmp 1b \n\t" //储存器,比内存条快得多。
".previous \n\t"
:
: "r"(1), "m"(*lock));
}
KLINE void x86_spin_unlock(spinlock_t * lock)
{
__asm__ __volatile__(
"movl $0, %0\n\t"
:
: "m"(*lock));
}
KLINE void x86_spin_lock_cli(spinlock_t * lock,u32_t* flags)
{
__asm__ __volatile__(
"pushfl \n\t"
"cli \n\t"
"popl %0 \n\t"
"1: \n\t"
"lock; xchg %1, %2 \n\t"
"cmpl $0,%1 \n\t"
"jnz 2f \n\t"
".section .spinlock.text,""\"ax\"""\n\t"// 重新定义一个代码段
//所以jnz 2f下面并不是
"2: \n\t" //cmpl $0,%1 事实上下
//面的代码不会常常执行,
"cmpl $0,%2 \n\t" //这是为了不在cpu指令
//高速缓存中填充无用代码
"jne 2b \n\t" //要知道那可是用晶体管
//做的双极性静态
"jmp 1b \n\t" //储存器,比内存条快得多
".previous \n\t"
:"=m"(*flags)
: "r"(1), "m"(*lock));
}
KLINE void x86_spin_unlock_sti(spinlock_t* lock,u32_t* flags)
{
__asm__ __volatile__(
"movl $0, %0\n\t"
"pushl %1 \n\t"
"popfl \n\t"
:
: "m"(*lock), "m"(*flags));
}
#endif
暂不提供更多的信息。不好意思了。大家见谅。
这么丑陋的代码,公开实在是惭愧死我了。还请大家看看吧。喷青就不
要看了,哈哈,另外还请大家不要拿着我的代码,到处乱说话,谢谢。