从打开电源开始…

这神秘的黑色背后发生着什么?…

打开电源,计算机执行的第一句指令什么?

计算模型(图灵机) ⇒ 我们要 关注 指针IP 及其 指向的内容

看看x86 PC

(1) 刚开机时CPU 处于实模式(和保护模式对应,实模式的寻址CS:IP(CS 左移4 位+IP) ,和保护模式不一样!)

(2) 开机时,CS=0xFFFF; IP=0x0000

(3) 寻址0xFFFF0(ROM BIOS 映射区)

(4) 检查RAM ,键盘,显示器,软硬磁盘

(5) 将磁盘0 磁道0 扇区读入0x7c00 处

(6) 设置cs=0x07c0 ,ip=0x0000

0x7c00 处存放的代码

就是从磁盘引导扇区读入的那512 个字节

  • 引导扇区就是 启动设备的第一个扇区(开机时按住del 键可进入启动设备设置界面,可以设置为光盘启动!)
  • 启动设备信息被设置在CMOS(CMOS: 互补金属氧化物半导体(64B-128B) 。用来存储实时钟和硬件配置信息。) 中…
  • 因此,硬盘的第一个扇区上存放着开机后执行的第一段我们可以控制的程序。
  • 操作系统的故事从这里开始…

引导扇区代码: bootsect.s

.globl begtext,begdata,begbss,endtext,enddata,endbss

.text // 文本段 .text 等是伪操作符,告诉编译器产生文本段,.text 用于标识文本段的开始位置。此处的.text 、.data 、.bss 表明这3 个段重叠,不分段!

begtext:

.data // 数据段

begdata:

.bss // 未初始化数据段

begbss:

entry start // 关键字entry 告诉链接器 "程序入口"

start:

mov ax, #BOOTSEG     mov ds, ax//此条语句就是0x7c00处存放的语句!

mov ax, #INITSEG     mov es, ax

mov cx, #

sub si, si           sub di,di//将0x07c0:0x0000 处的256 个字移动到0x9000:0x0000处

rep movw

jmpi go, INITSEG

BOOTSEG = 0x07c0

INITSEG = 0x9000

SETUPSEG = 0x9020

jmpi go, INITSEG

jmpi (jump intersegment 段间跳转): cs=INITSEG, ip=go

go: mov ax,cs //cs=0x9000

mov ds,ax         mov es,ax          mov ss,ax       mov sp,#0xff00//为call 做准备!

load_setup: // 载入setup 模块

mov dx,#0x0000     mov cx,#0x0002     mov bx,#0x0200

mov ax,#0x0200+SETUPLEN     int 0x13 //BIOS 中断 0x13 是BIOS 读磁盘扇区的中断: ah=0x02- 读磁盘,al=扇区数量(SETUPLEN=4) ,ch= 柱面号,cl= 开始扇区,dh= 磁头号,dl= 驱动器号,es:bx=内存地址
jnc ok_load_setup

mov dx,#0x0000

mov ax,#0x0000 // 复位

int 0x13

j load_setup //重读

读入setup 模块后: ok_load_setup

Ok_load_setup: // 载入setup 模块

mov dl,#0x00     mov ax,#0x0800 //ah=8 获得磁盘参数

int 0x13     mov ch,#0x00     mov sectors,cx

mov ah,#0x03     xor bh,bh     int 0x10 // 读光标

mov cx,#24     mov bx,#0x0007//7 是显示属性!

mov bp,#msg1     mov ax,#1301     int 0x10 // 显示字符

mov ax,#SYSSEG //SYSSEG=0x1000

mov es,ax

call read_it // 读入system 模块

jmpi 0,SETUPSEG//转入0x9020:0x0000执行setup.s

bootsect.s据 中的数据 // 在文件末尾

sectors: .word  // 磁道扇区数

msg1:.byte ,

  .ascii "Loading system..."

  .byte ,,,

boot 工作: 读setup ,读system…

read_it // 读入system

为什么读入system 模块还需要定义一个函数?

  system 模块可能很大,要跨越磁道!

