[No000031]操作系统 Operating Systems 之Open the OS!
从打开电源开始…

这神秘的黑色背后发生着什么?…
打开电源,计算机执行的第一句指令什么?
计算模型(图灵机) ⇒ 我们要 关注 指针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的汇编和前面不一样?
- as86 汇编:能产生16 位代码的Intel 8086(386) 汇编
mov ax, cs //cs -> ax, 目标操作数在前
- 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!的更多相关文章
- [No00003D]操作系统Operating Systems信号量的代码实现Coding Semaphore &死锁处理Deadlock
操作系统Operating Systems信号量的代码实现Coding Semaphore &死锁处理Deadlock 可以操刀了—从纸上到实际 从Linux 0.11 那里学点东西… 读磁盘 ...
- [No00003C]操作系统Operating Systems进程同步与信号量Processes Synchronization and Semaphore
操作系统Operating Systems进程同步与信号量Processes Synchronization and Semaphore 进程合作:多进程共同完成一个任务 从纸上到实际:生产者− − ...
- [No00003A]操作系统Operating Systems 内核级线程Kernel Threads内核级线程实现Create KernelThreads
开始核心级线程 内核级线程对多核的支持怎么样? 和用户级相比,核心级线程有什么不同? ThreadCreate 是系统调用,内核管理TCB ,内核负责切换线程 如何让切换成型? − − 内核栈,TCB ...
- [No000037]操作系统Operating Systems操作系统历史与硬件概况History of OS & Summaries!
培根:读史使人明智 操作系统的简史 (1955-1965) 计算机非常昂贵,上古神机IBM7094 ,造价在250万美元以上 计算机使用原则:只专注于计算 批处理操作系统(Batch system) ...
- [No000038]操作系统Operating Systems -CPU
管理CPU ,先要使用CPU… CPU 的工作原理 CPU上电以后发生了什么? 自动的取指 — 执行 CPU 怎么工作? CPU怎么管理? 管理CPU 的最直观方法 设好PC 初值就完事! 看看这样做 ...
- [No000036]操作系统Operating Systems系统调用的实现System_Call
实现一个whoami 系统调用 系统调用的直观实现 问题+直观想法… 用户程序调用whoami, 一个字符串"systemcall "放在操作系统中(系统引导时载入) ,取出来打印 ...
- [No000039]操作系统Operating Systems用户级线程User Threads
多进程是操作系统的基本图像 是否可以资源不动而切换指令序列? 进程 = 资源 + 指令执行序列 线程: 保留了并发的优点,避免了进程切换代价 实质就是映射表不变而PC 指针变 多个执行序列+ 一个地址 ...
- 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 ...
- 操作系统Unix、Windows、Mac OS、Linux的故事
电脑,计算机已经成为我们生活中必不可少的一部分.无论是大型的超级计算机,还是手机般小巧的终端设备,都跑着一个操作系统.正是这些操作系统,让那些硬件和芯片得意组合起来,让那些软件得以运行,让我们的世界在 ...
随机推荐
- Android高级模糊技术[转]
今天我们来更深入了解一下Android开发上的模糊技术.我读过几篇有关的文章,也在StackOverFlow上看过一些相关教程的帖子,所以我想在这里总结一下学到的东西. 为什么学习这个模糊技术? 现在 ...
- Java虚拟机JVM学习05 类加载器的父委托机制
Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...
- 转如何分析解决Android ANR
一:什么是ANR ANR:Application Not Responding,即应用无响应 二:ANR的类型 ANR定义:在Android上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示 ...
- 操作系统开发系列—13.a.进程 ●
进程的切换及调度等内容是和保护模式的相关技术紧密相连的,这些代码量可能并不多,但却至关重要. 我们需要一个数据结构记录一个进程的状态,在进程要被挂起的时候,进程信息就被写入这个数据结构,等到进程重新启 ...
- Java中的经典算法之选择排序(SelectionSort)
Java中的经典算法之选择排序(SelectionSort) 神话丿小王子的博客主页 a) 原理:每一趟从待排序的记录中选出最小的元素,顺序放在已排好序的序列最后,直到全部记录排序完毕.也就是:每一趟 ...
- Atitit.android js 的键盘按键检测Back键Home键和Menu键事件
Atitit.android js 的键盘按键检测Back键Home键和Menu键事件 1. onKeyDown @Override public boolean onKeyDown(int keyC ...
- wifi强度数据采集器(android)
来源:毕业设计 关键词:wifi数据的采集 SQLite数据库的使用 需求 采集实验室内各坐标处各wifi信号的强度 UI 因为是辅助工具,所以UI写的很简单,如下图 Wifi相关操作 //获取Wif ...
- 软件测试人员必备Linux命令(初、中、高级)
有些技能可以事半功倍,有些命运掌握在我们手中.熟练的掌握和使用这些命令可以提高工作效率,并且结合这些命令对测试过程中遇到的问题进行一些初步的定位. 1 目录与文件操作1.1 ls(初级)使用权限:所有 ...
- 在VC环境下执行代码出现错误
这是在执行代码过程中出现的错误,源代码在别的电脑上能运行,在自己的VC里运行就出现错误,在网上也搜过解决办法,但还是有点不太理解,是编程环境的问题h还是代码本身也存在问题???
- Java对象的序列化
1.概念 序列化:把Java对象转换为字节序列的过程. 反序列化:把字节序列恢复为Java对象的过程. 2.用途 对象的序列化主要有两种用途: 1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个 ...