Linux C头文件查找与动态库搜索

一、编译程序时,头文件路径搜索

本文介绍在linux中头文件的搜索路径,也就是说你通过include指定的头文件,linux下的gcc编译器它是怎么找到它的呢。在此之前,先了解一个基本概念。

头文件是一种文本文件,使用文本编辑器将代码编写好之后,以扩展名.h保存就行了。头文件中一般放一些重复使用的代码,例如函数声明、变量声明、常数定义、宏的定义等等。当使用#include语句将头文件引用时,相当于将头文件中所有内容,复制到#include处。#include有两种写法形式,分别是:

#include <> : 直接到系统指定的某些目录中去找某些头文件。

#include “” : 先到源文件所在文件夹去找,然后再到系统指定的某些目录中去找某些头文件。

#include文件可能会带来一个问题就是重复应用,如a.h引用的一个函数是某种实现,而b.h引用的这个函数却是另外一种实现,这样在编译的时候将会出现错误。所以,为了避免因为重复引用而导致的编译错误,头文件常具有:

#ifndef LABEL

#define LABEL

//代码部分

#endif

的格式。其中LABEL为一个唯一的标号,命名规则跟变量的命名规则一样。常根据它所在的头文件名来命名。

例如,如果头文件的文件名叫做hardware.h,那么可以这样使用:

#ifndef __HARDWARE_H__

#define __HARDWARE_H__

//代码部分

#endif

这样写的意思就是,如果没有定义__HARDWARE_H__,则定义__HARDWARE_H__,并编译下面的代码部分,直到遇到#endif。这样当重复引用时,由于__HARDWARE_H__已经被定义,则下面的代码部分就不会被编译了,这样就避免了重复定义。

一句话,头文件事实上只是把一些常用的命令集成在里面,你要用到哪方面的命令就载入哪个头文件就可以了。

gcc寻找头文件的路径(按照1->2->3的顺序)

1. 在gcc编译源文件的时候,通过参数-I指定头文件的搜索路径,如果指定路径有多个路径时,则按照指定路径的顺序搜索头文件。命令形式如:“gcc -I /path/where/theheadfile/in sourcefile.c“,这里源文件的路径可以是绝对路径,也可以是相对路径。

eg:设当前路径为/root/test,include_test.c如果要包含头文件“include/include_test.h“,有两种方法:

1) include_test.c中#include “include/include_test.h”或者#include "/root/test/include/include_test.h",然后gcc include_test.c即可

2) include_test.c中#include <include_test.h>或者#include <include_test.h>,然后gcc –I include include_test.c也可

2. 通过查找gcc的环境变量C_INCLUDE_PATH/CPLUS_INCLUDE_PATH/OBJC_INCLUDE_PATH来搜索头文件位置

3. 再找内定目录搜索,分别是

/usr/include

/usr/local/include

/usr/lib/gcc-lib/i386-linux/2.95.2/include

最后一行是gcc程序的库文件地址,各个用户的系统上可能不一样。

gcc在默认情况下,都会指定到/usr/include文件夹寻找头文件。

gcc还有一个参数:-nostdinc,它使编译器不再系统缺省的头文件目录里面找头文件,一般和-I联合使用,明确限定头文件的位置。在编译驱动模块时,由于非凡的需求必须强制GCC不搜索系统默认路径,也就是不搜索/usr/include要用参数-nostdinc,还要自己用-I参数来指定内核头文件路径,这个时候必须在Makefile中指定。

4. 当#include使用相对路径的时候,gcc最终会根据上面这些路径,来最终构建出头文件的位置。如#include <sys/types.h>就是包含文件/usr/include/sys/types.h

二、编译程序时,动态库文件路径搜索

编译的时候:

1)gcc会去找-L指定的路径,例如

-L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk

2)再找gcc的环境变量LD_LIBRARY_PATH

3)再找内定目录 /lib:/usr/lib: /usr/local/lib:这是当初compile gcc时写在程序内的

二、运行程序时,Linux动态库文件搜索路径

linux共享库链接器ld.so有五种方法查找已经编译好的库libbase.so的位置,分别为:

1)ELF可执行文件中动态段中DT_RPATH所指定的路径。即在编译目标代码时, 对gcc加入链接参数“-Wl,-rpath”指定动态库搜索路径,eg:gcc -Wl,-rpath,/home/arc/test,-rpath,/lib/,-rpath,/usr/lib/,-rpath,/usr/local/lib test.c

