老庄主软件一信息化服务商 發表於 2024-2-27 23:20:00

汇编语言自学笔记

<h1 id="227">2.27</h1>
<p>回顾计算机组成原理中学到的冯诺依曼结构的计算机取指执行的过程。</p>
<p>x86计算机开机时,CPU处于实模式。寻址CS:IP(CS左移4位+IP)<br>
CS和IP是8086CPU中两个关键的寄存器,它们指示了CPU当前要读取指令的地址。 CS : 代码段寄存器;IP : 指令指针寄存器。 在8086机中,任意时刻,CPU将CS:IP指向的内容当作指令来执行。</p>
<p>【了解实模式与保护模式】</p>
<p>寻址0xFFFF0(ROM BIOS映射区)<br>
检查RAM、键盘、显示器、软硬磁盘<br>
将磁盘0磁道0扇区(操作系统的引导扇区)读入0x7c00处<br>
设置CS=0x07c0,ip=0x0000,寻址07c00</p>
<p>引导扇区512个子节是什么?<br>
bootsect.s 这是一段汇编代码</p>
<h1 id="36">3.6</h1>
<p>CPU有很多寄存器,AX,BX...</p>
<p>机器码和汇编指令是一一对应的。</p>
<p>汇编语言:汇编指令、伪指令(编译器执行)、其他符号(编译器识别)</p>
<p>存储器被划分为若干个存储单元</p>
<p>CPU进行数据的读写,必须和芯片(内存、显存、网卡内存等)进行三类交互:地址信息、控制信息、数据信息。连接它们的导线是总线。分为地址总线、控制总线、数据总线。</p>
<p>地址总线上能传送多少个不同的信息,CPU就可以对多少个存储单元进行寻址。【如64位:CPU、操作系统、软件】<br>
一个CPU有N根地址总线,宽度就为N,也就是N位,可以寻址2^N个存储单元。</p>
<p>数据总线也是如此,宽度决定传送速度。</p>
<p>8088 8位<br>
8086 16位</p>
<p>控制总线,有一根是读信号输出控制线,一根是写信号输出控制线</p>
<h1 id="314">3.14</h1>
<p>对于一个硬盘来说,最多只能创建三个主分区,一个扩展分区。在扩展分区上又可以划分若干逻辑分区。对于一个常规的操作系统来说,一般只能安装在主分区中,并且安装在主分区中的操作系统远比安装在逻辑分区中的方便管理且安全得多。(注:Linux就可安装在逻辑分区中)</p>
<p>硬盘的物理第一扇(0柱面,0磁道,1扇区)是硬盘主引导记录扇MBR,计算机启动时,首先就读取该扇,读出硬盘分区表,从中选择三个主分区中唯一一个具有活动标记的分区,引导该分区上的操作系统。也就是说,无论有几个主分区(≤3),其中必须有一个分区是活动的。Linux的引导记录可以安装在MBR中,或者Linux所在主分区中。如果Linux安装在逻辑分区中则Linux的引导记录必须安装在MBR中。</p>
<p>Linux对硬盘设备的命名方式与Windows系列大不一样。每一个硬盘拥有自己的设备名。IDE硬盘为hd? , SCSI硬盘为sd? 。?表示a到z的小写字母,不同的字母代表不同的硬盘设备,这个字母为主设备号。而硬盘上的分区用hd??或sd??中的第二个?表示,它由数字组成,1-4表示主分区,从5开始表示逻辑分区。</p>
<p>在MOS给出的磁盘文件系统布局图上,可以发现每个磁盘分区最开始都有一个引导块,这个引导块中也可以有grub程序。主引导块之所以叫做”主”,是因为它是BIOS默认调用的引导块,并且主引导块可以选择让权,即把引导工作让给其他磁盘分区中的引导块完成。一般windows系统的主引导块没有这个作用,linux有,而安装多系统的时候主引导块会相互覆盖。因此应该首先安装windows系统然后安装linux系统,如此主引导块就可以选择让权或者直接引导了。</p>
<h1 id="320">3.20</h1>
<h2 id="一内存相关">一、内存相关</h2>
<p>内存地址空间<br>
主板上的器件通过总线连接<br>
CPU对外部设备的控制通过的是<strong>接口卡</strong><br>
ROM:装有BIOS,在主板和某些接口卡上,加电后执行的第一个程序<br>
<img src="https://img2024.cnblogs.com/blog/2997174/202403/2997174-20240320231738009-519953991.png" alt="image" loading="lazy"></p>
<p>注意:这些存储器和CPU的总线相连,且CPU通过控制总线发出读写命令对它们进行读写<br>
8086有20位地址总线,内部为16位结构(运算器一次最多处理16位,寄存器宽度16位,寄存器和运算器通路16位)<br>
<img src="https://img2024.cnblogs.com/blog/2997174/202403/2997174-20240320231909095-1400593211.png" alt="image" loading="lazy"><br>
上图可以看出对CPU来说所有存储器的存储单元都在一个统一的逻辑存储器中。CPU编出来的唯一的地址是物理地址。</p>
<p>下图为8086的内存地址空间分配<br>
<img src="https://img2024.cnblogs.com/blog/2997174/202403/2997174-20240320232003064-1252447120.png" alt="image" loading="lazy"></p>
<h2 id="二寄存器">二、寄存器</h2>
<p>8086CPU有14个16位寄存器<br>
AX BX CX DX SI DI SP BP IP CS SS DS ES PSW<br>
10010的存储方式如下图:<br>
<img src="https://img2024.cnblogs.com/blog/2997174/202403/2997174-20240320232355377-135690913.png" alt="image" loading="lazy"></p>
<p>AX BX CX DX每一个都可以分为两个独立的8位寄存器<br>
如AX=AH+AL<br>
存储方式如下图<br>
<img src="https://img2024.cnblogs.com/blog/2997174/202403/2997174-20240320232608836-605403640.png" alt="image" loading="lazy"></p>
<h2 id="三寻址">三、寻址</h2>
<p>8086CPU如何在内部用两个16位地址合成的方法形成一个20位的物理地址?<br>
<img src="https://img2024.cnblogs.com/blog/2997174/202403/2997174-20240320233143360-1083121859.png" alt="image" loading="lazy"></p>
<p>怎么理解段?<br>
若干连续的内存单元。段地址×16就是段的起始地址,再用偏移地址定位段中具体位置。<br>
一个段最长就是2的16次方=64KB,因为偏移地址是16位的</p>
<p>现在我们就知道2000:1F60的意思了。<br>
即2000段的1F60H单元,也即物理地址21F60H</p>
<p>CS SS DS ES就是专门用于存放段地址的寄存器<br>
CS为代码段地址寄存器<br>
IP为指令指针寄存器,每次加上读取指令的长度</p>
<p>加电时CS:IP=FFFF:0000<br>
因此FFFF0H单元中的指令就是加电后执行的第一条指令</p>
<p>mov指令不能修改CS、IP的值,要用jmp<br>
jmp ax 即把IP的值赋值为ax的值</p>
<p>代码段的概念</p>
<h2 id="四debug">四、Debug</h2>
<p>Debug:实模式调试工具<br>
R:查改寄存器<br>
D:看内存<br>
E:改内存<br>
U:翻译成汇编<br>
T:执行一条机器指令<br>
A:以汇编指令格式在内存中写入一条机器指令<br>
我去,好东西啊。</p>
<p>可以在FFF00H-FFFFFH的某几个单元中,看到ROM上记录的生产日期<br>
B8100H开始的单元,是显存的。修改它可以发现屏幕上出现字符。</p>
<h2 id="五">五、</h2>
<p>内存中字的存储:高地址对应高位,低地址对应低位<br>
<img src="https://img2024.cnblogs.com/blog/2997174/202403/2997174-20240321000611678-89377839.png" alt="image" loading="lazy"><br>
如上图,1地址字单元中存储的字为124EH</p>
<p>回忆:CS SS DS ES就是专门用于存放段地址的寄存器<br>
CS是代码段地址寄存器<br>
我们再学一个:DS是数据段地址寄存器<br>
mov bx,1000H<br>
mov ds,bx<br>
mov al,<br>
这样就能把1000:0处数据读到AL中<br>
【总结:mov可以把数直接送入寄存器,可以把寄存器的数据送入另一个寄存器,还可以把内存单元中的内容送入寄存器】【[]内部的是偏移地址】<br>
注意不可以用mov bx,1000H直接修改DS!要借助一下通用寄存器的力量。</p>
<p>sub a,b 即a=a-b</p>
<p>数据段的概念</p>
<p><strong>add,sub不能对段寄存器操作</strong></p>
<h2 id="六栈">六、栈</h2>
<p>学过了CS、IP、DS<br>
再来学SS和SP<br>
SS:栈顶段地址寄存器<br>
SP:栈顶偏移地址寄存器</p>
<p>push一个16位的寄存器存的字时,SP=SP-2再加进去<br>
pop就存数据,再SP=SP+2</p>
<p>栈越界问题</p>
<h1 id="324">3.24</h1>
<p>主板寻找并启动硬盘系统的方式有两种,就是 传统模式(Legacy)和 UEFI模式<br>
传统模式(Legacy)和MBR格式的硬盘相对应<br>
UEFI模式和GPT格式硬盘 相对应</p>
<p>有一些主板的UEFI固件只识别GPT格式的分区,这种情况下MBR分区格式就不支持UEFI了。部分自识别的主板(Legacy+UEFI启动模式)UEFI固件能识别MBR。</p>
<p>而硬盘分区是GPT格式的分区,只能采用UEFI方式安装引导,否则无法启动。</p>
<p>MBR启动模式流程:<br>
1.上电并稳定后,CPU执行地址0xFFFF0h处指令,此处为BIOS程序;<br>
2.BIOS进行硬件自检,没有问题后加载硬盘的第一个扇区到内存0x7c00h处,第一个扇区为MBR(Master<br>
Boot Record),MBR包含执行程序和分区表;<br>
3.CPU开始执行MBR程序,查找第一个活动分区,把活动分区的第一个扇区加载到内存中,活动分区第一个扇区为PBR(Partition Boot Record);<br>
4.CPU开始执行PBR,第一个指令就是跳过BPB(BIOS Parameter Block)到可执行代码处;BPB包含比较多参数,有族的大小、MFT记录大小、MFT位置等,用于读取NTFS文件;<br>
5.PBR读取VBR(Volume BootRecord,占用分区开始的16扇区)剩余的15扇区到内存中;接着CPU跳转到0x07C0:027A处,执行BOOTMGR代码(第二个扇区中);<br>
6.开始寻找bootmgr.exe,找不到则寻找ntldr.exe(win vista之前的系统);<br>
7.CPU加载并跳转到bootmgr.exe处执行,读取BCD文件,如果含有多个系统,则列举显示供用户选择;<br>
8.选择的是Windows则读取winload.exe文件到内存中,CPU跳转到winload.exe处执行,读取文件\windows\bootstat.dat,,有需要则显示引导菜单,比如安全引导等等;接着加载内核程序Ntoskrnl.exe,相关辅助HAL.dll、CI.dll、PSSHED.dll、BootVID.dll,把CPU执行权交给内核程序;</p>
<p>  LBA与C/H/S 之间的转换:</p>
<p>  设NS为每磁道扇区数,NH为磁头数,C、H、S分别表示磁盘的柱面、磁头和扇区编号,LBA表示逻辑扇区号,div为整除计算,mod为求余计算,则:</p>
<p>  LBA=NH×NS×C+NS×H+S-1;</p>
<p>  C=(LBA div NS)div NH;</p>
<p>  H=(LBA div NS)mod NH;</p>
<p>  S=(LBA mod NS)+1</p>
<p>  例如 LBA = 0 则 CHS = 0/0/1</p>
<p>  从C/H/S到LBA的计算公式:</p>
<p>  LBA=(C-CS)<em>PH</em>PS+(H-HS)*PS+(S-SS)</p>
<p><img src="https://img2024.cnblogs.com/blog/2997174/202403/2997174-20240324162150509-2144333452.png" alt="image" loading="lazy"></p>
<p>例子:<br>
(00) (FE FF FF)(0F) (FE FF FF) (00 50 3B 4F) (00 18 35 25)<br>
(0F) :表示该分区为扩展分区<br>
(FE FF FF) :磁头号:(254) 10;扇区号:(11 1111) 2 = (63) 10 ; 柱面号:(11 1111 1111) 2 = (1023) 10<br>
(FE) 16 = (254) 10 (FF) 16 = (1111 1111) 2 (FF) 16 = (1111 1111) 2;<br>
但这是不准确的,因为当柱面号的真实值超过1023时,表示柱面号的10位也依然是1023。<br>
(00 50 3B 4F) : 反向,(4F 3B 50 00)16=(1329287168)10,表示扩展分区共的起始扇区号为1329287168扇区。指向E盘的地址。<br>
(00 18 35 25): 反向,(25 35 18 00)16=(624236544)10,表示扩展分区有624236544个扇区。扩展分区的大小为624236544/2/1024/1024=297G,这是这个扩展分区的大小,里面又可以分无限分区。如图,我的扩展分区又分为E盘和G盘。</p>
<h1 id="328">3.28</h1>
<h2 id="实验三">实验三</h2>
<p>8253/4:8254是8253的一个升级版,支持更高频率的脉冲输入且有读回功能(不只是计数完成才输出,也可以读取当前计数到哪个值了)</p>
<p>汇编中的10H中断是由BIOS对显示器和屏幕所提供的服务程序。使用int 10h服务程序时,必须先指定ah寄存器为以下显示服务编号之一,以指定需要调用的功用。</p>
<h1 id="410">4.10</h1>
<h2 id="操作系统概论">操作系统概论</h2>
<p><strong>用户角度、计算机工作角度看操作系统:</strong><br>
操作系统是一个程序,充当计算机用户与计算机硬件之间的媒介,目的是为用户提供一个环境,使用户能够以便利和有效的方式运行程序。(扩展机器-抽象与虚拟)<br>
操作系统是管理系统资源、控制程序执行、改善人机界面、提供各种服务,合理组织计算机工作流程和为用户有效使用计算机提供良好运行环境的一种系统软件。(资源管理-保护与复用)</p>
<p><strong>操作系统两大任务:</strong></p>
<ol>
<li>自顶向下(扩展机):为应用开发人员提供简洁易用的资源抽象</li>
<li>自底向上(资源管理者):管理种类繁多、纷繁复杂的硬件资源<br>
和上面的对操作系统的描述是对应的。<br>
理解抽象、虚拟、保护、复用的含义。</li>
</ol>
<p><strong>操作系统的功能和目标:</strong><br>
功能:资源管理(包括了处理器/存储/设备/文件的管理)、控制执行(进程调度、并发控制)、提供接口<br>
目标:方便用户使用、提升机器能力、提高运行效率、提供开放环境。</p>
<p><strong>操作系统的主要特征</strong><br>
并发性、共享性、异步性、虚拟性</p>
<p>区分并发性与并行性:并行性是同一时刻发生,限制更加严格。并发性更为概括。<br>
共享方式可分为互斥访问、同时访问。</p>
<p><strong>计算机启动时的引导</strong></p>
<p><strong>CPU硬件机制保障</strong></p>
<p>一方面是区分核心态(管态)、用户态(目态):<br>
是为了区别执行特权指令与非特权指令,在CPU的所有指令中,有一些指令是非常危险的,如果错用,将导致整个系统崩溃。比如:清内存、设置时钟等。所以,CPU将指令分为特权指令和非特权指令,对于那些危险的指令,只允许操作系统及其相关模块使用,普通的应用程序只能使用那些不会造成灾难的非特权指令。</p>
<p>另一方面和CPU保护模式有关(主存保护机制):<br>
保护模式主要目的保护多任务之间代码访问的完全隔离(使用虚拟地址),单任务的用户代码和系统代码的访问格式(使用cpu的privilege环)<br>
保护模式是CPU的一种工作方式,在保护模式下,CPU会启用内存保护、特权级等多种机制,以保护系统的稳定和安全。保护模式下的内存地址计算方式与实模式下不同,保护模式下的计算方式更为复杂。</p>
<p><strong>用户代码切换(核心态和用户态之间的切换)</strong></p>
<p>call/ret<br>
int/iret<br>
trap<br>
syscall/sysret</p>
<p><strong>阶段发展</strong></p>
<p>手工操作<br>
管理程序(批处理)<br>
多道批处理<br>
分时系统:时钟中断?时间片?分时操作系统将CPU的时间划分成若干个片段,称为时间片。操作系统以时间片为单位,轮流为每个终端用户服务。<br>
实时系统</p>
<p><strong>当前分类</strong></p>
<p>微机操作系统 普遍采用微内核</p>
<p>并行操作系统 针对并行计算机硬件而专门设计</p>
<p>网络操作系统 Unix 通常有客户服务器、PTP两种工作模式</p>
<p>分布式操作系统</p>
<p><strong>操作系统提供的服务与接口</strong></p>
<p>服务:完成功能(...)、提高效率(...)<br>
接口:用户接口、程序接口API、系统调用SystemCall</p>
<pre><code>POSIX:Portable Operating System Interface based on UNIX 可移植操作系统接口
应用程序通过应用编程接口(API)而不是直接通过系统调用来编程。一个API定义了一组应用程序使用的编程接口。它们可以实现成一个系统调用,也可以通过调用多个系统,也可以不使用任何系统调用。API可以在各种不同的操作系统上实现,给应用程序提供完全相同的接口,而它们本身在这些系统上的实现却可能迥异。许多操作系统,像Windows NT,尽管和Unix没有什么关系,也提供了与POSIX兼容的库。

