2012.1.23 - Linux内核 - 操作系统的引导

Posted on 2013-02-13 22:50  SnakeHunt2012  阅读(199)  评论(0)    收藏  举报

本文始作于2012年1月23日,刊登于人人网,于2013年2月13日迁移至此

晚上从姥姥家回来看了会儿鸟哥的私房菜 - 如何规划 Linux 主机,讲了一些硬件的事。

主频 vs 倍频:

  CPU的主频,即CPU内核工作的时钟频率(CPU Clock Speed)。通常所说的某某CPU是多少兆赫的,而这个多少兆赫就是“CPU的主频”。很多人认为CPU的主频就是其运行速度,其实不然。CPU的主频表示在CPU内数字脉冲信号震荡的速度,与CPU实际的运算能力并没有直接关系。主频和实际的运算速度存在一定的关系,但目前还没有一个确定的公式能够定量两者的数值关系,因为CPU的运算速度还要看CPU的流水线的各方面的性能指标(缓存、指令集,CPU的位数等等)。由于主频并不直接代表运算速度,所以在一定情况下,很可能会出现主频较高的CPU实际运算速度较低的现象。比如AMD公司的AthlonXP系列CPU大多都能以较低的主频,达到英特尔公司的Pentium 4系列CPU较高主频的CPU性能,所以AthlonXP系列CPU才以PR值的方式来命名。因此主频仅是CPU性能表现的一个方面,而不代表CPU的整体性能。
  主频一般是指电脑CPU的标称频率,外频一般是指电脑电脑系统总线(外部设备)的频率。由于CPU的速度较高,而电脑外部设备的速度较慢,无法跟得上CPU的处理速度,严重影响电脑的整体性能,于是人们在CPU与电脑外部设备之间设计了一个可以暂时存放外部设备处理的数据的设备,即高速缓存,来解决这个问题,你可以把高速缓存看成是一座水库,有一条进水的小河道和一条放水的闸门,虽然流进这座水库的水虽然流速很慢很少,但由于水库里已经预先存放了大量的水,依然可以满足闸门大量放水推动水轮机发电的需要。在这个事例中,进水的速度就相当于外频,放水的速度就相当于主频,而放水速度与进水速度的比值就是倍频。于是得出,主频=外频×倍频。比如说老奔三的CPU主频是1GHz,它的外频是133MHz,那么它的倍频就是7.5。
  我是这样理解的。小明会算10以内的加法,他算题的速度是恒定的,一分钟一道题,现在有一个老师考他,老师出题的速度是十分钟一道题,这样老师十分钟出题一道,这样他俩解决问题的速度就是十分钟一道题,显然这很慢。现在有三个老师一起出题,十分钟出题3道,小明这十分钟前三分钟在算题,后七分钟都没事干。这种情况下小明的晶体振荡频率为10Hz(假设一分钟为一秒),主频为3Hz,外频为1Hz,倍频为3。

今天写setup.s完成了所有实验要求的功能:

 

代码如下:

