链接详解

来源:互联网 发布:高中数学讲课视频软件 编辑:程序博客网 时间:2024/06/16 21:57

1. 定义和声明

1.1  extern和static关键字

extern关键字表示这个标识符具有External Linkage ,static关键字修饰一个函数声明,则表示该标识符具有Internal Linkage 。

当把两个 .c 文件放在一起编译链接时, main.c用到的函数push、pop和is_empty由stack.c提供, 然而函数main.c找不到这些函数的原型,只好根据函数调用代码做隐式声明 。所以编译器只能通过隐式声明来猜测函数原型,这种猜测往往会出错,但在比较简单的情况下还算可用 。

1.2 头文件

为什么在有些.c文件中的头文件#include <stdio.h>用角括号,而#include "stack.h"用引号?

对于用角括号包含的头文件,gcc首先查找-I选项指定的目录,然后查找系统的头文件目录(/usr/include或 /usr/lib/gcc/i486-linux-gnu/4.3.2/include );而对于用引号包含的头文件,gcc首先查找包含头文件的.c文件所在的目录,然后查找-I选项指定的目录,然后查找系统的头文件目录。

例如:

$ tree
|-- main.c
|-- stack.c
`-- stack.h
0 directories, 3 files   

gcc会自动在main.c所在的目录中找到stack.h

 

$ tree
|-- main.c
`-- stack
   |-- stack.c
   `-- stack.h
1 directory, 3 files

则需要用gcc -c main.c -Istack编译。用-I选项告诉gcc头文件要到子目录stack里找。

如把代码头文件改成 #include "stack/stack.h",那么编译时就不需要加-Istack选项了,因为gcc会自动在main.c所在的目录中查找 。

2. 静态库

库文件名都是以libxx开头的,静态库以.a作为后缀。

把libstack.a库和main.c编译链接在一起格式:

$ gcc  main.c  -L.  -lstack  -Istack  -o  main

注:L选项告诉编译器去哪里找需要的库文件,-L.表示在当前目录找。-lstack告诉编译器要链接libstack库,-I选项告诉编译器去哪里找头文件。注意,即使库文件就在当前目录,编译器默认也不会去找的,所以-L.选项不能少。 

gcc首先到 -L 的选项指定的目录下查找,看有没有共享库libstack.so,如果有就链接它,否则再找有没有静态库libstack.a。gcc在链接时优先考虑共享库。

 

那么链接共享库和链接静态库有什么区别呢? 

链接共享库时只是指定了动态链接器和该程序所需要的库文件,并没有真的做链接,要在运行时做动态链接。  而在链接静态库时,链接器会把静态库中的目标文件(当前main函数中需调用的)取出来和可执行文件真正链接在一起。

 

3. 共享库

组成共享库的目标文件和一般的目标文件有所不同,在编译时要加-fPIC选项,例如:

$ gcc  -c  -fPIC  stack/stack.c  stack/push.c  stack/pop.c  stack/is_empty.c

-f后面跟一些编译选项,PIC是其中一种,表示生成位置无关代码 。

我们知道一般的目标文件称为Relocatable,在链接时可以把目标文件中各段的地址做重定位, 重定位之后,各段的加载地址就定死了,因为在指令中使用了绝对地址。

而共享库各段的加载地址并没有定死,可以加载到任意位置,因为指令中没有使用绝对地址,因此称为位置无关代码。 这是为什么?

因为不同的进程虽然共享libc所在的物理地址,但这些物理地址被映射到各进程的虚拟地址空间时却位于不同的地址,所以要求libc的代码不管加载到什么地址都能正确执行。 <摘自虚拟内存管理>

 

4. 虚拟内存管理

操作系统的虚拟内存管理起到了什么作用呢?

第一,虚拟内存管理可以控制物理内存的访问权限。物理内存本身是不限制访问的,任何地址都可以读写,而操作系统要求不同的页面具有不同的访问权限,这是利用CPU模式和MMU的内存保护机制实现的。

第二,每个进程有独立的地址空间。不同进程中相同VA被MMU映射到不同的PA,因此在某一个进程中访问任何虚拟地址都不可能访问到属于另外一个进程的物理内存页面。并且,每个进程都认为自己独占整个用户地址空间(3G);这样使得任何一个进程由于执行错误指令或恶意代码导致的非法内存访问都不会意外改写其它进程的数据,不会影响其它进程的运行,从而保证整个系统的稳定性。

第三, VA到PA的映射会给分配和释放内存带来方便,物理地址不连续的几块内存可以映射成虚拟地址连续的一块内存。 比如要用malloc分配一块很大的内存空间,虽然有足够多的空闲物理内存,却没有足够大的连续空闲内存,这时就可以分配多个不连续的物理页面而映射到连续的虚拟地址范围。

第四,一个系统如果同时运行着很多进程,为各进程分配的内存之和可能会大于实际可用的物理内存,虚拟内存管理使得这种情况下各进程仍然能够正常运行。 因为进程访问的是虚拟内存页面(每个进程在/proc下都有一个子目录,而/proc目录下并不是真正的磁盘文件,而是由内核虚拟出来的),  

 

原创粉丝点击