![ea715521c6c9f5c09173b39c8635f825.png](https://i-blog.csdnimg.cn/blog_migrate/303713c538772ea939022f3a89bf9f32.jpeg)
第一部分 简介
计算机主要分为硬件和软件,计算机中最核心的三个硬件是CPU,内存,I/O控制芯片。这三个部件的速度不同,计算机的发展一方面为了提升三者各自的速度,同时又更好的加强三者之间协助。发展历程大致分为早期 -- 北桥 -- 南桥 -- 多核。软件分为应用软件和系统软件。硬件和软件的布局如图1所示。应用软件还是很好理解的,系统软件一般指的是的一些开发工具,它们都是调用的操作系统应用程序编程借口(API),API的提供者是运行库(简单理解就是一些大佬写好的代码给我等小白使用),什么样的运行库提供什么样的API,比如Linux下的Glibc库提供POSIX的API。接着是系统调用,内核,硬件规格(告知如何与硬件进行通信的接口)。关于系统调用以及系统调用与API的区别,会在书的第12章详细讲解。
![636f51fe104fad3b0840c03d294824ec.png](https://i-blog.csdnimg.cn/blog_migrate/8e2d6120611a40d82760645cc4d0db36.jpeg)
那么操作系统主要做些什么呢?就是尽可能的发挥CPU Memory IO的潜力,对于CPU而言,由分时操作系统转向实时操作系统的发展;对于内存管理(要解决的问题是如何将计算机上有限的物理资源分配给多个应用程序来使用)而言,由直接使用物理内存再到虚拟地址空间的出现,再到分段--分页--段页式等一系列的发展;对于IO而言,设备驱动的发展,应用程序员是不需要与硬件直接打交道的,在如今的操作系统中,硬件被抽象成一系列概念,在Unix中,硬件也被抽象成文件,所有繁琐的硬件交互都是由硬件驱动程序来完成的。
关于线程的问题。线程本身的特点;线程调度与优先级(优先级调度和轮转法);线程安全问题,主要包括:竞争与原子操作;可重入函数与线程安全(一个可重入函数在多线程环境下是可放心使用的);过度优化(寄存器中的数据暂时不放回,指令乱序执行(可以使用屏障来解决这个问题));多线程的内部情况。
第二部分 静态链接
- 编译和链接
#include <stdio.h>
int main(void)
{
printf("hello worldn");
return 0;
}
对于上面一个简单打印helloworld的代码,你对它到底了解多少呢?
一个高级语言写的代码文本文件到可执行文件大致经历了下面四个步骤。
gcc -E helloworld.c -o helloworld.i //1. 预处理
➜ test ls
helloworld.c helloworld.i
➜ test gcc -S helloworld.i -o helloworld.s //2. 编译
➜ test ls
helloworld.c helloworld.i helloworld.s
➜ test gcc -c helloworld.s -o helloworld.o //3. 汇编
➜ test ls
helloworld.c helloworld.i helloworld.o helloworld.s
➜ test gcc helloworld.o
➜ test ls
a.out helloworld.c helloworld.i helloworld.o helloworld.s
➜ test gcc helloworld.o -o helloworld //4. 由目标文件生成可执行文件 链接
➜ test ls
a.out helloworld helloworld.c helloworld.i helloworld.o helloworld.s
- 预处理:预处理期间做的事情有,将所有的头文件展开 #include;替换到所有的宏定义#define;处理所有的条件预编译指令#if #endif;去掉注解;加上行号;保留所有的编译器指令#pragma。
- 编译:词法分析,语法分析,语义分析,中间语言生成,目标代码生成与优化。
- 汇编:汇编器将汇编语言源代码翻译成二进制文件的过程。
- 链接:这是一个比较复杂的过程,后文会详细介绍。
2. 目标文件里有什么
编译器 编译源代码后生成的问价叫做目标文件(个人觉得,应该是已经发生了汇编的过程,这里写的有点不是很严谨)。目标文件从结构上讲,它是编译后的可执行文件格式,只是还没有经历过链接的过程,其中可能有些符号或者地址还没有被调整。可执行文件格式涵盖了程序的编译、链接、装载和执行的各个方面(好像汇编过程简略没有说了)。
1. 目标文件的格式 (我主要关注Linux环境,其它环境类似)
在Linux环境中目标文件叫作ELF文件,不仅如此,还有其它相似的文件也叫作ELF文件,如下。
可重定位文件、可执行文件、共享目标文件(个人理解包含动态库和静态库文件)、Core dump file。
2 . 目标问价是什么样的
目标文件中除了编译后的机器指令代码和数据,还包含链接时需要的符号表、调试信息、字符串(不懂)等。目标文件将这些信息按照不同的属性,以“节“(Section)的形式存储,有时也叫作”段”(Segment)
程序源代码编译后的机器指令放在代码段“.text", 局部变量和全局变量放在数据段“.data"。
![1cc7ca276b3af7d9ef7750e46ddf2947.png](https://i-blog.csdnimg.cn/blog_migrate/b4a6fec7300f86a0c073e4f7b9bb593d.jpeg)
这里对bss段再进行一下补充,未初始化的全局变量和局部静态变量默认值都是0,本来它们也可以放在.data 段里,但是因为它们都是0,所以为它们在.data段中分配空间并存放数据0是没必要的。程序运行的时候,它们确实是要占用空间的,并且可执行文件必须记录所有未初始化的全局变量和局部静态变量的大小总和,记为.bss段(不理解)。所以.bss段只是为未初始化的全局变量和局部静态变量预留位置而已,它并没有内容,所以它在文件中也不占用空间。
总体来说,程序源代码编译后被分为两段:程序指令和程序数据
关于疑问,为什么要把代码和数据分开呢?
![0187cb853316ec816826beecbc17d2cd.png](https://i-blog.csdnimg.cn/blog_migrate/e8ab8c9af833b2e7ed033e505986f3bb.jpeg)
3.3 挖掘SimpleSection.o
objdump -h SimpleSection.o 查看文件内部结构
![45f096addc91b4fe641d92062070fc22.png](https://i-blog.csdnimg.cn/blog_migrate/3beaf01eba4fccfbd26e3a10da2dce1f.jpeg)
除了最基本三个段之外多了3个段,ELF
![ee50af5dd1c75f81316d9c3d39473ffc.png](https://i-blog.csdnimg.cn/blog_migrate/7797c3048244f0ec882dd6c0330b70f4.jpeg)
除了上述常见的一些段,还有一些其它段,如下表所示。
![7ed2cd3996c13e8404440e813d9768a4.png](https://i-blog.csdnimg.cn/blog_migrate/bb2d37f267e60768759046463eac7964.jpeg)
3.4 ELF文件结构描述
下面是ELF文件的基本结构,ELF目标文件格式的最前部分是ELF文件头,它包含了描述整个文件的基本属性,比如文件版本号,目标机器型号,程序入口地址等。紧接着是文件中的各个段。其中ELF文件中段有关的重要结构是段表,它表述了ELF文件包含的所有段的信息,比如段名、段的长度、在文件中的偏移、读写权限等。
![e77a3df9ff27722236f0d15f8179b7e2.png](https://i-blog.csdnimg.cn/blog_migrate/cc8006f80d1f190cbda1ea2d68a13781.jpeg)
下面是readelf显示的ELF文件的结构
![b23e766ed662e16985a46c3bf8fa6be0.png](https://i-blog.csdnimg.cn/blog_migrate/12e0d126b545c6c29658e6775cadb943.jpeg)
重定位表
注意到ELF文件中有一个叫作“re l.txt"的段,这是一个重定位表。连接器在处理目标文件时,需要对目标文件中的某些部位进行重定位,即代码段和数据段中那些绝对地址的引用的位置。这些重定位的信息都记录在ELF文件的重定位表里。
3.5 链接的接口 --- 符号
链接过程的本质就是要把多个不同的目标文件互相“粘”在一起。在链接中,目标文件之间互相拼合实际上是目标文件之间对地址的引用ÿ