深入理解unix系统内核计算机系统第三章可以跳过没

&&对C/C++程序员来说,内存管理是个不小的挑战,绝对值得慎之又慎,否则让由上万行代码构成的模块跑起来后才出现内存崩溃,是很让人痛苦的。因为崩溃的位置在时间和空间上,通常是在距真正的错误源一段距离之后才表现出来。前几天线上模块因堆内存写越界1个字节引起各种诡异崩溃,定位问题过程中的折腾仍历历在目,今天读到《深入理解计算机系统》第9章-虚拟存储器,发现书中总结了C程序中常见的内存操作有关的10种典型编程错误,总结的比较全面。故作为笔记,记录于此。
1. 间接引用无效指针& & & & 进程虚拟地址空间的某些地址范围可能没有映射到任何有意义的数据,如果我们试图间接引用一个指向这些地址的指针,则操作系统会以终止进程。而且,虚拟存储器的某些区域是只读的(如.text或.rodata),试图写这些区域会以保护异常中止当前进程。& & & & 如从stdin读取一个int变量时,scanf("%d", &val)是正确用法,若误写为scanf("%d", val)时,val的值会被解释为一个地址,并试图向该地址写数据。在最好的情况下,进程立即异常中止。在最坏的情况下,val的值恰好对应于虚拟存储器的某个合法的具有读/写权限的内存区域,于是该内存单元会被改写,而这通常会在相当长的一段时间后造成灾难性的、令人困惑的后果。
2. 读未初始化的存储器& & & & C语言的malloc并不负责初始化申请到的内存区域,因此,常见的错误是假设堆存储器被初始化为0,例如:
int * foo(int **A, int *x, int n)
int * y = (int *)malloc(n * sizeof(int));
for(i = 0; i & i++) {
for(j = 0; j & j++){
y[i] += A[i][j] * x[j];
  &上述代码中,错误地假设了y被初始化为0。正确的实现方式是显式将y[i]置为0或者使用calloc。
3. 栈缓冲区溢出& & & & &例如:
char buf[5];
sprintf(buf, "%s", "hello world");
   &上面的代码导致栈缓冲区溢出,安全的做法是:1)根据需求定义合适的buffer;2)采用snprintf(buf, sizeof(buf), "%s", "hello world")来及时截断。
