标签 内存对齐 下的文章

也谈’SIGBUS和SIGSEGV’

SIGBUS和SIGSEGV也许是我们在平时遇到的次数最多的两个内存错误信号。内存问题一直是最令我们头疼的事情,弄清楚两个信号的发生缘由对我们很好的理解程序的运行是大有裨益的。

我们来看两段程序:
//testsigsegv.c
int main() {
        char *pc = (char*)0×00001111;
        *pc = 17;
}

//testsigbus.c
int main() {
        int *pi = (int*)0×00001111;
        *pi = 17;
}

上面的代码那么的相似,我们也同样用gcc编译(加上-g选项,便于gdb调试;平台Solaris Sparc),执行结果也都是dump core。但通过GDB对core进行观察,你会发现细微的不同。第一个例子出的core原因是:Program terminated with signal 11, Segmentation fault. 而第二个例子的core则提示:Program terminated with signal 10, Bus error. 两者有什么不同呢?这两段代码的共同点都是将一个非法地址赋值给指针变量,然后试图写数据到这个地址。

如果要说清楚这个问题,我们就要结合汇编码和一些计算机的体系结构的知识来共同分析了。

先来看testsigsegv.c的汇编码:
… …
main:
        !#PROLOGUE# 0
        save    %sp, -120, %sp
        !#PROLOGUE# 1
        sethi   %hi(4096), %i0
        or      %i0, 273, %i0
        st      %i0, [%fp-20]
        ld      [%fp-20], %i1
        mov     17, %i0
        stb     %i0, [%i1]
        nop
        ret
        restore
… …

我们关注的是这句:stb     %i0, [%i1]
从计算机底层的执行角度来说,过程是如何的呢?%i0寄存器里存储的是立即数17,我们要将之存储到寄存器%i1的值指向的内存地址。这一过程对于CPU来说其指挥执行的正常过程是:将寄存器%i0中的值送上数据总线,将寄存器%i1的值送到地址总线,然后使能控制总线上的写信号完成这一向内存写1 byte数据的过程。

我们再看testsigbus.c的汇编码:
… …
main:
        !#PROLOGUE# 0
        save    %sp, -120, %sp
        !#PROLOGUE# 1
        sethi   %hi(4096), %i0
        or      %i0, 273, %i0
        st      %i0, [%fp-20]
        ld      [%fp-20], %i1
        mov     17, %i0
        st      %i0, [%i1]
        nop
        ret
        restore
… …

同样最后一句:st      %i0, [%i1],CPU执行的过程与testsigsegv.c中的一致(只是要存储数据长度是4字节),那为什么产生错误的原因不同呢?一个是SIGSEGV,而另一个是SIGBUS。这里涉及到的就是对内存地址的校验的问题了,包括对内存地址是否对齐的校验以及该内存地址是否合法的校验。

我们假设如果首先进行的内存地址是否合法的校验(是否归属于用户进程的地址空间),那么我们回顾一下,这两个程序中的地址0×00001111显然都不合法,按照这种流程,两个程序都应该是SIGSEGV导致的core才对,但是事实并非如此。那难道是先校验内存地址的对齐?我们再看这种思路是否合理?

testsigsegv.c中,0×00001111这个地址值被赋给了char *pc;也就是告诉CPU通过这个地址我们要存取一个字节的值,对于一个字节长度的数据,无所谓对齐,所以该地址通过对齐校验;并被放到地址总线上了。而在testsigbus.c里,0×00001111这个地址值被赋给了int *pi;也就是告诉CPU通过这个地址我们要存取一个起码4个字节的值,那么对于长度4个字节的对象,其存放地址起码要被4整除才可以,而0×00001111这个值显然不能满足要求,也就不能通过内存对齐的校验。也就是说SIGBUS这个信号在地址被放到地址总线之后被检查出来的不符合对齐的错误;而SIGSEGV则是在地址已经放到地址总线上后,由后续流程中的某个设施检查出来的内存违法访问错误。

一般我们平时遇到SIGBUS时总是因为地址未对齐导致的,而SIGSEGV则是由于内存地址不合法造成的。

从本源看世界-读'Write Great Code'

