操作系统笔记之基础

1.进入操作系统

神秘的开机背景后面到底发生了什么?
打开电源,计算机就开始工作了,那计算机怎么工作?
冯。诺依曼存储程序思想:把程序和数据存放在计算机存储器中,计算机在程序的控制下一步一步执行。
设定一个程序指针PC指向指令,由PC指针从存储器中取出指令,交给运算器和控制器,程序指针PC自动指向下一个指令。

那么问题:打开电源以后,计算机执行的第一条指令是什么?
x86pc,计算机刚打开时pc = ?
x86pc刚开机时CPU处于实模式(寻址CS:IP PC=CS左移4位+IP)
内存中固化了一段程序,这就是ROM BIOS(Basic Input Output System),程序固化在地址0XFFF0.开机时,CS=0XFFFF, IP=0X0000, 于是开机后PC读到的第一条指令地址是0XFFF0,这就是BIOS的第一条指令。然后检查RAM,键盘,主板,显示器,磁盘等硬件,然后从将磁盘0磁道0扇区的内容读入0X7C00处,这个0磁道0扇区存储的是操作系统的引导扇区,然后再设置CS=0x07c0, ip=OX0000这样PC就变成0X07C0,接着就开始执行操作系统的引导扇区,从这里开始就进入操作系统了,前面都还是在硬件中。

引导扇区读入的那512个字节代码(地址0X7C00处)
引导扇区是启动设备的第一个扇区,启动设备信息被设置在CMOS中,硬盘的第一个扇区存放着开机后第一段我们能控制的程序
操作系统由此开始。

linux0.11引导扇区代码bootsect.s
bootsect.s最后会被汇编成机器指令,放在引导扇区。

SETUPLEN = 4				! nr of setup-sectors
BOOTSEG  = 0x07c0			! original address of boot-sector
INITSEG  = 0x9000			! we move boot here - out of the way
SETUPSEG = 0x9020			! setup starts here
SYSSEG   = 0x1000			! system loaded at 0x10000 (65536).
ENDSEG   = SYSSEG + SYSSIZE		! where to stop loading

! ROOT_DEV:	0x000 - same type of floppy as boot.
!		0x301 - first partition on first drive etc
ROOT_DEV = 0x306

entry _start
_start:

	mov	ax,#BOOTSEG
	mov	ds,ax                         //ds = 0x07c00
	mov	ax,#INITSEG
	mov	es,ax                        //es  = 0x9000
	mov	cx,#256
	sub	si,si   // si = 0
	sub	di,di  // di = 0      ds : si 为 0x07c0 : 0x0000, es : di 为 0x9000 : 0x0000
	rep
	movw          
	**// 重复移动,将0x07c0:0x0000处的256个字移动到0x9000:0x0000 目的是腾出空间,后面在setup.s中会有用,避免在将system模块移动到0地址是覆盖bootsect.s**
go:	
    mov	ax,cs
    mov	ds,ax
    mov	es,ax!   !put stack at 0x9ff00.
    mov	ss,ax
    mov	sp,#0xFF00		! arbitrary value >>512

! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.

load_setup:
	mov	dx,#0x0000		! drive 0, head 0
	mov	cx,#0x0002		! sector 2, track 0       
	**//bootsect.s占据第一个扇区,因此要从第二扇区读**
	mov	bx,#0x0200		! address = 512, in INITSEG   **/ /es : bx 0x9000 : 0x0200 **
	mov	ax,#0x0200+SETUPLEN	! service 2, nr of sectors **// setup扇区数量** 
	int	0x13			! read it          **//0x13是BIOS读磁盘扇区的中断**
	jnc	ok_load_setup		! ok - continue
	mov	dx,#0x0000
	mov	ax,#0x0000		! reset the diskette
	int	0x13              
	j	load_setup

ok_load_setup:
! Get disk drive parameters, specifically nr of sectors/track
	mov	dl,#0x00
	mov	ax,#0x0800		! AH=8 is get drive parameters
	int	0x13
	mov	ch,#0x00
	seg cs
	mov	sectors,cx
	mov	ax,#INITSEG
	mov	es,ax

