操作系统笔记之基础
来源:互联网 发布:农村淘宝村小二招募 编辑:程序博客网 时间:2024/05/18 00:15
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,#BOOTSEGmov ds,ax //ds = 0x07c00mov ax,#INITSEGmov es,ax //es = 0x9000mov cx,#256sub si,si // si = 0sub di,di // di = 0 ds : si 为 0x07c0 : 0x0000, es : di 为 0x9000 : 0x0000repmovw **// 重复移动,将0x07c0:0x0000处的256个字移动到0x9000:0x0000 目的是腾出空间,后面在setup.s中会有用,避免在将system模块移动到0地址是覆盖bootsect.s**
go:
mov ax,csmov ds,axmov es,ax! !put stack at 0x9ff00.mov ss,axmov 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 0mov 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 - continuemov dx,#0x0000mov ax,#0x0000 ! reset the disketteint 0x13 j load_setup
ok_load_setup:
! Get disk drive parameters, specifically nr of sectors/track
mov dl,#0x00mov ax,#0x0800 ! AH=8 is get drive parametersint 0x13mov ch,#0x00seg csmov sectors,cxmov ax,#INITSEGmov es,ax
! Print some inane message
mov ah,#0x03 ! read cursor pos **//读光标位置**xor bh,bhint 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 cursorint 0x10 **//显示字符的BIOS中断**
! ok, we’ve written the message, now
! we want to load the system (at 0x10000)
mov ax,#SYSSEGmov es,ax ! segment of 0x010000call 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 csmov ax,root_devcmp ax,#0jne root_definedseg csmov bx,sectorsmov ax,#0x0208 ! /dev/ps0 - 1.2Mbcmp bx,#15je root_definedmov ax,#0x021c ! /dev/PS0 - 1.44Mbcmp bx,#18je root_defined
undef_root:
jmp undef_root
root_defined:
seg csmov 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,estest ax,#0x0fff
die:
jne die ! es must be at 64kB boundaryxor bx,bx ! bx is starting address within segment
rp_read:
mov ax,escmp ax,#ENDSEG ! have we loaded all yet?jb ok1_read //ENDSEG=SYSSEG+SYSSIZEret **//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,axmov ah,#0x03 ! read cursor posxor bh,bhint 0x10 ! save it in known place, con_init fetchesmov [0],dx ! it from 0x90000.mov ah,#0x88int 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,#0x0000cld ! 'direction'=0, movs moves forward
do_move:
mov es,ax ! destination segmentadd ax,#0x1000cmp ax,#0x9000jz end_movemov ds,ax ! source segmentsub di,disub si,simov cx,#0x8000 // 要移动的长度repmovsw 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,axlidt idt_48 ! load idt with 0,0lgdt 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) bitlmsw 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,%eaxmov %ax,%dsmov %ax,%esmov %ax,%fsmov %ax,%gs //指向gdt的0x10项(数据段)lss stack_start,%esp //设置系统栈call setup_idt //初始化idt表call setup_gdt //初始化gdt表movl $0x10,%eax # reload all the segment registersmov %ax,%ds # after changing gdt. CS was alreadymov %ax,%es # reloaded in 'setup_gdt'mov %ax,%fsmov %ax,%gslss stack_start,%espxorl %eax,%eax
1:
incl %eax # check that A20 really IS enabledmovl %eax,0x000000 # loop forever if it isn'tcmpl %eax,0x100000je 1bmovl %cr0,%eax # check math chipandl $0x80000011,%eax # Save PG,PE,ETorl $2,%eax # set MPmovl %eax,%cr0call check_x87jmp after_page_tables
设置了页表之后after_page_tables
after_page_tables:
pushl $0 # These are the parameters to main :-)pushl $0pushl $0pushl $L6 # return address for main, if it decides to.pushl $mainjmp setup_paging //设置页表 结束后跳转到mainL6: 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为系统调用中断。
总结一下:
应用程序如何调用系统调用:
- 操作系统笔记之基础
- ubuntu操作系统学习笔记之------网络基础
- Ubuntu操作系统学习笔记之FTP基础
- Ubuntu操作系统学习笔记之NFS基础
- Ubuntn操作系统学习笔记之SMB基础
- 操作系统基础课堂笔记
- linux笔记-操作系统基础
- Ubuntn操作系统学习笔记之 Shell编程基础
- Linux基础学习笔记之操作系统(Operating System)
- 操作系统基础之设备管理
- 操作系统基础--现代操作系统学习笔记
- 操作系统基础--现代操作系统学习笔记
- 操作系统之设备管理笔记
- 操作系统之文件系统笔记
- 操作系统之文件系统笔记
- linux操作系统基础学习笔记
- 面试CS基础之操作系统
- 操作系统基础之进程管理
- 算法系列15天速成——第五天 五大经典查找【中】
- 微服务项目整合遇到的一些问题
- 二叉树三种遍历方法的递归与非递归实现
- 背景切换效果显示
- 文章标题
- 操作系统笔记之基础
- 算法系列15天速成——第六天 五大经典查找【下】
- C++程序设计案例实训教程第13章
- 系统调用实现过程
- 医学图像处理入门(1):相关开源类库介绍
- MySQL第一次实验的一些操作
- UINavigationController详解(一)
- Shell排序
- HTTP权威指南学习笔记(1)-简介及HTTP