从打开电源开始…

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

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

计算模型(图灵机) ⇒ 我们要 关注 指针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. SharePoint2013 错误

    英文错误信息: This operation can be performed only on a computer that is joined to a server farm by users ...

  2. JSON数据解析 基础知识及链接收集

    JSON数据解析学习 JSON介绍 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式. JSON 是存储和交换文本信息的语法.类似 XML.但是JSON 比 ...

  3. Android 使用xml序列化器生成xml文件

    在<Android 生成xml文件>一文中使用流的形式写入xml格式文件,但是存在一定的问题,那就是在短信内容中不能出现<>之类的括号,本文使用xml序列化器来解决 xml序列 ...

  4. 【代码笔记】iOS-离线地图

    一,效果图. 二,工程图. 三,代码. ViewController.h #import <UIKit/UIKit.h> #import <CoreLocation/CoreLoca ...

  5. Swift 初步了解

    Swift 初步了解 前言: 本篇博客会结合OC对Swift进行简单介绍. OC 用NSLog输出日志 NSLog(@"旭宝爱吃鱼"); Swift 用print输出日志 prin ...

  6. response设置输出文件编码

    在java后台的Action代码或者Servlet代码中用response的方法来设置输出内容的编码方式,有以下三个方法: 1.response.setCharacterEncoding(" ...

  7. C文件编译、链接指令

    通过mac终端 输入指令: cc -c 文件名.c 可以把C文件编译成.o文件(其实是2进制文件) 然后通过指令 cc 文件名.o 把.o文件链接C文件所需要的C语言的底层库,成为可以直接运行的lin ...

  8. 自动化部署教程(一) redhat安装jenkins

    自动化部署教程(一)  redhat安装jenkins 源配置: sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.or ...

  9. Java内存泄露简述

    Java的一个最显著的优势是内存管理.你只需要简单的创建对象而不需要负责释放空间,因为Java的垃圾回收器会负责内存的回收.然而,情况并不是这样简单,内存泄露还是经常会在Java应用程序中出现. 本篇 ...

  10. django中tinymce添加图片上传功能

    主要参考以下: https://pixabay.com/en/blog/posts/direct-image-uploads-in-tinymce-4-42/ http://blog.csdn.net ...