您的位置:首页 > 运维架构 > Linux

GCC 编译动态链接库和静态链接库 + 大型工程Makefile编写 + Linux环境变量的设置和查看方法

2012-10-14 13:00 471 查看
GCC 编译使用动态链接库和静态链接库

1 库的分类

根据链接时期的不同,库又有静态库和动态库之分。

静态库是在链接阶段被链接的,所以生成的可执行文件就不受库的影响了,即使库被删除了,程序依然可以成功运行。

有别于静态库,动态库的链接是在程序执行的时候被链接的。所以,即使程序编译完,库仍须保留在系统上,以供程序运行时调用。

2 静态库和动态库的比较

链接静态库其实从某种意义上来说也是一种粘贴复制,只不过它操作的对象是目标代码而不是源码而已。因为静态库被链接后库就直接嵌入可执行文件中了,这样就带来了两个问题。

首先就是系统空间被浪费了。这是显而易见的,想象一下,如果多个程序链接了同一个库,则每一个生成的可执行文件就都会有一个库的副本,必然会浪费系统空间。

再者,人非圣贤,即使是精心调试的库,也难免会有错。一旦发现了库中有bug,挽救起来就比较麻烦了。必须一一把链接该库的程序找出来,然后重新编译。

而动态库的出现正弥补了静态库的以上弊端。因为动态库是在程序运行时被链接的,所以磁盘上只须保留一份副本,因此节约了磁盘空间。如果发现了bug或要升级也很简单,只要用新的库把原来的替换掉就行了。

那么,是不是静态库就一无是处了呢?

答 曰:非也非也。不是有句话么:存在即是合理。静态库既然没有湮没在滔滔的历史长河中,就必然有它的用武之地。想象一下这样的情况:如果你用libpcap 库编了一个程序,要给被人运行,而他的系统上没有装pcap库,该怎么解决呢?最简单的办法就是编译该程序时把所有要链接的库都链接它们的静态库,这样, 就可以在别人的系统上直接运行该程序了。

所谓有得必有失,正因为动态库在程序运行时被链接,故程序的运行速度和链接静态库的版本相比必然会打折扣。然而瑕不掩瑜,动态库的不足相对于它带来的好处在现今硬件下简直是微不足道的,所以链接程序在链接时一般是优先链接动态库的,除非用-static参数指定链接静态库。

动态链接库

注意: little end 要加 -EL 编译选项.

1. 创建动态链接库

#include<stdio.h>

void hello()

{

printf("hello world/n");

}

用命令gcc -shared -fPIC hello.c -o libhello.so编译为动态库。可以看到,当前目录下多了一个文件libhello.so。

2. 再编辑一个测试文件test.c,内容如下

#include<stdio.h>

int main()

{

printf("call hello()");

hello();

}

编译 gcc test.c -lhello

-l 选项告诉编译器要使用hello这个库。奇怪的地方是动态库的名字是libhello.so,这里却使用hello,其实lhello即是去掉前面的lib和后面的.so.



但这样还不行,编译会出错。

In function `main':

test.c:(.text+0x1d): undefined reference to `hello'

collect2: ld returned 1 exit status

这是因为hello这个库在我们自己的路径中,编译器找不到。

需要使用-L选项,告诉hello库的位置

gcc test.c -lhello -L. -o test

-L .告诉编译器在当前目录中查找库文件

3. 编译成功后执行./test,

仍然出错,说找不到库, 有两种方法:

一、可以把当前路径加入 /etc/ld.so.conf中然后运行ldconfig,或者以当前路径为参数运行ldconfig(要有root权限才行)。

二、把当前路径加入环境变量LD_LIBRARY_PATH中



当然,如果你觉得不会引起混乱的话,可以直接把该库拷入/lib,/usr/lib/等位置(无可避免,这样做也要有权限),这样链接器和加载器就都可以准确的找到该库了。

我们采用第二种方法:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./

这样,再执行就成功了。



下面再讲讲静态链接库

仍使用刚才的hello.c和test.c。

1. gcc -c hello.c 注意这里没有使用-shared选项

2. 把目标文件归档 ar -r libhello.a hello.o

程序 ar 配合参数 -r 创建一个新库 libhello.a 并将命令行中列出的对象文件插入。采用这种方法,如果库不存在的话,参数 -r 将创建一个新的库,而如果库存在的话,将用新的模块替换原来的模块。

