操作系统

MBR到Loader的过渡 —— 在汇编层面读取硬盘数据

Posted on

之前的文章有提到,PC(一般为32位)的启动过程为 BIOS -> MBR -> loader -> kernel。

BIOS在休眠后的最后任务是从硬盘中的第0柱面第0磁头第1扇区中读取MBR,其实就是逻辑0扇区。因为扇区是从1开始计数的。
BIOS所做的内部原理对我来说是从来不去涉及的,所以也就不讨论这个。

从MBR开始才是我们程序猿所能任意写的内容,而MBR同BIOS一样,也需要对下一阶段loader进行过渡。
而一个特别麻烦的地方就是,没有任何库与函数供我们调用……因此我们只能在汇编层面从硬盘读取数据。

老实说,我也不清楚应该从哪开始讲,后续内容都建立在以下基础知识之上:

  • CPU通过端口访问设备
  • 硬盘中的块表示有CHS和LBA方式
  • 通道的概念
  • 一个PATA线可以挂两个硬盘,一个主盘,一个从盘
  • 以前一般主板只支持4个PATA硬盘,所以只需两个通道,这里也只提两个通道
  • 硬盘的IO接口称为硬盘控制器

硬盘控制器的主要端口寄存器

Primary 通道Secondary 通道读操作 写操作
0x1F00x170DataData
0x1F10x171ErrorFeatures
0x1F20x170Sector countSector count
0x1F30x170LBA lowLBA low
0x1F40x170LBA midLBA mid
0x1F50x170LBA highLBA high
0x1F60x170DeviceDevice
0x1F70x170StatusCommand
0x3F60x170Alternate statusDevice Control

注:
1. 以上寄存器中除了data寄存器,其他寄存器都是8位的,data寄存器为16位,这也很容易理解么,总所周知,当前PC的速度卡点就在IO上。
2. 这里的LBA方式使用的是LBA28,即28位描述一个扇区,最大支持128G。另外一种是48位的。
3. 某一些寄存器在读和写的时候用途不同,这是为了提高利用率。
4. 简单介绍一下这些寄存器
- Data: 管理数据
- Error : 读取数据时出错会在这里有记录
- Feature: 存放调用参数
- Sector count: 待读写的扇区数量
- Device: 低4位用于LBA,第4位用于指定主从盘,第6位用于设置LBA/CHS。其他两个MBS位设置为1。
- Status: 存放状态信息
- Command: 存放命令,主要有硬盘识别 ( 0xEC ) 、读扇区 ( 0x20 )、写扇区 ( 0x30 )。

常用读写硬盘的操作方法

  1. 先选择通道,往该通道的 sector count 寄存器中写入待操作扇区数
  2. 往该通道的3个LBA寄存器中写入扇区起始地址
  3. 往Device寄存器中写入低4位( 0 ~ 3 )数据,第6位置1,表示选择LBA,第4位选择主从盘,其他两位置1.
  4. 往通道上的command 寄存器中写入命令
  5. 读取status寄存器,判断是否完成
  6. 读硬盘或者完工

最后是取数据,这个在操作系统课上有详说,什么DMA啊,通道啊,中断啊,什么的。这里不详说。

最后附上一个较为完整的 MBR ,取自郑钢的《操作系统真象还原》
书上的注释乱码了,只能我自己写一点

%include "boot.inc"
SECTION MBR vstart=0x7c00
   mov ax,cs
   mov ds,ax
   mov es,ax
   mov ss,ax
   mov fs,ax
   mov sp,0x7c00
   mov ax,0xb800
   mov gs,ax

   mov     ax, 0600h
   mov     bx, 0700h
   mov     cx, 0
   mov     dx, 184fh

   int     10h

   mov byte [gs:0x00],'1'
   mov byte [gs:0x01],0xA4

   mov byte [gs:0x02],' '
   mov byte [gs:0x03],0xA4

   mov byte [gs:0x04],'M'
   mov byte [gs:0x05],0xA4

   mov byte [gs:0x06],'B'
   mov byte [gs:0x07],0xA4

   mov byte [gs:0x08],'R'
   mov byte [gs:0x09],0xA4

   ; 重点这里开始
   mov eax,LOADER_START_SECTOR ; loader起始扇区
   mov bx,LOADER_BASE_ADDR        ; 载入内存的起始位置
   mov cx,1                                          ; 读取一个扇区
   call rd_disk_m_16

   jmp LOADER_BASE_ADDR