4. 误认为指针与其指向的对象是相同大小的& & & & 例如:
int **makeArray(int n, int m)
int **A = (int **)malloc(n*sizeof(int));
// 这里错误地认为int *与int两种变量类型具有相同的size
for(i = 0; i & i++) {
A[i] = (int *)malloc(m * sizeof(int));
   上述代码目的是创建一个由n个指针构成的数组,每个指针均指向一个包含m个int的数组,但误将sizeof(int *)写成sizeof(int)。这段代码只有在int和int *的size相同的机器上运行良好。如果在像Core i7这样的机器上运行这段代码,由于指针变量的size大于sizeof(int),则会引发代码中的for循环写越界。因为这些字中的一个很可能是已分配块的边界标记脚部,所以我们可能不会立即发现这个错误,直到进程运行很久释放这个内存块时,此时,分配器中的合并代码会戏剧性地失败,而没有任何明显的原因。这是"在远处起作用"(action at distance)的一个隐秘示例,这类"在远处起作用"是与存储器有关的编程错误的典型情况。
5. 造成错位错误& & & & &错位(Off-by-one)错误是另一种常见的覆盖错误来源:
int ** makeArray(int n, int m)
int **A = (int **)Malloc(n * sizeof(int *));
for(i = 0; i &= i++) {
A[i] = (int *)Malloc(m * sizeof(int));
& & & & &&很明显,for循环次数不合预期,导致写越界。幸运的话,进程会立即崩溃;不幸的话,运行很长时间才抛出各种诡异问题。
6. 引用指针,而不是它所指向的对象& & & & 如果不注意C操作符的优先级和结合性,就会错误地操作指针,而不是指针所指向的对象。& & & & 比如下面的函数,其目的是删除一个有*size项的二叉堆里的第一项,然后对剩下的*size-1项重建堆:
int * binheapDelete(int **binheap, int *size)
int *packet = binheap[0];
binheap[0] = binheap[*size - 1];
// 此处应该为(*size)--
heapify(binheap, *size, 0);
return (packet);
& & & &上述代码中,由于--和*优先级相同,从右向左结合,所以*size--其实减少的是指针自己的值,而非其指向的整数的值。因此,谨记:当你对优先级和结合性有疑问时,就应该使用括号。
7. 误解指针运算& & & & 在C/C++中,指针的算术操作是以它们指向的对象的大小为单位来进行的。例如下面函数的功能是扫描一个int的数组,并返回一个指针,指向val的首次出现:
int * search(int *p, int val)
while(*p && *p != val) {
p += sizeof(int); // 此处应该为p++,否则p += 4会导致大部分元素被跳过
8. 引用不存在的变量& & & & & &C/C++新手不理解栈的规则时,可能会引用不再合法的本地变量,例如:
int * stackref()
& & & & 函数返回的指针(假设为p)指向栈中的局部变量,但该变量在函数返回后随着stackref栈帧的销毁已经不再有效。也即:尽管函数返回的指针p仍然指向一个合法的存储器地址,但它已经不再指向一个合法的变量了。当程序后续调用其它函数时,存储器将重用刚才销毁栈帧处的存储器区域。再后来,如果程序分配某个值给*p,那么它可能实际上正在修改另一个函数栈帧中的数据,从而潜在地带来灾难性的、令人困惑的后果。
9. 引用空闲堆块中的数据& & & & 典型的错误为:引用已经被释放了的堆块中的数据,例如:
int * heapref(int n, int m)
int *x, *y;
x = (int *)Malloc(n * sizeof(int));
各种操作 */
y = (int *)Malloc(m * sizeof(int));
for(i = 0; i & i++) {
y[i] = x[i]++;
// 此处的x之前已经被释放了!
10. 内存泄露& & & &内存泄露是缓慢、隐性的杀手,当程序员忘记释放已分配块时会发生这种问题,例如:
void leak(int n)
int *x = (int *)malloc(n * sizeof(int));
& & & &如果leak在程序整个生命周期内只调用数次,则问题还不是很严重(但还是会浪费存储器空间),因为随着进程结束,操作系统会回收这些内存空间。但如果leak()被经常调用,那就会发生严重的内存泄露,最坏的情况下,会占用整个虚拟地址空间。对于像守护进程和服务器这样的程序来说,内存泄露是严重的bug,必须加以重视。
【参考资料】《深入理解计算机系统》第9章 & 虚拟存储器
============== EOF ==================
阅读(...) 评论()[深入理解计算机系统]对找工作功不可没――评《深入理解计算机系统》_深入理解计算机系统-牛bb文章网
[深入理解计算机系统]对找工作功不可没――评《深入理解计算机系统》_深入理解计算机系统
话题:,,
深入理解计算机操作系统引子:在我刚刚进入中科院计算所读研的时候,同宿舍的师兄便向我推荐了一本《深入理解计算机系统》,这本书从一个程序员的视角详细剖析了整个计算机系统,涵盖了组成原理、汇编语言、体系结构、操作系统、网络等计算机基础知识。由于时间所限,我并没有立刻阅读,而是将其列入了找工作前的复习书单。2010年8月,我用了一个月的时间读完了这本书的原版《Computer System:A programmer's perspective》。后来的事实证明,读完这本书对我找工作的历程帮助很大。正文:在阅读的过程中,我对该书的各个章节做了一些标注,以备将来重新翻阅的时候参考。这些标注主要从两个角度进行,一是对我找工作应试(包括笔试和面试)有没有用,二是对我自身的技术提高有没有用,所以分为应试和修炼两个指标,参照流行的打分标准将其分为从★到★★★★★五个等级。在找工作顺利结束之后,我又回顾了一下之前的标注,结合自己的笔试、面试经历,重新修订了一下。其中应试指标的评分主要是以我的求职目标(互联网行业偏算法的软件工程师)为参照,和其他职位的要求会有些出入。第一章 计算机系统漫游 A Tour of Computer System本章对计算机系统做了一个总体的介绍,用简单明了的语言概括了一些后续章节将要重点展开的概念。应试 ★★:在笔试中可能会碰到一些整体上的概念题。修炼 ★:属于计算机最基本的概念。第二章 信息的表示和处理 Representing and Manipulating Information本章介绍了信息在计算机中的表示形式,重点讲述整数和浮点数的表示形式。应试 ★:应试中很少会考到。修炼 ★★★:有很多人可能写了多年的代码都不知道浮点数是如何用那4(8)个字节存储的,不知道其实表达式(x-y&0)并不能替代(x&y)。第三章 程序的机器级表示 Machine-Level Representation of Program本章其实就是汇编语言课程的复习。应试:重要程度依赖于求职目标,如果是和我一样偏算法的工程师,汇编语言是不会考到的。修炼 ★★★★★:理解汇编语言和寄存器结构是后面很多内容的基础。第四章 处理器体系结构 Processor Architecture本章其实就是计算机体系结构课程的内容。主要介绍的内容有处理器结构,各种逻辑门、功能单元,指令集;指令的执行,指令执行的流水线等。应试 ★: 对于软件相关职位来说,很少会考到这么底层的东西的。修炼 ★★★: 对于从事软件层面的技术人员来说,不用深入,但是也应该理解,知道是怎么回事。第五章 优化程序性能 Optimizing Program Performance本章讲述如何优化程序的执行效率,包括代码的优化,编译器的优化,以及CPU级别的优化。5.1-5.6节 主要介绍了几种能有效提高代码性能的方法。应试 ★★★★: 经常会有一些让你寻找程序瑕疵的问题,如果你能看出代码在哪些细节上可以优化,必定能加分不少。修炼 ★★★★★: 你当然需要知道编译器在什么层面上能自动帮你优化代码,在编译器无法优化时你自己又如何在小细节上进行优化。5.7节以后 主要介绍了CPU级别的优化,微指令的概念,功能单元上微指令的并行,程序分支的预测等。应试 ★: 对于软件相关职位来说,基本不会考这些东西的。修炼 ★★★: 不用深入,但是应该知道并能够理解。第六章 存储器层次结构 The Memory Hierarchy本章详细介绍了计算机系统中的存储结构。6.1 介绍了不同种类的存储设备以及对应的存取数据的方式。应试和修炼 ★: 很少会考到,了解一下即可。6.2-6.7 介绍了存储设备的组织形式,着重介绍了Cache及其工作方式,程序是如何和cache打交道的,不同的循环嵌套顺序、遍历方向等对cache命中的影响。应试 ★★★★: 在面试中,经常会考到跟cache相关的题目;修改循环嵌套顺序以提高cache命中率也是一些程序改错题的高级玩法。修炼 ★★★★: 存储结构和cache是计算机中很基础也很重要的概念。第七章 链接 Linking顾名思义,本章详细讲解了程序的链接过程,主要分为静态链接和动态链接,以及链接过程中使用到的技术如符号解析、重定位等。应试 ★★:知道一些基本概念即可。修炼 ★★★★★:一个软件工程师应该懂得自己写出来的程序是怎么成为一个可执行文件的,有的时候,你很可能会被一个链接错误折磨好几天。第八章 异常控制流 Exceptional Control Flow顾名思义,本章主要讲解异常控制,不过这里的“异常”并不是Java或者C++里狭义的异常,而是一个广义的“Exceptions”的概念,包括中断(Interrupt),陷阱(Trap),错误(Falut),中止(Abort)等。本章同时引入了进程的概念,介绍了进程级别的 Exception:信号(signal)以及操作系统处理这个异常的手段--上下文切换(context switch)。应试 ★★: 主要掌握进程的概念即可。修炼 ★★★: 知道异常控制流是怎么回事,用户程序和系统交互(如系统调用)的原理和方式即可,不用太深入。第九章 虚拟存储器 Virtual Memory非常重要的一章,虚拟存储机制是计算机实现多任务的一项重要技术。计算机正是通过时间片技术使得每个进程在执行时仿佛独占CPU,进而又通过虚拟存储机制使得每一个进程在执行时仿佛独占内存。10.1介绍了虚拟地址和物理地址,CPU进行寻址操作产生的是虚拟地址,通过存储管理单元(memory management unit)转换为实际的物理内存地址。10.3~10.5讲述了虚拟存储机制的优点:使得内存可以作为硬盘的cache;能够更方便的管理内存;能更好的提供内存保护机制。10.6 介绍了虚拟地址如何转换为物理地址。10.7 很精彩的一节,通过Intel Pentium和Linux的实例讲述了整个存储管理机制。记得以前上操作系统课的时候,各种理论、机制学了一大堆,但就是不知道实际的操作系统到底用的哪套方法,而本节内容正是通过实例让你对刚学的理论机制有一个直观的了解。10.8~10.10 讲述了存储映射(Memory Mapping)、动态内存分配和垃圾回收机制。10.11 很实用的一节,列举了一些C编程中容易犯的内存引用错误。应试 ★★★★★: 本章内容是操作系统课程的重点内容,如果考操作系统,几乎肯定会考到;10.11节的知识也能帮你应对一些程序挑错题。修炼 ★★★★★: 操作系统中重要的基础内容,即使你只使用Java这样的高级编程语言,至少也应该弄懂垃圾回收机制吧。第十章 系统级I/O System-Level I/O介绍类Unix系统下的I/O读写,主要介绍系统层面的I/O接口。由于我们日常编程所用的I/O接口都是各种高级语言提供的经过封装的标准接口,故而如果不进行底层开发的话这部分知识不是必须的,我便跳过没读。第十一章 网络编程 Network Progranmming本章简单介绍了网络模型,TCP/IP协议,类Unix系统的socket接口等。应试和修炼 ★★: 因为篇幅限制,本章只做了简单讲解,要掌握网络编程知识还需要参考专门的网络技术书籍。第十二章 并发编程 Concurrent Programming本章简单介绍了并发程序设计的内容,主要包括:1.进程级别的并发,各子进程拥有不同的虚拟地址空间,需要IPC(InterProcess Communication)机制共享数据,切换开销大。2.I/O复用,事件驱动,单进程运行,共享虚拟地址空间,并发效果不理想。3.线程,介于上述两种中间,各子线程共享进程的虚拟地址空间,切换开销较小。另外介绍了并发编程中访问共享变量的信号量机制,并给出了4类容易引起线程不安全的函数。应试 ★★: 除应聘相关职位外,并发编程考的比较少,不过需要清楚锁机制和信号量机制等。修炼 ★★★: 多线程程序在现今的开发中还是很常见的,但是本章介绍的比较简单,需要参考另外专门的书籍。总的来说,我觉得这本书很适合在找工作之前读一读,因为从网上流传的各种笔试和面试题中,我们就能看出各大IT公司在招聘工程师的时候是很看重基础知识的,而对于像我这样上了三年研究生的同学来说,很多知识由于长时间没用早就忘了,所以需再要再复习一下。但是我们可能没有时间按照课程一门一门地去复习,即使一门一门去看了,也不一定能把知识都联系起来,而这本书正好提供了这样一个视角,从程序员的角度把计算机专业最重要的基础知识都串了起来,形成了一个完整的计算机系统的概念。 分享: >1700人阅读
&&& 前言:已经大四,没有去找工作,选择了保研,之所以这样选择,有三个原因,一、刚进校时,听说保研都是牛人才能行的事,所以一心努力保研;二、2008年开始,经济危机比较严重,工作不好找,虽然软件专业要找一份工作还是比较容易,但好工作的机会少了很多,再多学习几年,规避下风险;三、三年多的本科学习下来,虽然简历看起来还像回事,但内心很虚,感觉真才实学没多少,都是略懂皮毛,做了太多表面工作,没有特长,选择读研,希望在某一方面能够深入学习,不再流于肤浅,真正地学有所长。
&&&&& 保研成功后,过了近一个月猪一样的生活,实在过得空虚,看到考研同学每天辛苦地备考,在暗自庆幸的同时,也意识到,保研给我节约了很大一笔时间,不应该把这段时间荒废了。于是找了谭师兄和梁老师,让他们给我指点一二。梁老师的建议很中肯,他说如果他现在是HR,他会只招本科生,而不是研究生,因为现在研究生的质量比本科生高不到哪里去,而且一把年纪了,本科生出来工作三年后经验什么的都要比研究生强多了,说不定很多本科生还会当研究生的思想导师。这个说法当然比较绝对,但是我认为还是很有道理,因此,他说,你如果去读研,就不要再像本科一样把自己定位为一个coder,而应该至少是去做一个提出解决方案的,具体的实现让下面的人去做。你可以发现很多好东西好的解决方案都是老外提出来的,为什么中国人只能锦上添花,那里因为我的的基础知识不够扎实,知识体系不够健全,连自己手中的计算机是怎么工作的都不知道,怎么能提出很好的解决方案呢。所以,对于大四这一年,他给我推荐了两本书,一本《深入理解计算机系统》,这本书很基础,但是覆盖面很全,可以帮助加深对计算机的理解,知道它底层是怎么工作的,健全计算机体系的知识,而不是只学一些编程技术之类的表相的东西,这样永远只会被别人牵着走;另外一本书是《Unix环境高级编程》,他说你以后是去做网络方面的东西,而网络是绝对离不开Unix,你把这本书的程序当模板背下来以后,第一、编程就不成问题,第二、可以对Unix有全面的了解。
&&&&& 好记性不如烂笔头,看了书不能过了了事,遂做此读书笔记,仅为学习温习之用,高手勿笑:)
&&&&& 前言啰嗦完毕,回归正文:
&&&& &3.3数据格式(记住)
由于是从16位体系结构扩展成32位,intel用术语字(word)表示16位数据类型,因此32位为双字(double words),64位数为4字(quad words)。
&以下是比较容易模糊的数据类型大小:
&&&&&&&&& 32位机上:float 4&&& long int 4&& double 8&&& longlong 8&&& char* 4&& unsigned long 4
&&&&&&&&& 64位机上:float 4&&& long int&8&& double 8&&& longlong 8&&& char*&8&& unsigned long 8
&&&&&&&&& 另外,GCC 用long double表示扩展精度(10字节),出于存储器性能考虑,会被存储为12字节
&&&&& 3.4访问信息
一个IA32 CPU包含一组8个存储32位值的寄存器,用以存整数数据和指针:eax,ecx,edx,ebx,esi,edi&&&& esp,ebp。大多数情况下前六个都用作通用寄存器,eax,ecx,edx的存储和恢复惯例不同于ebx,edi,esi(前三者为被调用者保存,后三者为调用者保存,详见3.7.3);最后两个用于存储指针,由于在过处理中非常重要,分别指向栈帧的顶部和底部,必须保持。
&&&&&& 3.4.1操作数指示符(记住)
&&&&&&&&大多数指令有一到多个操作数,操作数有三种:
&&&&&&&&&& 立即数:即常数值
&&&&&&&&&& 寄存器:表示某个寄存器内容
&&&&&&&&&& 存储器引用:根据计算出来的地址(通常称有效地址)访问某个存储器位置
&&&&&&&因此寻址方式也有多种,如:立即数寻址、寄存器寻址、绝对寻址、间接寻址、变址寻址、伸缩化& 的变址寻址&&
&&&&&&3.4.2数据传送指令(记住)
&&&&&& 几个重要数据传送指令:mov族(之所以称这为族是因为mov指令还有很多兄弟指令如movb、movw、movsb、movzb,这是我个人对它们的称呼,便于记忆mov其他几个比较低调的兄弟)、pop、push。
&&&&& 另,对于mov族,movb、movw自不必做过多解释,movsb、movzb分别为符号扩展、零扩
展,它们只拷贝一个字节,源操作数均为单字节,并设置目的操作数中其余的位,效果如下:
&&&&& 初始假设:%dh=8D& %eax=
&&&&& 1&& movb&& %dh,%al&&&&&&&;%eax=9876548D
&&&&&&2&& movsbl %dh,%eax&&&&;%eax=FFFFFF8D(目的操作数高24位设为源字节最高位,在这里为很显然为1,所以前24位为全F)
&&&&& 3&& movzbl %dh,%eax&&&&;%eax=0000008D(目的操作数高24位被设为0)
&&&&&对于pushl指令等价于:
&&&&&&&&& subl $4,%esp
&&&&&&&&&&movl %ebp,(%esp)& //注意这里的括号引起的差别
&&&&& popl指令等价于:
&&&&&&&&&& movl (%esp),%eax
&&&&&&&&&& addl $4,%esp
&&&&& 3.4.3数据传输实例(理解)
&&&&&& /*******C代码**********/
int exchange(int *xp, int y){
//*********汇编代码******/
//1 movl 8(%ebp),%eax
//2 movl 12(%ebp),%edx
//3 movl (%eax),%ecx
Get x at *xp
//4 movl %edx,(%eax)
Store y at *xp
//5 movl %ecx,%eax
Set x as return value
&&&&&& 通过汇编代码可以得到两点收获:
&&&&&&&&&&&&& 1、指针其实是地址,间接引用指针就是将该指针放在一个寄存器中&,然后在间接存储器引
&&&&&&&&&&&&&&&&&& 用中引用这个寄存器
&&&&&&&&&&&&&&2、局部变量通常保存在寄存器中,而不是存储器(个人猜测应该是局部变量属于动态
&&&&&&&&&&&&&&&&&&&分配,局部变量因此被动态置入寄存器,而非存储器)
&&&&&&& 3.5算术和逻辑操作(看看便可,不必深究)
&&&&&&&这类操作符大致分为四个小类:
&&&&&&&&&&&1、加载有交地址(leal),通常用来执行简单算术操作,目前还不太懂这个与mov的区别
&&&&&&&&&&&2、一元或二元操作:incl& decl& negl& notl& addl& subl& imull& xorl& orl& andl
&&&&&&&&&&&3、移位操作 :sall==shll(填0)& sarl(算术右移,填符号位)& shrl(逻辑右移,填0)
&&&&&&&&&& 4、特殊算术操作:imull(有符号64位乘法)& mull(无符号64位乘法)& cltd(转换为四字)
&&&&&&&&&&&&&&&&idivl(有符号除法)&& divl(无符号除法)
版权声明:本文为博主原创文章,未经博主允许不得转载。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:91115次
积分:1168
积分:1168
排名:千里之外
原创:22篇
评论:57条}

我要回帖

更多关于 深入理解android系统 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信