目标文件里有什么
1.目标文件格式
现在PC平台流行的可执行文件格式主要是Windows下的PE和Linux的ELF,它们都COFF格式的变种。目标文件就是源代码编译后但未进行链接的那些中间文件,它跟可执行文件的内容与结构很相似,所以一般跟可执行文件格式一起采用一种格式存储。
不光是可执行文件(Windows的.exe和Linux下的ELF可执行文件)按照可执行文件格式存储。动态链接库(Windows的.dll和Linux的.so)和静态链接库(Windows的.lib和Linux的.a)文件都按照可执行文件格式存储。它们在Window是下偶读按照PE-COFF格式存储,Linux下按照ELF格式存储。
ELF文件标准里面把系统中采用ELF格式的文件归为如下表所列举的4类:
2.目标文件里的内容
目标文件中的内容至少有编译后的机器指令代码、数据。没错,除了这些内容外,目标文件还包括链接时所需要的一些信息,比如符号表、调试信息、字符串等。一般目标文件将这些信息按不同的属性,以“段”的形式存储,在一般情况下,它们都表示一个一定长度的区域,基本上不加区别,唯一的区别是在ELF的链接视图和装载视图的时候
程序源代码编译后的机器指令经常被放在代码段里,代码段常见的名字有“.code”或“.text”;全局变量和局部静态变量数据经常放在数据段,数据段的一般名字都叫".data",一般C语言的编译后执行语句都编译成机器代码,保存在.text段;已初始化的全局变量和局部静态变量都保存在.data段;未初始化的全局变量和局部静态变量一般放在一个叫.bss的段中,
总体来说,程序源代码经过编译之后主要分为两种段:程序指令和程序数据。代码段属于程序指令,而数据段和.bss段属于程序数据。
参考博客地址:
进程地址空间
(1)
内核除了管理本身的内存外,还必须管理用户空间中进程的内存
我们称这个内存为进程地址空间,也就是系统中每个用户空间进程所看到的内存
进程地址空间由进程可寻址的虚拟内存组成,而且更为重要的特点是内核允许进程使用这种虚拟内存中 的地址。
平坦:地址空间范围是一个独立的连续区间
段式:这些地址空间并非连续的,而是被分段的
通常情况下,每个进程都有唯一的这种平坦地址空间
2,
一个进程的地址空间与另一个进程的地址空间即使有相同的内存地址,实际上也彼此互不相干。我们称 这样的进程为线程
内存地址是一个给定的值,它要在地址空间范围之内。
在地址空间,我们更关心的是一些虚拟内存空间,比如:
0x08048000 —— 0x0804c000
它们可被进程访问
这些可被访问的合法地址空间称之为内存区域
通过内核,进程可以给自己的地址空间动态地添加或减少内存区域
3,
内存区域可以包含各种内存对象:
·可执行文件代码的内存映射,称为代码段
·可执行文件的已初始化全局变量的内存映射,称为数据段
·包含未初始化全局变量,也就是bss段的零页的内存映射
·用于进程用户空间栈(不要和进程内核栈混淆,进程的内核栈独立存在并由内核维护)的零页的内存映射
·每一个诸如C库或动态链接程序等共享库的代码段、数据段和bss也会被载入进程的地址空间
·任何内存映射文件
·任何共享内存段
·任何匿名内存映射,比如由malloc()分配的内存
参考博客地址
http://www.cnblogs.com/lfsblack/archive/2012/09/08/2676791.html
共享库
1.共享库的概念:
共享库以.so结尾. (so ==share object) 在程序的链接时候并不像静态库那样在拷贝使用函数的代码,而只是作些标记。然后在程序开始启动运行的时候,动态地加载所需模块。所以,应用程序在运行的时候仍然需要共享库的支持。 共享库链接出来的文件比静态库要小得多。
2.共享库的创建
创建共享库和链接可执行文件类似:首先把源代码编译成目标文件,然后把目标文件链接起来.目标文件需要创建成位置无关码(PIC),概念上就是在可执行程序装载它们的时候,它们可以放在可执行程序的内存里的任何地方,(用于可执行文件的目标文件通常不是用这个方式编译的.)链接动态库的命令包含特殊标志,与链接可执行文件的命令是有区别的。
参考博客地址
运行时库
1.概念
在计算机程序设计领域,运行时库是一种被编译器用来实现编程语言内置函数,以提供该语言程序运行时(执
行)支持的一种特殊的计算机程序库。这种库一般包括基本的输入输出或是内存管理等支持。
随着操作系统多线程技术的发展,最初的C运行时库无法满足程序的需求,出现了严重的问题。C运行时
库使用了多个全局变量(例如errno)和静态变量,这可能在多线程程序中引起冲突。假设两个线程都同
时设置errno,其结果是后设置的errno会将先前的覆盖,用户得不到正确的错误信息。
因此,Visual C++提供了两种版本的C运行时库。一个版本供单线程应用程序调用,另一个版本供多线程
应用程序调用。多线程运行时库与单线程运行时库有两个重大差别:
(1)类似errno的全局变量,每个线程单独设置一个;
这样从每个线程中可以获取正确的错误信息。
(2)多线程库中的数据结构以同步机制加以保护。
这样可以避免访问时候的冲突。
系统调用
1.简述
操作系统的主要功能是为管理硬件资源和为应用程序开发人员提供良好的环境来使应用程序具有更好的兼容性,为了达到这个目的,内核提供一系列具备预定功能的多内核函数,通过一组称为系统调用(system call)的接口呈现给用户。系统调用把应用程序的请求传给内核,调用相应的的内核函数完成所需的处理,将处理结果返回给应用程序。
2.为什么使用系统调用
(1)系统调用可以为用户空间提供访问硬件资源的统一接口,以至于应用程序不必去关注具体的硬件访问操作。比如,读写文件时,应用程序不用去管磁盘类型,甚至于不用关心是哪种文件系统。
(2)系统调用可以对系统进行保护,保证系统的稳定和安全。系统调用的存在规定了用户进程进入内核的具体方式,换句话说,用户访问内核的路径是事先规定好的,只能从规定位置进入内核,而不准许肆意跳入内核。有了这样的进入内核的统一访问路径限制才能保证内核的安全。
(3)系统调用具有更强的功能。
3系统调用原理
一般的,进程是不能访问内核的。它不能访问内核所占内存空间也不能调用内核函数。CPU硬件决定了
这些(这就是为什么它被称作”保护模式”)。系统调用是这些规则的一个例外。其原理是进程先用适当的
值填充寄存器,然后调用一个特殊的指令,这个指令会跳到一个事先定义的内核中的一个位置(当然,
这个位置是用户进程可读但是不可写的)。在Intel CPU中,这个由中断0x80实现。硬件知道一旦你跳到
这个位置,你就不是在限制模式下运行的用户,而是作为操作系统的内核–所以你就可以为所欲为。
进程可以跳转到的内核位置叫做sysem_call。这个过程检查系统调用号,这个号码告诉内核进程请求哪种
服务。然后,它查看系统调用表(sys_call_table)找到所调用的内核函数入口地址。接着,就调用函数,等
返回后,做一些系统检查最后返回到进程(或到其他进程,如果这个进程时间用尽)。如果你希望读这
段代码,它在<内核源码目录>/kernel/entry.S,Entry(system_call)的下一行。