! Print some inane message
	mov	ah,#0x03		! read cursor pos  **//读光标位置**
	xor	bh,bh
	int	0x10          **//显示字符的BIOS中断**	
	mov	cx,#24  **//输出的字符数**
	mov	bx,#0x0007		! page 0, attribute 7 (normal)**//7是显示属性**
	mov	bp,#msg1 **//bp要显示的字符在内存中的位置,msgl内容在代码末**
	mov	ax,#0x1301		! write string, move cursor
	int	0x10     **//显示字符的BIOS中断**

! ok, we've written the message, now
! we want to load the system (at 0x10000)
	mov	ax,#SYSSEG
	mov	es,ax		! segment of 0x010000
	call	read_it  **//读入system模块**
	call	kill_motor

! After that we check which root-device to use. If the device is
! defined (!= 0), nothing is done and the given device is used.
! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
! on the number of sectors that the BIOS reports currently.
	seg cs
	mov	ax,root_dev
	cmp	ax,#0
	jne	root_defined
	seg cs
	mov	bx,sectors
	mov	ax,#0x0208		! /dev/ps0 - 1.2Mb
	cmp	bx,#15
	je	root_defined
	mov	ax,#0x021c		! /dev/PS0 - 1.44Mb
	cmp	bx,#18
	je	root_defined
	
undef_root: 
	jmp undef_root	
root_defined: 
	seg cs
	mov	root_dev,ax

! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
	jmpi	0,SETUPSEG
! This routine loads the system at address 0x10000, making sure
! no 64kB boundaries are crossed. We try to load it as fast as
! possible, loading whole tracks whenever we can.
!
! in:	es - starting address segment (normally 0x1000)
!
sread:	.word 1+SETUPLEN	! sectors read of current track
head:	.word 0			! current head
track:	.word 0			! current track

read_it:
	mov ax,es
	test ax,#0x0fff
	
die:	 
    jne die			! es must be at 64kB boundary
	xor bx,bx		! bx is starting address within segment

rp_read: 
	mov ax,es
	cmp ax,#ENDSEG		! have we loaded all yet?
	jb ok1_read                     //ENDSEG=SYSSEG+SYSSIZE
	ret        **//system模块可能很大,要跨越磁道
	....**





msg1:
	.byte 13,10
	.ascii "Loading system ..."
	.byte 13,10,13,10
.org 508
root_dev:
	.word ROOT_DEV
boot_flag:
	.word 0xAA55  **//扇区的最后两个字节 BIOS用以识别引导扇区**

#2.操作系统启动
setup模块

start:
! ok, the read went well so we get current cursor position and save it for
! posterity.
mov	ax,#INITSEG	! this is done in bootsect already, but...
mov	ds,ax
mov	ah,#0x03	! read cursor pos
xor	bh,bh
int	0x10		! save it in known place, con_init fetches
mov	[0],dx		! it from 0x90000.

mov	ah,#0x88
int	0x15    **//BIOS中断,获得物理内存大小**
mov	[2],ax  //扩展内存大小

! now we want to move to protected mode ...
	cli			! no interrupts allowed !

! first we move the system to it's rightful place
	mov	ax,#0x0000
	cld			! 'direction'=0, movs moves forward

do_move: 
	mov	es,ax		! destination segment
	add	ax,#0x1000
	cmp	ax,#0x9000
	jz	end_move
	mov	ds,ax		! source segment
	sub	di,di
	sub	si,si
	mov 	cx,#0x8000  // 要移动的长度
	rep
	movsw  
	jmp	do_move
	**//es=0x0000, ds= 0x9000.     把0X9000开始的内容移动到0地址,那么从0地址开始就是操作系统的内容.这样从0地址开始是操作系统的内容**

进入保护模式

end_move:
mov	ax,#SETUPSEG	! right, forgot this at first. didn't work :-)
mov	ds,ax
lidt	idt_48		! load idt with 0,0
lgdt	gdt_48		! load gdt with whatever appropriate **//设置保护模式下的中断和寻址**

