posts - 403, comments - 310, trackbacks - 0, articles - 7
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

发信人: Zellux (null), 信区: Software_06
标 题: OSLab之中断处理
发信站: 日月光华 (2008年08月30日20:15:58 星期六), 站内信件

1. 准备工作
在开始分析Support Code之前,先配置下我们的Source Insight,使它能够支持.s文件的搜索。

在Options->Document Options->Document Types中选择x86 Asm Source File,在File fileter中增加一个*.s,变成*.asm;*.inc;*.s 然后在Project->Add and Remove
Project Files中重新将整个oslab的目录加入,这样以后进行文本搜索时.s文件也不会漏掉了。

2. Source Insight使用
接下来简单分析下内核启动的过程,在浏览代码的过程中可以迅速的掌握Source Insight的使用技巧。

lib/multiboot /multiboot.s完成了初始化工作,可以看到其中一句call
EXT(multiboot_main)调用了C函数multiboot_main,使用ctrl+/搜索包含multiboot_main的所有文件,最终base_multiboot_main.c中找到了它的定义。依次进行cpu、内存的初
始化,然后开启中断,跳转到kernel_main函数,也是Lab1中所要改写的函数之一。另外
在这里可以通过ctrl+单击或者ctrl+=跳转到相应的函数定义处,很方便。

3. irq处理初始化工作
来看下Lab 1的重点之一,irq的处理。跟踪multiboot_main->base_cpu_setup->base_cp
u_init->base_irq_init,可以看到这行代码
gate_init(base_idt, base_irq_inittab, KERNEL_CS);
继续使用ctrl+/找到base_irq_inittab的藏身之处:base_irq_inittab.s

4. base_irq_inittab.s
这个汇编文件做了不少重复性工作,方便我们在c语言级别实现各种handler。
GATE_INITTAB_BEGIN(base_irq_inittab) /* irq处理函数表的起始,还记得jump
table 吗? */
MASTER(0, 0) /* irq0 对应的函数 */


来看看这个MASTER(0, 0)宏展开后是什么样子:
#define MASTER(irq, num) \
GATE_ENTRY(BASE_IRQ_MASTER_BASE + (num), 0f, ACC_PL_K|ACC_INTR_GATE) ;\
P2ALIGN(TEXT_ALIGN) ;\
0: ;\
pushl $(irq) /* error code = irq vector */ ;\
pushl $BASE_IRQ_MASTER_BASE + (num) /* trap number */ ;\
pusha /* save general registers */ ;\
movl $(irq),%ecx /* irq vector number */ ;\
movb $1 << num,%dl /* pic mask for this irq */ ;\
jmp master_ints

依次push irq号,trap号(0x20+irq号),通用寄存器(eax ecx等)入栈,把irq号保
存到ecx寄存器,然后跳转到master_ints,master_ints是所有master interrupts公用
的代码。

跳过master_ints的前几行,从第七行开始
/* Acknowledge the interrupt */
movb $0x20,%al
outb %al,$0x20

/* Save the rest of the standard trap frame (oskit/x86/base_trap.h). */
pushl %ds
pushl %es
pushl %fs
pushl %gs

/* Load the kernel's segment registers. */
movw %ss,%dx
movw %dx,%ds
movw %dx,%es

/* Increment the hardware interrupt nesting counter */
incb EXT(base_irq_nest)

/* Load the handler vector */
movl EXT(base_irq_handlers)(,%ecx,4),%esi

注释写得很详细,首先发送0x20到0x20端口,也就是Lab1文档上所说的发送INT_CTL_DON
E到INT_CTL_REG,看来这一步support code已经替我们完成了。接下来保存四个段寄存
器ds es fs gs,并读入kernel态的段寄存器信息。

最后一句很关键,把base_irq_handlers + %ecx * 4这个值保存到了esi寄存器中,%ecx
中保存了irq号,而*4则是一个函数指针的大小,那么base_irq_handlers是什么呢?继
续用ctrl+/搜索,可以在base_irq.c中找到这个数组的定义
unsigned int (*base_irq_handlers[BASE_IRQ_COUNT])(struct trap_state *ts)
且初始时这个数组的每一项都是base_irq_default_handler

看来这句汇编代码的功能是把处理irq对应的函数地址保存到了esi寄存器中。
为了证实这一点,继续看base_irq_inittab.s的代码:
#else
/* Call the interrupt handler with the trap frame as a parameter */
pushl %esp
call *%esi
popl %edx
#endif
果然,在保存了esp值后,紧接着就调用了esi指向的那个函数。而从那个函数返回后,
之前在栈上保存的相关信息都被恢复了:

/* blah blah blah */
/* Return from the interrupt */
popl %gs
popl %fs
popl %es
popl %ds
popa
addl $4*2,%esp /* Pop trap number and error code */
iret
这样就恢复到了进入这个irq处理单元前的状态,文档中所要求的保存通用寄存器这一步
其实在这里也已经完成了,不需要我们自己写代码。