read_it: mov ax,es cmp ax,#ENDSEG  jb ok1_read   ENDSEG=SYSSEG+SYSSIZE               SYSSIZE=0x8000 // 该变量可根据              Image 大小设定( 编译操作系统时)

  ret

ok1_read:

  mov ax,sectors

  sub ax,sread //sread 是当前磁道已读扇区数,ax 未读扇区数

  call read_track // 读磁道...

引导扇区的末尾 //BIOS 用以识别引导扇区

.org 510

  .word 0xAA55 //扇区的最后两个字节

否则会打出非引导设备

可以转入setup 执行了,jmpi 0, SETUPSEG

Power On…

setup 模块,即setup.s

根据名字就可以想到: setup 将完成OS启动前的设置

start: mov ax,#INITSEG mov ds,ax mov ah,#0x03

xor bh,bh int 0x10// 取光标位置dx mov [0],dx   取出光标位置( 包括其他硬件参数)到0x90000

mov ah,#0x88 int 0x15 mov [2],ax ... //扩展内存大小 SYSSEG = 0x1000

cli /// 不允许中断

mov ax,#0x0000 cld

do_move: mov es,ax add ax,#0x1000

cmp ax,#0x9000 jz end_move

mov ds,ax sub di,di

sub si,si

mov cx,#0x8000

rep //将system 模块移到0

movsw

jmp do_move

内存地址

长度

名称

0x90000

2

光标位置

0x90002

2

扩展内存数

0x901FC

2

根设备号

0x9000C

2

显卡参数

将setup 移到0 地址处...

  • 但0 地址处是有重要内容的
  • 以后不调用int 指令了吗?
  • 因为操作系统要让硬件进入保护模式了…
  • 保护模式下int n 和cs:ip 解释不再和实模式一样

end_move: mov ax,#SETUPSEG mov ds,ax

lidt idt_48         lgdt gdt_48// 设置保护模式下的中断和寻址

进入保护模式的命令 ...

idt_48:.word 0 .word 0,0 // 保护模式中断函数表, 又一个函数表

gdt_48:.word 0x800 .word 512+gdt,0x9

gdt: .word 0,0,0,0

.word 0x07FF, 0x0000, 0x9A00, 0x00C0

.word 0x07FF, 0x0000, 0x9200, 0x00C0

用GDT 将cs:ip 变成物理地址

保护模式下的地址翻译和中断处理

进入保护模式

call empty_8042 mov al,#0xD1 out #0x64,al

  //8042 是键盘控制器,其输出端口P2 用来控制A20 地址线

call empty_8042 mov al,#0xDF out #0x60,al

  // 选通A20 地址线 call empty_8042

初始化8259( 中断控制) // 一段非常机械化的程序

mov ax,#0x0001 mov cr0,ax

jmpi 0,8

D1 表示写数据到8042 的P2

jmpi 0,8 //gdt中的8

跳到system 模块执行...

  • system 模块( 目标代码) 中的第一部分代码? head.s
  • system 由许多文件编译而成,为什么是head.s?

disk: Image

  dd bs=8192 if=Image of=/dev/PS0 if=input file /dev/PS0是软驱A

Image: boot/bootsect boot/setup tools/system tools/build

tools/build boot/bootsect boot/setup tools/system > Image linux/Makefile

tools/system: boot/head.o init/main.o $(DRIVERS) …

$(LD) boot/head.o init/main.o $(DRIVERS) … -o tools/system

明白为什么head.s 就这样一个名字了吧?

head.s // 一段在保护模式下运行的代码

setup 是进入保护模式,head 是进入之后的初始化

stratup_32: movl $0x10,%eax mov %ax,%ds mov %ax,%es

  mov %as,%fs mov %as,%gs // 指向gdt 的0x10 项( 数据段) idt_48:.word 0 word 0,0

  lss _stack_start,%esp // 设置栈( 系统栈) 现在忽略中断_        idt: .fill 256,8,0

  call setup_idt struct{long *a; short b;}stack_start={&user_stack[PAGE_SIZE>>2],0x10};

  call setup_gdt 和前面的代码不一样了? 因为是32 位汇编代码!

xorl %eax,%eax

1:incl %eax

