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

编程入门 行业动态 更新时间:2024-10-10 18:19:46

GCC 编译动态链接库和<a href=https://www.elefans.com/category/jswz/34/1771395.html style=静态链接库 + 大型工程Makefile编写 + Linux环境变量的设置和查看方法"/>

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

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
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=./JAVA_HOME/lib;$JAVA_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=./JAVA_HOME/lib;$JAVA_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用户是#,对于普通用户是$

更多推荐

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

本文发布于:2024-02-06 06:15:51,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1747344.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:静态   环境变量   链接库   动态链接库   方法

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!