从程序员的角度看,系统调用无关紧要;他们只需要跟API打交道就可以了。相反,内核只跟系统调用打交道;库函数及应用程序是怎么使用系统调用不是内核所关心的。

简单总结:完成同一功能,不同内核提供的系统调用(也就是一个函数)是不同的,例如创建进程,linux下是fork函数,windows下是creatprocess函数。这会让我们写的程序无法移植。posix标准的出现就是为了解决这个问题。linux和windows都要实现基本的posix标准,思路大概是,linux把fork函数封装成posix_fork,windows把creatprocess函数也封装成posix_fork,都声明在unistd.h里。这样,程序员编写普通应用时候,只用包含unistd.h,调用posix_fork函数,程序就在源代码级别可移植了。

不同操作系统的.h是在用不同的代码实现相同的功能。
</code></pre>
<p>而系统调用SystemCall,是用户请求内核操作系统服务的过程,由用户程序调用<strong>访管指令(陷入trap/异常)</strong> 开始,然后硬件保护现场,取功能号,载入入口地址表中对应的入口地址,进入处理子程序,结束后恢复现场。<br>
指令参数可能是指令中自带的,也可能是紧挨指令存放的,还可能是通过CPU的通用寄存器传递的,也可能是主存中的堆栈区域存放的。</p>
<pre><code>特权指令
从指令集角度定义,略