rd_disk_m_16:
      ; 备份现场
      mov esi,eax
      mov di,cx

      ; 设置读取扇区
      mov dx,0x1f2
      mov al,cl
      out dx,al

      mov eax,esi

      ; 存入LBA
      mov dx,0x1f3
      out dx,al

      mov cl,8
      shr eax,cl
      mov dx,0x1f4
      out dx,al

      shr eax,cl
      mov dx,0x1f5
      out dx,al

      shr eax,cl
      and al,0x0f
      or al,0xe0
      mov dx,0x1f6
      out dx,al

      ; 向command 寄存器中写入读命令  0x20
      mov dx,0x1f7
      mov al,0x20
      out dx,al

     ; 检测硬盘状态
  .not_ready:
      nop
      in al,dx
      and al,0x88 
      cmp al,0x08
      jnz .not_ready

     ; 读数据
      mov ax, di
      mov dx, 256
      mul dx
      mov cx, ax
      mov dx, 0x1f0
  .go_on_read:
      in ax,dx
      mov [bx],ax
      add bx,2
      loop .go_on_read
      ret

   times 510-(-$) db 0
   db 0x55,0xaa
操作系统

通过BIOS的0x15中断获取物理内存容量

Posted on

以前嫌麻烦留着没看,今天补了一下,发现其实只是一些api的调用罢了,这里做个笔记。

对于我们用户来来说,获取物理内存的容量一般用内核调用即可,比如Linux 2.6中使用的detect_memory函数调用。其本质就是使用BIOS的中断0x15检测的。

这里稍微解释一下BIOS(可跳过),BIOS,基本输入输出系统,PC启动加载的第一个软件,介于操作系统与硬件的抽象层,可用于检测,加载引导程序或者操作系统。显然,PC的启动流程应该是 BIOS -> MBR -> loader -> kernel。

简要说明

开始正文。中断0x15有三个子功能,子功能号放到EAX或者AX中,其简要说明:
+ EAX = 0xE820 遍历所有内存
+ AX = 0xE801 分别检测低 15 MB 和 16 MB ~ 4 GB的内存,最大支持 4 GB。
+ AX = 0x88 最简单的子功能,只能检测64MB的内存,若超过也只返回 64 MB。

三分之二的功能对于我们当今的64位系统基本是废的,可见BIOS被淘汰也是历史必然的趋势,虽然现在还活着。

0xE820

0xE820的功能最强,当然也最复杂,它需要多次调用,每次调用都返回一种类型的内存,直到检测完毕。其结果主要通过ARDS结构存储在内存中。ARDS全称地址范围描述符
ARDS结构如下

字节偏移量描述
0基地址低32位
4基地址高32位
8内存长度的低32位,单位字节
12内存长度的低32位,单位字节
16类型

因为我学的的是32位系统,所以没有直接说64位……
而类型分为操作系统可使用(1),不可使用(2),保留(其他)。

下面是使用前使用说明

寄存器用途说明
EAX子功能号,这里位 0xE820
EBXARDS后续值,必须初始化0,表示下一个待返回的ADRS
ES:DIARDS写入的内存
ECXARDS 的大小,这里是20字节
EDX固定为0x534d4150 ,"SMAP"的ASCII码,意义不明

使用后说明

寄存器用途说明
CF位CF为1,调用出错,否则正常
EAX固定为 SMAP的ASCII码
ES:DI同输入
ECX同输入
EBX更新后的ARDS后续值

0xE801

0x801是稍微适中一点的方法,但是最多只能识别4GB,对于32位系统一般是足够的。
调用结果放在两个寄存器中,低于15KB的内存以1KB位单位,存储在AX和CX中,高于16KB的内存以64KB位单位,存储在BX和DX中。
稍微计算一下便可得出,AX和CX的最大值应为 0x3c00 ,BX和DX的最大值应为 0x10000。

