第10.1B次重读csapp

第10.1B次重读csapp

  • 信息表示和处理
    • 处理分配函数的参数溢出
  • 程序的机器级表示
    • 编译系统
    • 汇编
    • 指针
      • 函数指针和指针函数
      • GDB调试
      • 内存越界引用和缓冲区溢出
  • 处理器体系结构
  • 优化程序性能
  • 存储器层次结构
  • 链接
  • 异常控制流
  • 虚拟内存
  • 系统级IO
  • 网络编程
  • 并发编程

为什么再读?
————当然是没读懂啊摔
学了快三年的计算机,才回头来梳理这些基础的知识框架,是了这就是小时候没好好读书
这本书隔了两年感觉还能再读,也是大浪淘沙的经典好书,当然也是因为我鶸

信息表示和处理

大多数计算机使用字节byte(8位bit)作为最小的可寻址的内存单位,而机器级程序将内存看作一个很大的字节数组,也就是虚拟内存,内存中的每一个字节都会有唯一的数字标识,即是他的地址,这些所有地址的集合就是虚拟地址空间。

而对于各种进制的转换想必不用多言,这是加减乘除的工作。

对于计算机的字长,2010年前主流的还是32位机器,这里会有一些基本数据类型默认字节数的差异,比如C的long和指针,机器中的差异还有大端法和小端法。

Linux,Windows也是小端
Android(from Google)和IOS(from Apple)只运行于小端模式

想想如何用C打印程序对象的字节表示?

二进制是计算机编码、存储和处理信息的核心,数值0和1的研究也于19世纪演化迅速,布尔代数就是其中一个产物。很简单的,布尔代数就是~、&、|、^,对应逻辑运算not、and、or、exclusive-or。

中间都是非常琐碎的数字计算相关,没什么写头。

处理分配函数的参数溢出

发现一个有趣的问题:在调用分配函数(如malloc)时,可能会有一些表达式溢出的问题,请编写一个calloc的可靠版本,题目如下:

第10.1B次重读csapp_第1张图片
可以先不看解答思考一下

#include 
#include 
#include 
#include 
#include 

void *another_calloc(size_t nmemb, size_t size) {
    if (nmemb == 0 || size == 0) {
        return NULL;
    }

    size_t buff_size = nmemb * size;
    if (nmemb == buff_size / size) {
        void *ptr = malloc(buff_size);
        memset(ptr, 0, buff_size);
        return ptr;
    }

    return NULL;
}


int main() {
    void *p;
    p = another_calloc(0x1234, 1);
    assert(p != NULL);
    free(p);

    p = another_calloc(SIZE_MAX, 2);
    assert(p == NULL);
    free(p);

    return 0;
}

程序的机器级表示

编译系统

首先回顾一下整个编译系统:
第10.1B次重读csapp_第2张图片

  1. 预处理阶段
      预处理器(cpp)根据以字符#号开头的命令,修改原始的c程序。比如hello.c中的第一行#include 命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入到程序文本中,结果得到了另一个C程序,通常是以.i为扩展名。在Linux下我们用GCC命令如下,得到一个hello.i文件,然后查看文件内容发现该程序依然是C语言程序,只不过多了头文件stdio.h的内容。
    gcc -E hello.c -o hello.i
    
  2. 编译阶段
      编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序。汇编语言中每条语句都以一种标准的文本格式确切地描述了一条低级机器语言指令。其实汇编语言是非常有用的,它为所有的高级语言提供了一种通用的输出语言。比如C编译器和Fortran编译器产生的输出文件用的都是一样的汇编语言。在Linux下,我们用下列命令
    得到一个hello.s汇编程序:
    gcc -S hello.i -o hello.s
    
  3. 汇编阶段
      汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可定位目标程序的格式,并将结果保存在目标文件hello.o中,hello.o是一个二进制文件,它的字节编码是“机器语言指令”而不是“字符”,所以,我们用文本编辑器打开hello.o文件看到是回事一堆乱码。使用gcc命令如下将得到hello.o文件,用vim打开看一下是乱码。
    gcc -c hello.s -o hello.o
    
  4. 链接阶段
      我们注意到,hello.c中有一个printf函数,它是每个C编译器都会提供的标准库中的一个函数。printf函数存在于一个名为printf.o的单独的预编译好的目标文件中,而这个文件必须以某种方式合并到我们的hello.o程序中。链接器(ld)就是负责处理这种合并。最后得到hello文件,一个可执行目标文件(可执行文件),可被加载到内存中,由系统执行。使用命令如下得到hello文件。
    gcc hello.o -o hello
    
    自此,编译系统的整个过程大致如此。总结一下,从源程序到目标文件(可执行文件)的转化是通过编译系统完成的,编译系统包含四个阶段:预处理,编译,汇编,链接。一般的编译驱动程序如GCC都实现了编译系统的所有功能,我们用编译驱动程序直接就可以实现源程序到目标文件的转化。

汇编

这部分非常琐碎,可以看我另一篇博客里有道自旋锁实现的汇编的例子
自旋锁汇编简单实现

指针

函数指针和指针函数

挖个小土包

GDB调试

等有例子了再更

内存越界引用和缓冲区溢出

等有例子了再更

处理器体系结构

优化程序性能

存储器层次结构

链接

异常控制流

虚拟内存

系统级IO

网络编程

并发编程

烂了啊。。。内容好多,等会等会等会

你可能感兴趣的:(第10.1B次重读csapp)