2)环境变量LD_LIBRARY_PATH 指定的动态库搜索路径

3)/etc/ld.so.cache中所缓存的动态库路径,这个可以通过先修改配置文件/etc/ld.so.conf中指定的动态库搜索路径,然后执行ldconfig命令使其立即生效。

4)默认的动态库搜索路径/lib

5)默认的动态库搜索路径/usr/lib

另外:在嵌入式Linux系统的实际应用中,1和2被经常使用,也有一些相对简单的的嵌入式系统会采用4或5的路径来规范动态库,3在嵌入式系统中使用的比较少, 因为有很多系统根本就不支持ld.so.cache。

那么,动态链接器ld.so在这五种路径中,是按照什么样的顺序来搜索需要的动态共享库呢?答案这里先告知就是按照上面的顺序来得,即优先级是:1-->2-->3-->4-->5。我们可以写简单的程序来证明这个结论。

首先,写成5个函数,这5个函数名称都叫pt,但是里面的内容不一样:

pt1.c

#include <stdio.h>

void pt(){

printf("1 path on the gcc give ");

}

pt2.c

#include <stdio.h>

void pt(){

printf("2 path on the LD_LIBRARY_PATH ");

}

pt3.c

#include <stdio.h>

void pt(){

printf("3 path on the /etc/ld.so.conf ");

}

pt4.c

#include <stdio.h>

void pt(){

printf("4 path on the /lib ");

}

pt5.c

#include <stdio.h>

void pt(){

printf("5 path on the /usr/lib ");

}

然后,分别编译这5个函数,然后将它们分别移到上面5种情况对应的5个不同目录下:

gcc -fPIC -c pt1.c -o pt.o

gcc -shared pt.o -o libpt.so

mv libpt.so /tmp/st/1/

gcc -fPIC -c pt2.c -o pt.o

gcc -shared pt.o -o libpt.so

mv libpt.so /tmp/st/2/

gcc -fPIC -c pt3.c -o pt.o

gcc -shared pt.o -o libpt.so

mv libpt.so /tmp/st/3/

gcc -fPIC -c pt4.c -o pt.o

gcc -shared pt.o -o libpt.so

mv libpt.so /lib/

gcc -fPIC -c pt5.c -o pt.o

gcc -shared pt.o -o libpt.so

mv libpt.so /usr/lib/

再次,编写一个main函数m,让它来调用函数pt:

m.c

#include <stdio.h>

int main(){

printf("start.... ");

pt();

printf("......end ");

return 0;

}

最后,准备环境,让ld都知道这5个路径:

(a) 往/etc/ld.so.conf总增加一行,内容:/tmp/st/3,然后执行 ldconfig 命令

(b) export LD_LIBRARY_PATH=/tmp/st/2

另外3中路径,ld都可以得到,请接着看下面。

之后测试:

gcc m.c -o m1 -L/tmp/st/1 -lpt -Wl,-rpath,/tmp/st/1

./m1

start....

1 path on the gcc give

......end

这里在可执行文件中动态段中DT_RPATH所指定的路径,因此需要在编译m.c的时候就指定路径,由于其他路径都也告诉了ld,很明显,此种方法优先级最高了。

gcc m.c -o m -L/tmp/st/1 -lpt

./m

start....

2 path on the LD_LIBRARY_PATH

......end

这里很显然调用了LD_LIBRARY_PATH指定了路径中的共享库,因此此种情况优先级第二。

mv /tmp/st/2/libpt.so /tmp/st/2/libpt2.so

/m

start....

3 path on the /etc/ld.so.conf

......end

这里是调用了/etc/ld.so.cache中所缓存的动态库路径中的共享库,因此此种情况优先级第三。

mv /tmp/st/3/libpt.so /tmp/st/3/libpt3.so

./m

start....

4 path on the /lib

......end

这里是调用/lib中的共享库,优先级第四。

rm /lib/libpt.so

./m

start....

5 path on the /usr/lib

......end

这里是调用/lib中的共享库,优先级第五。

故证明这五种路径指定方法的优先级是1-->2-->3-->4-->5!

引用自:http://blog.csdn.net/huangjm_13/article/details/21160445

原文地址:https://www.cnblogs.com/kimiway/p/4526339.html