提一下“消失”的 15MB ~ 16MB 去哪了,仍然是历史遗留问题,80286的寻址空间是16MB,当时有些ISA设备需要用到 这1MB空间作为缓存,然后就空出来了。为了兼容,就变成现在这样了。

以下是 0xE801 的使用步骤:
+ AX寄存器写入 0xE801
+ 调用中断0x15
+ CF为0的情况下,输出 AX \times 1024 + BX \times 64 \times 1024,最后单位是byte。

0x88

最简单的检测法,不详说,只能检测到 物理内存 1MB 之上的内存,故最多先是63MB。
其使用步骤为
+ 在 ax 中写入0x88
+ 调用中断0x15
+ CF位为0的情况下,单位大小为1KB,输出对应结果 ax \times 1024 + 1 MB 字节

下面是一段三个方法的汇编程序

    total_mem_bytes dd 0

    gdt_ptr  dw  GDT_LIMIT
    dd  GDT_BASE

    ards_buf times 244 db 0
    ards_nr dw 0 

loader_start:

    xor ebx, ebx
    mov edx, 0x534d4150
    mov di, ards_buf
.e820_mem_get_loop:
    mov eax, 0x0000e820
    mov ecx, 20
    int 0x15
    jc .e820_failed_so_try_e801
    add di, cx
    inc word [ards_nr]
    cmp ebx, 0
    jnz .e820_mem_get_loop

    mov cx, [ards_nr]
    mov ebx, ards_buf
    xor edx, edx 

.find_max_mem_area:
    mov eax, [ebx]
    add eax, [ebx+8]
    add ebx, 20
    cmp edx, eax
    jge .next_ards
    mov edx, eax
    .next_ards:
    loop .find_max_mem_area
    jmp .mem_get_ok

.e820_failed_so_try_e801:
    mov ax,0xe801
    int 0x15
    jc .e801_failed_so_try88

    mov cx,0x400
    mul cx
    shl edx,16
    and eax,0x0000FFFF
    or edx,eax
    add edx, 0x100000
    mov esi,edx

    xor eax,eax
    mov ax,bx
    mov ecx, 0x10000
    mul ecx
    add esi,eax
    mov edx,esi
    jmp .mem_get_ok

.e801_failed_so_try88:
    mov  ah, 0x88
    int  0x15
    jc .error_hlt
    and eax,0x0000FFFF

    mov cx, 0x400 
    mul cx
    shl edx, 16
    or edx, eax
    add edx,0x100000

.mem_get_ok:
    mov [total_mem_bytes], edx    ;存入total_mem_bytes处。

....

.error_hlt
    hlt

操作系统

保护模式大坑——特权级详解

Posted on

终于看懂系列…………

本菜鸡这次主要讲的内容是对三个特权级DPL,CPL,RPL在x86下的理解,如果大佬想看的不是这个就请关上这个标签页。

一些自我理解与背景

负责任地说,操作系统是一个死循环,其间有很多确定和与不确定的中断与调用,最后再被不确定地break掉。在中断与调用中,我们作为用户自然是不可能随意触碰,因为用户不都是满怀好意并且是极其细心的。因此操作系统需要对资源进行保护,而特权级就是保护模式最大的体现。

特权级简介

特权级使用两位4个级别,特权级0通常赋予内核,特权级1,2则是一些系统级服务,常见的就是外设,而我们的应用程序全都在特权级3。
值得注意的是,任何对段寄存器的修改都会触发CPU进行特权级检测,以防任务越权访问。

特权级字段有以下4个:
DPL: 段描述符特权级,最好理解的特权级,位于段描述符的高32位,表示的是这个段描述符所表示的段的特权级。
RPL: 请求特权级。位于选择子的最后两位。字面意思,表示请求时的特权级。这里强调,选择子可以保存在任意段寄存器中。
CPL: 当前特权级。当前任务/cpu/系统所处的特权级,永远位于当前CS寄存器所存的段选择子的RPL位。正如其存储位置表明,大部分情况下,CPL==RPL。在下面会说反例。
IOPL: I/O特权级,位于EFLAGS寄存器中,表示当前任务的I/O特权级别,通常是对端口访问的许可权。