View Code
  1 !
  2 !    setup.s        (C) 1991 Linus Torvalds
  3 !
  4 ! setup.s is responsible for getting the system data from the BIOS,
  5 ! and putting them into the appropriate places in system memory.
  6 ! both setup.s and system has been loaded by the bootblock.
  7 !
  8 ! This code asks the bios for memory/disk/other parameters, and
  9 ! puts them in a "safe" place: 0x90000-0x901FF, ie where the
 10 ! boot-block used to be. It is then up to the protected mode
 11 ! system to read them from there before the area is overwritten
 12 ! for buffer-blocks.
 13 !
 14 
 15 ! NOTE! These had better be the same as in bootsect.s!
 16 
 17 INITSEG  = 0x9000    ! we move boot here - out of the way
 18 SYSSEG   = 0x1000    ! system loaded at 0x10000 (65536).
 19 SETUPSEG = 0x9020    ! this is the current segment
 20 
 21 .globl begtext, begdata, begbss, endtext, enddata, endbss
 22 .text
 23 begtext:
 24 .data
 25 begdata:
 26 .bss
 27 begbss:
 28 .text
 29 
 30 entry start
 31 start:
 32 
 33 ! Show some message
 34 
 35     mov ax,#SETUPSEG
 36     mov es,ax
 37 
 38     mov ah,#0x03    ! read cursor pos
 39     xor bh,bh 
 40     int 0x10
 41 
 42     mov cx,#19
 43     mov bx,#0x0007
 44     mov bp,#message
 45     mov ax,#0x1301
 46     int 0x10
 47 
 48     mov ax,#0xe0d
 49     int 0x10
 50     mov al,#0x0a
 51     int 0x10
 52     mov ax,#0xe0d
 53     int 0x10
 54     mov al,#0x0a
 55     int 0x10
 56 
 57 ! ok, the read went well so we get current cursor position and save it for
 58 ! posterity.
 59 
 60     mov    ax,#INITSEG    ! this is done in bootsect already, but...
 61     mov    ds,ax
 62     mov    ah,#0x03    ! read cursor pos
 63     xor    bh,bh
 64     int    0x10        ! save it in known place, con_init fetches
 65     mov    [0],dx        ! it from 0x90000.
 66 
 67 ! Get memory size (extended mem, kB)
 68 
 69     mov    ah,#0x88
 70     int    0x15
 71     mov    [2],ax
 72 
 73 ! Get video-card data:
 74 
 75     mov    ah,#0x0f
 76     int    0x10
 77     mov    [4],bx        ! bh = display page
 78     mov    [6],ax        ! al = video mode, ah = window width
 79 
 80 ! check for EGA/VGA and some config parameters
 81 
 82     mov    ah,#0x12
 83     mov    bl,#0x10
 84     int    0x10
 85     mov    [8],ax
 86     mov    [10],bx
 87     mov    [12],cx
 88 
 89 ! Get hd0 data
 90 
 91     mov    ax,#0x0000
 92     mov    ds,ax
 93     lds    si,[4*0x41]
 94     mov    ax,#INITSEG
 95     mov    es,ax
 96     mov    di,#0x0080
 97     mov    cx,#0x10
 98     rep
 99     movsb