3. 在程序中链接静态库

gcc test.c -lhello -L. -static -o hello.static

或者 gcc test.c libhello.a -L. -o hello.static

生成的hello.static就不再依赖libhello.a了



两个有用的命令

file程序是用来判断文件类型的,在file命令下,所有文件都会原形毕露的。

顺便说一个技巧。有时在 windows下用浏览器下载tar.gz或tar.bz2文件,后缀名会变成奇怪的tar.tar,到Linux有些新手就不知怎么解压了。但 Linux下的文件类型并不受文件后缀名的影响,所以我们可以先用命令file xxx.tar.tar看一下文件类型,然后用tar加适当的参数解压。



另外,还可以借助程序ldd实用程序来判断。

ldd是用来打印目标程序(由命令行参数指定)所链接的所有动态库的信息的,如果目标程序没有链接动态库,则打印“not a dynamic executable”,ldd的用法请参考manpage

大型Makefile样本

CC=../mips-4.3/bin/mips-linux-gnu-gcc

LD=../mips-4.3/bin/mips-linux-gnu-ld

AR=../mips-4.3/bin/mips-linux-gnu-ar

AS=../mips-4.3/bin/mips-linux-gnu-as

STRIP=../mips-4.3/bin/mips-linux-gnu-strip

RANLIB=../mips-4.3/bin/mips-linux-gnu-ranlib

CROSS_COMPILE_PATH=../mips-4.3/bin/

CROSS_COMPILE=mips-linux-gnu-

CFLAGS= -EL -shared -fPIC -std=c99 -Wall -O3

CPPFLAGS= -I. -I../kernel/include

CPPFLAGS+= -I../gstreamer/include/gstreamer-0.10 -I../gstreamer/include/glib-2.0 \

-I../gstreamer/lib/glib-2.0/include -I../gstreamer/include/libxml2 \

-I../gstreamer/include/liboil-0.3 -I../mips-4.3/mips-linux-gnu/libc/usr/include

SOCFLAGS= -EL -fPIC -shared

SOLDLIBRARY= -L../gstreamer/lib/gstreamer-0.10 -L../gstreamer/lib -L../mips-4.3/mips-linux-gnu/libc/el/usr/lib \

-L../mips-4.3/mips-linux-gnu/libc/uclibc/el/usr/lib -L../mips-4.3/mips-linux-gnu/libc/uclibc/usr/lib \

-L../mips-4.3/mips-linux-gnu/libc/usr/lib -L../mips-4.3/lib/gcc/mips-linux-gnu/4.3.2/el \

-loil-0.3 -lgstbase-0.10 -lgstreamer-0.10 -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 \

-lrt -lz -lm -lxml2 -lglib-2.0 -lgcc

RM= rm -rf

TARGET= *.o *.so*

#####################################################################################################

TERMINAL_LIBRARY= libXXX_mpeg2video_decoder.so

subdir1= ./XXXGstLibs/

subdir2= ./XXXLibAvcodev/

subdir3= ./XXXLibAvutil/

SYSTEM_PATH= test@192.168.9.222:/pub/home/test/

#####################################################################################################

TOPOBJS= GstXXXVeDecoder.o GstXXXVePlugin.o

SUBOBJS=./XXXGstLibs/gstXXXmpegutils.o\

./XXXLibAvcodev/mpegdecsim.o\

./XXXLibAvutil/XXXavutils.o\

./XXXLibAvutil/log.o\

ALLOBJS= $(TOPOBJS)

ALLOBJS+= $(SUBOBJS)

SUBDIRS= $(subdir1) $(subdir2) $(subdir3)

#####################################################################################################

define make_subdir

@for subdir in $(SUBDIRS); do\

(cd $$subdir && make $1);\

done

endef

#####################################################################################################

PHONY_ALL:subsystem $(TERMINAL_LIBRARY)

subsystem:

$(call make_subdir)

$(TERMINAL_LIBRARY):$(ALLOBJS)

$(LD) $(SOCFLAGS) $^ -o $@ $(SOLDLIBRARY)

$(TOPOBJS):%o:%c

$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@

.PHONY:clean

clean:

$(RM) $(TARGET)

$(call make_subdir,clean)