CPL != RPL的情况

RPL真正强调的是请求者的特权级,因为在大多数情况下,请求者就是自己,所以CPL==RPL。而当请求者并非是自己的时候,两者自然不再相等,举个栗子:
我有一段程序需要从外设(比如键盘)中获取数据,而应用程序显然没有这个权限,但是可以通过调用门调用内核例程去获取外设的数据。
这个过程中,系统例程的请求者为应用程序,在使用调用门时必然会对任一段寄存器修改,其选择子的RPL为3,因为是系统调用,CS的段选择子的RPL为0。

引入RPL的必要性

段寄存器并非唯一,并且在大多数情况下CPL与RPL相同,看似RPL十分多余而费力,其实是为了更好地保护。
如果不存在RPL,当我们通过调用门访问系统数据段的时候,当前CPL为0,我们的特权上升到最高,就可以任意修改系统数据段,这是十分危险的。

控制转移的特权级检查规则

  • 在任何时候,都不允许将控制从较高的特权级代码段转移到控制较低的特权级代码段。这非常好理解,因为操作系统不会引用可靠性比自己低的代码。
  • 通过设置段描述符的TYPE字段的C位,更改其依从性,所谓依从性,即允许低特权级代码段转移到高特权级代码段,且在数值上满足 min(CPL , RPL) \geq 目标DPL。否则,只能同特权级转移。
  • 高特权级的代码段可以任意访问低特权级的数据段,反之则需要特权级检查:在数值上必须满足 max(CPL , RPL) \leq 目标DPL

这里需要强调控制转移和任务不要混淆…………

大概就这些了……

翻译

Photon 也许能成为你最喜爱的容器操作系统

Posted on

Photon OS

Phonton OS 专注于容器,是一个非常出色的平台。 —— Jack Wallen

容器在当下的火热,并不是没有原因的。正如之前讨论的,容器可以使您轻松快捷地将新的服务与应用部署到您的网络上,而且并不耗费太多的系统资源。比起专用硬件和虚拟机,容器都是更加划算的,除此之外,他们更容易更新与重用。

更重要的是,容器喜欢 Linux(反之亦然)。不需要太多时间和麻烦,你就可以启动一台 Linux 服务器,运行Docker,然后部署容器。但是,哪种 Linux 发行版最适合部署容器呢?我们的选择很多。你可以使用标准的 Ubuntu 服务器平台(更容易安装 Docker 并部署容器)或者是更轻量级的发行版 —— 专门用于部署容器。

Photon 就是这样的一个发行版。这个特殊的版本是由 VMware 于 2005 年创建的,它包含了 Docker 的守护进程,并可与容器框架(如 Mesos 和 Kubernetes )一起使用。Photon 经过优化可与 VMware vSphere 协同工作,而且可用于裸机、Microsoft Azure、 Google Compute Engine、 Amazon Elastic Compute Cloud 或者 VirtualBox 等。

Photon 通过只安装 Docker 守护进程所必需的东西来保持它的轻量。而这样做的结果是,这个发行版的大小大约只有 300MB。但这足以让 Linux 的运行一切正常。除此之外,Photon 的主要特点还有:

  • 内核为性能而调整。
  • 内核根据内核自防护项目(KSPP)进行了加固。
  • 所有安装的软件包都根据加固的安全标识来构建。
  • 操作系统在信任验证后启动。
  • Photon 的管理进程可以管理防火墙、网络、软件包,和远程登录在 Photon 机器上的用户。
  • 支持持久卷。
  • Project Lightwave 整合。
  • 及时的安全补丁与更新。

Photon 可以通过 ISO 镜像OVAAmazon Machine ImageGoogle Compute Engine 镜像Azure VHD 安装使用。现在我将向您展示如何使用 ISO 镜像在 VirtualBox 上安装 Photon。整个安装过程大概需要五分钟,在最后您将有一台随时可以部署容器的虚拟机。

创建虚拟机