! Well, now's the time to actually move into protected mode. To make
! things as simple as possible, we do no register set-up or anything,
! we let the gnu-compiled 32-bit programs do that. We just jump to
! absolute address 0x00000, in 32-bit protected mode.

	mov	ax,#0x0001	! protected mode (PE) bit
	lmsw	ax		! This is it!
	jmpi	0,8		! jmp offset 0 of segment 8 (cs)
**//jmpi 0,8很重要。在这里寻址方式发生了改变,由于之前的寻址模式(cs左移4位+ip的方式,cs 和ip都是16位寄存器,最大寻址空间20位,即1M)寻址空间太少。接下来要切换到32位模式(保护模式)。 这个时候寻址模式是查表**

这里写图片描述

gdt:

	.word	0,0,0,0		! dummy
	.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb)
	.word	0x0000		! base address=0
	.word	0x9A00		! code read/exec
	.word	0x00C0		! granularity=4096, 386

	.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb)
	.word	0x0000		! base address=0
	.word	0x9200		! data read/write
	.word	0x00C0		! granularity=4096, 386

这里写图片描述

关于jmpi 0, 8:
gdt表中0x00c09A00000007FFF, 表示 地址将跳转到0地址,0地址是system模块的地址开始处,到此setup模块的工作完成。
setup模块先后完成了读取硬件参数,把system移动到0地址处,设置为保护模式,最后再把程序指针移动到0地址(这时已是system)处。

跳到system模块执行
system中的第一部分代码head.s

startup_32:
	movl $0x10,%eax
	mov %ax,%ds
	mov %ax,%es
	mov %ax,%fs
	mov %ax,%gs     //指向gdt的0x10项(数据段)
	lss stack_start,%esp //设置系统栈
	call setup_idt    //初始化idt表
	call setup_gdt    //初始化gdt表
	movl $0x10,%eax		# reload all the segment registers
	mov %ax,%ds		# after changing gdt. CS was already
	mov %ax,%es		# reloaded in 'setup_gdt'
	mov %ax,%fs
	mov %ax,%gs
	lss stack_start,%esp
	xorl %eax,%eax	

1:
    incl %eax		# check that A20 really IS enabled
	movl %eax,0x000000	# loop forever if it isn't
	cmpl %eax,0x100000
	je 1b
	movl %cr0,%eax		# check math chip
	andl $0x80000011,%eax	# Save PG,PE,ET
	orl $2,%eax		# set MP
	movl %eax,%cr0
	call check_x87
	jmp after_page_tables

设置了页表之后after_page_tables

after_page_tables:
	pushl $0		# These are the parameters to main :-)
	pushl $0
	pushl $0
	pushl $L6		# return address for main, if it decides to.
	pushl $main
	jmp setup_paging   //设置页表   结束后跳转到main
	L6:  jmp L6			# main should never return here, but
				# just in case, we know what happens.

这里写图片描述

接下来跳转到main中,这就进入操作系统了。

#3.操作系统接口
上层软件如何与内核交流
这里写图片描述

#4.系统调用是如何实现的
不可以直接访问内核的代码:内核中有许多重要的数据,比如root用户的相关资料等,故不能随意访问。

如何实现不能随意访问内核代码?:这需要硬件来实现,比如有一种处理器的硬件设计
这里写图片描述
把内存分为不同的区域, 通过段寄存器来区分程序属于哪个段(内核段 or 用户段)
DPL: destination privilege level 要访问的目标区域的特权级
CPL: current privilege level 当前特权级
每次访问时,都要检查特权级。0是内核态,3是用户态。只有DPL >= CPL时才能够执行。
内核中的代码的特权级会被初始化为0,而用户的代码的特权级是3

那通过怎样的机制才能访问内核代码?
仍然是硬件来实现,对于x86那就是中断指令int。设计的某些中断能进入内核。
int指令将使CS中的CPL改成0,那么此时就能进入内核。
这里写图片描述
规定int 0x80为系统调用中断。

#总结一下:
这里写图片描述 
应用程序如何调用系统调用:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值