访管指令
同样从*指令集*的角度定义,是用户程序自愿进管的指令(进管同时也意味着程序放弃cpu的控制权),该指令本身属于非特权指令,可在用户态执行,执行后进入核心态。核心态是通过cpu置相应标志表明当前处于核心态。cpu进入核心态后才可以执行特权指令,但不执行访管指令。

陷入指令
陷入(自陷)指令强调*程序*从用户程序切换到了操作系统,和访管指令很像但角度不同。陷入指令即汇编中的中断指令,执行陷入指令程序中断,跳转到中断服务程序(操作系统的代码)。实际操作时,所有管态就是运行操作系统,访管指令本质上就是一条陷入(中断)指令。

系统调用
是从操作系统的角度定义的。指用户程序需要借助操作系统来完成的特定操作,通过陷入指令可以进入到操作系统内核代码进行系统调用。系统调用进入的内核代码是一段代码而不一定是单条指令。比如c语言的系统调用write(),在汇编层面这条语句包括,初始化相关参数和寄存器,执行陷入(中断)指令,跳转到中断服务程序,中断返回。之所以需要系统调用是因为用户程序不能执行特权指令,所以当需要完成特权指令才能做的特定操作,必须通过系统调用由操作系统完成。但系统调用仅仅强调“希望”让操作系统服务,即目标操作用户程序不需要自己做,直接调用操作系统即可完成,进行系统调用时也并非一定为了执行特权指令。
常见系统调用
进程管理fork, exec, wait, exit…
文件操作open, read, write, close, seek, …
内存管理malloc, free
进程通信pipe, …
信息维护get/set time, …

