Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1939513
  • 博文数量: 77
  • 博客积分: 2175
  • 博客等级: 大尉
  • 技术积分: 2491
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-20 20:49
个人简介

欢迎光临我的博客

文章分类

全部博文(77)

文章存档

2023年(1)

2018年(4)

2017年(1)

2016年(2)

2015年(2)

2013年(5)

2012年(29)

2010年(33)

分类: LINUX

2010-06-27 07:32:45

启动start

系统启动的具体过程在m40.s中的start函数中实现:



1.    .globl start, _end, _edata, _main
2.    start:
3.     bit $1,SSR0
4.     bne start    / loop if restart
5.     reset                

6.    / initialize systems segments

7.     mov $KISA0,r0            
8.     mov $KISD0,r1
9.     mov $200,r4
10.     clr r2
11.     mov $6,r3                
12.    1:
13.     mov r2,(r0)+        
14.     mov $77406,(r1)+    / comment. 4k rw
15.     add r4,r2
16.     sob r3,1b

17.     / initialize user segment

18.     mov $_end+63.,r2
19.     ash $-6,r2
20.     bic $!1777,r2
21.     mov r2,(r0)+                / ksr = sysu
22.     mov $USIZE-1\<8|6,(r1)+

23.    / initialize io segment
24.    / set up counts on supervisor segments

25.     mov $IO,(r0)+            
26.     mov $77406,(r1)+            / rw 4k

27.    / get a sp and start segmentation

28.     mov $_u+[USIZE*64.],sp
29.     inc SSR0                

30.    / clear bss
31.     mov$_edata,r0
32.    1:
33.     clr (r0)+
34.     cmp r0,$_end    
35.     blo 1b

36.    / clear user block

37.     mov $_u,r0
38.    1:
39.     clr (r0)+
40.     cmp r0,$_u+[USIZE*64.]
41     blo 1b

42.    / set up previous mode and call main
43.    / on return, enter user mode at 0R

44.     mov $30000,PS
45.     jsr pc,_main
46.     mov $170000,-(sp)
47.     clr -(sp)
48.     rtt        

m40.s的末尾是一些全局变量定义:
/* -------------------------*/
 .globl _u
 _u = 140000            //u变量地址固定在0o140000处
 USIZE= 16.            //u变量和内核栈大小是16个内存块(16*64=1024字节)

 PS= 177776            //PSW寄存器地址
  SSR0= 177572            
 SSR2= 177576        
 KISA0= 172340        //0号内核页地址寄存器地址
 KISA6= 172354        //6号内核页地址寄存器地址    
 KISD0= 172300         //0号内核页描述寄存器地址
 MTC= 172522
 UISA0= 177640        //0号用户页地址寄存器地址
 UISA1= 177642        //1号用户页地址寄存器地址
 UISD0= 177600        //0号用户页描述寄存器地址
 UISD1= 177602        //1号用户页描述寄存器地址
 IO = 7600             

 .data                //已初始化的全局变量
 /* -------------------------*/
 .globl _ka6, _cputype
 _ka6:KISA6
 _cputype:40.        //CPU类型是PDP 11/40

 .bss                //未初始化的全局变量
 /* -------------------------*/
 .globl nofault, ssr, badtrap
 nofault:.=.+2         //变量nofault,且占用2个字节
 ssr:.=.+6            //变量ssr,且占用6个字节,实际是数组int ssr[3]
 badtrap:.=.+2        //变量badtrap,且占用2个字节


这里我们先了解一下内存管理单元(MMU)相关寄存器的分布,如图4-4所示,它们位于最高物理地址空间248K256K。虽然在UNIX代码中所有的寄存器地址值都是位于56K64K之间,但PDP 11芯片会把16位物理内存的最高8K地址空间(56K64K)自动映射到18位最高8K地址空间(248K256K)。




4-4  MMU相关寄存器

start函数首先测试内存管理单元(MMU)状态寄存器SSR0的“启动位”(最低位)。

SSR0寄存器地址是0o177572,转换成10进制为65402

24行转成C语言是:while(*((int *)SR0) &1);

如果检测到MMU启动(SR0的最低位是1),则继续检测,直至MMU未启动(SR0的最低位是0),退出循环。