100 
101 ! Get hd1 data
102 
103     mov    ax,#0x0000
104     mov    ds,ax
105     lds    si,[4*0x46]
106     mov    ax,#INITSEG
107     mov    es,ax
108     mov    di,#0x0090
109     mov    cx,#0x10
110     rep
111     movsb
112     
113 ! Print cursor position
114 
115     mov ax,#SETUPSEG
116     mov es,ax
117 
118     mov ah,#0x03    ! read cursor pos
119     xor bh,bh 
120     int 0x10
121 
122     mov cx,#12
123     mov bx,#0x0007
124     mov bp,#cursor
125     mov ax,#0x1301
126     int 0x10
127 
128 print_cursor:
129 
130     mov    ax,#INITSEG    ! this is done in bootsect already, but...
131     mov    ds,ax
132     mov cx,#4
133     mov dx,[0]
134 
135 print_cursor_digit:
136     
137     rol dx,#4
138     mov ax,#0xe0f
139     and al,dl
140     add al,#0x30
141     cmp al,#0x3a
142     jl  outp_cursor
143     add al,#0x07
144 
145 outp_cursor:
146     
147     int 0x10
148     loop    print_cursor_digit
149 
150     mov ax,#0xe0d
151     int 0x10
152     mov al,#0x0a
153     int 0x10
154     
155 ! Print memory size
156 
157     mov ah,#0x03    ! read cursor pos
158     xor bh,bh 
159     int 0x10
160 
161     mov cx,#13
162     mov bx,#0x0007
163     mov bp,#memory
164     mov ax,#0x1301
165     int 0x10
166 
167 print_memory:
168 
169     mov cx,#4
170     mov dx,[2]
171 
172 print_memory_digit:
173     
174     rol dx,#4
175     mov ax,#0xe0f
176     and al,dl
177     add al,#0x30
178     cmp al,#0x3a
179     jl  outp_memory
180     add al,#0x07
181 
182 outp_memory:
183     
184     int 0x10
185     loop    print_memory_digit
186 
187     mov ah,#0x03    ! read cursor pos
188     xor bh,bh 
189     int 0x10
190 
191     mov cx,#2
192     mov bx,#0x0007
193     mov bp,#kb
194     mov ax,#0x1301
195     int 0x10
196 
197     mov ax,#0xe0d
198     int 0x10
199     mov al,#0x0a
200     int 0x10
201     
202 ! Print cyls
203 
204     mov ah,#0x03    ! read cursor pos
205     xor bh,bh 
206     int 0x10
207 
208     mov cx,#6
209     mov bx,#0x0007
210     mov bp,#cyls
211     mov ax,#0x1301
212     int 0x10
213 
214 print_cyls:
215 
216     mov cx,#4
217     mov dx,[0x80]
218 
219 print_cyls_digit:
220     
221     rol dx,#4
222     mov ax,#0xe0f
223     and al,dl
224     add al,#0x30
225     cmp al,#0x3a
226     jl  outp_cyls
227     add al,#0x07
228 
229 outp_cyls:
230     
231     int 0x10
232     loop    print_cyls_digit
233 
234     mov ax,#0xe0d
235     int 0x10
236     mov al,#0x0a
237     int 0x10
238     
239 ! Print heads
240 
241     mov ah,#0x03    ! read cursor pos
242     xor bh,bh 
243     int 0x10
244 
245     mov cx,#7
246     mov bx,#0x0007
247     mov bp,#heads
248     mov ax,#0x1301
249     int 0x10
250 
251 print_heads:
252 
253     mov cx,#4
254     mov dx,[0x82]
255 
256 print_heads_digit:
257     
258     rol dx,#4
259     mov ax,#0xe0f
260     and al,dl
261     add al,#0x30
262     cmp al,#0x3a
263     jl  outp_heads
264     add al,#0x07
265 
266 outp_heads:
267     
268     int 0x10
269     loop    print_heads_digit
270 
271     mov ax,#0xe0d
272     int 0x10
273     mov al,#0x0a
274     int 0x10
275     
276 ! Print sectors
277 
278     mov ah,#0x03    ! read cursor pos
279     xor bh,bh 
280     int 0x10
281 
282     mov cx,#9
283     mov bx,#0x0007
284     mov bp,#sectors
285     mov ax,#0x1301
286     int 0x10
287 
288 print_sectors:
289 
290     mov cx,#4
291     mov dx,[0x8e]
292 
293 print_sectors_digit:
294     
295     rol dx,#4
296     mov ax,#0xe0f
297     and al,dl
298     add al,#0x30
299     cmp al,#0x3a
300     jl  outp_sectors
301     add al,#0x07
302 
303 outp_sectors:
304     
305     int 0x10
306     loop    print_sectors_digit
307 
308     mov ax,#0xe0d
309     int 0x10
310     mov al,#0x0a
311     int 0x10
312     
313 ! Check that there IS a hd1 :-)
314 
315     mov    ax,#0x01500
316     mov    dl,#0x81
317     int    0x13
318     jc    no_disk1
319     cmp    ah,#3
320     je    is_disk1
321 
322 no_disk1:
323 
324 ! Show some message
325 
326     mov ax,#SETUPSEG
327     mov es,ax
328 
329     mov ah,#0x03    ! read cursor pos
330     xor bh,bh 
331     int 0x10
332 
333     mov cx,#15
334     mov bx,#0x0007
335     mov bp,#no
336     mov ax,#0x1301
337     int 0x10
338 
339     mov ax,#0xe0d
340     int 0x10
341     mov al,#0x0a
342     int 0x10
343     int 0x10
344     int 0x10
345     int 0x10
346     int 0x10
347 
348     mov    ax,#INITSEG
349     mov    es,ax
350     mov    di,#0x0090
351     mov    cx,#0x10
352     mov    ax,#0x00
353     rep
354     stosb
355 
356     jmp exit
357 
358 is_disk1:
359 
360 ! Show some message
361 
362     mov ax,#SETUPSEG
363     mov es,ax
364 
365     mov ah,#0x03    ! read cursor pos
366     xor bh,bh 
367     int 0x10
368 
369     mov cx,#12
370     mov bx,#0x0007
371     mov bp,#exist
372     mov ax,#0x1301
373     int 0x10
374 
375     mov ax,#0xe0d
376     int 0x10
377     mov al,#0x0a
378     int 0x10
379     int 0x10
380     int 0x10
381     int 0x10
382     int 0x10
383 
384 exit:
385 
386 ! now we want to move to protected mode ...
387 
388     cli            ! no interrupts allowed !
389 
390 ! first we move the system to it's rightful place
391 
392     mov    ax,#0x0000
393     cld            ! 'direction'=0, movs moves forward
394 do_move:
395     mov    es,ax        ! destination segment
396     add    ax,#0x1000
397     cmp    ax,#0x9000
398     jz    end_move
399     mov    ds,ax        ! source segment
400     sub    di,di
401     sub    si,si
402     mov     cx,#0x8000
403     rep
404     movsw
405     jmp    do_move
406 
407 ! then we load the segment descriptors
408 
409 end_move:
410     mov    ax,#SETUPSEG    ! right, forgot this at first. didn't work :-)
411     mov    ds,ax
412     lidt    idt_48        ! load idt with 0,0
413     lgdt    gdt_48        ! load gdt with whatever appropriate
414 
415 ! that was painless, now we enable A20
416 
417     call    empty_8042
418     mov    al,#0xD1        ! command write
419     out    #0x64,al
420     call    empty_8042
421     mov    al,#0xDF        ! A20 on
422     out    #0x60,al
423     call    empty_8042
424 
425 ! well, that went ok, I hope. Now we have to reprogram the interrupts :-(
426 ! we put them right after the intel-reserved hardware interrupts, at
427 ! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
428 ! messed this up with the original PC, and they haven't been able to
429 ! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
430 ! which is used for the internal hardware interrupts as well. We just
431 ! have to reprogram the 8259's, and it isn't fun.
432 
433     mov    al,#0x11        ! initialization sequence
434     out    #0x20,al        ! send it to 8259A-1
435     .word    0x00eb,0x00eb        ! jmp $+2, jmp $+2
436     out    #0xA0,al        ! and to 8259A-2
437     .word    0x00eb,0x00eb
438     mov    al,#0x20        ! start of hardware int's (0x20)
439     out    #0x21,al
440     .word    0x00eb,0x00eb
441     mov    al,#0x28        ! start of hardware int's 2 (0x28)
442     out    #0xA1,al
443     .word    0x00eb,0x00eb
444     mov    al,#0x04        ! 8259-1 is master
445     out    #0x21,al
446     .word    0x00eb,0x00eb
447     mov    al,#0x02        ! 8259-2 is slave
448     out    #0xA1,al
449     .word    0x00eb,0x00eb
450     mov    al,#0x01        ! 8086 mode for both
451     out    #0x21,al
452     .word    0x00eb,0x00eb
453     out    #0xA1,al
454     .word    0x00eb,0x00eb
455     mov    al,#0xFF        ! mask off all interrupts for now
456     out    #0x21,al
457     .word    0x00eb,0x00eb
458     out    #0xA1,al
459 
460 ! well, that certainly wasn't fun :-(. Hopefully it works, and we don't
461 ! need no steenking BIOS anyway (except for the initial loading :-).
462 ! The BIOS-routine wants lots of unnecessary data, and it's less
463 ! "interesting" anyway. This is how REAL programmers do it.
464 !
465 ! Well, now's the time to actually move into protected mode. To make
466 ! things as simple as possible, we do no register set-up or anything,
467 ! we let the gnu-compiled 32-bit programs do that. We just jump to
468 ! absolute address 0x00000, in 32-bit protected mode.
469     mov    ax,#0x0001    ! protected mode (PE) bit
470     lmsw    ax        ! This is it!
471     jmpi    0,8        ! jmp offset 0 of segment 8 (cs)
472 
473 ! This routine checks that the keyboard command queue is empty
474 ! No timeout is used - if this hangs there is something wrong with
475 ! the machine, and we probably couldn't proceed anyway.
476 empty_8042:
477     .word    0x00eb,0x00eb
478     in    al,#0x64    ! 8042 status port
479     test    al,#2        ! is input buffer full?
480     jnz    empty_8042    ! yes - loop
481     ret
482 
483 gdt:
484     .word    0,0,0,0        ! dummy
485 
486     .word    0x07FF        ! 8Mb - limit=2047 (2048*4096=8Mb)
487     .word    0x0000        ! base address=0
488     .word    0x9A00        ! code read/exec
489     .word    0x00C0        ! granularity=4096, 386
490 
491     .word    0x07FF        ! 8Mb - limit=2047 (2048*4096=8Mb)
492     .word    0x0000        ! base address=0
493     .word    0x9200        ! data read/write
494     .word    0x00C0        ! granularity=4096, 386
495 
496 cursor:                 ! 10byte
497     .ascii  "Cursor POS: "
498 
499 memory:                 ! 13byte
500     .ascii  "Memory SIZE: "
501 
502 kb:                     ! 2byte
503     .ascii  "KB"
504 
505 cyls:                   ! 6byte
506     .ascii  "Cyls: "
507 
508 heads:                  ! 7byte
509     .ascii  "Heads: "
510 
511 sectors:                ! 9byte
512     .ascii  "Secotrs: "
513 
514 message:                ! 19byte
515     .ascii  "Now we are in SETUP"
516 
517 exist:                  ! 12byte
518     .ascii  "There is hd1"
519 
520 no:                     ! 15byte
521     .ascii  "There isn't hd1"
522 
523 idt_48:
524     .word    0            ! idt limit=0
525     .word    0,0            ! idt base=0L
526 
527 gdt_48:
528     .word    0x800        ! gdt limit=2048, 256 GDT entries
529     .word    512+gdt,0x9    ! gdt base = 0X9xxxx
530     
531 .text
532 endtext:
533 .data
534 enddata:
535 .bss
536 endbss:

 

实际上输出信息的关键代码在cms上已经做好了,当初没看指导书的时候感觉这个地方会非常扎手,因为int中断输出都是按照字符数出的,没有按照十六进制数字输出的,就是内存里存的数没办法直接输出成实际数据,只能输出成字符或字符串,所以要想看到内存的真实值,就必须将字符串再翻译回对应的十六进制字符,这个循环在语言上就比较有难度,但是sunner替我们将这个函数已经写好了,这个要是搞汇编,估计是那种很常用的模板:

下面是完成显示16进制数的汇编语言程序的关键代码,其中用到的BIOS中断为INT 0x10,功能号0x0E(显示一个字符),即AH=0x0E,AL=要显示字符的ASCII码。

 1 !以16进制方式打印栈顶的16位数
 2 print_hex:
 3     x,#4            ! 4个十六进制数字
 4     mov dx,(bp)     ! 将(bp)所指的值放入dx中,如果bp是指向栈顶的话
 5 print_digit:
 6     rol dx,#4       ! 循环以使低4比特用上 !! 取dx的高4比特移到低4比特处。
 7     mov ax,#0xe0f   ! ah = 请求的功能值,al = 半字节(4个比特)掩码。
 8     and al,dl       ! 取dl的低4比特值。
 9     add al,#0x30    ! 给al数字加上十六进制0x30
10     cmp al,#0x3a
11     jl  outp        !是一个不大于十的数字
12     add al,#0x07    !是a~f,要多加7
13 outp: 
14     int 0x10
15     loop    print_digit
16     ret

 

这 里用到了一个loop指令,每次执行loop指令,cx减1,然后判断cx是否等于0。如果不为0则转移到loop指令后的标号处,实现循环;如果为0顺 序执行。另外还有一个非常相似的指令:rep指令,每次执行rep指令,cx减1,然后判断cx是否等于0,如果不为0则继续执行rep指令后的串操作指 令,直到cx为0,实现重复。

!打印回车换行
print_nl:
    mov ax,#0xe0d   ! CR
    int 0x10
    mov al,#0xa     ! LF
    int 0x10
    ret

 

只 要在适当的位置调用print_bx和print_nl(注意,一定要设置好栈,才能进行函数调用)就能将获得硬件参数打印到屏幕上,完成此次实验的任 务。但事情往往并不总是顺利的,前面的两个实验大多数实验者可能一次就编译调试通过了(这里要提醒大家:编写操作系统的代码一定要认真,因为要调试操作系 统并不是一件很方便的事)。但在这个实验中会出现运行结果不对的情况(为什么呢?因为我们给的代码并不是100%好用的)。所以接下来要复习一下汇编,并 阅读《Bochs使用手册》,学学在Bochs中如何调试操作系统代码。

我没有当作函数使用,为了避免调配栈的麻烦,我直接将函数改成代码然后几个十六进制输出就写块代码:

print_cursor:

    mov    ax,#INITSEG    ! this is done in bootsect already, but...
    mov    ds,ax
    mov cx,#4
    mov dx,[0]

print_cursor_digit:
    
    rol dx,#4
    mov ax,#0xe0f
    and al,dl
    add al,#0x30
    cmp al,#0x3a
    jl  outp_cursor
    add al,#0x07

outp_cursor:
    
    int 0x10
    loop    print_cursor_digit

    mov ax,#0xe0d
    int 0x10
    mov al,#0x0a
    int 0x10

 

代码越来越多了,明天应该琢磨着学学用github了,实验一做完之后应该把BlackSun在github和bitbucket各备份一份。