好了,这样一分析后,我们要做的事情就很简单,就是把base_irq_handlers数组中的对
应项改成相应的handler函数就行了。
注意index是相应的idt_entry号减去BASE_IRQ_SLAVE_BASE,或者直接使用IRQ号。

另外这个数组的初始值都是base_irq_default_handler,用ctrl+左键跳到这个函数的定
义,可以看到这个函数只有一句简单的输出语句:
printf("Unexpected interrupt %d\n", ts->err);
而这就是没有注册handler前我们所看到的那句Unexpected interrupt 0的来源了。

5. struct trap_state *ts
所有的handler函数的参数都是一个struct trap_state *ts,这个参数是哪来的呢?
注意call *%esi的前一行
/* Call the interrupt handler with the trap frame as a parameter */
pushl %esp
这里把当前的esp当作指向ts的指针传给了handler,列一下从esp指向的地址开始的内容
,也就是在此之前push入栈的内容:

pushl $(irq) /* error code = irq vector */ ;\
pushl $BASE_IRQ_MASTER_BASE + (num) /* trap number */ ;\
pusha /* save general registers */ ;\
pushl %ds
pushl %es
pushl %fs
pushl %gs

再看一下trap_state的定义,你会发现正好和push的顺序相反:
/* Saved segment registers */
unsigned int gs;
unsigned int fs;
unsigned int es;
unsigned int ds;

/* PUSHA register state frame */
unsigned int edi;
unsigned int esi;
unsigned int ebp;
unsigned int cr2; /* we save cr2 over esp for page faults */
unsigned int ebx;
unsigned int edx;
unsigned int ecx;
unsigned int eax;

/* Processor trap number, 0-31. */
unsigned int trapno;

/* Error code pushed by the processor, 0 if none. */
unsigned int err;

而这个定义后面的
/* Processor state frame */
unsigned int eip;
unsigned int cs;
unsigned int eflags;
unsigned int esp;
unsigned int ss;
则是发生interrupt时硬件自动push的五个数据(参见Understand the Linux Kernel)

也就是说,ts指针指向的是调用当前handler前的寄存器状态,也是当前handler结束后
用来恢复的寄存器状态,了解这一点对以后的几个lab帮助很大。

p.s. 另外提一句和这个lab无关的话,非vm86模式下栈上是不会有v86_es等四个寄存器
信息的,所以以后根据task_struct指针计算*ts的地址时使用的偏移量不应该是sizeof(
struct trap_state)

6. The End
这样差不多就把support code中处理interrupt的方法过了一遍(另外还有base_trap_in
ittab.s,不过和irq的处理很相似)

了解这些后Lab1就比较简单了,不需要任何内嵌汇编代码即可完成。

posted @ 2008-09-02 11:55 ZelluX 阅读(641) | 评论 (5)编辑 收藏

     摘要: 美国为什么需要这么多大学生,而中国培育出这么多优秀大学生为什么失业?难道是我们学生程度不够?难道是我们同学不够用功?难道是我们同学专业不对口?那我告诉所有读者,为什么大学生就业难……   阅读全文

posted @ 2008-07-28 11:31 ZelluX 阅读(678) | 评论 (5)编辑 收藏

用ctags -R或者ctags * -R的时候只能生成当前目录下的tag,检查了半天发现原来这个版本的ctags的参数顺序只能老老实实的来:ctags -R *

太囧了,总归要bs下的,虽说也有那么一点点可能是bash解析参数时的问题,不过我猜问题来源还是这个低版本的ctags = =

话说我也挺圡的,不习惯用source insight,还是喜欢用vim写代码

posted @ 2008-07-15 10:41 ZelluX 阅读(544) | 评论 (3)编辑 收藏

没心思看离散,也不准备坚持看没有荷兰的欧洲杯决赛。闲着点好友的Q-Zone,原来Q-Zone首先会判断你的浏览器,如果是Firefox它会重定向到RSS阅读界面。

安然在开学后2个月写的一篇日志,“记忆里的名单”,惊喜的看到有我。也列出了一张属于我的名单。好,等待时间的遴选。

“于是想 如果有个妹妹 我要告诉她 好好放肆猖狂 做不可思议的事情 为友情和少年青涩的爱情花心思 做只是喜欢没有功利目的的事情 这么好的年华 就是用来这样浪费 和珍惜的~”

可惜我只保持了四五个月的这种疯狂,现在依然纠结于功利的选择。有时候曾想,或许那次失败更适合我,或许我终将把这么一条平淡无奇的路走到尽头。“表面强者”,或许还是很有道理的。