在部署第一台容器之前,您必须先创建一台虚拟机并安装 Photon。为此,打开 VirtualBox 并点击“新建”按钮。跟着创建虚拟机向导进行配置(根据您的容器将需要的用途,为 Photon 提供必要的资源)。在创建好虚拟机后,您所需要做的第一件事就是更改配置。选择新建的虚拟机(在 VirtualBox 主窗口的左侧面板中),然后单击“设置”。在弹出的窗口中,点击“网络”(在左侧的导航中)。

在“网络”窗口(图1)中,你需要在“连接”的下拉窗口中选择桥接。这可以确保您的 Photon 服务与您的网络相连。完成更改后,单击确定。

change settings

图 1: 更改 Photon 在 VirtualBox 中的网络设置。经许可使用

从左侧的导航选择您的 Photon 虚拟机,点击启动。系统会提示您去加载 ISO 镜像。当您完成之后,Photon 安装程序将会启动并提示您按回车后开始安装。安装过程基于 ncurses(没有 GUI),但它非常简单。

接下来(图2),系统会询问您是要最小化安装,完整安装还是安装 OSTree 服务器。我选择了完整安装。选择您所需要的任意选项,然后按回车继续。

installation type

图 2: 选择您的安装类型。经许可使用

在下一个窗口,选择您要安装 Photon 的磁盘。由于我们将其安装在虚拟机,因此只有一块磁盘会被列出(图3)。选择“自动”按下回车。然后安装程序会让您输入(并验证)管理员密码。在这之后镜像开始安装在您的磁盘上并在不到 5 分钟的时间内结束。

Photon

图 3: 选择安装 Photon 的硬盘。经许可使用

安装完成后,重启虚拟机并使用安装时创建的用户 root 和它的密码登录。一切就绪,你准备好开始工作了。

在开始使用 Docker 之前,您需要更新一下 Photon。Photon 使用 yum 软件包管理器,因此在以 root 用户登录后输入命令 yum update。如果有任何可用更新,则会询问您是否确认(图4)。

Updating

图 4: 更新 Photon。经许可使用

用法

正如我所说的,Photon 提供了部署容器甚至创建 Kubernetes 集群所需要的所有包。但是,在使用之前还要做一些事情。首先要启动 Docker 守护进程。为此,执行以下命令:

systemctl start docker
systemctl enable docker

现在我们需要创建一个标准用户,以便我们可以不用 root 去运行 docker 命令。为此,执行以下命令:

useradd -m USERNAME
passwd USERNAME

其中 “USERNAME” 是我们新增的用户的名称。

接下来,我们需要将这个新用户添加到 “docker” 组,执行命令:

usermod -a -G docker USERNAME

其中 “USERNAME” 是刚刚创建的用户的名称。

注销 root 用户并切换为新增的用户。现在,您已经可以不必使用 sudo 命令或者切换到 root 用户来使用 docker 命令了。从 Docker Hub 中取出一个镜像开始部署容器吧。

一个优秀的容器平台

在专注于容器方面,Photon 毫无疑问是一个出色的平台。请注意,Photon 是一个开源项目,因此没有任何付费支持。如果您对 Photon 有任何的问题,请移步 Photon 项目的 GitHub 下的 Issues,那里可以供您阅读相关问题,或者提交您的问题。如果您对 Photon 感兴趣,您也可以在该项目的官方 GitHub中找到源码。

尝试一下 Photon 吧,看看它是否能够使得 Docker 容器和 Kubernetes 集群的部署更加容易。

欲了解 Linux 的更多信息,可以通过学习 Linux 基金会和 edX 的免费课程,“Linux 入门”


via: https://www.linux.com/learn/intro-to-linux/2017/11/photon-could-be-your-new-favorite-container-os

作者:JACK WALLEN
译者:KeyLD (其实就是我啦~~)
校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

LinuxCN原文传送门

学习笔记

《真象》第四章 保护模式入门

Posted on

