hello world漫游

    xiaoxiao2021-03-25  56

    Hello world漫游

    在进入我们今天的主题之前,我想回顾一下冯诺依曼体系结构以及存储程序思想。太经典了! 1计算机是由运算器,控制器,存储器,输入输出设备五部分组成 2采用存储程序的方式,要执行的程序和数据先放到存储器中 3采用二进制编码数据 4程序是指令的集合,指令在存储器中按执行顺序存放

    回到我们今天的hello world漫游,下面是我们要重点讨论的部分,如果你确实看不下去,那么你也可以跳跃式阅读。 1操作系统的启动 2信息的表示 3程序的编写 4 I/O设备的控制 5程序性能优化 6程序的编译与链接 7存储器层次结构以及虚拟存储技术 8进程与异常控制流 9指令系统 10指令在硬件上执行 1:操作系统的启动 首先在我们漫游之前,让我们开启我们的计算机,启动操作系统,为hello world提供生存环境。操作系统的启动过程主要是下面几步,至于为什么我要介绍操作系统的启动,接着往下看就明白了。

    网上有很多详细的介绍,具体可以参考下面链接,我不再详述。 http://blog.chinaunix.net/uid-23069658-id-3142047.html

    2信息的表示 Hello world 的生命周期可以说是从源程序的产生开始,说到源程序我们必须先对源程序有一个基本概念—信息。信息就是位+上下文(深入理解计算机系统),在计算机系统中信息是用位和上下文来表示的。以文件形式存在于磁盘上,包括.txt .cpp .asm .obj…。其中文件主要包括文本文件和二进制文件,可以这么理解,和计算机打交道的是二进制文件,和我们打交道的是文本文件。二进制文件就是由一串二进制位0和1来表示的。对于这样的0和1我们是不怎么喜欢看的了。为了让我们更简单明了的看到计算机中的信息,计算机大牛们就想出了一个很巧妙的办法来实现人机交互的问题。那就是编码,对一串二进制位0和1进行编码,常见的有ascii,unicode,gb2312…。也就是通过一串0和1来表示成我们能看的懂的信息。例如ascii中,用二进制的65D 0010 0001B表示大写字母A,unicode中用\u6b22\u8fce表示中文 ”欢迎”。

    编码是很神奇的一步,也是实现人机交互很重要的一步,下面有兴趣的同学可以继续深入了解其他信息的表示方式和处理,如整数,浮点数,运算。说到数的表示,在计算机中同样也可以理解为是一个编码的过程。就如我们常说的,原码,反码,补码一样。想想我们平时使用的十进制原来也是一个编码的过程啊,只是我们没有过多想过这个问题,只是欣然的接收了而已,哈哈!

    下面就简单介绍下计算机中整数,浮点数,以及他们的运算问题。常见的数进制有二进制,八进制,十进制,十六进制,当然他们之间的转换我就不作介绍了。我们生活中通常使用十进制,而计算机中使用二进制,但是考虑到十进制和二进制之间转换的不易性。我们程序员在计算机表示中常用十六进制,如地址的表示我们不会用一长串0和1来表示,也不会用十进制表示,我们通常会用0XFFFF来表示。下面是C语言中常见的数据类型以及他的表示范围。当然,不同计算机系统,对数据类型所占的字节数有所不同,但总是二进制表示的形式。 说到这里,对于一个称职的程序员,我想有必要了解的就是,数据类型之间的转换问题, 关于这个问题,我想最根本的解释就是回到他们的二进制表示形式以及定义上来理解。下面就介绍强制类型转化和截断数字的准则。 1对于位不同的类型之间的转换,就是对数据的位进行扩充或者截断,当然扩充的方式需要根据数据的定义,有的是前补0,有的是前补符号位,截断就是保留数据的低有效位。

    2对于位相同的类型之间的转换,就是根据数据类型的定义来重新解释该数据(无符号数)。

    数据的机器级表示中有大端对其和小端对其,这里需要注意的就是,小端法(高有效位放在地地址)大端法(高有效位放在地址),下面是1234H在不同机器下的表示。

    s为符号位,表示正负 f为尾数,f是二进制小数,表示为0.的形式 e为阶码,e采用无符号数表示 IEEE浮点数表示有几种情况:规格化,非规格化,特殊值 1规格化:e不全为0,也不全为1 S=s,M=1+f,E=e-Bias(单精度Bias为127,双精度为1023) 那么 就等于 2非规格化:e全为0 S=s,M=f,E=1-Bias,换种理解,M=1+f,E=e-Bias也是一样,问题的关键是保证数的连续性 那么 就等于 3特殊值:e全为1,表示值为无穷大,NAN

    关于数的运算(算术运算,逻辑运算,关系运算,位运算),当然和我们生活中数的运算一样,只是在计算机中同样是采用二进制数运算,作为程序员,我们更关心的是如何安全,高效的进行运算。 1:有符号数和无符号数进行运算时,隐式转换为无符号数对待 浮点数与整数进行运算时,隐式转换为浮点数对待 2:乘法和除法运算,为了简化运算时间,常转换为位运算来代替 3:有符号数采用的是算术右移和无符号数采用的是逻辑右移

    1有符号数和无符号数比较求值 2整数乘除转位运算 如x*14表示为(x<<3)+(x<<2)+(x<<1) 3带符号数的算术右移操作

    (x<0? (x+(1<<k)-1):x)>>k

    好了,有了上面的编码基础,我们可以开始进行hello world源文件的编辑了。

    3:hello world编辑 说到编写源程序,我想大家一定会第一时间打开一个熟悉的编辑器,然后用不到几十秒的时间写出hello world代码,接着编译运行。然而很可惜的就是现在确实有那么些“程序员”只是在做这一部分工作,真是名副其实的“码农”。今天,我想简单从硬件角度介绍编辑源文件的问题,这也就是我为什么已开启要介绍操作系统启动的原因,因为只有操作系统启动后,我们才能更好的通过操作系统来管理我们的硬件资源以及我们的应用程序。当我们打开一个编辑器时,实际上是在执行一个应用程序,操作系统会为这个程序创建进程,通过进程来管理我们的编辑文本文件时执行的操作。也就是说进程在等待I/O设备。当我敲击键盘时很快会在编辑器行显示如下源文件。但是硬件上,我们是怎么实现源文件的编辑的呢,接着往下看。

    4:I/O控制方式 为了理解进程与I/O设备的数据交流,我们有必要先了解 I/O接口(适配器)。对于I/O接口,我们可以采用内存一样的对其进行编址,编址方式可以采用I/O独立编址,也可以与主存一起进行统一编址。编址之后,我们就通过I/O接口来访问控制I/O设备。我们就能像访问地址一样的来访问I/O设备,讲到后面的文件系统之后,我们也可以把I/O设备看作设备文件。对I/O设备的操作就好像是对文件操作一样。

    1 程序查询式 通过程序指令不断的去查询设备是否准备输入/输出数据,这对cpu来说是极大的浪费时间。多说无用!

    2中断

    中断是控制I/O设备的一个很重要的方式,同样是一个非常值得借鉴的控制机制。讲到中断,我们先来了解下8088的中断系统

    其中,中断的实现是通过8259A芯片硬件上来实现的,但中断的管理也需要操作系统来实现。至于中断嵌套可以自行看微机原理书。中断包括硬件中断和软中断,可屏蔽中断和不可屏蔽中断。需要注意的是中断是一种机制,而异常,更非错误。当外设向cpu发出请求时,会在8259A芯片上产生一个中断信号。cpu通过中断逻辑得到中断向量号,然后去访问中断向量表得到中断处理程序的入口地址,在系统启动后,中断向量表存放在主存的从0000H—03FEH地址(8088),共255个中断。由于中断向量地址占4个字节,而中断向量号是按序存放的,所以中断向量地址也等于4*中断向量号。

    中断响应过程(同jmp指令执行过程比较类似,至于保护现场和恢复现场只是一种简单的描述,不过就是些寄存器状态的保存,当然还是挺麻烦的),中断处理过程的示意图如下: 有了中断控制方式,cpu就不用不断的去查询等待外设是否处于准备状态,而是外设通过中断来请求cpu了,但是考虑到外设每一次请求cpu就中断一次的话,cpu需要的代价很大,不仅要停下来而且要保存现场,然后去执行中断,最后恢复现场,才能继续工作。所以又有了下面的DMA控制方式。

    3 DMA控制方式 DMA全称direct memory access也就是是直接在内存与I/O直接建立联系,每次DMA先缓存一定量的数据块,然后当数据块满了之后,向cpu申请占用总线,这时DMA直接读写内存,控制I/O与内存的操作,这样就大大减少中断的次数,提高了cpu的利用率。

    4通道控制方式 通道控制方式同样是采用硬件来实现的,采用I/O通道控制器,相对于DMA来说,通道控制器是一个简易的处理器,能执行有限的指令集,能控制数据传送的方式,如字节多路通道,选择通道,数组多路通道。

    5 思考 关于I/O设备基本概念就介绍这些,相信大家都有一定的理解。下面回想我们hello world的编辑,如果从根本出发,只要我们认为我们敲进去的不是键盘上的26个英文字母,而是敲进去一个中断信号,然后程序调用中断程序执行相应的中断程序,我们的一切行为都是在为程序服务,是程序为我们在磁盘上创建文件,编辑文件,保存文件就ok了,同样需要注意的我们的源文件是以ascii码的形式保存的。好了,编辑完hello world源文件之后,我们就可以开始编译运行了,相信很多C教材都是这么说的,但是作为程序员很有必要了解这个过程,因为在以后的工作中,我们经常会遇到类似的问题,我们常常会认为程序没有问题,但是却编译不成功,或者是编译不报错,但运行不起来,类似的问题会让我们头痛不以,下面我们就慢慢的理解编译链接的整个过程。

    5:hello world的编译与链接 1 预处理,把C语言中的一些预处理语句解释出来,如#include,#define ifdef,也就是宏定义,文件包含,条件编译。执行cpp命令,会在当前目录下产生test.i文件,查看文件可以看出预处理所做的工作。我们会发现test.i文件里面引入了很多我们没有编辑过的代码。

    2 编译 说到编译,就是把C语言程序编译成汇编代码。那么我们还是要有一定的汇编语言基础,至少能看懂汇编代码。当然,我们所学的汇编是inter汇编,与编译器汇编出来的汇编指令语法不同,但是还是可以相通的,如果你还是很遗憾的说,汇编我都几年没用了,差不多都忘了,那也没关系,下面一张常用汇编程序格式能给你简单的回忆(这里我省去了宏,条件编译等)。

    data segment commom;不同模块下同名段连接,即在同一模块下 dataname dw 20 dup(1,?) array dw 1,2,$+2,4,$+8 ;$表示当前地址计数器sp的值 org 0020h ;使下一偏移地址从0020h开始 even ;下一地址从偶地址开始 data ends ;------------------------------------------------------- code segment main proc far assume cs:code,ds:data start: push ds sub ax,ax push ax mov ax,data;把data的数据段地址给ax mov dx,ax mov ax,dataname ;把dataname数据给ax mov ax,offset dataname;把data的地址给ax mov ax,es:[di] ;默认es和di连用 mov ax,ds:table[si][bx] ;ds默认和si连用,相对基址变址寻址 call sun call far prt extern_sun jmp start mov dl,1h mov ah,display int 21h ret main endp ;-------------------------------------------------------- sun proc near . . ret sun endp code ends end start

    看完上面的汇编程序的一般格式后,默认大家对汇编有一定的理解,至少能看明白。毕竟我们确实很少会再去写汇编代码了。然而不得不说的是汇编语言确实很强大,而且很危险,然后我只想对早期的程序员说,你们真的辛苦了,汇编代码真难调试!!!

    下面我们来对比一下,汇编语言写的hello world,和C语言经过编译后的hello world有什么不一样 C语言版 汇编语言版 对比C语言汇编得到的汇编代码和直接用汇编语言写的代码,我们发现两者之间非常相似,但又有不同。其中我们注意到的是,C语言汇编后的代码有很多.file .def类的标识,这些我们可以暂时理解为是汇编器产生的一种标识,方便后面进行链接用的。还有一个就是_call _printf的调用,这个我们暂时理解为是对函数库的一个调用,在链接过程中继续讨论。可以看得出他们之间很类似,虽然目前看他们输出字符串执行的命令不同。


    既然谈到C语言和汇编,那就不可避免的得说一下栈的问题。因为C语言中的控制,函数调用同汇编中的过程控制都需要运用到栈。栈的一个基本思想就是先进后出。为了更好的理解C语言中函数的递归调用,我们这里用汇编语言来描述它对栈操作过程。

    C语言实现代码:

    #include <stdio.h> int fu(int x) { if(x<1) return 1; else return fu(x-2)+fu(x-1); } int main() { int x; fu(x); return 0; }

    机器反汇编代码:(每一步的执行都对应一条汇编指令,真正用汇编语言实现不用如此麻烦,因为,汇编语言也支持选择,循环,调用)

    00401020 push
    转载请注明原文地址: https://ju.6miu.com/read-26001.html

    最新回复(0)