看到fofo的博的文字,“我要去杭州,把所有的事情抛掉,不管后果。这个地方太让人压抑,尽管有很玩得来的室友,有很好的足球队的队友,可以看很多以前爸妈不让看的喜欢的书还有过米的比赛,吃的东西也都很习惯,还是会在天气很好的星期天下午突然想起曾经在冬日的阳光照射下一家人在阳台上围着一张桌子吃饭的情景,还是会在一个人骑在去计算机协会的路上很难过地想着再也不会有那么四个或者五个人在一起吃完小炒放肆地在铺满夕阳的校园小路上勾肩搭背地行走了,还是会在一百多个人的课堂上怀念起那些艰苦却简单的日子里所有的笑声,还是会在网吧包夜的时候想起初中时捏着饭钱偷偷摸摸地去电脑房玩星际……想找找朋友们,调整一下自己的心情。”

真的找不回来了。在写这篇博文时也找不到以前写字的感觉了。

明天离散考试,某个记录或许将要因此打破。

posted @ 2008-06-30 02:09 ZelluX 阅读(376) | 评论 (1)编辑 收藏

不枉我周末练了那么多ZvP

不过总比分太惨了。。

posted @ 2008-06-24 00:20 ZelluX 阅读(400) | 评论 (0)编辑 收藏

     摘要: 一篇关于函数式编程的介绍,在水木Java版引起了热烈讨论。  阅读全文

posted @ 2008-06-05 21:10 ZelluX 阅读(765) | 评论 (1)编辑 收藏

1. framwork/policies/Singleton.h
Singleton模式,可以指定相应的线程模型、创建策略和生命期控制策略。
对于全局范围的Singleton实例,定义了若干个宏便于访问,例如
#define sLog MaNGOS::Singleton<Log>::Instance()
#define sMaster MaNGOS::Singleton<Master>::Instance()

Singleton的定义:


不知道这里的注释Prohibited actions...this does not prevent hijacking.是什么意思,copy constructor和hijacking有什么关系呢?

另外注意这行typedef typename ThreadingModel::Lock Guard;,原来typedef还可以用在函数上。

Singleton的Instance方法用的是标准的double-checked lock方法,关于DCL可以参考这篇博文http://www.blogjava.net/zellux/archive/2008/04/07/191365.html

2. Explicit Constructors
game/WorkPacket.h中看到的语法,防止构造函数中参数的隐式转型
比如explicit String(int n); 用String('c')声明时就会报错

posted @ 2008-06-03 19:03 ZelluX 阅读(777) | 评论 (0)编辑 收藏

一套基于文件系统的安全方案,主要通过隔离运行不可信任的程序、taint记录、事故恢复。

我的presentation:
http://docs.google.com/Presentation?id=dcjk4xx7_473cv5ddgc8

出于时间考虑没有提到paper中进程间通信的解决方法

posted @ 2008-05-28 15:23 ZelluX 阅读(506) | 评论 (0)编辑 收藏

水木上有人贴了个有趣的程序

#include  < stdlib.h >
#include 
< stdio.h >

void  print_forever( int  n)
{
    printf(
" %d\n " , n);
    print_forever(n 
+   1 );
}


int  main( int  argc,  char   * argv[])
{
    print_forever(
0 );
    
return   0 ;
}


用gcc -O2编译运行后会不停地打印从0开始的自然数,注意如果编译器没有做优化的话,打印到某个数的时候肯定会发生栈溢出从而程序终止的情况,但这个程序却能一直运行下去,说明编译器做了尾递归优化。

用gcc -O2 -S生成这个程序的汇编代码后证实了这一点。
.L6:
        movl    
%ebx, 4(%esp)
        addl    $
1%ebx
        movl    $.LC0, (
%esp)
        call    printf
        jmp     .L6
print_forever的关键部分被优化成了一个n不断增加的死循环。

接下来是分析哪个优化选项处理了尾递归。

用O3 O2 O1三个优化强度编译程序,查看汇编代码后,发现尾递归优化是O2中新增的功能。于是查看O2新开启的优化开关:
gcc -c -Q -O1 --help=optimizers > /tmp/O1-opts
gcc -c -Q -O2 --help=optimizers > /tmp/O2-opts
diff /tmp/O2-opts /tmp/O1-opts | grep enabled
输出结果:

经证实是-foptimize-sibling-calls这个选项实现了尾递归的优化,具体内容可以参看
http://gcc.gnu.org./ml/gcc-patches/2000-03/msg00867.html

posted @ 2008-05-24 02:05 ZelluX 阅读(2431) | 评论 (1)编辑 收藏

睡觉去恩

P.S 点球真不是人看的

posted @ 2008-05-22 05:44 ZelluX 阅读(448) | 评论 (0)编辑 收藏

仅列出标题
共39页: 上一页 1 2 3 4 5 6 7 8 9 下一页 Last