程序员自我修养-总结 (1)

  1. 你可以不自己造轮子,但应该了解轮子的构造,而且越详尽越好,这就是程序员的自我修养吧。虽然我在这个系统上花费了很多时间和精力,却没有获得什么直接的收益,也没有让我跟上最新的技术潮流,但是它带给我的间接收获却是无法言表的,它使我在后来学习其他技术的时候能够很快地触类旁通、自下而上地去理解整个系统,往往能够理解得更加深刻更加透彻。真正了不起的程序员对自己的程序的每一个字节都了如指掌。

  2. CPU 体系结构、汇编、C 语言(包括 C++)和操作系统,永远都是编程大师们的护身法宝。计算机科学领域的任何问题都可以增加一个间接的中间层来解决。操作系统可以强制剥夺 CPU 资源并且分配给它认为目前最需要的进程。如果操作系统分配给每个进程的时间都很短,即 CPU 在多个进程间快速地切换,从而造成了很多进程都在同时运行的假象。

  3. 线程通常拥有至少三种状态:运行、就绪(此时线程可以立刻运行,但 CPU 已经被占用)、等待。一般把频繁等待的线程称之为 IO 密集型线程,而把很少等待的线程称为 CPU 密集型线程。IO 密集型线程总是比 CPU 密集型线程容易得到优先级的提升。

  4. Linux 将所有的执行实体,无论是线程还是进程,都称为任务,共享了同一个内存空间的多个任务构成了一个进程,这些任务也就成了这个进程里的线程。 fork : 复制当前进程;exec : 使用新的可执行映像覆盖当前可执行的映像; clone : 创建子进程并从指定位置开始执行。fork 函数调用之后,新的任务将启动并和本任务一起从 fork 函数返回。在父进程中,fork 返回新创建子进程的进程 ID,在子进程中,fork 返回 0。

  5. 写时复制(Copy on Write,COW),指的是两个任务可以同时自由地读取内存,但任意一个任务试图对内存进行修改时,内存就会复制一份提供给修改方单独使用,以免影响到其它的任务使用。用户态线程并不一定在操作系统内核里对应同等数量的内核线程。

  6. 所谓同步,即指在一个线程访问数据未结束的时候,其他线程不得对同一个数据进行访问。如此,对数据的访问被原子化了。同步的最常见方法是使用锁,常用的锁有:二元信号量(它适合只能被唯一一个线程独占访问的资源)、信号量(多元信号量的简称)、互斥量(要求哪个线程获取了互斥量,哪个线程就要负责释放这个锁)、临界区(其作用范围仅限于本进程,其他的进程无法获取该锁)、读写锁、条件变量。

  7. 当我们用 GCC 编译 c 的 Hello World 程序时,需要经过预处理 (Prepressing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。预处理会把 cpp 预编译成一个 .i 文件,经过预编译后的 .i 文件,其不包含任何宏定义。gcc 这个命令只是一些后台程序的包装,它会根据不同的参数要求去调用预编译编译程序 cc1、汇编器 as、链接器 ld。

  8. 编译过程就是把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。汇编器是将汇编代码转变成机器可以执行的指令,输出至目标 .o 文件。词法分析,首先源代码程序被输入到扫描器,其运用一种类似于有限状态机的算法可以很轻松地将源代码的字符序列分割成一系列的记号。

  9. 编译过程一般分为 6步:扫描、语法分析、语义分析、源代码优化、代码生成和目标代码优化。编译器前端负责产生机器无关的中间代码,编译器后端将中间代码转换成目标机器代码。这样对于一些可以跨平台的编译器而言,它们可以针对不同的平台使用同一个前端和针对不同机器平台的数个后端。编译器后端主要包括代码生成器 (Code Generator) 和目标代码优化器(Target Code Optimizer)。

  10. 链接过程主要包括了地址和空间分配(Address and Storage Allocation)、符号决议(Symbol Resolution)和重定位(Relocation)等这些步骤。编译器编译源代码后生成的文件叫做目标文件,而可执行文件格式涵盖了程序的编译、链接、装载和执行的各个方面。ELF (Executable Linkable Format) 文件类型有:可重定位文件( Relocatable File 如 .o 文件)、可执行文件、共享目标文件、核心转储文件(Core Dump File,当进程意外终止时,系统可以将该进程的地址空间的内容及终止时的一些其他信息转储到核心转储文件)。

  11. 程序源代码编译后的机器指令经常被放在代码段、全局变量和局部静态变量数据经常放在数据段。段表(Section Table)描述了文件中各个段在文件中的偏移位置及段的属性等,从段表里面可以得到每个段的所有信息。 未初始化的全局变量和局部静态变量一般放在一个叫 .bss 的段里, .bss 段只是为未初始化的全局变量和局部静态变量预留位置而已,它并没有内容,所以它在文件中也不占据空间。

  12. 总体来说,程序源代码被编译以后主要分成两种段:程序指令和程序数据。ELF 文件常见的段有:.dynamic(动态链接信息)、.strtab(字符串表)、.symtab(符号表)、.shstrtab( Section String Table 段名表)、.plt与.got(动态链接的跳转表和全局入口表)、.init和.fini(程序初始化与终结代码段)。

  13. 重定位表(Relocation Table 如:.rel.text 和 .rel.data),链接器在处理目标文件时,须要对目标文件中某些部位进行重定位,即代码段和数据段中的那些对绝对地址的引用的位置。这些重定位的信息都记录在 ELF 文件的重定位表里面,对于每个须要重定位的代码段或数据段,都会有一个相应的重定位表。

  14. 函数签名包含了一个函数的信息,包括函数名、它的参数类型、它所在的类和名称空间及其他信息。C++ 编译器会将在 extern C 的大括号内部的代码当作 C 语言代码处理,即 C++ 的名称修饰机制不会起作用。.bss 段在目标文件和可执行文件中并不占用文件的空间,但是它在装载时占用地址空间。

  15. 链接器为目标文件分配地址和空间,分两步:(1)第一步,空间与地址分配:扫描所有的输入目标文件,获得它们的各个段的长度、属性和位置,并且将输入目标文件中的符号表中所有的符号定义和符号引用收集起来,统一放到一个全局符号表。这一步中,链接器将能够获得所有输入目标文件的段长度,并且将它们合并,计算出输出文件中种个段合并后的长度与位置,并建立映射关系。 (2)第二步,符号解析与重定位:使用上面第一步中收集到的所有信息,读取输入文件中段的数据、重定位信息,并且进行符号解析与重定位,调整代码中的地址等。

  16. ld a.o b.o -e main -o ab ,其中 -e main 表示将 main 函数作为程序入口, ld 链接器默认的程序入为 _start。 -o ab 表示链接输出文件名为 ab,默认为 a.out。在 Linux 下,ELF 可执行文件默认从址 0x08048000 开始分配。链接器在完成地址和空间分配之后就已经可以确定所有符号的虚拟地址了,那么链接器就可以根据符号的地址对每个需要重定位的指令进行地址修正。每个要被重定位的地方叫一个重定位入口(Relocation Entry), 重定位入口的偏移(Offset)表示该入口在要被重定位的段中的位置。

  17. C++ 的全局对象的构造函数在 main 之前被执行,而析构函数在 main 之后被执行。如果一个函数放到 .init 段,那在 main 函数执行前系统就会执行它。我们把符号修饰标准、变量内存布局、函数调用方式等这些跟可执行代码二进制兼容性相关的内容称为 ABI (Application Binary Interface)。

你可能感兴趣的:(学习书籍-读书总结,Android逆向与安全,android,linux,安全)