movl %eax,0x000000 cmpl %eax,0x100000

je 1b //0 地址处和1M 地址处相同(A20 没开启) ,就死循环

jmp after_page_tables // 页表,什么东东?

setup_idt: lea ignore_int,%edx

movl $0x00080000,%eax movw %dx,%ax

lea _idt,%edi movl %eax,(%edi)

关于汇编…head.s的汇编和前面不一样?

  1. as86 汇编:能产生16 位代码的Intel 8086(386) 汇编

    mov ax, cs //cs -> ax, 目标操作数在前

  2. GNU as 汇编:产生32 位代码,使用AT&T 系统V 语法

AT&T 美国电话电报公司,包含贝尔实验室等,1983 年AT&T UNIX 支持组发布了系统V

  movl var, %eax //(var) ->%eax

movb -4(%ebp), %al // 取出一字节

(3) 内嵌汇编,gcc 编译x.c 会产生中间结果as 汇编文件x.s

__asm__(" 汇编语句": 输出 : 输入 : 破坏部分描述);

__asm__("movb %%fs:%2, %%al" :"=a"(_res) : "0"(seg), "m"(*(addr)) );

0 或空表示使用与相应输出一样的寄存器

a 表示使用eax ,并编号%0

%2 表示addr ,m表示使用内存

after_page_tables // 设置了页表之后

setup 是进入保护模式,head

after_page_tables:

pushl $0 pushl $0 pushl $0 pushl $L6

pushl $_main jmp set_paging

L6: jmp L6 //将来学到!

setup_paging: 设置页表 ret

简单的几句程序,控制流却很复杂

setup_paging 执行ret 后? 会执行函数main()

进入main() 后的栈为0 ,0 ,0 ,L6

main() 函数的三个参数是0 ,0 ,0

main() 函数返回时进入L6 ,死循环...

进入main 函数

在init/main.c 中 //开始C 语言程序了!

void main(void)

{  mem_init();

trap_init();

blk_dev_init();

chr_dev_init();

tty_init();

time_init();

sched_init();

buffer_init();

hd_init();

floppy_init();

sti();

move_to_user_mode();

if(!fork()){init();}

}

为什么是void?

三个参数分别是envp,argv,argc但此处main 并没使用

此处的main 只保留传统main 的形式和命名main 表示C 语言函数的入口!

main 的工作就是xx_init: 内存、中断、设备、时钟、CPU 等内容的初始化…

看一看mem_init…

在linux/mm/memory.c 中

void mem_init(long start_mem,long end_mem) //这两个参数从哪里来?

{

int i;

for(i=0; i<PAGING_PAGES; i++)

mem_map[i] = USED;

i = MAP_NR(start_mem);

end_mem -= start_mem;

end_mem >>= 12;

while(end_mem -- > 0) //干了些什么?

mem_map[i++] = 0; }

管理硬件? 如何管理?就是用数据结构+ 算法…

