4.8 程序装载进入内存并使用
之前讲过,在DOS中,可执行文件中P1若要运行,必须有一个正在运行的程序P2,将P1从可执行文件中加载入内存,将CPU的控制权交给它,P1才能得以运行;当P1运行完毕后,应该将CPU的控制权交还给使它得以运行的程序P2。
操作系统的shell
操作系统是由多个功能模块组成的庞大、复杂的软件系统。任何通用的操作系统,都要提供一个成为shell(外壳)的程序,用户使用这个程序来操作计算机系统运行工作。
- 在DOS中直接运行
.exe
时,是正在运行的command
,将.exe
中的程序加载入内存; - command设置CPU的CS:IP指向程序的第一条指令(即程序入口),从而使得程序得以运行;
- 程序运行结束后,返回到command中,CPU继续运行command。
汇编程序从写出到执行的过程
编程(edit) –>.asm
–> 编译(masm) –>.obj
–> 连接(link) –>.exe
加载(command) –> 内存中的程序 –> 运行(CPU)
程序执行过程的跟踪
下面我们用Debug来对一个程序进行跟踪,来检查运行过程中不容易发现的逻辑问题。
我们知道当DOS中运行一个程序的时候,是由command将程序从可执行文件中加载入内存,并使其得以执行。但是,这样我们不能逐条指令,因为command的程序加载,设置CS:IP指向程序的入口操作是连续完成的,而当CS:IP一指向程序的入口,command就放弃了CPU的控制权,CPU立即开始运行程序,直至程序结束。
Debug可以将程序载入内存,设置CS:IP指向程序的入口,但Debug并不放弃对CPU的控制,这样,我们就可以使用Debug的相关命令来单步执行程序,查看每一条指令的执行结果。
这里我们编程程序:
assume cs:codesg
codesg segment
start: mov ax, 0123H ; 这里start为命名,给与一个入口地址,也可换位其他名称
mov bx, 0456H
add ax, bx
add ax, ax
mov ax, 4C00H
int 21H
codesg ends
end start
注意start: 添加与否的区别,如果不添加start命名,link时则会如下提示:
LINK : warning L4038: program has no starting address
在获得目标程序1.exe
后,用debug运行程序,
debug 1.exe
在输入结果后输入r好看各个寄存器的地址情况。
我们发现DS=075A
而CS=076A
,这里的段地址和代码段地址不同,相差$076A-075A=10$ (段寄存器相差10, 意味着物理地址相差100)。同时由于1.exe
中含有15个字节,所以cx通用寄存器为CX=000F
共15个字节。
下面来讲一下为什么CS和DS差10,如下图:
- 程序加在后,DS中存放着程序所在内存区的段地址,这个内存区的偏移地址为0,则程序所在的内存区的地址为ds:0。
- 这个内存区的前256个字节中存放的是PSP, DOS用来和程序进行通信。从256字节处向后的空间存放的程序。PSP占256字节(100H)
- 程序物理地址为:SA16+0+256=SA16+1616+0=(SA+16)16+0 即 SA+10H:0 ,值得注意的是,这里也可以写成 SA:256 但为了更好的区分PSP和程序,DOS一般将二者划分到不同的段。
我们可以查看PSP,
-u 075A:0 100
和1.exe
中程序代码,
-u 076A:0
最后,我们通过t命令来进行逐步跟踪:
-t
如下图所示,
这里需要注意当执行到最后一步,INT 21
时,要输入p命令才能正常终止程序:
-p
Program terminated normally
否则将会跳转到其他的地址并无法正常终止!
使用Q命令退出Debug,将返回到command中,因为Debug是由command加载运行的。在DOS中用debug 1.exe
运行Debug对1.EXE进行跟踪式,程序加在顺序为:command加载Debug,Debug加载1.EXE。返回的顺序为:从1.EXE中的程序返回到Debug,再从Debug返回到command。