.PHONY:fcp

fcp:

scp $(TERMINAL_LIBRARY) $(SYSTEM_PATH)

Linux环境变量的设置和查看方法

1. 显示环境变量HOME

$ echo $HOME

/home/redbooks

2. 设置一个新的环境变量hello

$ export HELLO="Hello!"

$ echo $HELLO

Hello!

3. 使用env命令显示所有的环境变量

$ env

HOSTNAME=redbooks.safe.org

PVM_RSH=/usr/bin/rsh

Shell=/bin/bash

TERM=xterm

HISTSIZE=1000

...

4. 使用set命令显示所有本地定义的Shell变量

$ set

BASH=/bin/bash

BASH_VERSINFO=([0]="2"[1]="05b"[2]="0"[3]="1"[4]="release"[5]="i386-redhat-linux-gnu")

BASH_VERSION='2.05b.0(1)-release'

COLORS=/etc/DIR_COLORS.xterm

COLUMNS=80

DIRSTACK=()

DISPLAY=:0.0

...

5. 使用unset命令来清除环境变量

set可以设置某个环境变量的值。清除环境变量的值用unset命令。如果未指定值,则该变量值将被设为NULL。示例如下:

$ export TEST="Test..." #增加一个环境变量TEST

$ env|grep TEST #此命令有输入,证明环境变量TEST已经存在了

TEST=Test...

$ unset $TEST #删除环境变量TEST

$ env|grep TEST #此命令没有输出,证明环境变量TEST已经存在了

6. 使用readonly命令设置只读变量

如果使用了readonly命令的话,变量就不可以被修改或清除了。示例如下:

$ export TEST="Test..." #增加一个环境变量TEST

$ readonly TEST #将环境变量TEST设为只读

$ unset TEST #会发现此变量不能被删除

-bash: unset: TEST: cannot unset: readonly variable

$ TEST="New" #会发现此也变量不能被修改

-bash: TEST: readonly variable

环境变量的设置位于/etc/profile文件

如果需要增加新的环境变量可以添加下属行

export path=$path:/path1:/path2:/pahtN

-----------------------------------------------------------------------------------------------------------------------

1.Linux的变量种类

按变量的生存周期来划分,Linux变量可分为两类:

1.1 永久的:需要修改配置文件,变量永久生效。

1.2 临时的:使用export命令声明即可,变量在关闭shell时失效。

2.设置变量的三种方法

2.1 在/etc/profile文件中添加变量【对所有用户生效(永久的)】

用vi在文件/etc/profile文件中增加变量,该变量将会对Linux下所有用户有效,并且是“永久的”。

例如:编辑/etc/profile文件,添加CLASSPATH变量

# vi /etc/profile

export CLASSPATH=./J***A_HOME/lib;$J***A_HOME/jre/lib

注:修改文件后要想马上生效还要运行# source /etc/profile不然只能在下次重进此用户时生效。

2.2 在用户目录下的.bash_profile文件中增加变量【对单一用户生效(永久的)】

用vi在用户目录下的.bash_profile文件中增加变量,改变量仅会对当前用户有效,并且是“永久的”。

例如:编辑guok用户目录(/home/guok)下的.bash_profile

$ vi /home/guok/.bash.profile

添加如下内容:

export CLASSPATH=./J***A_HOME/lib;$J***A_HOME/jre/lib

注:修改文件后要想马上生效还要运行$ source /home/guok/.bash_profile不然只能在下次重进此用户时生效。

2.3 直接运行export命令定义变量【只对当前shell(BASH)有效(临时的)】

在shell的命令行下直接使用[export 变量名=变量值] 定义变量,该变量只在当前的shell(BASH)或其子shell(BASH)下是有效的,shell关闭了,变量也就失效了,再打开新shell时就没有这个变量,需要使用的话还需要重新定义。

3.常用的环境变量

PATH 决定了shell将到哪些目录中寻找命令或程序

HOME 当前用户主目录

HISTSIZE 历史记录数

LOGNAME 当前用户的登录名

HOSTNAME 指主机的名称

SHELL   当前用户Shell类型

LANGUGE  语言相关的环境变量,多语言可以修改此环境变量

MAIL   当前用户的邮件存放目录

PS1    基本提示符,对于root用户是#,对于普通用户是$
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: