现在,到了真正开始阅读unix源码的时候了。当然,要从系统启动开始。
PDP-11启动时,首先激活常驻在处理机中ROM里的引导程序。ROM中的引导装入程序装入一更大
的装入程序(从系统盘的#0块),该程序查找并将称为“ / u n i x”的文件装入到内存的低地址部分。
然后,它将控制转移到已装入#0地址的指令。
我们的代码之旅,就从#0地址开始。
502 br4 = 200
503 br5 = 240
504 br6 = 300
505 br7 = 340
506
507 .=0^. /当前地址 =0 octal
508 br 1f /跳到后面的标号“1”
509 4 /其实是”IOT”指令啦,反正目前执行不到,不多说了。
510
511 / Trap Vector
512 Trap; br7+0;
…………………..
522 1: jump start /跳到start处执行
start
611 .globl _start, _end, _edata, _main
612 start:
613 bit $1, SSR0 /检查SSR0 bit 0,即“存管系统indicator”
614 bne start /存管系统已启用,错误!跳回start
615 reset / reset
616 .
当系统初次启动时,存管indicator应该是关闭的。之所以要进行这种看似不必要的检查,莱昂书中
的解释是:当双总线超时错时,会跳到#0地址执行中断处理程序(即它的中断矢量地址是#0)。但是
,翻遍了全书,也找不到对“双总线超时”的中断的介绍。而且,PDP-11是unibus系统,这个双总线
也不知是何物。我猜测是其它系统的硬件中断,代码移植时保留了下来。姑且存疑。
617 / initialize systems segments
618 .
619 mov $KISA0, r0 /r0指向Kernel Address Register的第一个(编号为:0)
620 mov $KISD0, r1 /r1指向Kernel Description Register的第一个(编号为:0)
621 mov $200, r4
622 clr r2
623 mov $6, r3
624 1:
625 mov r2, (r0)+
626 mov $77406, (r1)+
627 add r4, r2
628 sob r3, 1b
循环结束后,前6个Address Register(kisa0~kisa5)分别被设置为:0、0200、0400、0600、01000和01200。
如果您做过前面的思考题,应该对这6个数字很熟悉——这样设置,正好将前6个虚存page映射到前6个物理page上。
629 .
630 /* initialize user segment
631 .
632 mov $_end+63., r2 /_end以前的地址已经被启动进程的程序区和数据区占用了
/从_end地址开始,内存可以分配作它用
633 ash $-6, r2 /圆整到一个block的起始地址
634 bic $!1777, r2
635 mov r2, (r0)+ /将kisa6指向为bss段后的第一个block
636 mov $USIZE-1\8|6, (r1)+
637 .
638 / initialize io segment
639 /set up counts on supervisor segment
640 .
641 mov $IO, (r0)+ /将kisa7指向最后一个物理Page,以访问外设
642 mov $77046, (r1)+
643 .
644 /set a sp and start segment
645 .
646 mov $_u+[USIZE*64.], sp /sp指向PPDA后,见以下解释
首先要明确一点,即内核程序Load系统之后,它占据的是物理内存的低地址空间,它的逻辑地址和物理地址是重合的,
此时 ~ _end之间的物理空间已经被Load进系统的程序占据,而_end也就是可用于分配的最低物理地址。正如代码所示,
会首先会为当前进程分配一个很重要的区域,即进程数据区(PPDA区域)——每个进程都有一个私有的PPDA区域。
第7个Page Register(kisa6)将指向这一个特殊的区域。
PPDA区域分为两部分,开头是struct user结构,其后是核心stack:
0413: struct user
0414: {
0415: int u_rsav[2]; /* save r5,r6 when exchanging stacks */
0416: int u_fsav[25];
……
0458: } u;
【注】: _u被编址为0140000,即第7个逻辑page的开头,而这个page正好指向PPDA区域,因此u就被编址到PPDA区的开头。
1440: .globl _u
1441: _u = 140000
读到这里,我们可以画出进程的栈空间配置:
整个PPDA长1024个字节,u占用了低地址部分。sp初始化为PPDA的尾部,指针向上移动以分配空间。
611 inc SSR0 /8组Page Register已经设置好了,开启存管系统
612 .
613 /clear bss
614 .
615 mov $_edata, r0 /下列代码将地址处于_data ~ _end之间的内容清0
616 1: /即将bss段初始化为0
617 clr (r0)+
618 cmp r0, $_end
619 blo 1b
正如前面所说的那样,data segment属于二进制文件的一部分,随着文件的装入,data segment就完成了初始化。
而bss不同,必须由进程进行初始化。
620
621 / clear user block
622 .
623 mov $_u, r0 /clear整个PPDA区域
624 1:
625 clr (r0)+
626 cmp r0, _u+[USIZE*64.]
627 blo 1b
628
629 / set up previos mode and call main
630 / on return enter user mode at 0R
631 .
632 mov $30000, PS
633 jsr pc, _main /执行main
634 mov $170000, -(sp)
635 clr -(sp)
636 rtt
博客地址:http://blog.csdn.net/cszhao1980