一、了解程序运行逻辑的必要性
通过前几篇博客的汇编代码和C语言源代码进行比较,相比对程序的运行方式有了新的理解,而且,从汇编代码中获取的知识,也有助于了解Java等高级语言的特性,比如Java中就有 native 关键字修饰的变量,那么这个变量的底层就是使用C语言编写,还有一些Java中的语法只能通过汇编代码才能知道其运行逻辑。在某些情况下,对于查找bug的原因也是由帮助的
串行处理的特点:
串行处理最大的特点就是 专心只做一件事,一件事做完之后才能去做另外一件事情
计算机是支持多线程的,多线程的核心就是CPU切换,如下图所示:
可举个例子,看下面代码:
// 定义全局变量 int counter = 100; // 定义MyFunc() void MyFunc(){ counter *= 2; } // 定义MyFunc2() void MyFunc(){ counter *= 2; }
上述代码是更新counter的值的C语言程序,MyFunc10和MyFunc20的处理内容都是把 counter的值扩大至原来的二倍,然后再把 counter的值赋值给 counter.这里,我们假设使用 多线程处理,同时调用了一次MyFunc1和MyFunc2 函数,这时,全局变量counter的值,理应编程100*2*2=400.如果你开启了多个线程的话,你会发现counter的数值有时也是200,对于为什么出现这种情况,如果你不了解程序的运行方式,是很难找到原因的
我们将上面的代码转换成汇编语言的代码如下
mov eax,dword ptr [_counter] ; 将 counter 的值读入 eax 寄存器 add eax,eax ; 将 eax 寄存器的值扩大2倍 mov dword ptr [_counter] ,eax ; 将 eax 寄存器的值存入 counter 中
在多线程程序中,用汇编语言表示的代码每运行一行,处理都有可能切换到其他线程中。因而,假设MyFun1 函数在读出counter数值100后,还未来得及将它的二倍值200写入counter时,正巧 MyFun2函数读出了 counter的值100,那么结果就将变为200
多线程交互程序处理步骤:
为了避免bug,我们可以采用函数或C语言代码的行为单位来禁止线程切换的 锁定 方法,或者使用某种线程安全的方式来避免该问题的出现
汇编语言的经验很重要的,通过借助汇编语言,我们可以更好的了解计算机运行机制
二、应用和硬件的关系
C、Java等高级语言编写的程序起到间接控制硬件的作用,所以大家很少直接接触到硬件的指令,硬件的控制由 Windows 操作系统全权负责
虽然Windows屏蔽了控制硬件的细节,但是Windows却为你开放了系统调用 功能来实现对硬件的控制。在Windows 中,系统调用称为 API,API就是应用调用的函数,这些函数的实体被存放在 DLL文件中
应用通过API间接控制硬件:
下面式一个通过系统调用来间接控制硬件的实例
假如要在窗口中显示字符串,就可以使用Windows API中的 TextOut 函数。
TextOut 函数的语法(C语言)如下:
BOOL TextOut{ HDC hdc, // 设备描述表的句柄 int nXStart, // 显示字符串的x坐标 int nYStart, // 显示字符串的y坐标 LPCTSTR lpString, // 指向字符串的指针 int cbString //字符串的文字数 }
那么,在处理TextOut 函数的内容时,从结果来看,Windows直接控制了作为硬件的显示器。但Windows本身也是软件,由此可见,Windows应该向CPU传递了某个指令,从而通过软件控制了硬件
Windows 提供的TextOut 函数API可以向窗口和打印机输出字符。C语言提供的 printf 函数,是用来在命令提示符中显示字符串的函数。使用printf函数是无法向打印机输出字符的