为什么需要这样的逻辑?因为在操作员启动系统时,他通常会触发终端的“清除”按钮,从而把SR0寄存器清成0。所以,在正常启动时,MMU应该是被关闭的。另外,最重要的一点是:在双总线超时错的情况下,CPU将转移到地址0处执行,也就会跳转到start函数中执行,而这时MMU是被打开的。所以,在系统启动时,如果MMU打开,表明系统已出错,程序则不应该继续执行(当然,如果这时再判断SSR0的其他出错位来进一步确定系统是否出错,就更加严密了,但是为简洁起见,当时并没有这样做)。

循环后紧跟的reset指令复位所有总线设备(当然包括I/O设备)及相关寄存器。现在,系统运行在内核模式,而且内存管理单元未启动。也就是说,现在所有的地址引用都是物理地址。

从第716行设定05号内核活动页寄存器(Kernel Active Page Register)。

13行设定内核页地址寄存器的值,从而指定页起始物理内存块号为r214行设定内核页描述寄存器的值,ED=0PLF=127ACF=11,从而指定页朝高地址方向自动延伸,页大小为8K字节(注释是指4K字),页操作权限为“读写”。注意第9行赋r4的值为8进制数200,等于10进制数128。所以在第21行把物理块号r2增加128,作为下一个页地址寄存器的值(128*64 = 8K)。注意第16行的sob指令,它自减计数器r3,若不为0则跳转,类似于do {}while(--i)。关于页地址/描述寄存器的具体描述,请参照2.1.4节。

1822行设定第6号内核活动页寄存器的值(想想为什么)。第1819行设定r2_end下一个紧邻的物理块号(64字节边界,第19行右移r2寄存器6位),等效于C语法:r2 = _end+63/64。这里_end指向bss段的结尾,也是整个UNIX启动时,系统程序数据空间的结尾地址。也就是说,UNIX程序数据空间的最后一个字节是_end-1,记为_end_vaild,则_end=_end_valid+1,所以r2=_end_valid+1+63/64=_end_valid/64+1,这样r2就指向了内存中最后一个有效数据区的下一个紧邻物理块。

20行保留r2的低10位,这样使r2 < 1024,从而使r2指向的物理块位于64K地址空间内。

21行设定6号内核页地址寄存器的值;第22行设定6号内核页描述寄存器的值:PLF=USIZE16-1ED=0ACF=11。从而该页大小为USIZE*64=1024字节,“读写”权限。它用来存放进程u变量和进程内核栈空间。

2526行设定7号内核页地址寄存器和页描述寄存器的值。其中,页地址寄存器的值为IOIO = 7600(八进制,等于十进制3968,在m40.s文件末定义),从而它所对应的物理地址为:3968*64=248K。页描述寄存器值774068进制)表示该页长度为8K字节,“读写”权限。

至此,8个内核活动页寄存器已经全部设定完毕,64K内核虚拟空间中的57K都已经映射到物理内存(第6号活动页寄存器只映射了1K)。系统启动后的内核虚拟地址空间映射图如图4-5所示。

4-5中虚线标识的虚拟空间和物理空间并未映射。而虚存中的[48K,49K]空间被映射到[_end, _end+1K],并作为内核栈,这从第28行可以看出来。_u引用C程序中的全局变量u,它在.c第行定义(struct user{…}u),该结构变量所占空间为1K字节,该变量被分配到固定地址0o140000(在m40.s文件末定义),可以知道它是属于6号活动页寄存器KISA6/KISD6空间。

29行启动内存管理单元,从这一步开始,下面所有的地址引用(包括指令地址)都被解释成为虚拟地址,它首先被内存管理单元翻译成物理地址,再进行访问。

这里有一个问题,既然6号活动页寄存器被设置为u变量和内核栈空间,那为什么它的页描述寄存器的ED不设置成为1呢?因为栈空间的该位通常都是设成1、以使空间自动向上增长,而且UNIX在设置用户栈空间的时候也是这样做的。那是因为进程的内核栈空间通常提供给中断服务函数和系统调用使用,而它们所使用的栈空间是很有限的,除去u变量的289字节,还有1024-289=735字节作为栈空间,这已经足够,所以内核栈空间不需要自动增长功能。

3135行清除bss段,即设置所有未初始化的全局变量为0。注意这里的_edata_end都是伪变量,由编译器生成,分别指向data结束(bss段的起始)和bss段的结束地址。UNIX程序大小(0_end)大概为29K


上一章 虚拟内存                                   目录                            下一章 进程管理和调度
本书在全国各大书店及网城均有销售:
                         
                       
阅读(13030) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

qingfenghao2012-01-16 21:39:54

对,谢谢纠正!

hellhell1232012-01-13 16:11:03

63面第三自然段第21行应为15行