中断
中断是指程序执行过程中,当发生某个事件时,中止(暂停)CPU上现行程序的运行,引出处理该事件的服务程序执行的过程。中断事件处理需要硬件(中断装置)和软件(中断处理程序)配合完成。
按中断事件的性质和激活的手段分为:
强迫性中断事件,包括机器故障、程序性错误(异常)、外部中断、输入输出中断事件…
自愿性中断事件,包括调用访管指令
【内外的划分标准:处理器和主存为内,其他硬件为外】
按中断信号的来源分:
外中断(狭义中断),包括电源故障中断、时钟中断(外部)、控制台中断、输入输出中断、以及调试程序中设置的断点等引起的调试中断等。
内中断(异常),包括通路校验错、主存奇偶校验错、非法操作码、地址越界、页面失效、调试指令、访管中断(trap自陷)、算术操作溢出、…
(外)中断特点:
        与现行指令无关
        发生时间与CPU所处状态无关
        两条指令之间才能响应中断
        可被屏蔽
        可嵌套
异常特点:
        由现行指令执行而引起
        在目态发生
        可在一个指令周期内处理
        不可屏蔽、不可嵌套
        可细分为:出错,处理完后回到当前出错指令;陷入,处理完后执行下一条指令(常用于系统功能调用)、可编程中断:这类中断可由编程者用int指令来触发。在Linux中,使用了唯一的一个可编程中断,就是int 0x80系统调用。硬件对可编程中断的处理与对trap的处理类似,即从这类异常返回时也是返回到触发异常的下一条指令。关于可编程中断,还有另外一种说法:软中断(software interrupt),其实是一个意思。