以前曾经说过自己并非计算机科班出身。想想自己在大学时的学习过程未免有些底气不足,记得当时一直坚持去旁听计算机专业的课,但是鉴于本专业老师的点名和课堂作业,自己未免耽误了很多节课,弄得自己学的很不系统,效果不是很好。工作后一直从事应用级的开发,对计算机方面基础的本源性的知识也逐渐陌生起来。但我是那种知其然也要知其所以然的人,这两年也不间断的买了不少讲解计算机底层知识的书,目的是让那些计算机本源性的东西在我脑袋里逐渐清晰了起来。这不又一本好书问世了-'Write Great Code'第一卷,我很早就已经下载了其英文版,只是没来得急看,这两天看了其中几章,发现很适合我的口味。

在我眼中每个领域的大师级人物都是知其领域本源的人,他们把本领域的知识融汇贯通,而且大多时候我们在聆听大师级人物的讲解时都有一种豁然开朗的感觉,那其实就是因为他的知识体系很成形,他们会从本源去讲解,从最简单的原始状态去讲解,这样听起来印象深刻,收获自然颇丰。

'Write Great Code'(中文名:编程卓越之道)一系书的作者是Randall Hyde,他同时也是'The Art of Assembly Language'的作者。'The Art of Assembly Language'一书算是汇编领域的佼佼者了,虽然我没看过^_^,不过网友的评价也是很重肯的哟。当初刚刚下载'Write Great Code' Vol1时曾经浏览一遍目录,'Numeric Representation','Binary Arithmetic and Bit Operations'等这些章节的名字让我心动,我就喜欢这样的书,而且和一般的教材性质的计算机组织结构或者计算机系统结构相比,这本书是从程序员角度来讲的,更加适合我们这些人的口味。这本书所讲解的知识层次就在我们工作层次的下一层,对于想挖掘知识本源的我来说再合适不过了,东西要一点一点的吃,你说是不是^_^。

我没有从开篇'Numeric Representation'这张开始读,我直接跳到了第六章'Memory Organization and Access',因为前不久又对内存对齐等有新的认识,所以我也希望通过这章的阅读知道更多的东西,让我脑子中的知识点'串'起来。这本书没有令我失望,本章第一节关于三大总线的介绍就格外精彩:
The system bus connects the various components of a VNA machine.
-> A bus is a collection of wires on which electrical signals pass between components of the system.
-> Most CPUs have three major buses: the address bus, the data bus, and the control bus.
-> CPUs use the data bus to shuffle data between the various components in a computer system.
-> The data bus on an 80×86 family processor transfers information between a particular memory location or I/O device and the CPU. The only question is, 'Which memory location or I/O device?' The address bus answers that question.
-> The CPU uses the data bus to move data between itself and memory. This prompts the question, 'How does the system know whether it is sending or receiving data?' Well, the system uses two lines on the control bus, read and write, to determine the data flow direction (CPU to memory, or memory to CPU).

几句'关键意义'的句子循序渐进的把三大总线的用途描绘的淋漓尽致,其思路和方式完全符合认知的过程,同时让你的大脑里马上形成一个框架,带着这个框架再去读相关细节,只能让你越读越兴奋。

第6.2小节讲的是物理内存的组织以及CPU如何访问内存,但是你看完后再细致品味,实际上这节的内容完全可以作为'为什么要进行Data Alignment'的标准讲义,和我上次在'三谈内存对齐-背后的故事'一文中说的同出一辙,而且更加细致,让我对内存这块的内幕了解的更加透彻。

第6.3小节讲的则是'字节序'问题,讲解了'Big-endian'和'Little-endian'的由来,最后作者通过一个很实用的例子形象的说明了字节序带来的影响。

第6.4节和6.5节讲的略微有些深了,要细看才行,最好对更底层有所了解,可以参照别的书籍一起学。

我刚刚读到第7章-'Composite Data Types and Memory Objects',该章每一小节针对一种复合数据类型做深入分析,精彩在后头,我正准备继续呢,实在忍不住了,写下此篇,好让更多同仁知道有这么一本书,早读早受益,明天周末去书店买本中文版,坐在床上读更舒服。

从本源看世界,你会发现另一番天地。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats