Win知识 - 程序是怎样跑起来的——了解程序运行方式的必要性
通过对C语言源代码和汇编语言源代码进行比较,想必大家对“程序是怎样跑起来的”又有了更深的理解。而且,从汇编语言源代码中获得的知识,在某些情况下对查找bug的原因也是有帮助的。让我们来看个示例。代码清单10-13是更新全局变量counter的值的C语言程序。MyFunc1函数和MyFunc2函数的处理内容,都是把全局变量counter的值放大到2倍。counter*=2;指的是把counter的..
通过对C语言源代码和汇编语言源代码进行比较,想必大家对“程序是怎样跑起来的”又有了更深的理解。而且,从汇编语言源代码中获得的知识,在某些情况下对查找bug的原因也是有帮助的。
让我们来看个示例。代码清单10-13是更新全局变量counter的值的C语言程序。MyFunc1函数和MyFunc2函数的处理内容,都是把全局变量counter的值放大到2倍。counter*=2;指的是把counter的数值乘以2,然后再把所得结果赋值到counter的意思。这里,假设我们利用多线程处理①,同时调用了一次MyFunc1函数和MyFunc2函数。这时,全局变量counter的数值,理应变成100×2×2=400。然而,某些时候结果也可能会是200。至于为什么会出现该bug,如果没有调查过汇编语言的源代码,也就是说如果对程序的实际运行方式不了解的话,是很难找到其原因的。
代码清单10-13 两个函数更新同一个全局变量数值的C语言程序
//定义全局变量
int counter =100;
//定义MyFunc1函数
void MyFunc1()
{
counter *=2;
}
//定义MyFunc2函数
void MyFunc2()
{
counter *=2;
}
将代码清单10-13的counter *=2;部分转换成汇编语言源代码后,结果就如代码清单10-14所示。这里希望大家注意的是,C语言源代码中counter *=2;这一个指令的部分,在汇编语言源代码,也就是实际运行的程序中,分成了3个指令。如果只是看counter *=2;的话,就会以为counter的数值被直接扩大为了原来的2倍。然而,实际上执行的却是“把counter的数值读入eax寄存器”“将eax寄存器的数值变成原来的2倍”“把eax寄存器的数值写入counter”这3个处理。
代码清单10-14 将全局变量的值翻倍这一部分转换成汇编语言源代码的结果
mov eax, dword ptr[_counter] ; 将counter的值读入eax寄存器
add eax, eax ; 将eax寄存器的值扩大至原来的2倍
mov dword ptr[_counter], eax ; 将eax寄存器的数值存入counter中
在多线程处理中,用汇编语言记述的代码每运行1行,处理都有可能切换到其他线程(函数)中。因而,假设 MyFunc1函数在读出counter的数值100后,还未来得及将它的2倍值200写入counter时,正巧MyFunc2函数读出了counter的数值100,那么结果就会导致counter的数值变成了200(图10-8)。
图10-8 100×2×2的结果成200的过程
为了避免该bug,我们可以采用以函数或C语言源代码的行为单位来禁止线程切换的锁定方法。通过锁定,在特定范围内的处理完成之前,处理不会被切换到其他函数中。至于为什么要锁定MyFunc1函数和MyFunc2函数,大家如果不了解汇编语言源代码的话想必是不明白的吧。
现在基本上没有人用汇编语言来编写程序了。因为C语言等高级编程语言用1行就可以完成的处理,使用汇编语言的话有时就需要多行,效率很低。不过,汇编语言的经验还是很重要的。因为借助汇编语言,我们可以更好地了解计算机的机制。特别是对专业程序员来说,至少要有一次使用汇编语言的经验。
下面让我们以开车为例进行说明。没有汇编语言经验的程序员,就相当于只知道汽车的驾驶方法而不了解汽车结构的驾驶员。对这样的驾驶员来说,如果汽车出现了故障或奇怪的现象,他们就无法自己找到原因。不了解汽车结构的话,开车的时候还可能会浪费油。这样的话,作为职业驾驶员是不合格的。与此相对,有汇编语言经验的程序员,也就相当于了解计算机和程序机制的驾驶员,他们不仅能自己解决问题,还能在驾驶过程中省油。
本章的内容确实有些绕,但是对了解计算机和程序的实际运行方式来说,体验汇编语言是最有效的。如果大家会使用C语言的话,希望大家对C语言的各种语法所对应的汇编语言都一一确认一下。最好能编写一些简短的程序来进行反复的测试。笔者自身也是通过进行这些尝试才使自己的编程技能有了大幅提高的。
下一章,我们将会对I/O端口的输入输出及中断处理等用程序来控制硬件的方法进行说明,同时也会介绍一个使用汇编语言的示例程序。
Ps:注脚
① “线程”是操作系统分配给CPU的最小运行单位。源代码的一个函数就相当于一个线程。多线程处理指的是在一个程序中同时运行多个函数的意思。
![Logo](https://devpress.csdnimg.cn/79de2bf0b7994defa4242ef90d5513fa.jpg)
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)