(狭义)中断和陷阱(trap)的主要区别:
1 、陷阱通常由处理机正在执行的现行指令引起,而中断则是由与现行指令无关的中断源引起的。
2 、陷阱处理程序提供的服务为当前进程所用,而中断处理程序提供的服务则不是为了当前进程的。
3 、CPU 在执行完一条指令之后,下一条指令开始之前响应中断,而在一条指令执行中也可以响应陷阱。
4 、在有的系统中,陷入处理程序被规定在各自的进程上下文中执行,而中断处理程序则在系统上下文中执行。

库函数
操作系统提供的函数,供用户程序直接调用。与系统调用区别在于,可以在用户态执行,和普通的函数调用并没有什么区别。现在用户一般都不直接使用系统调用,而是使用封装好的库函数,由库函数进行系统调用。

函数调用
通过CALL指令来进行,功能和JMP类似,只是CALL同时要保存函数调用前下条指令的地址

i8086中
如果是段内调用,使用RET指令返回,从栈中弹出一个字,即IP(指令指针寄存器)
如果是段间调用,使用RETF指定,从栈中弹出两个字,即CS(代码段基址寄存器) 和IP
如果是系统调用,使用IREF指令,从栈中弹出三个字,即IP CS PSW(程序状态字)
</code></pre>
<p><strong>操作系统的基本元素</strong></p>
<p>内核、进程、线程</p>
<p><strong>操作系统结构发展</strong></p>
<p>整体式结构<br>
层次式结构<br>
虚拟机结构<br>
客户服务器及微内核结构</p>
<p><strong>经典操作系统</strong></p>
<p>DOS<br>
Windows系列<br>
Unix<br>
自由软件与Linux<br>
IBM系列<br>
其他</p>
<p><strong>微内核与宏内核</strong></p>
<p>宏内核其实就是把所有的功能都耦合起来,放在内核中,这样的优势就是性能极高,因为各个功能模块之间是可以直接调用的。缺点就是因为各个模块是相互关联,容易出现一错皆错的问题。</p>
<p>而微内核则恰恰相反,它为了降低耦合,内核中只会允许一些核心功能的存在,而其余所有功能都会被移出内核,变成一种特殊的用户进程——服务进程。其优点就是各个模块之间是独立的,不会相互影响,但其性能相比宏内核会大幅度下降。</p>
<p>宏内核设计将大部分操作系统功能实现为一个单独的、庞大的内核空间。这意味着所有的系统服务和设备驱动程序都运行在内核空间内。宏内核具有更高的性能,因为在同一地址空间内执行,减少了上下文切换和消息传递的开销。</p>
<p>微内核设计将操作系统的核心功能限制在最小的内核空间,只包含基本的调度、进程管理和通信机制,其他功能如文件系统、设备驱动等则作为用户空间的服务运行。<br>
如果一个用户空间服务崩溃,通常只会影响到该服务,而不会导致整个系统崩溃。</p>
<p>OS功能(函数)在用户进程内执行的模型:UNIX 宏内核<br>
OS功能作为独立进程执行的模型:Windows, Minix 微内核</p>
<h1 id="412">4.12</h1>
<p><strong>抢占式调度与非抢占调度</strong></p>
<h1 id="417">4.17</h1>
<p><strong>程序状态字 PSW</strong></p>
<p>用于区别不同程序的处理器工作状态<br>
每个程序都有一个与其执行相关的PSW,而每个处理器均设置一组相关寄存器用于存储PSW信息<br>
PSW的主要内容:程序基本状态(程序计数器、条件码、状态位)、中断码、中断屏蔽位</p>
<p><strong>中断装置</strong></p>
<p>定义:发现中断源并产生中断的硬件,通常包括逻辑电路和中断寄存器<br>
具体功能:捕获中断源,响应中断请求;保护现场;启动处理中断事件的中断处理程序,CPU从目态切换为管态</p>
<p><strong>中断处理程序</strong></p>
<p><strong>中断的优先级和多重中断</strong></p>
<p>同时有多个中断事件发生时,中断装置按一定顺序对其作出响应,其先后顺序即优先级</p>
<p>高优先级的中断响应过程中,屏蔽低优先级的中断(有些中断是不能被屏蔽的,如自愿访管中断)</p>
<p>中断处理过程中,又产生了新的中断事件:<br>
串行处理——中断处理过程中关中断<br>
嵌套处理——开中断,暂停当前执行的中断处理程序,转而执行更高优先级的中断处理程序<br>
即时处理——主要针对中断处理程序执行过程中发生的程序性中断</p>
<p><strong>快中断和慢中断</strong></p>
<p>慢中断处理前需要保存所有寄存器的值,而快中断仅需保存会被内核使用的寄存器的值;<br>
慢中断处理时,不关中断,快中断处理时,关中断;<br>
慢中断处理完成后,通常不立即返回被中断进程,而是转而执行调度程序。快中断处理完成后,通常返回被中断进程继续执行。</p>
<h1 id="418">4.18</h1>
<p>sleep,<br>
死循环的进程、app进程间的切换</p>
<p>sleep时间结束之后,切换到app进程</p>
<p>编译指令?</p>
<p>global?</p>
<p>静态编译</p>
<p>顺序 先main</p>
<p>————————————————————————————</p>
<p>wsl</p>
<h1 id="422">4.22</h1>
<h2 id="复习栈">复习栈</h2>
<p><img src="https://img2024.cnblogs.com/blog/2997174/202404/2997174-20240422103911296-630262328.png" alt="image" loading="lazy"></p>
<p>若0x10000到0x1FFFF是栈段,栈空的时候,SS=0x1000,SP=0x0000。栈只有一个字的时候,SP=0xFFFE。</p>
<blockquote>
<p>一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,也可以什么都不是,关键在于CPU中寄存器的设置</p>
</blockquote>
<h2 id="dos系统-汇编程序如何执行">DOS系统 汇编程序如何执行</h2>
<p>?? segment/ends 一对伪指令,定义一个段的开始和结束</p>
<p>end 结束编译</p>
<p>assume 假设某一段寄存器和某一个由segment/ends定义的段相关联,如 assume cs:codesg</p>
<p>标号</p>
<p>程序返回 mov ax, 0x4c00 // int 0x21</p>
<p>masm.exe编译,link.exe链接</p>
<p>EXE文件中的程序加载过程:<br>
<img src="https://img2024.cnblogs.com/blog/2997174/202404/2997174-20240422112352804-1547715570.png" alt="image" loading="lazy"></p>
<p>在DOS中用Debug 1.exe的过程:command加载Debug,Debug加载1.exe,再反向返回</p>
<h2 id="bx和loop">和loop</h2>
<p>(ax)代指ax中的内容,idata代表常量</p>
<p>loop实现循环,cx存放循环次数,每次减一</p>
<p>mov ax, 这样的用法,DEBUG可以,编译器不行,但加上段地址前缀就一定可以,如mov ax, ds:</p>
<p>loop例题:计算ffff:0~ffff:b单元中数据的和存在dx里<br>
小困难:用dx类型不匹配,加到dl里超界<br>
方法:再用一个16位寄存器如ax作中介</p>
<p>DOS方式下DOS和其他合法程序不会使用0:200-0:2FF</p>
<p>关于段前缀的妙用<br>
例题:ffff:0<sub>ffff:b数据拷贝到0:200</sub>0:20b<br>
每次循环设置两次ds太麻烦,再用一个es分担ds的作用</p>
<h2 id="包含多个段的程序">包含多个段的程序</h2>
<p>dw: define word</p>
<p>start标号,程序入口指定,在伪指令end之后出现</p>
<pre><code>assume cs:codesg
codesg segment
dw ...
start:
...
codesg ends
end start
</code></pre>
<p>多个段的写法更加清晰。</p>
<pre><code>assume cs:code ds:data ss:stack ;这里code/data/stack只是人为指定的没有意义的名字,在编译器眼中都是数值

