一对一模型:
线程之间的并发是真正的并发,一个线程因某原因阻塞时,其他线程不会收到影响
一般直接使用API和系统调用创建的线程均为一对一的线程
缺点:
由于操作系统限制内核数量,一对一会让用户的线程数量收到限制
许多操作系统内核线程调度时,上下文切换的开销较大,导致用户线程的执行效率下降
多对一:将多个用户线程映射到一个内核线程上,线程之间的切换由用户态的代码进行,因此相对于一对一,多对一的线程切换要快速许多,而且有几乎无限制的线程数量
缺点:
若一个用户线程阻塞,那么所有的线程都将无法执行,因为内核里的线程也随之阻塞
多对多模型:结合一对一和多对一
编译和链接
将编译和链接合并到一起的过程称为构建(build)
预处理(processing)、编译(compilation)、汇编(assembly)、链接(linking)
1. 预编译
首先是源代码文件hello.c和相关的头文件,如stdio.h等被预编译器cpp编译成一个.i文件,对于c++来说,源代码文件的扩展名可能是.cpp或.cxx,头文件扩展名可能是.hpp,预编译后的文件扩展名为.ii
预编译主要处理源代码中的以”#”开始的预编译指令
经过预编译的.i文件不包含任何宏定义,因为所有的宏已被展开,并且包含的文件已经被插入到.i文件,所以当无法判断宏定义是否正确或头文件包含是否正确时,可以查看预编译后的文件来确定问题
2. 编译
编译过程就是把与预处理完成的文件进行词法分析、语法分析、语义分析及优化后生产相应的汇编代码文件,这是程序构建的核心部分
实际上gccx命令只是后台一些程序的包装,它会根据不同的参数去调用预编译编译程序ccl,汇编器as,链接器ld
3. 汇编
汇编器将汇编代码转换为机器可以执行的指令
编译器就是将高级语言翻译成机器语言的一个工具
编译过程一般分为6步:
1. 词法分析
首先源代码程序被输入到扫描器(scanner),进行词法分析,运用一种类似有限状态机(finite state machine)的算法将源代码的字符序列分隔成一系列的记号(token)
词法分析产生的记号种类:关键字、标识符、字面量(数字、字符串)和特殊符号(加号、等号等)。在识别记号同时,扫描器也完成其他工作,如将标识符存放到符号表、将数字、字符串常量存放到文字表等,以备后用
2. 语法分析
语法分析器(grammer parser)将对由扫描器发出的信号进行语法分析,从而产生语法树(syntax tree),整个分析过程采用了上下文无关语法(context-freegrammer)的分析手段。由语法分析器生成的语法树就是以表达式(expression)为节点的树
如果出现了表达式不合法吗,比如名称括号不匹配,表达式缺少操作等、编译器就会报告语法分析阶段的错误
3. 词义分析
由语义分析器(semantic analyzer)来完成。编译器所能分析的语义是静态语义(static semantic),所谓静态语义是指在编译期可以确定的语义,与之对应的动态语义(dynamic semantic)只有在运行期才能确定
静态语义通常包含声明和类型的匹配,类型的转换
经过语义分析阶段以后,整个语法树的表达式都被标识了类型,如果有些类型需要做隐式转换,语义分析程序会在语法树中插入相应的转换节点
4. 中间语言生成
源码级优化器(source code optimizer)将整个语法树转换成中间代码(intermediate code),它是语法树的顺序表示,但是和目标机器、运行时环境无关,比如不包含数据尺寸,变量地址、寄存器名字等。中间代码有多种类型,比如有三地址码(three-address code)和P-代码(P-code)
中间代码使得编译器可以被分为前端和后端。编译器前端负责产生机器无关的中间代码、编译器后端将中间代码转换成目标机器代码。对于跨平台的编译器,她们就可以针对不同平台使用同一个前端和针对不同机器平台的后端
5. 目标代码生成与优化
编译器后端主要包括代码生成器(code generator)和目标代码优化器(target code optimizer),代码生成器将中间代码转换成目标代码,此过程依赖于目标机器;目标代码优化器对目标代码优化,比如选择合适的寻找方式,使用位移来代替乘法运算,删除多余指令等
由于CPU本身采用了如流水线、多发射、超标量等特性,为支持,编译器的机器指令优化过程也变得十分复杂
由于定义其他模块的全局变量和函数在最终运行时的绝对地址都是在最终链接的时候才能确定,所以现代的编译器可以将一个源代码文件编译成一个未连接的目标文件,然后由链接器最终将这些目标文件链接起来形成可执行文件
重新计算各个目标地址的过程称为重定位(relocation)
在java中,每一个类是一个基本的模块,若干个模块组成一个包(package),若干个包形成一个程序
链接的主要内容就是把各个模块之间相互引用的部分都处理好,使得各个模块之间能够正确的衔接
链接过程主要包括了地址和空间重分配(address and storage allocation),符号决议(symbol resolution)和重定位 (relocation)等步骤
每个模块的源代码文件(.c)经过编译器编译成目标文件(objectfile,.o或.obj),目标文件和库(library)一起连接形成最终可执行文件
最常见的库就是运行时库(runtime library),它是支持程序运行的基本函数的集合。库其实是一组目标文件的包,就是一些最常用的代码编译成目标文件后打包存放
在链接器运行时,地址被的过程,叫做重定位(relocation),每个要被修正的地方叫做重定位入口(relocation entry)。重定位所做的就是给程序中每个这样的绝对地址引用的位置“打补丁”,使它们执行正确的地址