[No000031]操作系统 Operating Systems 之Open the OS!的更多相关文章

  1. [No00003D]操作系统Operating Systems信号量的代码实现Coding Semaphore &死锁处理Deadlock

    操作系统Operating Systems信号量的代码实现Coding Semaphore &死锁处理Deadlock 可以操刀了—从纸上到实际 从Linux 0.11 那里学点东西… 读磁盘 ...

  2. [No00003C]操作系统Operating Systems进程同步与信号量Processes Synchronization and Semaphore

    操作系统Operating Systems进程同步与信号量Processes Synchronization and Semaphore 进程合作:多进程共同完成一个任务 从纸上到实际:生产者− − ...

  3. [No00003A]操作系统Operating Systems 内核级线程Kernel Threads内核级线程实现Create KernelThreads

    开始核心级线程 内核级线程对多核的支持怎么样? 和用户级相比,核心级线程有什么不同? ThreadCreate 是系统调用,内核管理TCB ,内核负责切换线程 如何让切换成型? − − 内核栈,TCB ...

  4. [No000037]操作系统Operating Systems操作系统历史与硬件概况History of OS & Summaries!

    培根:读史使人明智 操作系统的简史 (1955-1965) 计算机非常昂贵,上古神机IBM7094 ,造价在250万美元以上 计算机使用原则:只专注于计算 批处理操作系统(Batch system) ...

  5. [No000038]操作系统Operating Systems -CPU

    管理CPU ,先要使用CPU… CPU 的工作原理 CPU上电以后发生了什么? 自动的取指 — 执行 CPU 怎么工作? CPU怎么管理? 管理CPU 的最直观方法 设好PC 初值就完事! 看看这样做 ...

  6. [No000036]操作系统Operating Systems系统调用的实现System_Call

    实现一个whoami 系统调用 系统调用的直观实现 问题+直观想法… 用户程序调用whoami, 一个字符串"systemcall "放在操作系统中(系统引导时载入) ,取出来打印 ...

  7. [No000039]操作系统Operating Systems用户级线程User Threads

    多进程是操作系统的基本图像 是否可以资源不动而切换指令序列? 进程 = 资源 + 指令执行序列 线程: 保留了并发的优点,避免了进程切换代价 实质就是映射表不变而PC 指针变 多个执行序列+ 一个地址 ...

  8. Location of Docker images in all Operating Systems (Linux, Windows, Redhat, Mac OS X)

    原文:http://www.scmgalaxy.com/tutorials/location-of-dockers-images-in-all-operating-systems/ Location ...

  9. 操作系统Unix、Windows、Mac OS、Linux的故事

    电脑,计算机已经成为我们生活中必不可少的一部分.无论是大型的超级计算机,还是手机般小巧的终端设备,都跑着一个操作系统.正是这些操作系统,让那些硬件和芯片得意组合起来,让那些软件得以运行,让我们的世界在 ...

随机推荐

  1. sharepoint2010问卷调查(2)-实现问卷的图片调查(采用自定义字段类型)

    1. 首先建立个图片库上传图片 并建立文件夹1和2,1下有1.1文件夹,2下2.1文件夹,2.1下有文件夹2.1.1. 在1文件夹下放如下图片: 2.建立自定义字段类型,如下图: 3.部署后建立栏目的 ...

  2. 如何收缩超大的SharePoint_Config数据库

    前言 在已经运行了2年多的SharePoint服务器上,发现SharePoint_Config的数据库文件越来越大,已经达到90几个GB,收缩可以减小20几个GB,但是一周以后又会恢复到90几个GB大 ...

  3. SharePoint 2013 配置基于AD的Form认证

    前 言 配置SharePoint 2013基于AD的Form认证,主要有三步: 1. 修改管理中心的web.config: 2. 修改STS Application的web.config: 3. 修改 ...

  4. 交换两个变量的值swap(a,b)

    方法一:使用第三方变量 , b = , temp; temp = a; a = b; b = temp; 方法二:变量加减法(即121,加减减) , b = ; a = a + b; b = a - ...

  5. Android studio git 本地仓库和远程仓库节点对比

    1.初始状态 2.本地修改文件,然后commit 3.本地再次修改文件,然后commit 4.本地push 从上图可以看出,push完成后,本地仓库的节点和远程仓库的节点是一样的.

  6. 跨域iframe的高度自适应

    If you cannot hear the sound of the genuine in you, you will all of your life spend your days on the ...

  7. .NET文档生成工具ADB使用图文教程

    .NETv3.0 可再发行组件包 类型:编程辅助大小:2.7M语言:中文 评分:1.8 标签: 立即下载 ADB2.3使用指南 ADB2.3使用方法如下图所示: (1)主界面: (2)批量选择: 4. ...

  8. CSS3 Gradient线性渐变

    废话小说,看代码 <!DOCTYPE html > <html > <head> <meta charset="utf-8"> &l ...

  9. windows,linux,mac生成ssh public key 和 private key

    https://help.launchpad.net/YourAccount/CreatingAnSSHKeyPair Creating the key How you create your SSH ...

  10. openstack数据库获取一个虚机的floating_ip, fix_ip, project_name, user_name, hostname, host

     转载请注明 http://www.cnblogs.com/juandx/p/5418204.html openstack有3个库,nova,neutron,keystone,我现在需要做的是跨库联表 ...