data segment
...
data ends

stack segment
...
stack ends

code segment(后面同上)

</code></pre>
<h2 id="灵活定位内存">灵活定位内存</h2>
<p>and/or指令</p>
<p>ASCII码(1字节)特点:<br>
大小写字母区别仅仅在于第5位,大写0小写1。A65,a97。</p>
<p><code>mov ax, </code> 的含义与应用</p>
<p>SI和DI:都是16位寄存器,但不能分成高位和低位。多用于数据的传送。</p>
<p>循环嵌套,只有一个CX用于loop计数,怎么办?<br>
每次开始内层循环的时候,把CX临时存到其他寄存器或者内存里,执行外层loop前再恢复。<br>
但这种方法有局限性,最好用栈结构来存。</p>
<pre><code>stacksg segment
        dw 0,0,0,0,0,0,0,0
stacksg ends
codesg segment
start: mov ax, stacksg
        mov ss, ax
        mov sp, 16
</code></pre>
<h1 id="423">4.23</h1>
<h2 id="ss-sp-bp-三个寄存器">SS, SP, BP 三个寄存器</h2>
<p>SS:存放栈的段地址;<br>
SP:堆栈寄存器SP(stack pointer)存放栈的偏移地址;</p>
<p>BP: 基数指针寄存器BP(base pointer)是一个寄存器,它的用途有点特殊,是和堆栈指针SP联合使用的,作为SP校准使用的,只有在寻找堆栈里的数据和使用个别的寻址方式时候才能用到<br>
比如说,堆栈中压入了很多数据或者地址,你肯定想通过SP来访问这些数据或者地址,但SP是要指向栈顶的,是不能随便乱改的,这时候你就需要使用BP,把SP的值传递给BP,通过BP来寻找堆栈里数据或者地址.一般除了保存数据外,可以作为指针寄存器用于存储器寻址,此时它默认搭配的段寄存器是SS-堆栈段寄存器.BP是16位的,再扩充16位就是EBP,用于32位编程环境的.一般高级语言的参数传递等等,转换为汇编后经常由BP/EBP来负责寻址\处理.</p>
<p>SP,BP一般与段寄存器SS 联用,以确定堆栈寄存器中某一单元的地址,SP用以指示栈顶的偏移地址,而BP可 作为堆栈区中的一个基地址,用以确定在堆栈中的操作数地址。</p>
<h1 id="424">4.24</h1>
<h2 id="数据处理的基本问题">数据处理的基本问题</h2>
<p><strong>WARNING: 只有SI DI BX BP寄存器可以用[...]寻址</strong><br>
<strong><code>mov ax, </code>默认使用SS内容为段地址</strong><br>
<strong>指令要处理的数据可能在三个地方:CPU内部(指令缓冲器/寄存器)、内存、端口</strong></p>
<p>这样我们数电里学到的寻址方式就简单了<br>
<code>mov ax, </code>就属于直接寻址<br>
<code>mov ax, </code>就属于寄存器间接寻址<br>
<code>mov ax, </code>就属于寄存器相对寻址<br>
<code>mov ax, </code>就属于基址变址寻址<br>
<code>mov ax, </code>就属于相对基址变址寻址</p>
<p>没有指明寄存器的情况,要说明是word还是byte<br>
如<code>mov word ptr , 1</code></p>
<p><strong>div指令</strong></p>
<p>除数 被除数<br>
8位16位(AX中的值)<br>
16位32位(DX+AX的值)</p>
<p>运算    8位    16位<br>
商      AL   AX<br>
余数    AH   DX</p>
<p>例子:<br>
<code>div byte ptr </code><br>
<code>div word ptr </code></p>
<p><strong>db, dw, dd伪指令</strong><br>
db: define byte<br>
dw: define word<br>
db: define double word</p>
<p><strong>dup和db,dw,dd配合,用于数据重复</strong><br>
<code>db 3 dup (0,1,2)</code>相当于012012012共9个字节</p>
<h1 id="59">5.9</h1>
<h2 id="进程">进程</h2>
<p>进程(process)是一个可并发执行的具有独立功能的程序关于某个数据集合的一次执行过程,也是操作系统进行资源分配和保护的基本单位。</p>
<p>具有共享性、动态性、独立性、制约性、并发性</p>
<p><strong>三态模型</strong>、五态模型</p>
<p>运行态→等待态:等待使用资源;如等待外设传输;等待人工干预。<br>
等待态→就绪态:资源得到满足;如外设传输结束;人工干预完成。<br>
运行态→就绪态:运行时间片到;出现有更高优先权进程。<br>
就绪态→运行态:CPU 空闲时选择一个就绪进程。</p>
<p>进程挂起:将进程对换到外部存储器上,释放其占有的系统资源,排除在进程调度之外</p>
<h1 id="510">5.10</h1>
<h2 id="转移指令">转移指令</h2>
<p>操作符offset<br>
作用:取得标号的偏移地址</p>
<p><code>jmp 0x0B</code>的机器码为什么是EB 03?因为此时IP=0x08</p>
<p><code>jmp far ptr 标号</code></p>
<p><code>jmp 16位寄存器</code><br>
作用:赋IP的值</p>
<p><code>jmp word ptr 内存单元地址</code><br>
如<code>jmp word ptr ds:</code><br>
只修改IP,段内转移</p>
<p><code>jmp dword ptr 内存单元地址</code><br>
CS=内存单元地址+2<br>
IP=内存单元地址<br>
段内转移</p>
<p><code>jcxz 标号</code><br>
当(cx)=0时,IP=IP+8位位移</p>
<h2 id="call和ret">call和ret</h2>
<p>ret做的事:<br>
1.(IP)=((ss)*16+sp)<br>
2.(sp)=(sp)+2</p>
<p>retf做的事:<br>
1.(IP)=((ss)<em>16+sp)<br>
2.(sp)=(sp)+2<br>
3.(CS)=((ss)</em>16+sp)<br>
4.(sp)=(sp)+2</p>
<p>call做的事:<br>
1.(sp)=(sp)-2<br>
2.(IP)=(IP)+16位位移<br>
相当于<br>
1.push ip<br>
2.jmp near ptr 标号</p>
<p><code>call far ptr 标号</code><br>
段间转移<br>
相当于<br>
push CS<br>
push IP<br>
jmp far ptr 标号</p>
<p><code>call AX</code></p>
<p><code>call word ptr 内存单元地址</code></p>
<p><code>call dword ptr 内存单元地址</code></p>
<h2 id="标志寄存器">标志寄存器</h2>
<p><img src="image.png" alt="alt text" loading="lazy"></p>
<p>ZF<br>
PF 二进制表示中,奇数个1为0,偶数个1为0<br>
SF 结果负为1,正为0<br>
CF 无符号运算 发生进位为1<br>
OF 有符号运算溢出</p>
<p>adc指令 带进位加法<br>
<code>adc ax, bx</code><br>
相当于<br>
(ax)=(ax)+(bx)+CF</p><br><br>
来源:https://www.cnblogs.com/tengjun12/p/18038699
頁: [1]
查看完整版本: 汇编语言自学笔记