??xml version="1.0" encoding="utf-8" standalone="yes"?> 在《LINUX下动态链接库的创Z?
用》一文中,我介l了LINUX动态链接库的基本知?其要Ҏ:用户Ҏ实际情况需?利用dlopen,dlsym,dlclose{动态链接库操作
函数,装入指定的动态链接库中指定的函数,然后加以执行.E序中用很的动态函数时,q样的做法尚?如果E序需要调用大量的动态函?那么采用q样?
~程手段是非常J复?所以我们必M用一U更明的办法,以减代码量,提高工作效率.q就是现在我要D例介l的《LINUX动态链接库高?
用? 3.3 动态链接库如何׃n Trackback: http://tb.donews.net/TrackBack.aspx?PostId=75642
动态链接库是一U通用的Y件组件技术,是多U操作系l中提供基本服务的方式。比如Win32内核是3个DLL文g构成。这U技术在Linux操作pȝ下也有对应的实现Q就是Linux标准对象Standard OjbectQ对应的文g扩展名ؓ.so?/font>
下面通过一个简单的例子开始介lLinux标准对象?/font>
我们的标准对象文件含有一个函敎ͼ不需要声明export导出W号Q只需要编译器讄卛_。如下:
#include
#include
void show() {
printf("Standard Object by gashero\n");
}
保存为myso.c文gQ按照如下编译:
$ gcc -fPIC -shared -o libmyso.so myso.c
执行生成一个libmyso.so文gQ按照Linux标准对象的命名惯例,应该在库名称之前加上"lib"前缀Q尽不是必ȝ。编译开?fPIC代表函数W号可以重定向,-shared代表~译l果是一个标准对象?/font>
不同于Win32DLLQLinux标准对象中的所有函数都是直接导出的Q都可以被调用程序所讉K。下面我们编写调用程序:
#include
int main() {
printf("Invoke my so\n");
show();
return 0;
}
保存为invoke.cQ按照如下gcc开关编译:
$ gcc -o test invoke.c ./libmyso.so
~译生成test可执行文件。如上编译条件的最后一条需要是所调用的标准对象文件名Q注意必d有\径。如果只是用libmyso.soQ则必须保q个文g在可讉K的PATH下面。本例所使用的文件名"./libmyso.so"是当前\径下的,使用了相对\径?/font>
如下试l果Q?/font>
$ ./test
Invoke my so
Standard Object by gashero
?本文丄例子cM上篇文章,只是文g的内容已做相应修?裁减了不?CZE序ady.c和两个动态函数的源程序getdate.c与gettime.c仅修改了头文件的名字,其内容不再列?本文使用头文件ؓadatetime.h.
要想高效地应用LINUX动态链接库(其是用戯q写的),需要做以下工作:
一、编写合格的动态链接库头文?
C语言的头文g,可供一个或多个E序引用,里面一般定义程序所需的常?自定义类型及函数原型说明{?其中的函数原型说?则供~译器检查语?用于排除引用参数时类型不一致的错误.只有~写合格的动态链接库头文?E序员才能正用动态链接库内的函数.
动态链接库头文件要采用C语言标准格式,其中的动态函数原型定?不必象上文介l的那样?*动态函数名)的描qŞ?L下面的例?(每行开始的数字为所在行行号,为笔者添?供注解?
1 /* adatetime.h : U|软g制作中心雨亦?zhsoft@371.net)~写, 2002-03-06. */
2
3 #ifndef __DATETIME_H
4
5 #define __DATETIME_H
6
7 /* 日期l构 */
8 typedef struct
9 {
10 int year;
11 int mon;
12 int day;
13 }DATETYPE;
14
15 /* 旉l构 */
16 typedef struct
17 {
18 char hour;
19 char min;
20 char sec;
21 }TIMETYPE;
22
23 int getdate(DATETYPE *d); /* 取当前日?*/
24 int gettime(TIMETYPE *t); /* 取当前时?*/
25
26 #endif
27
?与上文的datetime.h文g比较,从该头文件第23,24行可以看?动态函数getdate,gettime的原型定义改变了,不再使用(*getdate),(*gettime)的格式了(q种格式使用较ؓ|嗦).
二、正编译与命名动态链接库
Z让GCC~译器生成动态链接库,~译旉加选项-shared.(q点ȝ?
LINUXpȝ?Z让动态链接库能被pȝ中其它程序共?其名字应W合“lib*.so*”这U格?如果某个动态链接库不符合此格式,则LINUX的动态链接库自动装入E序(ld.so)搜索不到此链接?其它E序也无法共享之.
格式?W一?通常表示为简写的库名,W二?通常表示库的版本??在我的系l中,基本C动态链接库的名字ؓ
libc.so.6,U程
pthread动态链接库的名字ؓlibpthread.so.0{等.本文例子所生成的动态链接库的名字ؓlibmy.so,虽没有版本号,但也W合所
要求的格?
生成该动态链接库的维护文件makefile-lib内容如下:
1 # makefile : U|软g制作中心雨亦奇编? 2002-03-07.
2
3 all : libmy.so
4
5 SRC = getdate.c gettime.c
6
7 TGT = $(SRC:.c=.o)
8
9 $(SRC) : adatetime.h
10 @touch $@
11
12 %.o : %.c
13 cc -c $?
14
15 # 动态链接库(libmy.so)生成
16 libmy.so : $(TGT)
17 cc -s -shared -o $@ $(TGT)
18
q行命o:
$ make -f makefile-lib
$
即生成libmy.so?
? l护文g?W?7行用-shared选项以生成动态链接库,?s选项以去掉目标文件中的符可,从而减文仉?
三、共享动态链接库
3.1 动态链接库配置文g
Z让动态链接库为系l所使用,需要维护动态链接库的配|文?-/etc/ld.so.conf.此文件内,存放着可被LINUX׃n
的动态链接库所在目录的名字(pȝ目录/lib,/usr/lib除外),各个目录名间以空白字W?I格,换行{?或冒h逗号分隔.一般的LINUX?
行版?此文件均含一个共享目?usr/X11R6/lib,为X windowH口pȝ的动态链接库所在的目录.
下面看看我的pȝ中此文g的内容如?
# cat /etc/ld.so.conf
/usr/X11R6/lib
/usr/zzz/lib
#
׃可以看出,该动态库配置文g?增加了一?usr/zzz/lib目录.q是我自己新建的׃n库目?下面存放我新开发的可供pȝ׃n的动态链接库.
3.2 动态链接库理命o
Z让动态链接库为系l所׃n,q需q行动态链接库的管理命?-ldconfig.此执行程序存攑֜/sbin目录?
ldconfig命o的用?主要是在默认搜寻目录(/lib?usr/lib)以及动态库配置文g/etc/ld.so.conf?
所列的目录?搜烦出可׃n的动态链接库(格式如前介绍,lib*.so*),q而创建出动态装入程?ld.so)所需的连接和~存文g.~存文g默认
?/etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表.
ldconfig通常在系l启动时q行,而当用户安装了一个新的动态链接库?需要手工运行这个命?
ldconfig命o行用法如?
ldconfig [-v|--verbose] [-n] [-N] [-X] [-f CONF] [-C CACHE] [-r
ROOT] [-l] [-p|--print-cache] [-c FORMAT] [--format=FORMAT] [-V]
[-?|--help|--usage] path...
ldconfig可用的选项说明如下:
(1) -v?-verbose : 用此选项?ldconfig显C正在扫描的目录及搜索到的动态链接库,q有它所创徏的连接的名字.
(2) -n : 用此选项?ldconfig仅扫描命令行指定的目?不扫描默认目?/lib,/usr/lib),也不扫描配置文g/etc/ld.so.conf所列的目录.
(3) -N : 此选项指示ldconfig不重建缓存文?/etc/ld.so.cache).若未?X选项,ldconfig照常更新文g的连?
(4) -X : 此选项指示ldconfig不更新文件的q接.若未?N选项,则缓存文件正常更?
(5) -f CONF : 此选项指定动态链接库的配|文件ؓCONF,pȝ默认?etc/ld.so.conf.
(6) -C CACHE : 此选项指定生成的缓存文件ؓCACHE,pȝ默认的是/etc/ld.so.cache,此文件存攑ַ排好序的可共享的动态链接库的列?
(7) -r ROOT :
此选项改变应用E序的根目录为ROOT(是调用chroot函数实现?.选择此项?pȝ默认的配|文?etc/ld.so.conf,实际对应的ؓ
ROOT/etc/ld.so.conf.如用-r
/usr/zzz?打开配置文g/etc/ld.so.conf?实际打开的是/usr/zzz/etc/ld.so.conf文g.用此选项,可以
大大增加动态链接库理的灵zL?
(8) -l : 通常情况?ldconfig搜烦动态链接库时将自动建立动态链接库的连?选择此项?进入专家模?需要手工设|连?一般用户不用此?
(9) -p?-print-cache : 此选项指示ldconfig打印出当前缓存文件所保存的所有共享库的名?
(10) -c FORMAT ?--format=FORMAT : 此选项用于指定~存文g所使用的格?共有三种:old(老格?,new(新格?和compat(兼容格式,此ؓ默认格式).
(11) -V : 此选项打印出ldconfig的版本信?而后退?
(12) -? ?--help ?--usage : q三个选项作用相同,都是让ldconfig打印出其帮助信息,而后退?
举三个例?
?:
# ldconfig -p
793 libs found in cache `/etc/ld.so.cache'
libzvt.so.2 (libc6) => /usr/lib/libzvt.so.2
libzvt.so (libc6) => /usr/lib/libzvt.so
libz.so.1.1.3 (libc6) => /usr/lib/libz.so.1.1.3
libz.so.1 (libc6) => /lib/libz.so.1
......
#
?
有时候用h知道pȝ中有哪些动态链接库,或者想知道pȝ中有没有某个动态链接库,q时,可用-p选项让ldconfig输出~存文g中的动态链接库?
?从而查询得?例子?ldconfig命o的输出结果第1行表明在~存文g/etc/ld.so.cache中找?93个共享库,W?行开始便?
一pd׃n库的名字及其全名(l对路径).因ؓ实际输出l果太多,省篇q??.....表示省略的部?
?:
# ldconfig -v
/lib:
liby.so.1 -> liby.so.1
libnss_wins.so -> libnss_wins.so
......
/usr/lib:
libjscript.so.2 -> libjscript.so.2.0.0
libkspell.so.2 -> libkspell.so.2.0.0
......
/usr/X11R6/lib:
libmej-0.8.10.so -> libmej-0.8.10.so
libXaw3d.so.7 -> libXaw3d.so.7.0
......
#
?
ldconfig命o在运行正常的情况?默认不输Z么东?本例中用?v选项,以ldconfig在运行时输出正在扫描的目录及搜烦到的׃n?
用户可以清楚地看到运行的l果.执行l束?ldconfig刷新缓存文?etc/ld.so.cache.
?:
# ldconfig /usr/zhsoft/lib
#
? 当用户在某个目录下面创徏或拷贝了一个动态链接库,若想使其被系l共?可以执行一?ldconfig
目录?q个命o.此命令的功能在于让ldconfig指定目录下的动态链接库被系l共享v?意即:在缓存文?etc/ld.so.cache中追
加进指定目录下的׃n?本例让系l共享了/usr/zhsoft/lib目录下的动态链接库.需要说明的?如果此目录不?lib,/usr/lib
?etc/ld.so.conf文g所列的目录里面,则再度运行ldconfig?此目录下的动态链接库可能不被pȝ׃n?
了解了以上知?我们可以采用以下三种Ҏ来共享动态链接库:(?均须在超U用L态下操作,以我的动态链接库libmy.so׃nq程Z)
(1)拯动态链接库到系l共享目录下,或在pȝ׃n目录下ؓ该动态链接库建立个连?连接或W号q接均可,常用W号q接).q里说的
pȝ׃n目录,指的是LINUX动态链接库存放的目?它包?lib,/usr/lib以及/etc/ld.so.conf文g内所列的一pd目录.
# cp libmy.so /lib
# ldconfig
#
?
# ln -s `pwd`/libmy.so /lib
# ldconfig
#
(2)动态链接库所在目录名q加到动态链接库配置文g/etc/ld.so.conf?
# pwd >> /etc/ld.so.conf
# ldconfig
#
(3)利用动态链接库理命oldconfig,强制其搜索指定目?q更新缓存文?便于动态装?
# ldconfig `pwd`
#
需要说明的?q种操作Ҏ虽然有效,但效果是暂时?供程序测试还可以,一旦再度运行ldconfig,则缓存文件内容可能改?所需
的动态链接库可能不被pȝ׃n?与之相比?前两U方法是可靠的方?值得业已定型的动态链接库׃n旉?前两U方法还有一个特?x后一条命令都
?ldconfig,也即均需要更C下缓存文?以确保动态链接库的共享生?
四、含有动态函数的E序的编?
4.1 防止~译因未指定动态链接库而出?
当一个程序用动态函数时,~译该程序时必L定含所用动态函数的动态链接库,否则~译会出错退?如本文示例程序ady.c的编?未明引用动态链接库libmy.so):
# cc -o ady ady.c
/tmp/ccL4FsJp.o: In function `main':
/tmp/ccL4FsJp.o(.text+0x43): undefined reference to `gettime'
collect2: ld returned 1 exit status
#
? 因ؓady.c所含的动态函数getdate,gettime不在pȝ函数库中,所以连接时出错.
4.2 ~译时引用动态链接库的几U方?
(1)当所用的动态链接库在系l目?/lib,/usr/lib)下时,可用~译选项-l来引??
# cc -lmy -o ady ady.c
#
?~译时用-l选项引用动态链接库?库名M用其~写形式.本例的my,表示引用libmy.so?若引用光标库libncurses.so,ȝ-lncurses.注意,-l选项与参C间不能有I格,否则会出?
(2)当所用的动态链接库在系l目?/lib,/usr/lib)以外的目录时,ȝ~译选项-L来指定动态链接库所在的目录(供编译器查找?,同时?l选项指定~写的动态链接库??
# cc -L/usr/zzz/lib -lmy -o ady ady.c
#
(3)直接引用所需的动态链接库.?
# cc -o ady ady.c libmy.so
#
?
# cc -o ady ady.c /lib/libmy.so
#
{等.其中,动态链接库的库名可以采用相对\径Ş?文g名不?开?,也可采用l对路径形式(文g名以/开?.
五、动态链接程序的q行与检?
5.1 q行
~译q接好含动态函数的E序?可以运行它?动态链接程序因为共享了pȝ中的动态链接库,所以其I间占用很小.但这q不意味功能的减?它的执行与静态连接的E序执行,效果完全相同.在命令提C符下键入程序名及相兛_数后回R卛_,如下?
$ ady
动态链接库高应用C
当前日期: 2002-03-11
当前旉: 19:39:06
$
5.2 ?
查什?查动态链接程序究竟需要哪些共享库,pȝ中是否已有这些库,没有的话,用户好想办法把这些库装上.
怎么查呢?q里,告诉你一个实用程?-ldd,q个E序是专门用来查动态链接程序依赖哪些共享库?
ldd命o行用法如?
ldd [--version] [-v|--verbose] [-d|--data-relocs] [-r|--function-relocs] [--help] FILE...
各选项说明如下:
(1) --version : 此选项用于打印出ldd的版本号.
(2) -v ?--verbose : 此选项指示ldd输出关于所依赖的动态链接库的尽可能详细的信?
(3) -d ?--data-relocs : 此选项执行重定?q且昄不存在的函数.
(4) -r ?--function-relocs : 此选项执行数据对象与函数的重定?同时报告不存在的对象.
(5) --help : 此选项用于打印出ldd的帮助信?
? 上述选项?常用-v(?-verbose)选项.
ldd的命令行参数为FILE...,即一个或多个文g?动态链接程序或动态链接库).
?:
$ ldd ady
libmy.so => ./libmy.so (0x40026000)
libc.so.6 => /lib/libc.so.6 (0x40028000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
$
?
每行=>前面?为动态链接程序所需的动态链接库的名??>后面?则是q行时系l实际调用的动态链接库的名?所需的动态链接库在系l?
中不存在?=>后面显C?not
found",括号所括的数字拟的执行地址.本例列出ady所需的三个动态链接库,其中libmy.so己新建的动态链接库,?
libc.so.6?lib/ld-linux.so.2均ؓpȝ的动态链接库,前一个ؓ基本C?后一个动态装入库(用于动态链接库的装入及q行).
?:
$ ldd -v ady
libmy.so => ./libmy.so (0x40026000)
libc.so.6 => /lib/libc.so.6 (0x40028000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
Version information:
./ady:
libc.so.6 (GLIBC_2.1.3) => /lib/libc.so.6
libc.so.6 (GLIBC_2.0) => /lib/libc.so.6
./libmy.so:
libc.so.6 (GLIBC_2.1.3) => /lib/libc.so.6
libc.so.6 (GLIBC_2.0) => /lib/libc.so.6
/lib/libc.so.6:
ld-linux.so.2 (GLIBC_2.1.1) => /lib/ld-linux.so.2
ld-linux.so.2 (GLIBC_2.2.3) => /lib/ld-linux.so.2
ld-linux.so.2 (GLIBC_2.1) => /lib/ld-linux.so.2
ld-linux.so.2 (GLIBC_2.2) => /lib/ld-linux.so.2
ld-linux.so.2 (GLIBC_2.0) => /lib/ld-linux.so.2
$
?本例?v选项以显C尽可能多的信息,所以例中除列出ady所需要的动态链接库?q列ZE序所需动态链接库版本斚w的信?
结:
在LINUX动态链接库的高U应用中,关键有两?一是如何让动态链接库为LINUXpȝ所׃n,二是~译q接E序时如何做.让动态链接库为系l所׃n,
主要是用ldconfig理命o,l护好系l共享库的缓存文?etc/ld.so.cache.~译q接时如何做?注意q接上所用的动态链接库可?
?LINUX动态链接库的高U应?用一用就明白:其实,是q么?
点击q里下蝲CZE序?
]]>
int main(int argc,char **argv)
{
printf("Hello Linux\n");
}
要编译这个程?我们只要在命令行下执?
gcc -o hello hello.c
gcc ~译器就会ؓ我们生成一个hello的可执行文g.执行./hello可以看到程序的输出l果?命o行中
gcc表示我们是用gcc来编译我们的源程?-o 选项表示我们要求~译器给我们输出的可执行文g名ؓhello
而hello.c是我们的源程序文?
gcc~译器有许多选项,一般来说我们只要知道其中的几个够?
-o选项我们已经知道?表示我们要求输出的可执行文g? -c选项表示我们只要求编译器输出目标代码,而不必要输出可执行文?
-g选项表示我们要求~译器在~译的时候提供我们以后对E序q行调试的信?
知道了这三个选项,我们可以编译我们自己所写的单的源程序了,如果你想要知道更多的选项,可以查看gcc的帮助文?那里有着许多对其它选项的详l说?
2.Makefile的编?
假设我们有下面这L一个程?源代码如?
/* main.c */
#include "mytool1.h"
#include "mytool2.h"
int main(int argc,char **argv)
{
mytool1_print("hello");
mytool2_print("hello");
}
/* mytool1.h */
#ifndef _MYTOOL_1_H
#define _MYTOOL_1_H
void mytool1_print(char *print_str);
#endif
/* mytool1.c */
#include "mytool1.h"
void mytool1_print(char *print_str)
{
printf("This is mytool1 print %s\n",print_str);
}
/* mytool2.h */
#ifndef _MYTOOL_2_H
#define _MYTOOL_2_H
void mytool2_print(char *print_str);
#endif
/* mytool2.c */
#include "mytool2.h"
void mytool2_print(char *print_str)
{
printf("This is mytool2 print %s\n",print_str);
}
当然׃q个E序是很短的我们可以q样来编?
gcc -c main.c
gcc -c mytool1.c
gcc -c mytool2.c
gcc -o main main.o mytool1.o mytool2.o
q?
L话我们也可以产生mainE序,而且也不时很ȝ.但是如果我们考虑一下如果有一天我们修改了其中的一个文?比如说mytool1.c)那么我们?
道还要重新输入上面的命o?也许你会?q个很容易解军_,我写一个SHELL脚本,让她帮我d成不可以了.是的对于q个E序来说,是可以vC?
?但是当我们把事情想的更复杂一?如果我们的程序有几百个源E序的时?N也要~译器重C个一个的ȝ?
为此,聪明的程序员们想?
了一个很好的工具来做qg事情,q就是make.我们只要执行以下make,可以把上面的问题解x.在我们执行make之前,我们要先~写一个非帔R
要的文g.--Makefile.对于上面的那个程序来?可能的一个Makefile的文件是:
# q是上面那个E序的Makefile文g
main:main.o mytool1.o mytool2.o
gcc -o main main.o mytool1.o mytool2.o
main.o:main.c mytool1.h mytool2.h
gcc -c main.c
mytool1.o:mytool1.c mytool1.h
gcc -c mytool1.c
mytool2.o:mytool2.c mytool2.h
gcc -c mytool2.c
有了q个Makefile文g,不过我们什么时候修改了源程序当中的什么文?我们只要执行make命o,我们的编译器都只会去~译和我们修改的文g有关的文?其它的文件她q理都不惛_理的.
下面我们学习Makefile是如何编写的.
在Makefile中也#开始的行都是注释行.Makefile中最重要的是描述文g的依赖关pȝ说明.一般的格式?
target: components
TAB rule
W一行表C的是依赖关p?W二行是规则.
比如说我们上面的那个Makefile文g的第二行
main:main.o mytool1.o mytool2.o
?C我们的目标(target)main的依赖对?components)是main.o mytool1.o mytool2.o
当倚赖的对象在目标修改后修改的?pL行规则一行所指定的命?p我们的上面那个MakefileW三行所说的一栯执行 gcc -o
main main.o mytool1.o mytool2.o 注意规则一行中的TAB表示那里是一个TAB?
Makefile有三个非常有用的变量.分别?@,$^,$<代表的意义分别是:
$@--目标文g,$^--所有的依赖文g,$<--W一个依赖文?
如果我们使用上面三个变量,那么我们可以化我们的Makefile文g?
# q是化后的Makefile
main:main.o mytool1.o mytool2.o
gcc -o $@ $^
main.o:main.c mytool1.h mytool2.h
gcc -c $<
mytool1.o:mytool1.c mytool1.h
gcc -c $<
mytool2.o:mytool2.c mytool2.h
gcc -c $<
l过化后我们的Makefile是简单了一?不过Z有时候还想简单一?q里我们学习一个Makefile的缺省规?
.c.o:
gcc -c $<
q个规则表示所有的 .o文g都是依赖与相应的.c文g?例如mytool.o依赖于mytool.cq样Makefileq可以变?
# q是再一ơ简化后的Makefile
main:main.o mytool1.o mytool2.o
gcc -o $@ $^
.c.o:
gcc -c $<
好了,我们的Makefile 也差不多?如果想知道更多的关于Makefile规则可以查看相应的文?
3.E序库的链接
试着~译下面q个E序
/* temp.c */
#include
int main(int argc,char **argv)
{
double value;
printf("value:%f\n",value);
}
q个E序相当?但是当我们用 gcc -o temp temp.c ~译时会出现下面所C的错误.
/tmp/cc33Kydu.o: In function `main:
/tmp/cc33Kydu.o(.text+0xe): undefined reference to `log
collect2: ld returned 1 exit status
?
现这个错误是因ؓ~译器找不到log的具体实?虽然我们包括了正的头文?但是我们在编译的时候还是要q接定的库.在Linux?Z使用数学?
?我们必须和数学库q接,为此我们要加?-lm 选项. gcc -o temp temp.c
-lmq样才能够正的~译.也许有h要问,前面我们用printf函数的时候怎么没有q接库呢?是这L,对于一些常用的函数的实?gcc~译器会?
动去q接一些常用库,q样我们没有必要自己去指定? 有时候我们在~译E序的时候还要指定库的\?q个时候我们要用到~译器的
-L选项指定路径.比如说我们有一个库?/home/hoyt/mylib?q样我们~译的时候还要加?
-L/home/hoyt/mylib.对于一些标准库来说,我们没有必要指出路径.只要它们在v~省库的路径下就可以?pȝ的缺省库的\?lib
/usr/lib /usr/local/lib 在这三个路径下面的库,我们可以不指定\?
q有一个问?有时候我们用了某个函数,?
是我们不知道库的名字,q个时候怎么办呢?很抱?对于q个问题我也不知道答?我只有一个傻办法.首先,我到标准库\径下面去扄看有没有和我用的函数
相关的库,我就q样扑ֈ了线E?thread)函数的库文g(libpthread.a).
当然,如果找不?只有一个笨Ҏ.比如我要找sinq个函数所在的? 只好用 nm -o /lib/*.so|grep
sin>~/sin 命o,然后看~/sin文g,到那里面L?
在sin文g当中,我会扑ֈq样的一行libm-2.1.2.so:00009fa0 W sin q样我就知道了sin?
libm-2.1.2.so库里?我用 -lm选项可以了(L前面的lib和后面的版本标志,剩下m了所以是 -lm).
如果你知道怎么?误快告诉我,我回非常感激?谢谢!
4.E序的调?
我们~写的程序不太可能一ơ性就会成功的,在我们的E序当中,会出现许许多多我们想不到的错?q个时候我们就要对我们的程序进行调试了.
最 常用的调试Y件是gdb.如果你想在图形界面下调试E序,那么你现在可以选择xxgdb.记得要在~译的时候加?
-g选项.关于gdb的用可以看gdb的帮助文?׃我没有用q这个Y?所以我也不能够说出如何使用.
不过我不喜欢用gdb.跟踪一个程序是很烦的事?我一般用在程序当中输Z间变量的值来调试E序?当然你可以选择自己的办?没有必要d别h??
在有了许多IDE环境,里面已经自己带了调试器了.你可以选择几个试一试找己喜Ƣ的一个用.
5.头文件和pȝ求助
有时候我们只知道一个函数的大概形式,不记得确切的表达?或者是不记得着函数在那个头文gq行了说?q个时候我们可以求助系l?
?如说我们想知道freadq个函数的确切Ş?我们只要执行 man fread
pȝ׃输出着函数的详l解释的.和这个函数所在的头文件说明了. 如果我们要writeq个函数的说?当我们执行man
write?输出的结果却不是我们所需要的.
因ؓ我们要的是writeq个函数的说?可是出来的却是writeq个命o的说?Z得到write的函数说明我们要?man 2 write.
2表示我们用的writeq个函数是系l调用函?q有一个我们常用的?表示函数是C的库函数.
C不管什么时?man都是我们的最好助?
----------------------------------------------------------------
好了,q一章就讲这么多?有了q些知识我们可以进入激动h心的Linux下的CE序探险zd.
]]>
]]>
下面通过一个例子来介绍如何生成一个动态库。这里有一个头文gQso_test.hQ三?c文gQtest_a.c、test_b.c、test_c.cQ我们将q几个文件编译成一个动态库Qlibtest.so?br />
so_test.hQ?br />
#include
#include
void test_a();
void test_b();
void test_c();
test_a.cQ?br />
#include "so_test.h"
void test_a()
{
printf("this is in test_a...\n");
}
test_b.cQ?br />
#include "so_test.h"
void test_b()
{
printf("this is in test_b...\n");
}
test_a.cQ?br />
#include "so_test.h"
void test_c()
{
printf("this is in test_c...\n");
}
这几个文g~译成一个动态库Qlibtest.so
$ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so
2、动态库的链?br />
?、中Q我们已l成功生成了一个自q动态链接库libtest.soQ下面我们通过一个程序来调用q个库里的函数。程序的源文件ؓQtest.c?br />
test.cQ?br />
#include "so_test.h"
int main()
{
test_a();
test_b();
test_c();
return 0;
}
l test.c与动态库libtest.so链接生成执行文gtestQ?br />
$ gcc test.c -L. -ltest -o test
l 试是否动态连接,如果列出libtest.soQ那么应该是q接正常?br />
$ ldd test
l 执行testQ可以看到它是如何调用动态库中的函数的?br />3、编译参数解?br />最主要的是GCC命o行的一个选项:
-shared 该选项指定生成动态连接库Q让q接器生成Tcd的导出符可Q有时候也生成p接Wcd的导出符PQ不用该标志外部E序无法q接。相当于一个可执行文g
l -fPICQ表C编译ؓ位置独立的代码,不用此选项的话~译后的代码是位|相关的所以动态蝲入时是通过代码拯的方式来满不同q程的需要,而不能达到真正代码段׃n的目的?br />
l -L.Q表Cq接的库在当前目录中
l -ltestQ编译器查找动态连接库时有隐含的命名规则,卛_l出的名字前面加上libQ后面加?so来确定库的名U?br />
l LD_LIBRARY_PATHQ这个环境变量指C动态连接器可以装蝲动态库的\径?br />
l 当然如果有root权限的话Q可以修?etc/ld.so.conf文gQ然后调?/sbin/ldconfig来达到同L目的Q不q如果没有root权限Q那么只能采用输出LD_LIBRARY_PATH的方法了?br />4、注?br />
调用动态库的时候有几个问题会经常碰刎ͼ有时Q明明已l将库的头文件所在目?通过 ?I?includeq来了,库所在文仉过
?L”参数引|q指定了?l”的库名Q但通过ldd命o察看Ӟ是L找不C指定链接的so文gQ这时你要作的就是通过修改
LD_LIBRARY_PATH或?etc/ld.so.conf文g来指定动态库的目录。通常q样做就可以解决库无法链接的问题了?br />
- 作者: 像风一h?讉Kl计Q?1 2005q?0?1? 星期?17:08 加入博采
]]>
{案是肯定的QLINUX的动态链接库不仅有,而且为数不少。在/lib目录下,有许多?so作后~的文Ӟq就是LINUXpȝ应用的动态链接库Q?
只不q与WINDOWS叫法不同Q它叫soQ即Shared ObjectQ共享对象?在LINUX下,静态函数库是以.a作后~?
X-WINDOW作ؓLINUX下的标准囑ŞH口界面Q它本n采用了很多的动态链接库(?usr/X11R6/lib目录?Q以方便E序间的׃nQ?
节省占用I间。著名的APACHE|页服务器,也采用了动态链接库Q以便扩充程序功能。你只需PHP动态链接库拷到其共享目录,修改一下配|,
APACHE可以支持PHP|页了。如果你愿意Q可以自q写动态链接库Q让APACHE支持你自己定义的|页格式。这是动态链接的好处?/td> 1、LINUX下动态链接库的创?/b>
在LINUXpȝ下,创徏动态链接库是g再简单不q的事情。只要在~译函数库源E序时加?shared选项卛_Q这h生成的执行程序即为动态链接库。从某种意义上来_动态链接库也是一U执行程序。按一般规则,E序名应?so后缀。下面D个例子说说?/td>
我准备编写两个函敎ͼ一个用于查询当前日期getdateQ一个用于查询当前时间gettimeQƈ这两个函数存于动态链接库my.so中。ؓ此,需要做以下几项工作?/td>
1.1 ~写用户接口文gdatetime.hQ内容如?每行前面的数字ؓ行号)Q?/td>
----------------------------------------------------------------------
1 /* datetime.h : U|软g制作中心雨亦奇编? 2001-06-28. */
2
3 #ifndef __DATETIME_H
4
5 #define __DATETIME_H
6
7 /* 日期l构 */
8 typedef struct
9 {
10 int year;
11 int mon;
12 int day;
13 }DATETYPE;
14
15 /* 旉l构 */
16 typedef struct
17 {
18 char hour;
19 char min;
20 char sec;
21 }TIMETYPE;
22
23 /* 函数原型说明 */
24
25 #ifdef SHARED
26 int (*getdate)(DATETYPE *d);
27 #else
28 int getdate(DATETYPE *d);
29 #endif
30
31 #ifdef SHARED
32 int (*gettime)(TIMETYPE *t);
33 #else
34 int gettime(TIMETYPE *t);
35 #endif
36
37 #endif
38
----------------------------------------------------------------------
q个用户接口文g中,先定义了日期与时间结构,接着定义一下函数的原型。动态函C静态函数的原型说明不同的是Q动态函数应使用(*函数?的Ş式,以便引用其指针。若要引用文件中的动态函数说明,用户应该定义一下SHARED宏,q样才能使用?/td>
1.2 ~写getdate.cQ源E序如下Q?/td>
----------------------------------------------------------------------
1 /* getdate.c : U|软g制作中心雨亦奇编? 2001-06-28. */
2
3 #include "time.h"
4 #include "datetime.h"
5
6 int getdate(DATETYPE *d)
7 {
8 long ti;
9 struct tm *tm;
10
11 time(&ti);
12 tm=localtime(&ti);
13 d->year=tm->tm_year+1900;
14 d->mon=tm->tm_mon+1;
15 d->day=tm->tm_mday;
16 }
17
----------------------------------------------------------------------
在getdate函数中,先调用time取得以秒计的pȝ旉Q再用localtime函数转换一下时间结构,最后调整得到正的日期?/td>
1.3 ~写gettime.cQ源E序如下Q?/td>
----------------------------------------------------------------------
1 /* gettime.c : U|软g制作中心雨亦奇编? 2001-06-28. */
2
3 #include "time.h"
4 #include "datetime.h"
5
6 int gettime(TIMETYPE *t)
7 {
8 long ti;
9 struct tm *tm;
10
11 time(&ti);
12 tm=localtime(&ti);
13 t->hour=tm->tm_hour;
14 t->min=tm->tm_min;
15 t->sec=tm->tm_sec;
16 }
17
----------------------------------------------------------------------
gettime函数与getdate函数总Q先用time函数取得以秒计的pȝ旉Q再用localtime函数转换一下时间结构,最后返回当前的旉(不需调整)?/td>
1.4 ~写l护文gmakefile-libQ内容如下:
----------------------------------------------------------------------
1 # makefile-lib : U|软g制作中心雨亦奇编? 2001-06-28.
2
3 all : my.so
4
5 SRC = getdate.c gettime.c
6
7 TGT = $(SRC:.c=.o)
8
9 $(SRC) : datetime.h
10 @touch $@
11
12 %.o : %.c
13 cc -c $?
14
15 # 动态函数库(my.so)生成
16 my.so : $(TGT)
17 cc -shared -o $@ $(TGT)
18
----------------------------------------------------------------------
~写l护文g的目的,在于方便E序员维护程序,其是维护比较大的工E项目。一个素质良好的E序员应该学会熟l地~写l护文gmakefile。定义了?
仉的依赖关pdQ一旦源文g发生变化Q仅需make一下,其目标文件维护代码会自动执行Q从而自动更新目标文Ӟ减少了许多工作量。注?
每行l护代码必须以TAB(x?开始,不是的话make时将出错?/td>
本维护文件第1行是注释行,?号开_文gW?行定义所有需要维护的函数库;W?行定义相xE序文gQ第7行定义目标文ӞW?-10行说明所有源E?
序依赖于datetime.h头文Ӟq有相应l护代码Q即touch一下,更新一下源文g的时_W?2-13行定?o文g依赖于相应的.c文gQƈ
指定了维护代码,即用cc~译一下;W?6-17行定义共享库my.so依赖的目标文Ӟl护代码中用-shared~译选项Q以生成动态链接库
my.so?/td>
1.5 q行make -f makefile-lib 命o
makeq行后,动态链接库my.so׃生了Q我们就可以在程序中调用了。如果想让系l所有用户都可以使用Q则应以root用户dpȝQ将q个库拷?
?lib目录?命oQcp my.so /lib)Q或者在/lib目录下徏个符可接即?命oQln -s `pwd`/my.so
/lib)?/td> 2、LINUX下动态链接库的?/b>
2.1 重要的dlfcn.h头文?/td>
LINUX下用动态链接库Q源E序需要包含dlfcn.h头文Ӟ此文件定义了调用动态链接库的函数的原型。下面详l说明一下这些函数?/td>
2.1.1 dlerror
原型? const char *dlerror(void);
当动态链接库操作函数执行p|Ӟdlerror可以q回出错信息Q返回gؓNULL时表C操作函数执行成功?/td>
2.1.2 dlopen
原型? void *dlopen (const char *filename, int flag);
dlopen用于打开指定名字(filename)的动态链接库Qƈq回操作句柄?/td>
filename: 如果名字不以/开_则非l对路径名,按下列先后序查找该文件?/td>
(1) 用户环境变量中的LD_LIBRARY|
(2) 动态链接缓冲文?etc/ld.so.cache
(3) 目录/libQ?usr/lib
flag表示在什么时候解x定义的符?调用)。取值有两个:
1) RTLD_LAZY : 表明在动态链接库的函C码执行时解决?/td>
2) RTLD_NOW : 表明在dlopenq回前就解决所有未定义的符P一旦未解决Qdlopen返回错误?/td>
dlopen调用p|Ӟ返回NULL|否则q回的是操作句柄?/td>
2.1.3 dlsym : 取函数执行地址
原型? void *dlsym(void *handle, char *symbol);
dlsymҎ动态链接库操作句柄(handle)与符?symbol)Q返回符号对应的函数的执行代码地址。由此地址Q可以带参数执行相应的函数?/td>
如程序代? void (*add)(int x,int y); /* 说明一下要调用的动态函数add */
add=dlsym("xxx.so","add"); /* 打开xxx.so׃n?取add函数地址 */
add(89,369); /* 带两个参?9?69调用add函数 */
2.1.4 dlclose : 关闭动态链接库
原型? int dlclose (void *handle);
dlclose用于关闭指定句柄的动态链接库Q只有当此动态链接库的用计Cؓ0?才会真正被系l卸载?/td>
2.2 在程序中使用动态链接库函数
2.2.1 E序范例
下面的程序装载了动态链接库my.soQƈ用getdate,gettime取得当前日期与时间后输出?/td>
----------------------------------------------------------------------
1 /************************************/
2 /* 文g名称: dy.c */
3 /* 功能描述: 动态链接库应用CE序 */
4 /* E序~写: U|软g制作中心雨亦?*/
5 /* ~写旉: 2001-06-28 */
6 /************************************/
7
8 #include "stdio.h" /* 包含标准输入输出文g */
9
10 #include "dlfcn.h" /* 包含动态链接功能接口文?*/
11 #define SOFILE "./my.so" /* 指定动态链接库名称 */
12
13 #define SHARED /* 定义?认׃n,以便引用动态函?*/
14 #include "datetime.h" /* 包含用户接口文g */
15
16 main()
17 {
18 DATETYPE d;
19 TIMETYPE t;
20 void *dp;
21 char *error;
22
23 puts("动态链接库应用C");
24
25 dp=dlopen(SOFILE,RTLD_LAZY); /* 打开动态链接库 */
26
27 if (dp==NULL) /* 若打开p|则退?*/
28 {
29 fputs(dlerror(),stderr);
30 exit(1);
31 }
32
33 getdate=dlsym(dp,"getdate"); /* 定位取日期函?*/
34
35 error=dlerror(); /* 错?*/
36 if (error) /* 若出错则退?*/
37 {
38 fputs(error,stderr);
39 exit(1);
40 }
41
42 getdate(&d); /* 调用此共享函?*/
43 printf("当前日期: %04d-%02d-%02d\n",d.year,d.mon,d.day);
44
45 gettime=dlsym(dp,"gettime"); /* 定位取时间函?*/
46
47 error=dlerror(); /* 错?*/
48 if (error) /* 若出错则退?*/
49 {
50 fputs(error,stderr);
51 exit(1);
52 }
53
54 gettime(&t); /* 调用此共享函?*/
55 printf("当前旉: %02d:%02d:%02d\n",t.hour,t.min,t.sec);
56
57 dlclose(dp); /* 关闭׃n?*/
58
59 exit(0); /* 成功q回 */
60
61 }
----------------------------------------------------------------------
E序说明:
W?? 包含标准输入输出头文?因ؓE序中用了printf,puts,fputs{标准输入输出函?需要让~译器根据头文g中函数的原型,查一下语?
W?0-11? 包含动态链接库功能头文?q定义动态链接库名称;
W?3-14? 定义宏SHARED以便引用14行的头文件datetime.h中的动态函数说?
W?5? 用dlopen打开SOFILE׃n?q回句柄dp;
W?7-31? dp是否为空,为空则显C错误后退?
W?3? 用dlsym取得getdate函数动态地址;
W?5-40? 如果dlerrorq回g为空,则dlsym执行出错,E序昄错误后退?
W?2-43? 执行getdate调用,输出当前日期;
W?5? 用dlsym取得gettime函数动态地址;
W?7-52? 如果dlerrorq回g为空,则dlsym执行出错,E序昄错误后退?
W?4-55? 执行gettime调用,输出当前旉;
W?7? 用dlclose关闭dp所指示的动态链接库;
W?9? E序退?q回0倹{?/td>
2.2.2 ~写l护文g
l护文gmakefile内容如下:
----------------------------------------------------------------------
1 # makefile : U|软g制作中心雨亦奇编? 2001-06-28.
2
3 all : dy
4
5 DYSRC = dy.c
6
7 DYTGT = $(DYSRC:.c=.o)
8
9 %.o : %.c
10 cc -c $?
11
12 # 动态库应用CE序
13 dy : $(DYTGT)
14 cc -rdynamic -s -o $@ $(DYTGT) -ldl
15
----------------------------------------------------------------------
l护文g说明:
W?? 定义所有需要维护的模块;
W?? 定义源程?
W?? 定义目标文g;
W?-10? 定义.o文g依赖?c文g,l护代码为“cc -c 变动的源文g名?
W?3-14? 定义dy依赖于变量DYTGT指示的?l护代码中采?rdynamic选项以指定输出文件ؓ动态链接的方式Q选项-s指定删除目标文g中的W号?最后的选项-ldl则指C配程序ld需要装载dl函数库?/td>
2.2.3 q行make命o
q行make后将产生执行文gdyQ运行后生如下类g息:
动态链接库应用C
当前日期: 2001-06-28
当前旉: 10:06:21
当删除my.so文g?出C下信?
动态链接库应用C
my.so: cannot open shared object file: 文g或目录不存在 3、小l?/b>
LINUX创徏与用动态链接库q不是一仉事?/td>
~译函数源程序时选用-shared选项卛_创徏动态链接库Q注意应?so后缀命名Q最好放到公用库目录(?lib,/usr/lib{?下面Qƈ要写好用h口文Ӟ以便其它用户׃n?/td>
点击q里下蝲源程?/font>?img src ="http://www.shnenglu.com/mydriverc/aggbug/33162.html" width = "1" height = "1" />
使用动态链接库Q源E序中要包含dlfcn.h头文Ӟ写程序时注意dlopen{函数的正确调用Q编译时要采?rdynamic选项?ldl选项Q以产生可调用动态链接库的执行代码?/td>
]]>
]]>
gcc ~译选项,自己译?
-o 讑֮输出文g?
-c 只编?不连?
-E 只做预编?
-pipe 在多个编译过E之间用管?
--version 昄版本.
-static 静态连?
-ansi C 模式下支持所?ISO C90 标准?C E序, C++ 模式下去除对 GNU C++ 扩展的支?GNU扩展会与 ISO C++ 冲突)
-std=
定~译语言的标?目前只在~译 C ?C++ 时有?-fno-asm 不将 "asm" "inline" "typeof"
作ؓ关键?可以用他们做变量名等. -funsigned-char ?char"的数据类型设?unsigned",xW号.
-fsigned-char 正好相反,?char"设ؓ"signed".
-fsyntax-only 只检查语法错?不做其他M?
-pedantic 昄所有的 ISO C ?ISO C++ 的警?q且拒绝所有用禁止扩展的E序
-Wall 昄所有警?
-g 编译时的调试信息保存到本地文g? stabs,COFF,XCOFF,DWARF)
-ggdb ?GDB 产生调试信息,包含 GDB 的扩?
-ggdb(level) 讑֮产生何种{的调试信? level ?1-3, 1 最?3 最?
-ftime-reprot l计~译消耗的旉q显C报?
-fmem-report 昄所有的静态内存分?
-ftest-coverages ?gcov工具产生数据文g.
gcc ~译选项,译出来用v来方?
VC~译选项Q{载)- -
TagQ?~程
/***********************************************************************************************/
VC~译选项 csdnb3a [原作]
关键?VC~译选项 出处
-优化-
/O1 最化I间 minimize space
/Op[-] 改善点C致?improve floating-pt consistency
/O2 最大化速度 maximize speed
/Os 优选代码空?favor code space
/Oa 假设没有别名 assume no aliasing
/Ot 优选代码速度 favor code speed
/Ob 内联展开Q默?n=0Q?inline expansion (default n=0)
/Ow 假设交叉函数别名 assume cross-function aliasing
/Od 用优化Q默认| disable optimizations (default)
/Ox 最大化选项?/Ogityb2 /Gs) maximum opts. (/Ogityb1 /Gs)
/Og 启用全局优化 enable global optimization
/Oy[-] 启用框架指针省略 enable frame pointer omission
/Oi 启用内徏函数 enable intrinsic functions
-代码生成-
/G3 ?80386 q行优化 optimize for 80386
/G4 ?80486 q行优化 optimize for 80486
/GR[-] 启用 C++ RTTI enable C++ RTTI
/G5 ?Pentium q行优化 optimize for Pentium
/G6 ?Pentium Pro q行优化 optimize for Pentium Pro
/GX[-] 启用 C++ 异常处理Q与 /EHsc 相同Q?enable C++ EH (same as /EHsc)
/EHs 启用同步 C++ 异常处理 enable synchronous C++ EH
/GD ?Windows DLL q行优化 optimize for Windows DLL
/GB 为合模型进行优化(默认Q?optimize for blended model (default)
/EHa 启用异步 C++ 异常处理 enable asynchronous C++ EH
/Gd __cdecl 调用U定 __cdecl calling convention
/EHc extern“C”默认ؓ nothrow extern "C" defaults to nothrow
/Gr __fastcall 调用U定 __fastcall calling convention
/Gi[-] 启用增量~译 enable incremental compilation
/Gz __stdcall 调用U定 __stdcall calling convention
/Gm[-] 启用最重新生?enable minimal rebuild
/GA ?Windows 应用E序q行优化 optimize for Windows Application
/Gf 启用字符串池 enable string pooling
/QIfdiv[-] 启用 Pentium FDIV 修复 enable Pentium FDIV fix
/GF 启用只读字符串池 enable read-only string pooling
/QI0f[-] 启用 Pentium 0x0f 修复 enable Pentium 0x0f fix
/Gy 分隔链接器函?separate functions for linker
/GZ 启用q行时调试检?enable runtime debug checks
/Gh 启用钩子函数调用 enable hook function call
/Ge Ҏ有函数强制堆栈检?force stack checking for all funcs
/Gs[num] 用堆栈查调?disable stack checking calls
-输出文g-
/Fa[file] 命名E序集列表文?name assembly listing file
/Fo 命名对象文g name object file
/FA[sc] 配置E序集列?configure assembly listing
/Fp 命名预编译头文g name precompiled header file
/Fd[file] 命名 .PDB 文g name .PDB file
/Fr[file] 命名源浏览器文g name source browser file
/Fe 命名可执行文?name executable file
/FR[file] 命名扩展 .SBR 文g name extended .SBR file
/Fm[file] 命名映射文g name map file
-预处理器-
/FI 命名强制包含文g name forced include file
/C 不吸取注?don't strip comments
/U U除预定义宏 remove predefined macro
/D{=|#} 定义?define macro
/u U除所有预定义?remove all predefined macros
/E 预处理定向到标准输?preprocess to stdout
/I d到包含文件的搜烦路径 add to include search path
/EP 预处理定向到标准输出,不要带行?preprocess to stdout, no #line
/X 忽略“标准位|?ignore "standard places"
/P 预处理到文g preprocess to file
-语言-
/Zi 启用调试信息 enable debugging information
/Zl 忽略 .OBJ 中的默认库名 omit default library name in .OBJ
/ZI 启用调试信息的“编辑ƈl箋”功?enable Edit and Continue debug info
/Zg 生成函数原型 generate function prototypes
/Z7 启用旧式调试信息 enable old-style debug info
/Zs 只进行语法检?syntax check only
/Zd 仅要行号调试信息 line number debugging info only
/vd{0|1} 用/启用 vtordisp disable/enable vtordisp
/Zp[n] ?n 字节边界上包装结?pack structs on n-byte boundary
/vm 指向成员的指针类?type of pointers to members
/Za 用扩展Q暗?/OpQ?disable extensions (implies /Op)
/noBool 用“bool”关键字 disable "bool" keyword
/Ze 启用扩展Q默认) enable extensions (default)
- 杂项 -
/?, /help 打印此帮助消?print this help message
/c 只编译,不链?compile only, no link
/W 讄警告{Q默?n=1Q?set warning level (default n=1)
/H 最大化外部名称长度 max external name length
/J 默认 char cd?unsigned default char type is unsigned
/nologo 取消昄版权消息 suppress copyright message
/WX 警告视为错?treat warnings as errors
/Tc 文件编译ؓ .c compile file as .c
/Yc[file] 创徏 .PCH 文g create .PCH file
/Tp 文件编译ؓ .cpp compile file as .cpp
/Yd 调试信息放在每?.OBJ ?put debug info in every .OBJ
/TC 所有文件编译ؓ .c compile all files as .c
/TP 所有文件编译ؓ .cpp compile all files as .cpp
/Yu[file] 使用 .PCH 文g use .PCH file
/V 讄版本字符?set version string
/YX[file] 自动?.PCH 文g automatic .PCH
/w 用所有警?disable all warnings
/Zm 最大内存分配(默认?%Q?max memory alloc (% of default)
-链接-
/MD ?MSVCRT.LIB 链接 link with MSVCRT.LIB
/MDd ?MSVCRTD.LIB 调试库链?link with MSVCRTD.LIB debug lib
/ML ?LIBC.LIB 链接 link with LIBC.LIB
/MLd ?LIBCD.LIB 调试库链?link with LIBCD.LIB debug lib
/MT ?LIBCMT.LIB 链接 link with LIBCMT.LIB
/MTd ?LIBCMTD.LIB 调试库链?link with LIBCMTD.LIB debug lib
/LD 创徏 .DLL Create .DLL
/F 讄堆栈大小 set stack size
/LDd 创徏 .DLL 调试?Create .DLL debug libary
/link [链接器选项和库] [linker options and libraries]
qmake是用来ؓ不同的^台的开发项目创建makefile的Trolltech开发一个易于用的工具?em>qmake化了makefile的生成,所以ؓ了创Z个makefile只需要一个只有几行信息的文g?em>qmake可以供Q何一个Y仉目用,而不用管它是不是用Qt写的Q尽它包含了ؓ支持Qt开发所拥有的额外的特征?/p>
qmakeZ一个项目文件这L信息来生成makefile。项目文件可以由开发者生成。项目文仉常很简单,但是如果需要它是非常完善的。不用修攚w目文Ӟqmake也可以ؓ为Microsoft Visual Studio生成目?/p>
举例来说Q如果你在Windows下用Microsoft Visual StudioQ然后你需要把QMAKESPEC环境变量讄?em>win32-msvc。如果你在Solaris上用gccQ你需要把QMAKESPEC环境变量讄?em>solaris-g++?/p>
在qt/mkspecs中的每一个目录里面,都有一个包含了q_和编译器特定信息?em>qmake.conf文g。这些设|适用于你要?em>qmake的Q何项目,请不要修改它Q除非你是一个专家。例如,假如你所有的应用E序都必d一个特定的库连接,你可以把q个信息d到相应的qmake.conf文g中?/p>
一个项目文件是用来告诉qmake关于个应用程序创建makefile所需要的l节。例如,一个源文g和头文g的列表、Q何应用程序特定配|、例如一个必需要连接的额外库、或者一个额外的包含路径Q都应该攑ֈ目文g中?/p>
你可以ؓ目文gd注释。注释由?”符号开始,一直到q一行的l束?/p>
模板变量告诉qmake个应用程序生成哪Umakefile。下面是可供使用的选择Q?/p>
app - 建立一个应用程序的makefile。这是默认|所以如果模板没有被指定Q这个将被用?/p>
lib - 建立一个库的makefile?/p>
vcapp - 建立一个应用程序的Visual Studio目文g?/p>
vclib - 建立一个库的Visual Studio目文g?/p>
subdirs - q是一个特D的模板Q它可以创徏一个能够进入特定目录ƈ且ؓ一个项目文件生成makefileq且为它调用make的makefile?/p>
“app”模板告?em>qmake为徏立一个应用程序生成一个makefile。当使用q个模板Ӟ下面q些qmakepȝ变量是被承认的。你应该在你?pro文g中用它们来Z的应用程序指定特定信息?/p>
HEADERS - 应用E序中的所有头文g的列表?/p>
SOURCES - 应用E序中的所有源文g的列表?/p>
FORMS - 应用E序中的所?ui文gQ由Qt设计?/em>生成Q的列表?/p>
LEXSOURCES - 应用E序中的所有lex源文件的列表?/p>
YACCSOURCES - 应用E序中的所有yacc源文件的列表?/p>
TARGET - 可执行应用程序的名称。默认gؓ目文g的名U。(如果需要扩展名Q会被自动加上。)
DESTDIR - 攄可执行程序目标的目录?/p>
DEFINES - 应用E序所需的额外的预处理程序定义的列表?/p>
INCLUDEPATH - 应用E序所需的额外的包含路径的列表?/p>
DEPENDPATH - 应用E序所依赖的搜索\径?/p>
VPATH - L补充文g的搜索\径?/p>
DEF_FILE - 只有Windows需要:应用E序所要连接的.def文g?/p>
RC_FILE - 只有Windows需要:应用E序的资源文件?/p>
RES_FILE - 只有Windows需要:应用E序所要连接的资源文g?/p>
你只需要用那些你已经有值的pȝ变量Q例如,如果你不需要Q何额外的INCLUDEPATHQ那么你׃需要指定它Q?em>qmake会ؓ所需的提供默认倹{例如,一个实例项目文件也许就像这P
TEMPLATE = app
DESTDIR = c:\helloapp
HEADERS += hello.h
SOURCES += hello.cpp
SOURCES += main.cpp
DEFINES += QT_DLL
CONFIG += qt warn_on release
如果条目是单值的Q比如template或者目的目录,我们是用?”,但如果是多值条目,我们使用?=”来个类?em>d现有的条目。用?”会用新值替换原有的|例如Q如果我们写?tt>DEFINES=QT_DLLQ其它所有的定义都将被删除?/p>
“lib”模板告?em>qmake为徏立一个库而生成makefile。当使用q个模板Ӟ除了“app”模板中提到pȝ变量Q还有一?em>VERSION是被支持的。你需要在为库指定特定信息?pro文g中用它们?/p>
VERSION - 目标库的版本P比如Q?.3.1?/p>
“subdirs”模板告诉qmake生成一个makefileQ它可以q入到特定子目录qؓq个目录中的目文g生成makefileq且为它调用make?/p>
在这个模板中只有一个系l变?em>SUBDIRS可以被识别。这个变量中包含了所要处理的含有目文g的子目录的列表。这个项目文件的名称是和子目录同名的Q这?em>qmake可以发现它。例如,如果子目里是“myapp”,那么在这个目录中的项目文件应该被叫做myapp.pro?/p>
配置变量指定了编译器所要用的选项和所需要被q接的库。配|变量中可以dM东西Q但只有下面q些选项可以被qmake识别?/p>
下面q些选项控制着使用哪些~译器标志:
release - 应用E序以release模式q编。如果“debug”被指定Q它被忽略?/p>
debug - 应用E序以debug模式q编?/p>
warn_on - ~译器会输出可能多的警告信息。如果“warn_off”被指定Q它被忽略?/p>
warn_off - ~译器会输出可能少的警告信息?/p>
下面q些选项定义了所要连~的?应用E序的类型:
qt - 应用E序是一个Qt应用E序Qƈ且Qt库将会被q接?/p>
thread - 应用E序是一个多U程应用E序?/p>
x11 - 应用E序是一个X11应用E序或库?/p>
windows - 只用于“app”模板:应用E序是一个Windows下的H口应用E序?/p>
console - 只用于“app”模板:应用E序是一个Windows下的控制台应用程序?/p>
dll - 只用于“lib”模板:库是一个共享库QdllQ?/p>
staticlib - 只用于“lib”模板:库是一个静态库?/p>
plugin - 只用于“lib”模板:库是一个插Ӟq将会dll选项生效?/p>
例如Q如果你的应用程序用Qt库,q且你想把它q编Z个可调试的多U程的应用程序,你的目文g应该会有下面q行Q?/p>
CONFIG += qt thread debug
注意Q你必须使用?=”,不要使用?”,否则qmake׃能正用连~Qt的设|了Q比如没法获得所~译的Qt库的cd了?/p>
1,用Qt designer创徏 BaseClass.ui 文g ?BaseClass.ui.h Q如有必要)?/p>
2,?ui文g生成相应?.h ?.cpp 文gQ?br />uic -o BaseClass.h BaseClass.ui
uic -o BaseClass.cpp -i(-impl) BaseClass.h BaseClass.ui
3,生成子类?.h ?.cpp 文gQ如有必要)Q?br />uic -o SubClass.h -subdecl SubClassBaseClassBaseClass.ui
uic -o SubClass.cpp -subimpl SubclassSubClass.h BaseClass.ui
4,生成moc文gQ?br />moc -o moc_BaseClass.cpp BaseClass.h
moc -o moc_SubClass.cpp SubClass.h
5,生成目文g.proQ?br />qmake -o Project.pro -project
如不指定文g名,则文件名默认为当前目录的名字?/p>
6,生成Makefile
qmake
7,~译链接Qmake
注:Linux Redhat 9.0, Qt3.3。绿色可Ҏ实际目自行更改?/p>
参见Q?br />Integrating Qt Designer Files Into Your Project
http://docsrv.caldera.com:8457/en/QtTutorial/chap5_4.html
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=933964
关键词:QtE ARM9 GUI SBC-2410X
引言
q?
q来Q由于ARM(Advanced RISC
Machines)在性能、功耗、成本和体积上的优势Q得它在嵌入式pȝ中的发展如日中天Q它在工业控制、航I天、军事领域、消费电子、智能家电和?
频监控等斚w都发挥了重大的作用。很多h都迫切地惛_习ARMQ以W者的l验Q从最基本的LED控制学vQ是一个不错的选择?/p>
?
果不熟悉LinuxQ可能对QtE的概늟之甚,它是挪威Trolltech公司为各U系l设计的囑Ş用户界面工具包,采用c++语言~程。QtE的优
ҎQ跨q_Q可以方便的q接数据库,可以程序与Java集成{。其实,QtE在一些高端的Ud手持讑֤中早已深入h心,Troll—tech公司?
QtE的基上开发了一个应用环境QtopiaQ目前已有很多公叔R用Qtopia来开发他们的LPDA。当ӞQt也有它的~点Q既不能提供IDE
(集成开发环?Q但q个~点在强大的Linux下显得微不道。用gccQg++Q加上Qt的开发工PQt
DesignerQtmakeQqmakeQUIC{,开发可视化E序变得十分容易?/p>
要学习ARMQ选择一Ƒ֥的开发板是必?
可少的。SBC一2410X是广州友善之臂公司设计的一Ƒ֟于ARM9的开发板Q操作系l是韩国的mizi-linux。SBC-2410X不同?
uClinuxQ它的接口丰富,且支持MMU、QtE2Q?Q?Q同时提供了控制LED的命令行方式?/p>
1 pȝ需?/p>
◇完全安装RedHat9Q??br />◇在Linux下徏立QtE~译环境?br />◇开发板上已l加载LED讑֤驱动E序?/p>
注意Q你所建立的QtE版本必须与开发板所支持的版本一致?/p>
2 E序设计
在Linux
下,用系l自带的Qt Designer来编写程序。Qt
Designer是一个优U的可视化开发工P用它来设计十分容易。它的界面类gDelphiQ但使用C++语言~写应用E序。QtE序设计里的一个亮
点就是SignaI-_Slot机制Q它有点cM于VC++里的消息机制。当一个组件发出SignalӞ其他一个组件或多个lg可以通过Slot接收?
来的SignaIQ组件本w也可以接收自己的SignalQ这样处理一个事件将变得非常Ҏ。Qt
Designer的优点不仅在于它可以十分方便的设计图形用L面,q表现在可以用连接工具很Ҏ的把Signals和Slotsq接h。用戯p?
的程序添加在**QuiQh中?/p>
打开Qt DeslgnerQ选择FileQnew
C++ProjectQ命名ؓledQproq保存。接着选择FileQnew-DialogQƈ在此设计自己的用L面。最后选择FileQnew
C++Main—FileQ系l自动生成一个mainQcpp文g。图形用L面如?所C,Initlalize按钮主要完成LED讑֤的打开Q定时器
的关闭和其他一些初始化工作QBegim按钮则针对LED Display
Mode的开始;Exit按钮为退出应用程序。左面的l合框包?个RadioButtonQ对?个LED的亮或灭Q右面的l合框包??
RadioButton(对应不同的闪烁模式和闪烁快慢)?个LCDNumber(用来昄闪烁的时间间?。本E序建立的Signal—S1otq接
有许多,下面对几个重要的q接和函数进行说明?/p>
(1)connect(initPushButtonQSIGNAL(clicked())QthisQSLOT(initial()))
当Initialize
按钮按下ӞLedForm对话框接收此信号Qƈ执行initial()函数。Linux操作pȝ把所有的讑֤都看成文Ӟ因此对设备的操作控制可以通过
open()、ioctl(){函数实现。在initial()函数中就是调用了open()函数来打开LED讑֤的?/p>
(2)connect(okPushButtonQSIGNAL(clicked())QthisQSLOT(modeSel()))
当Begin
按钮按下ӞLedForm对话框接收此信号Qƈ执行modeSel()函数。在modeSel()函数中开启定时器Qƈ创徏另外一个连接用来@环显C?
LED。在命o行方式中QLED的@环显C用到d@环,q用键盘中断来控制@环;而在Qt或其他可视化语言中,则要用到定时器来控制循环?/p>
(3)函数open()
在中声明Q含义ؓ打开讑֤文g。打开成功q回0Q失败返回一1?/p>
(4)函数ioctl()
在中声明Q控制IQO端口。例如:
Int fd=open(”/devQleds”,0)Q?br />ioctl(fdQ?Q?)Q?Q/让LED4?/p>
(5)函数select()
在中声明Q含义ؓl过多长旉讉K一ơ设备?/p>
3 E序UL与运?/p>
要想使程序运行在开发板的Qtopia中,则必d建立的QtE环境下编译,q设|好环境变量。终端进入所建立的QtE目录后,环境变量或运行环境变量和脚本讄如下Q?/p>
export QTDIR=$PWDQqt
export QPEDlR=$PWDQqtopia
export TMAKEDIR=$PWDQtmake
export TMAKEPATH=$TMAKEDIRQlibQqwsQlinux-armg++
export PATH=$QTDIRQbinQ?QPEDIRQbinQ?TMAKEDIRQ?br />binQ?PATH
值得注意的一ҎQ用Linux自带的Qt Designer创徏的工E文件只适用于qmakeQ而笔者徏立的QtE环境只能用tmake来生成makefileQ因此需要修改ledQpro文gQ让它适用于tmakeQ修改如下:
TEMPLATE =app
CoNFIG +=qtopia warn_on release
SoURCES +=mainQepp
INTERFACES =ledQui
TARGET =led
以上的工作做完后Q就可以~译了。编译成功后生成可执行的二进制文件ledQ然而要使程序运行在开发板上的qtopia桌面上,q需要一个桌面文件ledQdesktop和自定义的一个图标ledQpng。桌面文件如下:
[Desktop Entry]
Comment=a LED Control Program
Exee=led
Icon=led
Type=Application
Name=LED Control
?
后,把led复制到SBC-2410X下/optQqtopiaQbin目录下,ledQpng复制刎ͼoptQqtopiaQpics目录下,ledQ?
desktop复制刎ͼ0ptQqtopiaQappsQApplications目录下。最后,用ehmod a+X
led改变文gled的权限,重新启动pȝQ便可看到ledQpng的图标显C在qtopia桌面上,点击此图标就可运行程序了?/p>
4 部分源程序及注释
׃源程序代码比较长Q这里就不全部列ZQ仅l出重要部分的代码及注释Q以供读者参考?/p>
(1) E序的初始化
int fdQ?Q/LED讑֤文g句柄
int type=1Q?Q/默认方式数器
double period=0Q?Q?Q/默认旉间隔
QTimer*t=new QTimer(Q″timer?Q/Q创建定时器
void LedFormQ:initial(){
fd=open(※IdevQleds※I0)Q?Q/打开LED讑֤
Q/其他一些初始化语句
一>stop()Q?Q/关闭定时?br />?/p>
(2) LED昄方式的设|?br />void LedFormQ:push-leds(void){
static unsigned stepQ?br />unsigned led—bitmapQ?br />intiQ?br />switch(type){
case 0Q?Q/方式0Q跑马灯
if(step>=14){step=0Q}
if(step<7){led_bitmap=1<else{led_bitmap=1<<(14一step)Q}
breakQ?br />case 1Q?Q/方式1Q计数器
if(step>255){step=0Q?
Ied_bitmap=step!
breakQ?br />case 2Q?Q/方式2Q停?br />led_bitmap=0Q?br />break;
step十十;
for(i=0Qi<8Ql++){
ioctl(fdQled_bitmapQ?Qi)Q?br />led_bitmap>>=1Q?br />}
}
(3) LED讑֤的读取及调用LED昄E序
void LedFormQ:ledDisplay(){
fd_set rdsQ?br />struct timeval stepQ?br />FD_ZERO(&rds)Q?br />stepQtv_sec=periodQ?br />stepQtv_usec=(pefiod—stepQW_sec)*1000000LQ?br />select(fdQ?amp;rdsQNULLQNULLQ?amp;step)Q?br />push_leds()Q?br />}
(4)循环昄的实?br />void LedFormQ:modeSel(){
t-->start(O)Q/Q启动定时器Qƈ立即发出Signal- timeout()
connect(tQSIGNAL(timeout())QthisQSLOT(1edDisplay()))Q?br />}
l??/p>
?
着通信行业的迅猛发展,Ud手持讑֤必将成ؓZ工作、学习和生活的主,用Qt设计Ud手持讑֤的GUI有着得天独厚的优ѝ目前,Qt应用于全世界?
百个软g开发项目中。在我国QQt的发展也有星星之火可以燎原之ѝ本文通过一个最基础的小E序向读者演CZ怎样使用Qt开发应用程序,以及怎样建立Qt
与ARM的联p,希望对读者学习ARM和Qt起到一定的帮助作用?/p>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
pthread_key_t key;
void echomsg(int t)
{
printf("destructor excuted in thread %d,param=%d\n",pthread_self(),t);
}
void child()
{
int tid;
tid = pthread_self();
printf("thread %d enter\n",tid);
sleep(1);
pthread_setspecific(key,(void *)tid);
printf("thread %d returns %d\n",tid,pthread_getspecific(key));
sleep(1);
}
int main(int argc,char** argv)
{
int * status;
int gc;
char command[64]="./clu";
char comarg[64]="";
argv[1]=comarg;
while(1)
{
if((gc=getchar())=='g')
{
if(fork()==0)
{
if(execve(command,argv,0)==-1)
{
printf("process error is %s\n",strerror(errno));
}
printf("process is ok\n");
}
}
if(gc=='k')
{
printf("to be continue\n");
}
if(gc=='e')
{
return 1;
}
}
}
功能是这LQ启动以后会从键盘接受字W,如果是gp行预先指定好的程序(在这里是一个叫clu的程序)Q如果是k打印to be continueQ如果是e退?br />下面是这个叫clu的程序的代码
#include <stdio.h>
#include <time.h>
int main()
{
long begin=0;
long end=24000;
long finish=0;
long loop1,loop2;
time_t flag;
time_t nowtime1,nowtime2;
if((flag=time(&nowtime1))==-1)exit(1);
for(loop1=begin;loop1<end;loop1++)
{
for(loop2=begin;loop2<end;loop2++)
{
if((loop1%2)==0)
{
if((loop2%2)==0)
{
if(finish>65535)
finish=0;
finish+=((loop1/2+loop2/2)%2)%2;
}
}
}
}
if((flag=time(&nowtime2))==-1)exit(1);
printf("%ld\n",finish);
printf("proccess time %ldsec\n",nowtime2-nowtime1);
}
q个E序本n没有M意义Q只是纯_的延误一下时_输出一下运行的旉Q便于测试,在我的AMD2500+上,大概?U,你可以试试哦^_^
要测试这个程序,可以在程序运行时输入gQ但是这个计不会马上结束,在屏q还没有输出的时候再输入kQ就会先输出k的内容,后台clu的结果出来后才输出到屏幕Q也是clu在后台运行了
…?br />
/* Type of a signal handler. */
typedef void (*__sighandler_t)(int);
…?br />
#ifdef __KERNEL__
struct old_sigaction {
__sighandler_t sa_handler;
old_sigset_t sa_mask;
unsigned long sa_flags;
void (*sa_restorer)(void);
};
struct sigaction {
__sighandler_t sa_handler;
unsigned long sa_flags;
void (*sa_restorer)(void);
sigset_t sa_mask; /* mask last for extensibility */
};
struct k_sigaction {
struct sigaction sa;
};
#else
/* Here we must cater to libcs that poke about in kernel headers. */
struct sigaction {
union {
__sighandler_t _sa_handler;
void (*_sa_sigaction)(int, struct siginfo *, void *);
} _u;
sigset_t sa_mask;
unsigned long sa_flags;
void (*sa_restorer)(void);
};
#define sa_handler _u._sa_handler
#define sa_sigaction _u._sa_sigaction
#endif /* __KERNEL__ */
sa_handler的原型是一个参CؓintQ返回类型ؓvoid的函数指针。参数即Z号|所以信号不能传递除信号g外的M信息;
sa_sigaction的原型是一个带三个参数Q类型分别ؓintQstruct siginfo *Qvoid *,q回cd为void的函数指针。第一个参Cؓ信号?W二个参数是一个指向struct siginfol构的指针,此结构中包含信号携带的数据?W三个参数没有用?/p>
sa_mask指定在信号处理程序执行过E中Q哪些信号应当被d。默认当前信hw被d?/p>
sa_flags包含了许多标志位Q比较重要的一个是SA_SIGINFOQ当讑֮了该标志位时Q表CZ号附带的参数可以传递到信号处理函数中。即 使sa_sigaction指定信号处理函数Q如果不讄SA_SIGINFOQ信号处理函数同样不能得C号传递过来的数据Q在信号处理函数中对q些? 息的讉K都将DD错误?/p>
sa_restorer已过ӞPOSIX不支持它Q不应再使用?/p>
因此Q当你的信号需要接攉加信息的时候,你必ȝsa_sigaction赋信号处理函数指针,同时q要lsa_flags赋SA_SIGINFO,cM下面的代码:
#include <signal.h>
…?br />
void sig_handler_with_arg(int sig,siginfo_t *sig_info,void *unused){……}
int main(int argc,char **argv)
{
struct sigaction sig_act;
…?br />
sigemptyset(&sig_act.sa_mask);
sig_act.sa_sigaction=sig_handler_with_arg;
sig_act.sa_flags=SA_SIGINFO;
…?br />
}
如果你的应用E序只需要接收信P而不需要接攉外信息,那你需要的讄的是sa_handler,而不是sa_sigaction,你的E序可能cM下面的代码:
#include <signal.h>
…?br />
void sig_handler(int sig){……}
int main(int argc,char **argv)
{
struct sigaction sig_act;
…?br />
sigemptyset(&sig_act.sa_mask);
sig_act.sa_handler=sig_handler;
sig_act.sa_flags=0;
…?br />
}
如果需要更详细说明Q请参阅sigaction的man手册?/p>
void signal_rec(int signum, siginfo_t *info, void *myop)
{
printf("receive signal %d\n", signum);
sleep(5);
}
int main(int argc,char **argv)
{
struct sigaction act;
int sig,i;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = signal_rec;
for(i=1; i<argc; i++){ //argv[0]用程序名
sig = atoi(argv[i]);
if(sigaction(sig, &act, NULL) <0){
printf("install signal error\n");
}
}
while(1){
sleep(2);
printf("wait for the signal\n");
}
}
用法Q?br />文件保存ؓtest.cQ执行make testQ编译生成test文g?br />./test 36 39&
命o的意思是注册信号36,39。命令结果会q回pidQ假设ؓ2625?br />通过另外一个终端向2625q程发送信受例?kill -s 36 2625
则我们可以看到signal_rec函数被执行了?br />当我们发?0信号Q我们会发送程序终止了Q这是因为对于实时信P默认的操作是l止?br />关于信号Q?br /> Linux
支持的信号分为可靠信号和不可靠信受此处的可靠与不可靠不是指系l是不是可靠的可靠,而是对于可靠信息来说Q每一ơ信号发实都每被加到信号链中Q所以是
可靠的;而对于不可靠信号来说Q如果进E之前已l接收到该信P则不会被加到信号链中Q因此对于此ơ发送的信号来说Q对于该q程来说是不知道的,所是?
信号丢失了,因此是不可靠的?br /> 不可靠信号主要是在早期信h制上的信受一般来_信号值小于SIGRTMIN的信号ؓ不可靠信P不可靠信号又UCؓ非实时信受可靠信号又UCؓ实时信号。实时信hSIGRTMIN和SIGRTMAX间的所有信受?br /> 我们可以通过kill -l查看SIGRTMIN和SIGRTMAX的倹{在Debianpȝ和Redhat上面Q一般SIGRTMIN=33,SIGRTMAX=64?br /> Linux既支持新的信号安装函数sigation以及信号发送函数sigqueueQ又支持早期的signal信号安装函数和kill信号发送函数?br /> 信号的可靠与不可靠只与信号值有养I与信L发送和安装函数无关Q也是说在Linux下即使用sigaction和sigqueue也不可能不可靠信号变ؓ可靠信号?br />信号发送函C要有以下几个Qkill,raise,sigqueue,alarm,settimer,abort?br />信号安装函数主要有signal和sigaction。sigaction主要用于与sigqueuepȝ调用配合使用Q主要用于实时信号处理?br />信号集操作主要有以下几个函数Qsigemptyset,sigfillset,sigaddset,sigdelset,sigismember?br />信号的阻塞的未决主要有以下几个函敎ͼsigprocmask,sigsuspend,sigpending?br />最后以一个读串口讑֤时用到的信号处理作ؓl尾(在上ơ的初始化串口设备中已经出现q?Q?br />在读取设备数据或者进行网l应用的时候,Z防止E序q入死锁Q我们需要设|超时操作,x如我们读串口讑֤Q尝试一定时间后仍然没有响应Q则可能讑֤没有正常工作。那么在时以后我们需要退出,否则E序锁住了?br />我们现在使用SIGALRM信号来进行这个处?q种方式q不是最好的办法)Q?br />我们假设有一个标记是否超时的全局变量caught_alrmQ默认ؓ0Qؓ1时则表示时?br />static volatile sig_atomic_t caught_alrm;
static void sig_alrm(int signo){//信号处理函数Q设|超时全局变量??br /> caught_alrm = 1;
return;
}
int a_function{
…?br /> if(signal(SIGALRM, sig_alrm) == SIG_ERR){
syslog(LOG_ERR,"signal error in function:%s",__FUNCTION__);
return -1;
}
caught_alrm = 0;
alarm(expalarm);
do{
//do something
//if the work is finished,call alarm(0) to clean the timer.
}while(caught_alrm == 0);
}
注:
1、本文ؓ整理以前的工作笔讎ͼ如果您要转蝲Q请注意来源为尔雅,作者覃士国。如果您有Q何问题欢q交:shiguo.qin@gmail.com
2?
此文仅仅是一则笔讎ͼ如果您正在寻扑օ于Linux信号~程斚w的资料,可以参考郑彦兴的一文?http://www-
128.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html。此文详l描qC很多关于?
号处理中的结构等内容Q是学习信号~程的一不错的教程式文章?/p>
wait的函数原型是Q?/strong>
#include /* 提供cdpid_t的定?*/
#include
pid_t wait(int *status)
q程一旦调用了waitQ就立即d自己Q由wait自动分析是否当前q程的某个子q程已经退出,如果让它扑ֈ了这样一个已l变成僵的子进E,
wait׃攉q个子进E的信息Qƈ把它d销毁后q回Q如果没有找到这样一个子q程Qwait׃一直阻塞在q里Q直到有一个出Cؓ止?br />
参数status用来保存被收集进E退出时的一些状态,它是一个指向intcd的指针。但如果我们对这个子q程是如何死掉的毫不在意Q只xq个僵尸q程消灭掉,Q事实上l大多数情况下,我们都会q样惻IQ我们就可以讑֮q个参数为NULLQ就象下面这P
pid = wait(NULL);
如果成功Qwait会返回被攉的子q程的进EIDQ如果调用进E没有子q程Q调用就会失败,此时waitq回-1Q同时errno被置为ECHILD?br />
waitpid的函数原型是Q?/strong>
?br />
waitpidpȝ调用在Linux函数库中的原型是Q?br />
#include /* 提供cdpid_t的定?*/
#include
pid_t waitpid(pid_t pid,int *status,int options)
从本质上Ԍpȝ调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可qh制的参数pid和optionsQ从而ؓ我们~程提供了另一U更灉|的方式。下面我们就来详l介l一下这两个参数Q?br />
?pid
从参数的名字pid和类型pid_t中就可以看出Q这里需要的是一个进EID。但当pid取不同的值时Q在q里有不同的意义?br />
pid>0Ӟ只等待进EID{于pid的子q程Q不其它已l有多少子进E运行结束退ZQ只要指定的子进E还没有l束Qwaitpid׃一直等下去?br />
pid=-1Ӟ{待M一个子q程退出,没有M限制Q此时waitpid和wait的作用一模一栗?br />
pid=0Ӟ{待同一个进E组中的M子进E,如果子进E已l加入了别的q程l,waitpid不会对它做Q何理睬?br />
pid<-1Ӟ{待一个指定进E组中的M子进E,q个q程l的ID{于pid的绝对倹{?br />
?options
options提供了一些额外的选项来控制waitpidQ目前在Linux中只支持WNOHANG和WUNTRACED两个选项Q这是两个常敎ͼ可以?|"q算W把它们q接h使用Q比如:
ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);
如果我们不想使用它们Q也可以把options设ؓ0Q如Q?/p>
ret=waitpid(-1,NULL,0);
如果使用了WNOHANG参数调用waitpidQ即使没有子q程退出,它也会立卌回,不会像wait那样永远{下厅R?br />
而WUNTRACED参数Q由于涉及到一些跟t调试方面的知识Q加之极用刎ͼq里׃多费W墨了,有兴的读者可以自行查阅相x料?br />
看到q里Q聪明的读者可能已l看出端倪了--wait不就是经q包装的waitpid吗?没错Q察?lt;内核源码目录>/include/unistd.h文g349-352行就会发C下程序段Q?br />
static inline pid_t wait(int * wait_stat)
{
return waitpid(-1,wait_stat,0);
}
q回值和错误
waitpid的返回值比waitE微复杂一些,一共有3U情况:
?当正常返回的时候,waitpidq回攉到的子进E的q程IDQ?br />
?如果讄了选项WNOHANGQ而调用中waitpid发现没有已退出的子进E可攉Q则q回0Q?br />
?如果调用中出错,则返?1Q这时errno会被讄成相应的g指示错误所在;
当pid所指示的子q程不存在,或此q程存在Q但不是调用q程的子q程Qwaitpid׃出错q回Q这时errno被设|ؓECHILD
其它Q?/font>
调用 waitQwaitpid 来处理终止的子进E:
两个函数都返回两个|函数的返回值和l止的子q程IDQ而子q程l止的状态则是通过statloc指针q回的?br />
waitQwaitpid 的区别是显而易见的Qwait{待W一个终止的子进E,而waitpid则可以指定等待特定的子进E。这L区别可能会在下面q种情况时表现得更加明显Q?br /> 当同时有5个客戯上服务器Q也是说有五个子进E分别对应了5个客P此时Q五个客户几乎在同时hl止Q这样一来,几乎同时Q五个FIN发向服务器, 同样的,五个SIGCHLD信号到达服务器,然而,UNIX的信号往往是不会排队的Q显然这样一来,信号处理函数只会执行一ơ,D留剩余四个子进E作? 僵尸q程ȝ在内核空间。此Ӟ正确的解军_法是利用waitpid(-1, &stat, WNOHANG)防止留下僵尸q程。其中的pid为-1表明{待W一个终止的子进E,而WNOHANG选择w知内核在没有已l止q程Ҏ不要d?
waitQwaitpid 区别
waitpid提供了wait函数不能实现?个功?
q两个函数返回的子进E状态都保存在statloc指针? 用以?个宏可以查该状?
if(fork()>0) /* 如果是父q程 */
wait(NULL); /* 攉僵尸q程 */
}
大家看完代码后,׃觉得q个代码正是我刚才讲的进E一生是怎么LQ进E死后,一定要Z收尸Q否则他׃~程僵尸q程。下面就让我们来看看未能把死ȝq程收尸会变成什么样子?
/**********************************僵尸q程******zombie.c*****************************************************/
#include <sys/types.h>
#include <unistd.h>
main()
{
fork(); /*开始创Z个子q程*/
if(fork>0) /* 如果是父q程 */
sleep(30); /* 休眠30U,q段旉里,父进E什么也q不?*/
wait(NULL); /* 收v僵尸q程 */
/*因ؓ父进E死了,子进E也得一起陪葬,那么我们p父进E睡?0U,在这30U内我们可以看到僵进E?br /> 30U过后,父进E醒来后得死,所以到那时子进E也死厅R?/
}
/********************************************************************************************/
Z让大家更清楚的看到僵进E我们把q个僵尸q程代码~译Q然后执行,
#gcc -o zombie zombie.c
~译成功后我们开始执行这个程序因Z码里的sleep(30)是让父进E睡?0U,如果我们在前台执行我们这个程序,那么׃能执行其他shell命o了,所以这里我们在执行的命令后面加一?amp; 代表后台q行的意思?br />#./zomber &
好了Q我们在30U内执行查看q程命o来看看我们这个zombieq程是什么样的?
#ps -aux |grep zombie
q个命o是显C有关zombie的所有信息。具体操作请看图Q?Q?/font>
看到q里后,聪明的朋友会问,既然子进E是qq程创造出来的Q那么如果一个父E序执行完退出后Q子q程不管是不是僵进E也直接退ZQ因为父q程? 了嘛。那么这也不会造成多大危害啊?如果大家q么认ؓ那可错了,当我们刚才用ps命o观察q程的执行状态时Q看到某些进E的状态栏为defunctQ这 是所谓的“僵”进E。“僵”进E是一个早已死亡的q程Q但在进E表Qprocesss tableQ中仍占了一个位|(slotQ。由于进E表的容量是有限的,所以,defunctq程不仅占用pȝ的内存资源,影响pȝ的性能Q而且如果其数 目太多,q会Dpȝ瘫痪Q具体请看下面的代码Q?br />/***********************************无限创徏子进E?*******************************************/
#include <sys/types.h>
#include <unistd.h>
main()
{
for (;;) /*制作一个死循环*/
fork(); /*开始创Z个子q程*/
}
/********************************************************************************************/
懂C语言的朋友会知道for(;;)是一个死循环。那么这仅有2行代码的E序可以把你的linux瞬间LQ因Z的作用是无限制的在内存里增加新的子进E,一个系l根据内存的定w会分配进E的最大限Ӟ一旦同时运行的q程数超W合Q那么你的机器必然会L?br /> 我这里分别用?个用戯行测试过Q一个是普通权限的用户Q一个是Root用户Q测试结果是“Q何用户只要执行这个程序都会让机器L”。原因就是默认的 Linuxpȝ没有Ҏ通用L使用最大进E进行限制。所以这样对pȝ造成很大一个威胁,那么我们如何避免普通用户非法执行过多资源或q程Dpȝ拖死Q? 所以我们的l除了Root的用户做一下限制?/font>
Ҏ通用戯行限Ӟ
W?步:首先q到Linuxl端用vi~辑/etc/security /limits.conf文gQ在里面加入Q?
* hard core 0
* hard rss 5000
* hard nproc 20
q里? 代表除了Root的所有用P(* hard core 0) 是禁止core files“core 0”,(* hard rss
5000) 是限制内存用ؓ5MB“rss 5000? (* hard nproc 20 )是限制进E数为“nproc
50“。大家可以根据自ql内存大进行合理配|?/font>
W?步:用vi~辑/etc/pam.d/login文gQ然后加上下面这行保存退出就可以?
session required /lib/security/pam_limits.so
好了Q现在我们已l对普通用户限制了q程和内存用极限,修改完配|以后大家可以用Root和普通用户分别进行测试,l果当然是普通用h行完我们刚才做的E序不会L了?/font>
一个系l的安全性不取决与打不打补丁Q虽然打补丁很重要,但是对系l做出相应的配置也是相当重要的?br />
Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q?/p>
在fork
()/execve()q程中,假设子进E结束时父进E仍存在Q而父q程fork()之前既没安装SIGCHLD信号处理函数调用waitpid(){待
子进E结束,又没有显式忽略该信号Q则子进E成为僵进E,无法正常l束Q此时即使是rootw䆾kill
-9也不能杀d进E。补救办法是杀d进E的父进E?僵尸q程的父q程必然存在)Q僵进E成?孤儿q程"Q过l给1可EinitQinit?
l会负责清理僵尸q程。 ?br />
===========================================
在Linux中可以用
ps auwx
发现僵尸q程
a all w/ tty, including other users 所有窗口和l端Q包括其他用Lq程
u user-oriented 面向用户(用户友好)
-w,w wide output 宽格式输?
x processes w/o controlling ttys
在僵进E后?会标?
ps axf
看进E树Q以树Ş方式现实q程列表
ps axm
会把U程列出?在linux下进E和U程是统一的,是轻量q程的两U方式?
ps axu
昄q程的详l状?
===========================================
killall
kill -15
kill -9
一般都不能杀?defunctq程
用了kill -15,kill -9以后 之后反而会多出更多的僵进E?
kill -kill pid
fuser -k pid
可以考虑杀M的parent processQ?
kill -9 他的parent process
===========================================
一个已l终?但是其父q程未对其q行善后处理Q获取终止子q程的有关信息、释攑֮仍占用的资源Q的q程被称为僵死进E?Zombie Process)?
避免zombie的方法:
1)在SVR4中,如果调用signal或sigsetSIGCHLD的配|设|ؓ忽略,则不会生僵dq程。另?使用SVR4版的sigaction,则可讄SA_NOCLDWAIT标志以避免子q程僉|?
Linux中也可用这个,在一个程序的开始调用这个函?
signal(SIGCHLD,SIG_IGN);
2)调用fork两次。程? - 5 实现了这一炏V?
3)用waitpid{待子进E返?
===========================================
zombieq程是僵死进E。防止它的办法,一是用wait,waitpid之类的函数获?
q程的终止状态,以释放资源。另一个是fork两次
===========================================
defunctq程只是在process table里还有一个记录,其他的资源没有占用,除非你的pȝ的process个数的限制已l快过了,zombieq程不会有更多的坏处?
可能唯一的方法就是rebootpȝ可以消除zombieq程?
===========================================
ME序都有僵尸状态,它占用一点内存资?也就是进E表里还有一个记?Q仅仅是表象而已不必x。如果程序有问题有机会遇见,解决大批量僵简单有效的办法是重赗kill是无M效果?
fork与zombie/defunct"
在Unix
下的一些进E的q作方式。当一个进E死亡时,它ƈ不是完全的消׃。进E终?它不再运?但是q有一些残留的东西等待父q程收回。这些残留的东西包括
子进E的q回值和其他的一些东ѝ当父进E?fork() 一个子q程?它必ȝ wait() 或?waitpid()
{待子进E退出。正是这?wait() 动作来让子进E的D留物消失?
自然的,在上q规则之外有个例?父进E可以忽?SIGCLD 软中断而不必要 wait()。可以这样做?在支持它的系l上,比如Linux):
main()
{
signal(SIGCLD, SIG_IGN); /* now I don't have to wait()! */
.
.
fork();
fork();
fork(); /* Rabbits, rabbits, rabbits! */
?
现在Q子q程M时父q程没有 wait()Q通常?ps 可以看到它被昄为“”。它永q保持这?直到 父进E?wait()Q或者按以下Ҏ处理?
q?
里是你必ȝ道的另一个规?当父q程在它wait()子进E之前死亡了(假定它没有忽?SIGCLD),子进E将?init(pid
1)q程作ؓ它的父进E。如果子q程工作得很好ƈ能够控制Q这q不是问题。但如果子进E已l是
defunctQ我们就有了一点小ȝ。看Q原先的父进E不可能?wait()Q因为它已经消亡了。这Pinit 怎么知道 wait() q些
zombie q程?
{案Q不可预料的。在一些系l上Qinit周期性的破坏掉它所有的defunctq程。在另外一些系l中,它干
脆拒l成ZQ何defunctq程的父q程Q而是马上毁灭它们。如果你使用上述pȝ的一U?可以写一个简单的循环Q用属于init的defunctq程?
满进E表。这大概不会令你的系l管理员很高兴吧?
你的dQ确定你的父q程不要忽略 SIGCLDQ也不要 wait() ?
fork() 的所有进E。不q,你也未必 ?Lq样做(比如Q你要v一?daemon 或是别的什么东西),但是你必d心编E,如果你是一?
fork() 的新手。另外,也不要在心理上有M束缚?
ȝQ?
子进E成?defunct 直到父进E?wait()Q除非父q程忽略?SIGCLD ?
更进一步,父进E没?wait() 消亡(仍假讄q程没有忽略 SIGCLD Q的子进E(zd的或?defunctQ成?init 的子q程Qinit 用重手法处理它们?/b>
Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=