回过头来看以前写的代码居然有点看不懂了,还是应该写一下笔记啊……顺便吧第四章复习一下。
《真象》是指《操作系统真象还原》这是第四章笔记,保护模式入门。

  1. 保护模式起于80286,为16位的CPU,兴起于80386,32位的CPU。保护模式下一般都是32位及以上。
  2. CPU有三种模式:实模式,虚拟8086模式,保护模式。严格意义上32位CPU无实模式。
  3. 制定运行模式编译:使用法 [bits] 16/32
  4. 运行模式反转:指运行模式在16位实模式,操作数为32位;或者当前运行模式是32为保护模式,操作数为16位。操作系统兼容两种运行模式,所以支持两种操作,一般临时的反转会在指令前自动加上'66'或者'67'。(不过这实际上是编译器的工作,我们只需要知道就好了
  5. 在保护模式下,必须用ecx表示循环次数,其他都兼容。
  6. 对于段寄存器的入栈,无论指定在哪种模式下运行,都是按照当前模式的默认操作数大小压入的。而对于通用寄存器和内存,入栈是sp移动大小有操作数决定。
  7. 段描述符,8字节大小。保存着段基址和段界限和各种保护信息,用于描述一个段(代码段,栈段等),具体格式见 P151。
  8. 全局描述符表GDT,局部描述符表LDT。通俗的说,描述符表就是一个段描述符数组,所谓选择子就是数组下标。GDT是全局数组,LDT为局部数组。通过选择子在描述符表中找出段描述符,段描述符中又记录中段基址和段界限,即可得到一个段。
  9. GDT Register。全局表述符表保存在内存中,而GDT寄存器保存着这个表的位置与大小。寄存器大小为 6 字节。前2个字节为GDT界限,后4个字节为GDT内存其实地址。赋值格式 lgdt 48位数据。而 LDT Register的格式为 lldt 16位寄存器/16位内存
  10. 选择子结构。前2位用于表示选择子请求者的当前特权级别RPL,2位4个等级。第3位为类型标识,0为GDT,1为LDT。一个选择子初始化为全0。初始选择子会访问GDT的第一个段描述符,为了保护作用,GDT的第一个段空间不可用,当访问第一个段空间的时候会报错。
  11. A20Gate。在实模式下的wrap-around,当地址超过20位的时候就会发生回绕。保护模式下为了兼容实模式,有了A20Gate。A20Gate被禁止并访问20位以上时,会发生地址回绕,否则不会,也就在寻址上进入了保护模式。
  12. 保护模式的开关,CR0寄存器的PE位。将其置为1后,将在真正意义上进入保护模式。一般或1即可。
  13. 由实模式进入保护模式的三个步骤:
    1. 打开A20Gate
    2. gdtr寄存器中加载GDT的地址会界限
    3. 将CR0寄存器的pe位置1
  14. 流水线。将一个目标指令具体化为多个指令,在已知(或预测)指令的情况下,CPU并行处理多个指令,因此某段时间后的每一个时间周期都会执行完一个目标指令。当遇到jmp指令的时候,流水线会清空。
  15. 对流水线的优化有无先后逻辑的指令有乱序执行和缓存等。
  16. 在实模式进入保护模式之后,应优先清空流水线。因为实模式下的指令存储与保护模式不同,实模式会先把段基址左移后在加入缓存,这将与保护模式的指令格式不符,导致错误。
  17. 分支预测。最简单的分支预测算法是 2位预测法,用2位bit的计数器来记录跳转状态,没跳转一次加1,达到3则不再增加,否则减1,直到0则不再减了。优先计数大的,否则随机。
  18. 加载选择子时的保护:
    1. 验证段描述符是否越界。
    2. 检查段的类型。不同段的不同type允许被加载的寄存器不同。详细见P174。
    3. 检查段是否存在。CPU通过段描述符的P位来确认内存段是否存在。
  19. 代码段与数据段的保护。CPU每访问一个地址,都要确认该地址不能超过该内存段范围。确认方式为 (描述符中段界限+1) × (段界限的粒度大小:4k或者1) - 1,化简可得
    • 指令: EIP的偏移地址+指令长度-1 <= 实际界限大小
    • 数据: 偏移地址+数据长度-1 <= 实际界限大小
  20. 栈段并不一定都是向下扩展的。(这里有地方不是很明白,P175
学习笔记

《操作系统真象还原》的一些笔记

Posted on

终于有时间开始搞我的OS Demo了。
希望能在元旦之前搞完。
这里不定时更新记录我在《操作系统真象还原》的一些笔记。以此督促和勉励自己。
很早之前就想看这本书了,之前在一个腾讯员工的书单上看到的,虽然貌似没什么名气,但看了一部分觉得还是挺有趣的,是我能非常乐意看下去的类型。那种带着程序员的幽默而不乏真实技术的书籍。

2017-10-24 23:50:14 星期二

  1. 编译器提供库函数,库函数封装了系统调用,这样的代码集合称之为运行库。
  2. 用户进程永远不会因为进入了内核态而变身为操作系统。
  3. cs: ip。 汇编指令,表示当前执行的指令。cs是代码段寄存器,ip是指令指针寄存器,指令指针计算为 cs \times 16 + ip
  4. DRAM,动态随机访问内存。物理内存,也就是内存条就属于DRAM。其动态并不是内容变化的意思,而是保存时间短,需要顶起的刷新。
  5. 地址总线宽度决定可以访问的内存空间容量。书中所说是决定内存空间大小,我觉得应该是容量,或者说是上限,更为合适。yy 总线先分配外设地址,最后才将其余可用地址分配给DRAM。所以说,内存空间存在一个上限。
  6. 在8086中,内存空间中最前面的 1KB 地址为中断向量表。可以通过使用 int 中断号来实现相关的系统调用。
  7. 魔数。约定的含有具体意义但不说明的数字。在Linux中就是通过魔数来辨别文件系统。
  8. 尽管所有语言都会被转化成机器码,但中间存在效率,也许一个意思,Java语句有100条机器码,而汇编只有10条不到。
  9. 编译器的自我进化真的是非常神奇。内容太多,以后有机会可以再读p16。

2017-10-26 00:46:57 星期四

  1. CHS方式中扇区的编号是从1开始的
  2. 判别是否是主引导记录mbr的方式为文件末尾的两个魔数0x55和0xaa
  3. mbr固定会被加载到内存的 0x7c00 地址
  4. $ 是编译器给当前行安排的地址,$$则是本section的起始地址
  5. section 只是用于给程序员在逻辑上规划代码用的,并没有什么实质意义
  6. 编译器有一个重要的工作是给程序中各符号编址。很重要,符号包括数据类型,数据。编译器只负责编址,它只会将数据相对于文件开头的偏移量作为该数据的地址。
  7. vstart。虚拟起始地址。告知编译器在当前section之后的所有数据都编在这个vstart地址之后。用vstart的时机是我预先知道我的程序将被加载在某处。
  8. 数据的内存地址与文件地址应严格区分开来。数据的文件地址不会受到vstart影响。它的地址绝大多数是连续的。目前的个人理解,可能有错误

2017-10-27 00:09:24 星期五

麻辣格鸡的好难

  1. 实模式。是指CPU的寻址方式,寄存器大小,指令用法等,用来反应CPU在该环境下如何工作的概念。
  2. CPU唯一的任务就是执行指令。
  3. CPU大体分为三个部分,控制单元,运算单元,存储单元。
  4. 控制单元大致由指令寄存器,指令译码器,操作控制器组成。功能很好理解,先存储指令,再解码指令,最后进行操作,对其他CPU部分开始控制。
  5. 很多地方都用到了缓存,浏览器的访问就是一个简单例子。
  6. 寄存器之所以快,是由于它有触发器实现。已经完全忘了
  7. CPU的寄存器可分为对程序员不可见与可见。比如一些固定数据的存储,肯定是不能让程序员访问的。
  8. 实模式下,默认用到的寄存器都是16位宽。
  9. flags寄存器是计算机的窗口,展示了CPU内部的各项设置,指标。并不知道怎么用
  10. 八个通用寄存器 AX BX CX DX SI DI BP SP,其中前四个又可各自划分为两个部分。比如对于 AX来说,可以划分为 高位(High) AH ,低位(Low) AL。也可扩展(Extend) 为 EAX
  11. 是历史上第一款 x86 CPU,在8086之前都是不存在段的概念,直接用硬编码访问内存。
  12. 8086通过“段+偏移”来实现20位寻址,16 + 16 - 20,多出的 12 位不用管它,会自动进行取模运算。

2017-10-28 01:00:32 星期六

先学汇编去了 传送门