C/C++库文件简介

   最初发表在这里

 

   其实,C/C++世界开始时并没有库这个概念,我们编写程序的时候,都是自己搞定一切:Coding,Compile,Link,生成一个可执行文件后载入系统运行就可以了。但是,如果每个程序员都这样各自为政的话,将会导致大量的重复劳动。譬如,在很多程序中都需要输入输出的功能,按照现在这种状况,只有每个程序员都自己重新开发这样的功能模块,这样效率之低下可想而知。于是,大家渴望能够进行代码重用:一些通用的代码最好能够由别人提供,我只需调用即可。
   那么,我们如何得到这些可重用的代码呢?首先我们想到可以让编译器自动为我们生成这些代码。我们只需调用这些函数,编译器解析到这些函数时,如C语言的 printf函数,则自动为我们生成相应的代码。嗯,确实是个可行的办法。Pascal中的一些标准方法就是这样提供的。但是,这个方法也有很大的缺陷:第一,就是大大加重了编译器的负担,使编译器复杂化,对于C语言这样的标准方法很多的语言更是如此。第二,很难对这些方法进行添加或者更新,如果有这样的需要,则只能重新编写和编译整个编译器。
   第二个方法就是将所有的标准函数都编译到一个可重定位模块中去,譬如,我们可以将printf,scanf等标准函数统一编译到一个libc.o文件中,然后,我们就可以把它连接到我们的程序中去。这个方法对于程序员来说相当方便,因为我们只需要指定一个连接模块就可以使用各种函数了。但是,对于计算机来说,这并不是一件好事:它太庞大了!如果我的程序中仅仅需要其中一个printf 函数,我也必须得把整个libc.o连接进去,而这个libc.o的大小通常都是以M为单位的,成本太高了,不可行。
    那好,如果这样空间成本太高的话,我们可以将其化整为零,每个方法编译成一个.o文件,譬如printf.o,scanf.o等等。对,这样一来,计算机是满意了,但是我们程序员却惨了:你想想,如果我在应用程序中使用了30个函数,那么我在连接的时候,需要准确无误地提供这三十个函数对应的.o文件,痛苦啊!
   有没有一个两全其美的方法,既能够减少对内存的占用,又方便程序员使用呢?有,静态库(static library)就是这样一个解决方案。静态库是一个或多个.o文件的集合。程序员使用到这些.o文件对应的函数时,只需要在连接时提供该静态库即可,而不需要列举用到的.o文件。而连接器进行连接的时候,只会将程序中用到的函数对应的.o文件连接到程序中。譬如,我们有一个库文件libc.a,里面放有printf.o,scanf.o等多个.o文件,如果我们程序中只使用了printf函数,那么,连接器只会将printf.o连接进来,而不会把scanf.o也连接进来,虽然它们都同在一个库文件中。这样,计算机和程序员都满意了。下图( from Apple )是使用静态库的示例:
static library

    那是不是有了静态库以后就万事大吉了呢?当然不是,静态库是在编译阶段跟应用代码连接在一起的,从那以后,两者就紧密耦合在一起。这样一来,应用代码中库文件的更新就成了大问题了。如果库文件更新了,我想在应用程序中使用到最新的静态库,那么,我只能够将我的代码跟新的静态库重新编译一次,很难维护。此外,静态库的性质使每一个应有程序都有静态库中相应部分的拷贝,譬如,程序A和B都用到了printf函数,那么在两个程序中都会有printf.o拷贝。如果系统中有好几十个进程都使用到了printf函数,那么相同的代码将会重复出现几十次,这对于内存是极大的浪费。针对这些问题,共享库(shared library,也叫做动态连接库,*nix中为so文件,Windows中称为Dll)诞生了。使用该技术,一个共享库只会在系统中出现一次,而不管系统中有多少个进程使用到这个共享库。同时,共享库中的代码段还可以被各个进程所共享。共享库的示例图(from Apple)如下:
dynamic library

     共享库是在应用代码装载到系统中的时候由动态连接器动态加载到内存空间去的。静态连接器在编译阶段只是在应用代码中插入一些关于共享库的基本信息,而不会将共享库的实际代码连接到应用代码中去。这样,如果库文件更新了,我们只需要重新启动应用程序,就可以使用到最新的库文件了,同时,我们还大大节省了内存,一举两得。
     现在,一切都那么美好,但是并非完美。现在的情况是在加载过程中我们会把应用代码中的使用到共享库动态加载到系统中,但是,这个库在实际运行过程中会被使用到吗?看下面例子:

void show(int cmd)
{
      if( cmd == 0 )
    {
        //use library method
     }
}

    如果传入的参数不等于 0 的话,libraay method是永远都不会执行的,但是,我们却会把这个共享库加载到系统中去。如果我们能够在运行过程中决定是否加载一个共享库,那该多好啊!完全可以,Linux以及相关系统我们提供了相关的API,这些API包含在头文件<dlfcn.h>中。相关的函数有:

void *dlopen(const char *filename, int flag);
void *dlsym(void *handle, char *symbol);
int dlclose (void *handle);
const char *dlerror(void);

    在VC,也有相应的函数完成相关功能。此外,VC中还有延迟加载技术,使得Dll可以按需加载。

补充一段共享库存在的问题:from [4]

DLL Hell

While it is usually a good idea to share a single instance of a library rather than reduplicate it into every individual program, this sharing can become a serious problem. Think of two different Web browsers that reside on the same machine. Both browsers use the same shared libraries to access the system's modem, graphics card, and I/O routines. Now, suppose that you recently had to install an update of one of these shared libraries because of a security loophole found in the original version. Alas, the new version causes one of the browsers to crash. You can either wait for a new patch or revert to the previous version of the shared library.

None of these workarounds is ideal. Had the library been statically linked into each browser application, this mess would have been avoided. Unfortunately, such conflicts are reconciled only when you explicitly enable multiple versions of the same shared library to coexist on the same machine. However, this workaround complicates system management and forces users to become experts. In the Windows world, this problem is called the "DLL hell" although it exists in the POSIX world too.

Aliasing

 

Dynamic linking enables you to share identical code, which is usually a good idea. However, sharing the same data is rarely desirable.

If, for example, two processes call localtime(), it must return a different local static tm struct for every process. Designers of dynamically linked libraries were aware of this necessity and devised various techniques to cope with it. However, programmers must explicitly state which components of a shared library are truly shared and which ones aren't. The result is a larger and slower shared library as well as cluttered-up source files.

Code Cracking

Windows programmers who wish to thwart crackers' attempt to decompile their code often split an application into multiple DLLs. Ironically, this makes a cracker's job much easier because DLLs (and shared libraries in general) must contain metacode, i.e., code that documents code. This metacode (reminiscent of debug information) tells the runtime linker where each function and object is stored in the file, what their non-decorated names are, and so on. A hacker that knows how this information is encoded can decompile the code with less effort than required for decompiling an optimized, statically linked executable

Reference:
[1] Computer Systems: A Programmer's Perspective,Chapter 7 Linking
[2] Program Library HOWTO
[3] Overview of Dynamic Libraries , Apple
[4] Dynamic Linking: Advantages and Disadvantages

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值