﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-旅途-文章分类-Linux开发</title><link>http://www.cppblog.com/mydriverc/category/4799.html</link><description>如果想飞得高，就该把地平线忘掉</description><language>zh-cn</language><lastBuildDate>Mon, 19 May 2008 13:37:27 GMT</lastBuildDate><pubDate>Mon, 19 May 2008 13:37:27 GMT</pubDate><ttl>60</ttl><item><title>C语言程序静态库和动态库的创建及其应用</title><link>http://www.cppblog.com/mydriverc/articles/33170.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 28 Sep 2007 18:14:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/33170.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/33170.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/33170.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/33170.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/33170.html</trackback:ping><description><![CDATA[
在用c写程序时，很多时候需要存储一些简单的数据，如果为此而用mysql数据库就有些<br />
大才小用了，可以把这些数据以结构的形写入文件，然后再需要时读取文件，取出数据。<br /><br />
如下是定义函数的源文件和头文件：<br /><br />
源文件struct.c:<br /><br />
#include "struct.h"<br />
//第一个参数是要写入的文件名，第二个参数是缓冲区，第三个参数是缓冲区大小，<br />
第四个参数是打开文件流的形态，返回TRUE表示写入成功，返回FALSE表示写入失败<br />
int writeStruct(const char *fileName,char *buffer,int bufferLen,char *mode){<br />
    int ret;<br />
    FILE *fileID = NULL;<br />
    fileID = fopen(fileName,mode);<br />
    if (fileID == NULL){<br />
        perror("fopen");<br />
        goto writeEnd;<br />
    }<br />
    rewind(fileID);<br />
    ret = fwrite(buffer,bufferLen,1,fileID);<br />
    if (ret &lt;= 0){<br />
        perror("fwrite");<br />
        goto writeEnd;<br />
    }<br />
    if (fileID != NULL){<br />
        fclose(fileID);<br />
        fileID = NULL;<br />
    }<br />
    return TRUE;<br />
writeEnd:<br />
    if (fileID != NULL){<br />
        fclose(fileID);<br />
        fileID = NULL;<br />
    }<br />
    return FALSE;<br />
}<br />
//第一个参数是要读取的文件名，第二个参数是缓冲区，第三个参数是缓冲区大小，<br />
第四个参数是打开文件流的形态，返回TRUE表示读取成功，返回FALSE表示读取失败<br />
int readStruct(const char *fileName,char *buffer,int bufferLen,char *mode){<br />
    int ret;<br />
    FILE *fileID = NULL;<br />
    fileID = fopen(fileName,mode);<br />
    if (fileID == NULL){<br />
        perror("fopen");<br />
        goto readEnd;<br />
    }<br />
    rewind(fileID);<br />
    memset(buffer,0,sizeof(buffer));<br />
    ret = fread(buffer,bufferLen,1,fileID);<br />
    if (ret &gt;= 0){<br />
        strcat(buffer,"\0");<br />
    }else{<br />
        perror("fread")    ;<br />
        goto readEnd;<br />
    }<br />
    if (fileID != NULL){<br />
        fclose(fileID);<br />
        fileID = NULL;<br />
    }<br />
    return TRUE;<br />
readEnd:<br />
    if (fileID != NULL){<br />
        fclose(fileID);<br />
        fileID = NULL;<br />
    }<br />
    return FALSE;<br />
      }<br /><br />
头文件struct.h:<br /><br />
#ifndef OWNSTRUCT_H_<br />
#define OWNSTRUCT_H_<br />
#include&lt;stdio.h&gt;<br />
#include&lt;string.h&gt;<br />
#include&lt;stdlib.h&gt;<br />
#define FALSE 0<br />
#define TRUE 1<br />
//第一个参数是要写入的文件名，第二个参数是缓冲区，第三个参数是缓冲区大小，<br />
第四个参数是打开文件流的形态，返回TRUE表示写入成功，返回FALSE表示写入失败<br />
int writeStruct(const char *fileName,char *buffer,int bufferLen,char *mode);<br />
//第一个参数是要读取的文件名，第二个参数是缓冲区，第三个参数是缓冲区大小，<br />
第四个参数是打开文件流的形态，返回TRUE表示读取成功，返回FALSE表示读取失败<br />
int readStruct(const char *fileName,char *buffer,int bufferLen,char *mode);<br />
      #endif<br />
为了使用方便，可以把这两个函数接口定义为动态链接库或静态链接库。用动态链接库<br />
编译生成的可执行文件需调用.so文件方可正常运行，灵活但稍显麻烦;用静态链接库编<br />
译生成的可执行文件可直接运行，不用再调用如.so般的依赖库文件，简单但不灵活。<br /><br />
静态链接库：<br /><br />
1、编译生成目标文件<br /><br />
gcc -c struct.c<br /><br />
2、创建静态库<br /><br />
ar cqs libstruct.a struct.o (顺序不能乱)<br /><br />
3、链接静态链接库，生成可执行文件<br /><br />
gcc main.c -static -L. -ltest -o main<br /><br />
动态链接库：<br /><br />
1、编译成动态链接库<br /><br />
gcc struct.c -fPIC -shared -o libstruct.so<br /><br />
2、链接动态链接库，生成可执行文件<br /><br />
gcc main.c -L. -lstruct -o main<br /><br />
3、设置库文件的环境路径<br /><br />
1)在bashrc或profile文件里用LD_LIBRARY_PATH定义，然后用source加载。<br /><br />
2)把库路径添加到ld.so.conf文件中，然后用ldconfig加载。<br /><br />
3)ldconfig /home/user/lib，仅能暂时性使用，若下次ldconfig时此目录下的动态链<br />
接库就不能被共享了。<br /><br />
gcc一些参数解析<br /><br />
-shared：指定生成动态链接库。<br /><br />
-static：指定生成静态链接库。<br /><br />
-fPIC：表示编译为位置独立的代码，用于编译共享库。目标文件需要创建成位置无关<br />
码，概念上就是在可执行程序装载它们的时候，它们可以放在可执行程序的内存里的任<br />
何地方。<br /><br />
-L.：表示要连接的库在当前目录中。<br /><br />
-l：指定链接时需要的动态库。编译器查找动态连接库时有隐含的命名规则，即在给出<br />
的名字前面加上lib，后面加上.so来确定库的名称。<br /><br />
-Wall：生成所有警告信息。<br /><br />
-ggdb：此选项将尽可能的生成gdb的可以使用的调试信息。<br /><br />
-g：编译器在编译的时候产生调试信息。<br /><br />
-c：只激活预处理、编译和汇编,也就是把程序做成目标文件(.o文件)。<br /><br />
-Wl,options：把参数(options)传递给链接器ld。如果options中间有逗号,就将<br />
options分成多个选项,然后传递给链接程序。<img src ="http://www.cppblog.com/mydriverc/aggbug/33170.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-29 02:14 <a href="http://www.cppblog.com/mydriverc/articles/33170.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在linux下编写动态链接库的步骤</title><link>http://www.cppblog.com/mydriverc/articles/33169.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 28 Sep 2007 18:13:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/33169.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/33169.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/33169.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/33169.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/33169.html</trackback:ping><description><![CDATA[
		<font style="font-size: 14pt;" color="#02368d">
				<font style="color: rgb(0, 1, 2);" size="2">引自：http://blog.chinaunix.net/u/20754/showart_370851.html<br /></font>
		</font>#  1.编写库的头文件和源文件.   <br />#  2.把所有涉及到的源文件用如下方式编译为目标文件: <br />  <br />#  g++/gcc   -g   -c   -fPIC   -o   library1.o   library1.cpp   <br />#  g++/gcc   -g   -c   -fPIC   -o   library2.o   library2.cpp   <br />#  ......   <br />#     <br />3.把所有的目标文件链接为动态库:  <br /><br /> #  g++/gcc   -g   -shared   -Wl,-soname,lib***.so -o   lib***.so.1.0.0 library1.o   library2.o   ....     -lc   <br /><br />*** 为你为动态链接库的命名，比如dl<br />4.建立一个库名链接   <br /><br />#  ln   -s   lib***.so.1.0.0   lib***.so   <br />#  现在你就可以引用库了.<br />#  假如你的应用程序源代码叫test.cpp   <br />#  采用如下方式编译:   <br />#  g++   -g   -o   test   test.cpp   -ldl   <br /><br /><br /><table style="border-collapse: collapse; color: rgb(2, 54, 141);" align="center" border="0" cellpadding="0" cellspacing="0" width="700"><tbody><tr><td colspan="3"><table style="border-collapse: collapse;" border="0" bordercolor="#111111" cellpadding="0" cellspacing="0" width="700"><tbody><tr><td valign="top" width="20"><br /></td><td width="580"><pre style="margin: 0px; line-height: 150%;" wrap="break-word">ps:编译时可能遇到找不到.so文件的提示，这时需要设置LD_LIBRARY_PATH环境变量：<br /><br />export LD_LIBRARY_PATH = .<br /><br />#仅是测试，不提倡设为当前目录<br /><br />然后，<br /><br />g++   -g   -o   test   test.cpp  -L./  -ldl 即可  </pre></td></tr></tbody></table></td></tr><tr><td colspan="3" height="15"><br /></td></tr></tbody></table><img src ="http://www.cppblog.com/mydriverc/aggbug/33169.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-29 02:13 <a href="http://www.cppblog.com/mydriverc/articles/33169.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>鸟哥的 Linux 与 ADSL 私房菜</title><link>http://www.cppblog.com/mydriverc/articles/33168.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 28 Sep 2007 18:07:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/33168.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/33168.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/33168.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/33168.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/33168.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 我们在 Linux是什么 一文当中，提到了 GNU 与 GPL 还有开放源码等咚咚，不过，前面都还没有提到真正的开放源码是什么的讯息！在这一章当中，我们将藉由Linux 操作系统里面的执行文件，来理解什么是可执行的程序，以及了解什么是编译器。另外，与程序息息相关的函式库(library)的信息也需要了解一番！不过，在这个章节当中，鸟哥并不是要您成为一个开放源码的程序设计师，而是希望您可...&nbsp;&nbsp;<a href='http://www.cppblog.com/mydriverc/articles/33168.html'>阅读全文</a><img src ="http://www.cppblog.com/mydriverc/aggbug/33168.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-29 02:07 <a href="http://www.cppblog.com/mydriverc/articles/33168.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LINUX动态链接库高级应用</title><link>http://www.cppblog.com/mydriverc/articles/33167.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 28 Sep 2007 18:05:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/33167.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/33167.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/33167.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/33167.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/33167.html</trackback:ping><description><![CDATA[
		<span class="t18">
				<p>在《LINUX下动态链接库的创建与应
用》一文中,我介绍了LINUX动态链接库的基本知识.其要点是:用户根据实际情况需要,利用dlopen,dlsym,dlclose等动态链接库操作
函数,装入指定的动态链接库中指定的函数,然后加以执行.程序中使用很少的动态函数时,这样的做法尚可.如果程序需要调用大量的动态函数,那么采用这样的
编程手段将是非常繁复的,所以我们必须使用一种更为聪明的办法,以减少代码量,提高工作效率.这就是现在我要举例介绍的《LINUX动态链接库高级应
用》.<br /> <br />注:本文举的例子类似上篇文章,只是文件的内容已做相应修改,裁减了不少.示例程序ady.c和两个动态函数的源程序getdate.c与gettime.c仅修改了头文件的名字,其内容不再列出.本文使用头文件为adatetime.h. </p>
				<p> <br />要想高效地应用LINUX动态链接库(尤其是用户自己编写的),需要做以下工作: <br /> <br />一、编写合格的动态链接库头文件 <br /> <br />C语言的头文件,可供一个或多个程序引用,里面一般定义程序所需的常量,自定义类型及函数原型说明等.其中的函数原型说明,则供编译器检查语法,用于排除引用参数时类型不一致的错误.只有编写合格的动态链接库头文件,程序员才能正确使用动态链接库内的函数. </p>
				<p> <br />动态链接库头文件要采用C语言标准格式,其中的动态函数原型定义,不必象上文介绍的那样用(*动态函数名)的描述形式.请看下面的例子:(每行开始的数字为所在行行号,为笔者添加,供注解使用) </p>
				<p> <br />1 /* adatetime.h : 纵横软件制作中心雨亦奇(<a href="mailto:zhsoft@371.net">zhsoft@371.net</a>)编写, 2002-03-06. */ <br />2  <br />3 #ifndef __DATETIME_H <br />4  <br />5 #define __DATETIME_H <br />6  <br />7 /* 日期结构 */ <br />8 typedef struct <br />9 { <br />10 int year; <br />11 int mon; <br />12 int day; <br />13 }DATETYPE; <br />14  <br />15 /* 时间结构 */ <br />16 typedef struct <br />17 { <br />18 char hour; <br />19 char min; <br />20 char sec; <br />21 }TIMETYPE; <br />22  <br />23 int getdate(DATETYPE *d); /* 取当前日期 */ <br />24 int gettime(TIMETYPE *t); /* 取当前时间 */ <br />25  <br />26 #endif <br />27  </p>
				<p> <br />注:与上文的datetime.h文件比较,从该头文件第23,24行可以看到,动态函数getdate,gettime的原型定义改变了,不再使用(*getdate),(*gettime)的格式了(这种格式使用较为罗嗦). </p>
				<p> </p>
				<p> <br />二、正确编译与命名动态链接库 </p>
				<p> </p>
				<p> <br />为了让GCC编译器生成动态链接库,编译时须加选项-shared.(这点须牢记) </p>
				<p> <br />LINUX系统中,为了让动态链接库能被系统中其它程序共享,其名字应符合“lib*.so*”这种格式.如果某个动态链接库不符合此格式,则LINUX的动态链接库自动装入程序(ld.so)将搜索不到此链接库,其它程序也无法共享之. </p>
				<p> <br />格式中,第一个*通常表示为简写的库名,第二个*通常表示为该库的版本号.如:在我的系统中,基本C动态链接库的名字为
libc.so.6,线程
pthread动态链接库的名字为libpthread.so.0等等.本文例子所生成的动态链接库的名字为libmy.so,虽没有版本号,但也符合所
要求的格式. </p>
				<p> <br />生成该动态链接库的维护文件makefile-lib内容如下: </p>
				<p> <br />1 # makefile : 纵横软件制作中心雨亦奇编写, 2002-03-07. <br />2  <br />3 all : libmy.so <br />4  <br />5 SRC = getdate.c gettime.c <br />6  <br />7 TGT = $(SRC:.c=.o) <br />8  <br />9 $(SRC) : adatetime.h <br />10 @touch $@ <br />11  <br />12 %.o : %.c <br />13 cc -c $? <br />14  <br />15 # 动态链接库(libmy.so)生成 <br />16 libmy.so : $(TGT) <br />17 cc -s -shared -o $@ $(TGT) <br />18  </p>
				<p> <br />运行命令: </p>
				<p> <br />$ make -f makefile-lib <br />$ </p>
				<p> <br />即生成libmy.so库. </p>
				<p> <br />注: 维护文件中,第17行用-shared选项以生成动态链接库,用-s选项以去掉目标文件中的符号表,从而减小文件长度. </p>
				<p> </p>
				<p> <br />三、共享动态链接库 </p>
				<p> </p>
				<p> <br />3.1 动态链接库配置文件 </p>
				<p> <br />为了让动态链接库为系统所使用,需要维护动态链接库的配置文件--/etc/ld.so.conf.此文件内,存放着可被LINUX共享
的动态链接库所在目录的名字(系统目录/lib,/usr/lib除外),各个目录名间以空白字符(空格,换行等)或冒号或逗号分隔.一般的LINUX发
行版中,此文件均含一个共享目录/usr/X11R6/lib,为X window窗口系统的动态链接库所在的目录. </p>
				<p> <br />下面看看我的系统中此文件的内容如何: </p>
				<p> <br /># cat /etc/ld.so.conf <br />/usr/X11R6/lib <br />/usr/zzz/lib <br /># </p>
				<p> <br />由上可以看出,该动态库配置文件中,增加了一个/usr/zzz/lib目录.这是我自己新建的共享库目录,下面存放我新开发的可供系统共享的动态链接库. </p>
				<p> <br />3.2 动态链接库管理命令 </p>
				<p> <br />为了让动态链接库为系统所共享,还需运行动态链接库的管理命令--ldconfig.此执行程序存放在/sbin目录下. </p>
				<p> <br />ldconfig命令的用途,主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内
所列的目录下,搜索出可共享的动态链接库(格式如前介绍,lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件.缓存文件默认
为 /etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表. </p>
				<p> <br />ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令. </p>
				<p> <br />ldconfig命令行用法如下: </p>
				<p> <br />ldconfig [-v|--verbose] [-n] [-N] [-X] [-f CONF] [-C CACHE] [-r
ROOT] [-l] [-p|--print-cache] [-c FORMAT] [--format=FORMAT] [-V]
[-?|--help|--usage] path... </p>
				<p> <br />ldconfig可用的选项说明如下: </p>
				<p> <br />(1) -v或--verbose : 用此选项时,ldconfig将显示正在扫描的目录及搜索到的动态链接库,还有它所创建的连接的名字. </p>
				<p> <br />(2) -n : 用此选项时,ldconfig仅扫描命令行指定的目录,不扫描默认目录(/lib,/usr/lib),也不扫描配置文件/etc/ld.so.conf所列的目录. </p>
				<p> <br />(3) -N : 此选项指示ldconfig不重建缓存文件(/etc/ld.so.cache).若未用-X选项,ldconfig照常更新文件的连接. </p>
				<p> <br />(4) -X : 此选项指示ldconfig不更新文件的连接.若未用-N选项,则缓存文件正常更新. </p>
				<p> <br />(5) -f CONF : 此选项指定动态链接库的配置文件为CONF,系统默认为/etc/ld.so.conf. </p>
				<p> <br />(6) -C CACHE : 此选项指定生成的缓存文件为CACHE,系统默认的是/etc/ld.so.cache,此文件存放已排好序的可共享的动态链接库的列表. </p>
				<p> <br />(7) -r ROOT :
此选项改变应用程序的根目录为ROOT(是调用chroot函数实现的).选择此项时,系统默认的配置文件/etc/ld.so.conf,实际对应的为
ROOT/etc/ld.so.conf.如用-r
/usr/zzz时,打开配置文件/etc/ld.so.conf时,实际打开的是/usr/zzz/etc/ld.so.conf文件.用此选项,可以
大大增加动态链接库管理的灵活性. </p>
				<p> <br />(8) -l : 通常情况下,ldconfig搜索动态链接库时将自动建立动态链接库的连接.选择此项时,将进入专家模式,需要手工设置连接.一般用户不用此项. </p>
				<p> <br />(9) -p或--print-cache : 此选项指示ldconfig打印出当前缓存文件所保存的所有共享库的名字. </p>
				<p> <br />(10) -c FORMAT 或 --format=FORMAT : 此选项用于指定缓存文件所使用的格式,共有三种:old(老格式),new(新格式)和compat(兼容格式,此为默认格式). </p>
				<p> <br />(11) -V : 此选项打印出ldconfig的版本信息,而后退出. </p>
				<p> <br />(12) -? 或 --help 或 --usage : 这三个选项作用相同,都是让ldconfig打印出其帮助信息,而后退出. </p>
				<p> <br />举三个例子: </p>
				<p> <br />例1: </p>
				<p> <br /># ldconfig -p <br />793 libs found in cache `/etc/ld.so.cache' <br />libzvt.so.2 (libc6) =&gt; /usr/lib/libzvt.so.2 <br />libzvt.so (libc6) =&gt; /usr/lib/libzvt.so <br />libz.so.1.1.3 (libc6) =&gt; /usr/lib/libz.so.1.1.3 <br />libz.so.1 (libc6) =&gt; /lib/libz.so.1 <br />...... <br /># </p>
				<p> <br />注:
有时候用户想知道系统中有哪些动态链接库,或者想知道系统中有没有某个动态链接库,这时,可用-p选项让ldconfig输出缓存文件中的动态链接库列
表,从而查询得到.例子中,ldconfig命令的输出结果第1行表明在缓存文件/etc/ld.so.cache中找到793个共享库,第2行开始便是
一系列共享库的名字及其全名(绝对路径).因为实际输出结果太多,为节省篇幅,以......表示省略的部分. </p>
				<p> </p>
				<p> <br />例2: </p>
				<p> <br /># ldconfig -v <br />/lib: <br />liby.so.1 -&gt; liby.so.1 <br />libnss_wins.so -&gt; libnss_wins.so <br />...... <br />/usr/lib: <br />libjscript.so.2 -&gt; libjscript.so.2.0.0 <br />libkspell.so.2 -&gt; libkspell.so.2.0.0 <br />...... <br />/usr/X11R6/lib: <br />libmej-0.8.10.so -&gt; libmej-0.8.10.so <br />libXaw3d.so.7 -&gt; libXaw3d.so.7.0 <br />...... <br /># </p>
				<p> <br />注:
ldconfig命令在运行正常的情况下,默认不输出什么东西.本例中用了-v选项,以使ldconfig在运行时输出正在扫描的目录及搜索到的共享库,
用户可以清楚地看到运行的结果.执行结束后,ldconfig将刷新缓存文件/etc/ld.so.cache. </p>
				<p> <br />例3: </p>
				<p> <br /># ldconfig /usr/zhsoft/lib <br /># </p>
				<p> <br />注: 当用户在某个目录下面创建或拷贝了一个动态链接库,若想使其被系统共享,可以执行一下"ldconfig
目录名"这个命令.此命令的功能在于让ldconfig将指定目录下的动态链接库被系统共享起来,意即:在缓存文件/etc/ld.so.cache中追
加进指定目录下的共享库.本例让系统共享了/usr/zhsoft/lib目录下的动态链接库.需要说明的是,如果此目录不在/lib,/usr/lib
及/etc/ld.so.conf文件所列的目录里面,则再度运行ldconfig时,此目录下的动态链接库可能不被系统共享了.</p>
				<p>3.3 动态链接库如何共享<br /> <br />了解了以上知识,我们可以采用以下三种方法来共享动态链接库:(注:均须在超级用户状态下操作,以我的动态链接库libmy.so共享过程为例) </p>
				<p> <br />(1)拷贝动态链接库到系统共享目录下,或在系统共享目录下为该动态链接库建立个连接(硬连接或符号连接均可,常用符号连接).这里说的
系统共享目录,指的是LINUX动态链接库存放的目录,它包含/lib,/usr/lib以及/etc/ld.so.conf文件内所列的一系列目录.
</p>
				<p> <br /># cp libmy.so /lib <br /># ldconfig <br /># </p>
				<p> <br />或: </p>
				<p> <br /># ln -s `pwd`/libmy.so /lib <br /># ldconfig <br /># </p>
				<p> <br />(2)将动态链接库所在目录名追加到动态链接库配置文件/etc/ld.so.conf中. </p>
				<p> <br /># pwd &gt;&gt; /etc/ld.so.conf <br /># ldconfig <br /># </p>
				<p> <br />(3)利用动态链接库管理命令ldconfig,强制其搜索指定目录,并更新缓存文件,便于动态装入. </p>
				<p> <br /># ldconfig `pwd` <br /># </p>
				<p> <br />需要说明的是,这种操作方法虽然有效,但效果是暂时的,供程序测试还可以,一旦再度运行ldconfig,则缓存文件内容可能改变,所需
的动态链接库可能不被系统共享了.与之相比较,前两种方法是可靠的方法,值得业已定型的动态链接库共享时采用.前两种方法还有一个特点,即最后一条命令都
是 ldconfig,也即均需要更新一下缓存文件,以确保动态链接库的共享生效. </p>
				<p> </p>
				<p> <br />四、含有动态函数的程序的编译 </p>
				<p> </p>
				<p> <br />4.1 防止编译因未指定动态链接库而出错 </p>
				<p> <br />当一个程序使用动态函数时,编译该程序时就必须指定含所用动态函数的动态链接库,否则编译将会出错退出.如本文示例程序ady.c的编译(未明确引用动态链接库libmy.so): </p>
				<p> <br /># cc -o ady ady.c <br />/tmp/ccL4FsJp.o: In function `main': <br />/tmp/ccL4FsJp.o(.text+0x43): undefined reference to `gettime' <br />collect2: ld returned 1 exit status <br /># </p>
				<p> <br />注: 因为ady.c所含的动态函数getdate,gettime不在系统函数库中,所以连接时出错. </p>
				<p> <br />4.2 编译时引用动态链接库的几种方式 </p>
				<p> <br />(1)当所用的动态链接库在系统目录(/lib,/usr/lib)下时,可用编译选项-l来引用.即: </p>
				<p> <br /># cc -lmy -o ady ady.c <br /># </p>
				<p> <br />注:编译时用-l选项引用动态链接库时,库名须使用其缩写形式.本例的my,表示引用libmy.so库.若引用光标库libncurses.so,须用-lncurses.注意,-l选项与参数之间不能有空格,否则会出错. </p>
				<p> <br />(2)当所用的动态链接库在系统目录(/lib,/usr/lib)以外的目录时,须用编译选项-L来指定动态链接库所在的目录(供编译器查找用),同时用-l选项指定缩写的动态链接库名.即: </p>
				<p> <br /># cc -L/usr/zzz/lib -lmy -o ady ady.c <br /># </p>
				<p> <br />(3)直接引用所需的动态链接库.即: </p>
				<p> <br /># cc -o ady ady.c libmy.so <br /># </p>
				<p> <br />或 </p>
				<p> <br /># cc -o ady ady.c /lib/libmy.so <br /># </p>
				<p> <br />等等.其中,动态链接库的库名可以采用相对路径形式(文件名不以/开头),也可采用绝对路径形式(文件名以/开头). </p>
				<p> </p>
				<p> <br />五、动态链接程序的运行与检查 </p>
				<p> </p>
				<p> <br />5.1 运行 </p>
				<p> <br />编译连接好含动态函数的程序后,就可以运行它了.动态链接程序因为共享了系统中的动态链接库,所以其空间占用很小.但这并不意味功能的减少,它的执行与静态连接的程序执行,效果完全相同.在命令提示符下键入程序名及相关参数后回车即可,如下例: </p>
				<p> <br />$ ady <br />动态链接库高级应用示范 <br />当前日期: 2002-03-11 <br />当前时间: 19:39:06 <br />$ </p>
				<p> <br />5.2 检查 </p>
				<p> <br />检查什么?检查动态链接程序究竟需要哪些共享库,系统中是否已有这些库,没有的话,用户好想办法把这些库装上. </p>
				<p> <br />怎么检查呢?这里,告诉你一个实用程序--ldd,这个程序就是专门用来检查动态链接程序依赖哪些共享库的. </p>
				<p> <br />ldd命令行用法如下: </p>
				<p> <br />ldd [--version] [-v|--verbose] [-d|--data-relocs] [-r|--function-relocs] [--help] FILE... </p>
				<p> <br />各选项说明如下: </p>
				<p> <br />(1) --version : 此选项用于打印出ldd的版本号. </p>
				<p> <br />(2) -v 或 --verbose : 此选项指示ldd输出关于所依赖的动态链接库的尽可能详细的信息. </p>
				<p> <br />(3) -d 或 --data-relocs : 此选项执行重定位,并且显示不存在的函数. </p>
				<p> <br />(4) -r 或 --function-relocs : 此选项执行数据对象与函数的重定位,同时报告不存在的对象. </p>
				<p> <br />(5) --help : 此选项用于打印出ldd的帮助信息. </p>
				<p> <br />注: 上述选项中,常用-v(或--verbose)选项. </p>
				<p> <br />ldd的命令行参数为FILE...,即一个或多个文件名(动态链接程序或动态链接库). </p>
				<p> <br />例1: </p>
				<p> <br />$ ldd ady <br />libmy.so =&gt; ./libmy.so (0x40026000) <br />libc.so.6 =&gt; /lib/libc.so.6 (0x40028000) <br />/lib/ld-linux.so.2 =&gt; /lib/ld-linux.so.2 (0x40000000) <br />$ </p>
				<p> <br />注:
每行=&gt;前面的,为动态链接程序所需的动态链接库的名字,而=&gt;后面的,则是运行时系统实际调用的动态链接库的名字,所需的动态链接库在系统
中不存在时,=&gt;后面将显示"not
found",括号所括的数字为虚拟的执行地址.本例列出ady所需的三个动态链接库,其中libmy.so为自己新建的动态链接库,而
libc.so.6与/lib/ld-linux.so.2均为系统的动态链接库,前一个为基本C库,后一个动态装入库(用于动态链接库的装入及运行).
</p>
				<p> <br />例2: </p>
				<p> <br />$ ldd -v ady <br />libmy.so =&gt; ./libmy.so (0x40026000) <br />libc.so.6 =&gt; /lib/libc.so.6 (0x40028000) <br />/lib/ld-linux.so.2 =&gt; /lib/ld-linux.so.2 (0x40000000) </p>
				<p> <br />Version information: <br />./ady: <br />libc.so.6 (GLIBC_2.1.3) =&gt; /lib/libc.so.6 <br />libc.so.6 (GLIBC_2.0) =&gt; /lib/libc.so.6 <br />./libmy.so: <br />libc.so.6 (GLIBC_2.1.3) =&gt; /lib/libc.so.6 <br />libc.so.6 (GLIBC_2.0) =&gt; /lib/libc.so.6 <br />/lib/libc.so.6: <br />ld-linux.so.2 (GLIBC_2.1.1) =&gt; /lib/ld-linux.so.2 <br />ld-linux.so.2 (GLIBC_2.2.3) =&gt; /lib/ld-linux.so.2 <br />ld-linux.so.2 (GLIBC_2.1) =&gt; /lib/ld-linux.so.2 <br />ld-linux.so.2 (GLIBC_2.2) =&gt; /lib/ld-linux.so.2 <br />ld-linux.so.2 (GLIBC_2.0) =&gt; /lib/ld-linux.so.2 <br />$ </p>
				<p> <br />注:本例用-v选项以显示尽可能多的信息,所以例中除列出ady所需要的动态链接库外,还列出了程序所需动态链接库版本方面的信息. </p>
				<p> <br />小结:
在LINUX动态链接库的高级应用中,关键有两点,一是如何让动态链接库为LINUX系统所共享,二是编译连接程序时如何做.让动态链接库为系统所共享,
主要是用ldconfig管理命令,维护好系统共享库的缓存文件/etc/ld.so.cache.编译连接时如何做?注意连接上所用的动态链接库就可以
了.LINUX动态链接库的高级应用,用一用就明白:其实,就是这么简单! <br />点击这里<a href="http://www.ccw.com.cn/htm/app/down/ld-source.tgz"><font color="#0000ff">下载示例程序</font></a>。 </p>
		</span>
		<!--正文内容结束-->
		<script type="text/javascript"><![CDATA[
cpro_client='sayyescpr';
cpro_cbd='#trans'; 
cpro_cbg='#trans'; 
cpro_ctitle='#0000ff'; 
cpro_cdesc='#444444'; 
cpro_curl='#FFFFFF'; 
cpro_clink='#FFFFFF'; 
cpro_flush=4; 
cpro_w=500; 
cpro_h=200; 
cpro_template='text_default_500_200'; 
]]&gt;</script>
		<script language="JavaScript" type="text/javascript" src="http://cpro.baidu.com/cpro/ui/cp.js">
		</script>
		<div style="display: none;">baidu</div>
		<iframe marginwidth="0" marginheight="0" allowtransparency="" src="http://cpro.baidu.com/cpro/ui/uijs.php?n=sayyescpr&amp;tn=text_default_500_200&amp;rsi1=200&amp;rsi0=500&amp;rad=&amp;rss0=%23trans&amp;rss1=%23trans&amp;rss2=%230000ff&amp;rss3=%23444444&amp;rss4=%23FFFFFF&amp;rss5=%23FFFFFF&amp;rsi5=4&amp;ts=1&amp;at=&amp;cn=3&amp;word=http%3A%2F%2Fwww.knowsky.com%2F303053.html&amp;refer=http%3A%2F%2Fwww.baidu.com%2Fs%3Fie%3Dgb2312%26bs%3Dgcc%2B%25B6%25AF%25CC%25AC%25C1%25B4%25BD%25D3%25BF%25E2%26sr%3D%26z%3D%26cl%3D3%26f%3D8%26wd%3Dgcc%2B%25CA%25B9%25D3%25C3%25B6%25AF%25CC%25AC%25C1%25B4%25BD%25D3%25BF%25E2%26ct%3D0&amp;fv=0&amp;if=0" align="center,center" frameborder="0" height="200" scrolling="no" width="500">
		</iframe>
<img src ="http://www.cppblog.com/mydriverc/aggbug/33167.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-29 02:05 <a href="http://www.cppblog.com/mydriverc/articles/33167.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>动态联接库</title><link>http://www.cppblog.com/mydriverc/articles/33166.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 28 Sep 2007 18:03:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/33166.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/33166.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/33166.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/33166.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/33166.html</trackback:ping><description><![CDATA[ 在Linux下面,如果要编译一个C语言源程序,我们要使用GNU的gcc编译器. 下面我们以一个实例来说明如何使用gcc编译器. <br /> 假设我们有下面一个非常简单的源程序(hello.c): <br /> int main(int argc,char **argv) <br /> { <br /> printf("Hello Linux\n"); <br /> } <br /><br /> 要编译这个程序,我们只要在命令行下执行: <br /> gcc -o hello hello.c <br />
gcc 编译器就会为我们生成一个hello的可执行文件.执行./hello就可以看到程序的输出结果了.命令行中
gcc表示我们是用gcc来编译我们的源程序,-o 选项表示我们要求编译器给我们输出的可执行文件名为hello
而hello.c是我们的源程序文件. <br /> gcc编译器有许多选项,一般来说我们只要知道其中的几个就够了.
-o选项我们已经知道了,表示我们要求输出的可执行文件名. -c选项表示我们只要求编译器输出目标代码,而不必要输出可执行文件.
-g选项表示我们要求编译器在编译的时候提供我们以后对程序进行调试的信息. <br /> 知道了这三个选项,我们就可以编译我们自己所写的简单的源程序了,如果你想要知道更多的选项,可以查看gcc的帮助文档,那里有着许多对其它选项的详细说明. <br /> 2.Makefile的编写 <br /> 假设我们有下面这样的一个程序,源代码如下: <br /><br /> /* main.c */ <br /> #include "mytool1.h" <br /> #include "mytool2.h" <br /><br /> int main(int argc,char **argv) <br /> { <br /> mytool1_print("hello"); <br /> mytool2_print("hello"); <br /> } <br /><br /> /* mytool1.h */ <br /> #ifndef _MYTOOL_1_H <br /> #define _MYTOOL_1_H <br /><br /> void mytool1_print(char *print_str); <br /><br /> #endif <br /><br /> /* mytool1.c */ <br /> #include "mytool1.h" <br /> void mytool1_print(char *print_str) <br /> { <br /> printf("This is mytool1 print %s\n",print_str); <br /> } <br /><br /> /* mytool2.h */ <br /> #ifndef _MYTOOL_2_H <br /> #define _MYTOOL_2_H <br /><br /> void mytool2_print(char *print_str); <br /><br /> #endif <br /><br /> /* mytool2.c */ <br /> #include "mytool2.h" <br /> void mytool2_print(char *print_str) <br /> { <br /> printf("This is mytool2 print %s\n",print_str); <br /> } <br /><br /><br /> 当然由于这个程序是很短的我们可以这样来编译 <br /> gcc -c main.c <br /> gcc -c mytool1.c <br /> gcc -c mytool2.c <br /> gcc -o main main.o mytool1.o mytool2.o <br />
这
样的话我们也可以产生main程序,而且也不时很麻烦.但是如果我们考虑一下如果有一天我们修改了其中的一个文件(比如说mytool1.c)那么我们难
道还要重新输入上面的命令?也许你会说,这个很容易解决啊,我写一个SHELL脚本,让她帮我去完成不就可以了.是的对于这个程序来说,是可以起到作用
的.但是当我们把事情想的更复杂一点,如果我们的程序有几百个源程序的时候,难道也要编译器重新一个一个的去编译? <br />
为此,聪明的程序员们想出
了一个很好的工具来做这件事情,这就是make.我们只要执行以下make,就可以把上面的问题解决掉.在我们执行make之前,我们要先编写一个非常重
要的文件.--Makefile.对于上面的那个程序来说,可能的一个Makefile的文件是: <br /> # 这是上面那个程序的Makefile文件 <br /> main:main.o mytool1.o mytool2.o <br /> gcc -o main main.o mytool1.o mytool2.o <br /> main.o:main.c mytool1.h mytool2.h <br /> gcc -c main.c <br /> mytool1.o:mytool1.c mytool1.h <br /> gcc -c mytool1.c <br /> mytool2.o:mytool2.c mytool2.h <br /> gcc -c mytool2.c <br /><br /> 有了这个Makefile文件,不过我们什么时候修改了源程序当中的什么文件,我们只要执行make命令,我们的编译器都只会去编译和我们修改的文件有关的文件,其它的文件她连理都不想去理的. <br /> 下面我们学习Makefile是如何编写的. <br /> 在Makefile中也#开始的行都是注释行.Makefile中最重要的是描述文件的依赖关系的说明.一般的格式是: <br /> target: components <br /> TAB rule <br /><br /> 第一行表示的是依赖关系.第二行是规则. <br /> 比如说我们上面的那个Makefile文件的第二行 <br /> main:main.o mytool1.o mytool2.o <br />
表 示我们的目标(target)main的依赖对象(components)是main.o mytool1.o mytool2.o
当倚赖的对象在目标修改后修改的话,就要去执行规则一行所指定的命令.就象我们的上面那个Makefile第三行所说的一样要执行 gcc -o
main main.o mytool1.o mytool2.o 注意规则一行中的TAB表示那里是一个TAB键 <br /> Makefile有三个非常有用的变量.分别是$@,$^,$&lt;代表的意义分别是: <br /> $@--目标文件,$^--所有的依赖文件,$&lt;--第一个依赖文件. <br /> 如果我们使用上面三个变量,那么我们可以简化我们的Makefile文件为: <br /> # 这是简化后的Makefile <br /> main:main.o mytool1.o mytool2.o <br /> gcc -o $@ $^ <br /> main.o:main.c mytool1.h mytool2.h <br /> gcc -c $&lt; <br /> mytool1.o:mytool1.c mytool1.h <br /> gcc -c $&lt; <br /> mytool2.o:mytool2.c mytool2.h <br /> gcc -c $&lt; <br /><br /> 经过简化后我们的Makefile是简单了一点,不过人们有时候还想简单一点.这里我们学习一个Makefile的缺省规则 <br /><b>.c.o: <br /> gcc -c $&lt; </b><br /><br /> 这个规则表示所有的 .o文件都是依赖与相应的.c文件的.例如mytool.o依赖于mytool.c这样Makefile还可以变为: <br /> # 这是再一次简化后的Makefile <br /> main:main.o mytool1.o mytool2.o <br /> gcc -o $@ $^ <br /> .c.o: <br /> gcc -c $&lt; <br /><br /> 好了,我们的Makefile 也差不多了,如果想知道更多的关于Makefile规则可以查看相应的文档. <br /> 3.程序库的链接 <br /> 试着编译下面这个程序 <br /><br /> /* temp.c */ <br /> #include <br /><br /> int main(int argc,char **argv) <br /> { <br /> double value; <br /> printf("value:%f\n",value); <br /> } <br /><br /> 这个程序相当简单,但是当我们用 gcc -o temp temp.c 编译时会出现下面所示的错误. <br /> /tmp/cc33Kydu.o: In function `main: <br /> /tmp/cc33Kydu.o(.text+0xe): undefined reference to `log <br /> collect2: ld returned 1 exit status <br /><br />
出
现这个错误是因为编译器找不到log的具体实现.虽然我们包括了正确的头文件,但是我们在编译的时候还是要连接确定的库.在Linux下,为了使用数学函
数,我们必须和数学库连接,为此我们要加入 -lm 选项. gcc -o temp temp.c
-lm这样才能够正确的编译.也许有人要问,前面我们用printf函数的时候怎么没有连接库呢?是这样的,对于一些常用的函数的实现,gcc编译器会自
动去连接一些常用库,这样我们就没有必要自己去指定了. 有时候我们在编译程序的时候还要指定库的路径,这个时候我们要用到编译器的
-L选项指定路径.比如说我们有一个库在 /home/hoyt/mylib下,这样我们编译的时候还要加上
-L/home/hoyt/mylib.对于一些标准库来说,我们没有必要指出路径.只要它们在起缺省库的路径下就可以了.系统的缺省库的路径/lib
/usr/lib /usr/local/lib 在这三个路径下面的库,我们可以不指定路径. <br />
还有一个问题,有时候我们使用了某个函数,但
是我们不知道库的名字,这个时候怎么办呢?很抱歉,对于这个问题我也不知道答案,我只有一个傻办法.首先,我到标准库路径下面去找看看有没有和我用的函数
相关的库,我就这样找到了线程(thread)函数的库文件(libpthread.a).
当然,如果找不到,只有一个笨方法.比如我要找sin这个函数所在的库. 就只好用 nm -o /lib/*.so|grep
sin&gt;~/sin 命令,然后看~/sin文件,到那里面去找了.
在sin文件当中,我会找到这样的一行libm-2.1.2.so:00009fa0 W sin 这样我就知道了sin在
libm-2.1.2.so库里面,我用 -lm选项就可以了(去掉前面的lib和后面的版本标志,就剩下m了所以是 -lm).
如果你知道怎么找,请赶快告诉我,我回非常感激的.谢谢! <br /> 4.程序的调试 <br /> 我们编写的程序不太可能一次性就会成功的,在我们的程序当中,会出现许许多多我们想不到的错误,这个时候我们就要对我们的程序进行调试了. <br />
最 常用的调试软件是gdb.如果你想在图形界面下调试程序,那么你现在可以选择xxgdb.记得要在编译的时候加入
-g选项.关于gdb的使用可以看gdb的帮助文件.由于我没有用过这个软件,所以我也不能够说出如何使用.
不过我不喜欢用gdb.跟踪一个程序是很烦的事情,我一般用在程序当中输出中间变量的值来调试程序的.当然你可以选择自己的办法,没有必要去学别人的.现
在有了许多IDE环境,里面已经自己带了调试器了.你可以选择几个试一试找出自己喜欢的一个用. <br /><br /> 5.头文件和系统求助 <br /> 有时候我们只知道一个函数的大概形式,不记得确切的表达式,或者是不记得着函数在那个头文件进行了说明.这个时候我们可以求助系统. <br />
比 如说我们想知道fread这个函数的确切形式,我们只要执行 man fread
系统就会输出着函数的详细解释的.和这个函数所在的头文件说明了. 如果我们要write这个函数的说明,当我们执行man
write时,输出的结果却不是我们所需要的.
因为我们要的是write这个函数的说明,可是出来的却是write这个命令的说明.为了得到write的函数说明我们要用 man 2 write.
2表示我们用的write这个函数是系统调用函数,还有一个我们常用的是3表示函数是C的库函数. <br /> 记住不管什么时候,man都是我们的最好助手. <br /><br /><br /> ---------------------------------------------------------------- <br /> 好了,这一章就讲这么多了,有了这些知识我们就可以进入激动人心的Linux下的C程序探险活动. <br /><br /><br /><p id="TBPingURL">Trackback: http://tb.donews.net/TrackBack.aspx?PostId=75642</p><img src ="http://www.cppblog.com/mydriverc/aggbug/33166.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-29 02:03 <a href="http://www.cppblog.com/mydriverc/articles/33166.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux动态链接库(标准对象)编程入门</title><link>http://www.cppblog.com/mydriverc/articles/33164.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 28 Sep 2007 17:45:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/33164.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/33164.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/33164.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/33164.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/33164.html</trackback:ping><description><![CDATA[
		<font id="zoom" class="f14">Linux动态链接库编程入门</font>
		<p>
				<font id="zoom" class="f14">　　动态链接库是一种通用的软件组件技术，是多种操作系统中提供基本服务的方式。比如Win32内核就是3个DLL文件构成。这种技术在Linux操作系统下也有对应的实现，就是Linux标准对象Standard Ojbect，对应的文件扩展名为.so。</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　下面通过一个简单的例子开始介绍Linux标准对象。</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　我们的标准对象文件含有一个函数，不需要声明export导出符号，只需要编译器设置即可。如下：</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　#include </font>
		</p>
		<p>
				<font id="zoom" class="f14">　　#include </font>
		</p>
		<p>
				<font id="zoom" class="f14">　　void show() {</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　printf("Standard Object by gashero\n");</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　}</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　保存为myso.c文件，按照如下编译：</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　$ gcc -fPIC -shared -o libmyso.so myso.c</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　执行生成一个libmyso.so文件，按照Linux标准对象的命名惯例，应该在库名称之前加上"lib"前缀，尽管不是必须的。编译开关-fPIC代表函数符号可以重定向，-shared代表编译结果是一个标准对象。</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　不同于Win32DLL，Linux标准对象中的所有函数都是直接导出的，都可以被调用程序所访问。下面我们编写调用程序：</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　#include </font>
		</p>
		<p>
				<font id="zoom" class="f14">　　int main() {</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　printf("Invoke my so\n");</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　show();</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　return 0;</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　}</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　保存为invoke.c，按照如下gcc开关编译：</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　$ gcc -o test invoke.c ./libmyso.so</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　编译生成test可执行文件。如上编译条件的最后一条需要是所调用的标准对象文件名，注意必须含有路径。如果只是使用libmyso.so，则必须确保这个文件在可访问的PATH下面。本例所使用的文件名"./libmyso.so"是当前路径下的，使用了相对路径。</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　如下测试结果：</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　$ ./test</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　Invoke my so</font>
		</p>
		<p>
				<font id="zoom" class="f14">　　Standard Object by gashero</font>
		</p>
<img src ="http://www.cppblog.com/mydriverc/aggbug/33164.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-29 01:45 <a href="http://www.cppblog.com/mydriverc/articles/33164.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用gcc编译生成动态链接库*.so文件的方法。</title><link>http://www.cppblog.com/mydriverc/articles/33163.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 28 Sep 2007 17:31:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/33163.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/33163.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/33163.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/33163.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/33163.html</trackback:ping><description><![CDATA[动态库*.so在linux下用c和c++编程时经常会碰到，最近在网站找了几篇文章介绍动态库的编译和链接，总算搞懂了这个之前一直不太了解得东东，这里做个笔记，也为其它正为动态库链接库而苦恼的兄弟们提供一点帮助。<br />1、动态库的编译<br /><br />下面通过一个例子来介绍如何生成一个动态库。这里有一个头文件：so_test.h，三个.c文件：test_a.c、test_b.c、test_c.c，我们将这几个文件编译成一个动态库：libtest.so。<br /><br />so_test.h：<br /><br /><br /><br />#include <br /><br />#include <br /><br /><br /><br />void test_a();<br /><br />void test_b();<br /><br />void test_c();<br /><br /><br /><br />test_a.c：<br /><br /><br /><br />#include "so_test.h"<br /><br /><br /><br />void test_a()<br /><br />{<br /><br />    printf("this is in test_a...\n");<br /><br />}<br /><br /><br /><br />test_b.c：<br /><br /><br /><br />#include "so_test.h"<br /><br /><br /><br />void test_b()<br /><br />{<br /><br />    printf("this is in test_b...\n");<br /><br />}<br /><br /><br /><br />test_a.c：<br /><br /><br /><br />#include "so_test.h"<br /><br /><br /><br />void test_c()<br /><br />{<br /><br />    printf("this is in test_c...\n");<br /><br />}<br /><br /><br /><br />将这几个文件编译成一个动态库：libtest.so<br /><br /><br /><br />$ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so<br /><br /><br />2、动态库的链接<br /><br />在1、中，我们已经成功生成了一个自己的动态链接库libtest.so，下面我们通过一个程序来调用这个库里的函数。程序的源文件为：test.c。<br /><br />test.c：<br /><br /><br /><br />#include "so_test.h"<br /><br /><br /><br />int main()<br /><br />{<br /><br />    test_a();<br /><br />    test_b();<br /><br />    test_c();<br /><br /><br /><br />    return 0;<br /><br />}<br /><br /><br /><br />l         将test.c与动态库libtest.so链接生成执行文件test：<br /><br /><br /><br />$ gcc test.c -L. -ltest -o test<br /><br /><br /><br />l         测试是否动态连接，如果列出libtest.so，那么应该是连接正常了<br /><br /><br /><br />$ ldd test<br /><br /><br /><br />l         执行test，可以看到它是如何调用动态库中的函数的。<br />3、编译参数解析<br />最主要的是GCC命令行的一个选项:<br />          -shared 该选项指定生成动态连接库（让连接器生成T类型的导出符号表，有时候也生成弱连接W类型的导出符号），不用该标志外部程序无法连接。相当于一个可执行文件<br /><br />l         -fPIC：表示编译为位置独立的代码，不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要，而不能达到真正代码段共享的目的。<br /><br />l         -L.：表示要连接的库在当前目录中<br /><br />l         -ltest：编译器查找动态连接库时有隐含的命名规则，即在给出的名字前面加上lib，后面加上.so来确定库的名称<br /><br />l         LD_LIBRARY_PATH：这个环境变量指示动态连接器可以装载动态库的路径。<br /><br />l         当然如果有root权限的话，可以修改/etc/ld.so.conf文件，然后调用 /sbin/ldconfig来达到同样的目的，不过如果没有root权限，那么只能采用输出LD_LIBRARY_PATH的方法了。<br />4、注意<br /><br />
调用动态库的时候有几个问题会经常碰到，有时，明明已经将库的头文件所在目录 通过 “-I” include进来了，库所在文件通过
“-L”参数引导，并指定了“-l”的库名，但通过ldd命令察看时，就是死活找不到你指定链接的so文件，这时你要作的就是通过修改
LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了。<br /><br />- 作者： 像风一样流浪 访问统计：31　2005年10月11日, 星期二 17:08 加入博采<img src ="http://www.cppblog.com/mydriverc/aggbug/33163.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-29 01:31 <a href="http://www.cppblog.com/mydriverc/articles/33163.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LINUX系统中动态链接库的创建与使用</title><link>http://www.cppblog.com/mydriverc/articles/33162.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 28 Sep 2007 17:21:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/33162.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/33162.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/33162.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/33162.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/33162.html</trackback:ping><description><![CDATA[大家都知道，在
WINDOWS系统中有很多的动态链接库(以.DLL为后缀的文件，DLL即Dynamic Link
Library)。这种动态链接库，和静态函数库不同，它里面的函数并不是执行程序本身的一部分，而是根据执行程序需要按需装入，同时其执行代码可在多个
执行程序间共享，节省了空间，提高了效率，具备很高的灵活性，得到越来越多程序员和用户的青睐。那么，在LINUX系统中有无这样的函数库呢？<table align="center" width="620"><tbody><tr><td class="a14">
答案是肯定的，LINUX的动态链接库不仅有，而且为数不少。在/lib目录下，就有许多以.so作后缀的文件，这就是LINUX系统应用的动态链接库，
只不过与WINDOWS叫法不同，它叫so，即Shared Object，共享对象。(在LINUX下，静态函数库是以.a作后缀的)
X-WINDOW作为LINUX下的标准图形窗口界面，它本身就采用了很多的动态链接库(在/usr/X11R6/lib目录下)，以方便程序间的共享，
节省占用空间。著名的APACHE网页服务器，也采用了动态链接库，以便扩充程序功能。你只需将PHP动态链接库拷到其共享目录，修改一下配置，
APACHE就可以支持PHP网页了。如果你愿意，可以自己编写动态链接库，让APACHE支持你自己定义的网页格式。这就是动态链接的好处。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"><b>1、LINUX下动态链接库的创建</b></td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> 
在LINUX系统下，创建动态链接库是件再简单不过的事情。只要在编译函数库源程序时加上-shared选项即可，这样所生成的执行程序即为动态链接库。从某种意义上来说，动态链接库也是一种执行程序。按一般规则，程序名应带.so后缀。下面举个例子说说。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> 
我准备编写两个函数，一个用于查询当前日期getdate，一个用于查询当前时间gettime，并将这两个函数存于动态链接库my.so中。为此，需要做以下几项工作。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> 
1.1 编写用户接口文件datetime.h，内容如下(每行前面的数字为行号)：</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
---------------------------------------------------------------------- </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     1 /* datetime.h : 纵横软件制作中心雨亦奇编写, 2001-06-28. */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     2 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     3 #ifndef __DATETIME_H</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     4 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     5 #define __DATETIME_H</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     6 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     7 /* 日期结构 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     8 typedef struct</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     9 {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    10 int    year;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    11 int    mon;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    12 int    day;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    13 }DATETYPE;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    14 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    15 /* 时间结构 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    16 typedef struct</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    17 {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    18 char   hour;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    19 char   min;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    20 char   sec;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    21 }TIMETYPE;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    22 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    23 /* 函数原型说明 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    24 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    25 #ifdef  SHARED</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    26 int     (*getdate)(DATETYPE *d);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    27 #else</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    28 int     getdate(DATETYPE *d);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    29 #endif</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    30 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    31 #ifdef  SHARED</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    32 int     (*gettime)(TIMETYPE *t);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    33 #else</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    34 int     gettime(TIMETYPE *t);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    35 #endif</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    36 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    37 #endif</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    38 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
---------------------------------------------------------------------- </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    这个用户接口文件中，先定义了日期与时间结构，接着定义一下函数的原型。动态函数与静态函数的原型说明不同的是，动态函数应使用(*函数名)的形式，以便引用其指针。若要引用文件中的动态函数说明，用户应该定义一下SHARED宏，这样才能使用。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    1.2 编写getdate.c，源程序如下：</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
---------------------------------------------------------------------- </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     1 /* getdate.c : 纵横软件制作中心雨亦奇编写, 2001-06-28. */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     2 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     3 #include        "time.h"</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     4 #include        "datetime.h"</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     5 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     6 int getdate(DATETYPE *d)</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     7 {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     8 long           ti;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     9 struct tm      *tm;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    10 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    11 time(&amp;ti);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    12 tm=localtime(&amp;ti);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    13 d-&gt;year=tm-&gt;tm_year+1900;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    14 d-&gt;mon=tm-&gt;tm_mon+1;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    15 d-&gt;day=tm-&gt;tm_mday;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    16 }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    17 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
---------------------------------------------------------------------- </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    在getdate函数中，先调用time取得以秒计的系统时间，再用localtime函数转换一下时间结构，最后调整得到正确的日期。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    1.3 编写gettime.c，源程序如下：</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
---------------------------------------------------------------------- </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     1 /* gettime.c : 纵横软件制作中心雨亦奇编写, 2001-06-28. */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     2 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     3 #include        "time.h"</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     4 #include        "datetime.h"</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     5 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     6 int gettime(TIMETYPE *t)</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     7 {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     8 long           ti;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     9 struct tm      *tm;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    10</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    11 time(&amp;ti);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    12 tm=localtime(&amp;ti);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    13 t-&gt;hour=tm-&gt;tm_hour;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    14 t-&gt;min=tm-&gt;tm_min;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    15 t-&gt;sec=tm-&gt;tm_sec;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    16 }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    17 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
---------------------------------------------------------------------- </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    gettime函数与getdate函数相仿，先用time函数取得以秒计的系统时间，再用localtime函数转换一下时间结构，最后返回当前的时间(不需调整)。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    1.4 编写维护文件makefile-lib，内容如下：</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
---------------------------------------------------------------------- </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     1 # makefile-lib : 纵横软件制作中心雨亦奇编写, 2001-06-28.</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     2 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     3 all : my.so</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     4 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     5 SRC = getdate.c gettime.c</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     6 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     7 TGT = $(SRC:.c=.o)</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     8 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     9 $(SRC) : datetime.h</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    10         @touch $@</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    11 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    12 %.o : %.c</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    13         cc -c $?</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    14 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    15 # 动态函数库(my.so)生成</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    16 my.so : $(TGT)</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    17         cc -shared -o $@ $(TGT)</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    18 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
---------------------------------------------------------------------- </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">
编写维护文件的目的，在于方便程序员维护程序，尤其是维护比较大的工程项目。一个素质良好的程序员应该学会熟练地编写维护文件makefile。定义了文
件间的依赖关系后，一旦源文件发生变化，仅需make一下，其目标文件维护代码会自动执行，从而自动更新目标文件，减少了许多工作量。注意:
每行维护代码必须以TAB(跳格键)开始，不是的话make时将出错。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">
本维护文件第1行是注释行，以#号开头；文件第3行定义所有需要维护的函数库；第5行定义相关源程序文件；第7行定义目标文件；第9-10行说明所有源程
序依赖于datetime.h头文件，并有相应维护代码，即touch一下，更新一下源文件的时间；第12-13行定义.o文件依赖于相应的.c文件，并
指定了维护代码，即用cc编译一下；第16-17行定义共享库my.so依赖的目标文件，维护代码中用-shared编译选项，以生成动态链接库
my.so。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    1.5 运行make -f makefile-lib 命令</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">
make运行后，动态链接库my.so就产生了，我们就可以在程序中调用了。如果想让系统所有用户都可以使用，则应以root用户登录系统，将这个库拷贝
到/lib目录下(命令：cp my.so /lib)，或者在/lib目录下建个符号连接即可(命令：ln -s `pwd`/my.so
/lib)。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"><b>2、LINUX下动态链接库的使用</b></td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> 
    2.1 重要的dlfcn.h头文件</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    LINUX下使用动态链接库，源程序需要包含dlfcn.h头文件，此文件定义了调用动态链接库的函数的原型。下面详细说明一下这些函数。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    2.1.1 dlerror</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    原型为: const char *dlerror(void);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    当动态链接库操作函数执行失败时，dlerror可以返回出错信息，返回值为NULL时表示操作函数执行成功。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    2.1.2 dlopen</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    原型为: void *dlopen (const char *filename, int flag);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    dlopen用于打开指定名字(filename)的动态链接库，并返回操作句柄。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    filename: 如果名字不以/开头，则非绝对路径名，将按下列先后顺序查找该文件。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    (1) 用户环境变量中的LD_LIBRARY值；</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    (2) 动态链接缓冲文件/etc/ld.so.cache</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    (3) 目录/lib，/usr/lib</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    flag表示在什么时候解决未定义的符号(调用)。取值有两个:</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    1) RTLD_LAZY : 表明在动态链接库的函数代码执行时解决。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    2) RTLD_NOW  : 表明在dlopen返回前就解决所有未定义的符号，一旦未解决，dlopen将返回错误。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    dlopen调用失败时，将返回NULL值，否则返回的是操作句柄。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    2.1.3 dlsym : 取函数执行地址</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    原型为: void *dlsym(void *handle, char *symbol);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    dlsym根据动态链接库操作句柄(handle)与符号(symbol)，返回符号对应的函数的执行代码地址。由此地址，可以带参数执行相应的函数。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    如程序代码: void (*add)(int x,int y);  /* 说明一下要调用的动态函数add */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
                add=dlsym("xxx.so","add"); /* 打开xxx.so共享库,取add函数地址 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
                add(89,369);               /* 带两个参数89和369调用add函数 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    2.1.4 dlclose : 关闭动态链接库</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    原型为: int dlclose (void *handle);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    dlclose用于关闭指定句柄的动态链接库，只有当此动态链接库的使用计数为0时,才会真正被系统卸载。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    2.2 在程序中使用动态链接库函数</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    2.2.1 程序范例</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    下面的程序装载了动态链接库my.so，并用getdate,gettime取得当前日期与时间后输出。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
---------------------------------------------------------------------- </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     1 /************************************/</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     2 /* 文件名称: dy.c                   */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     3 /* 功能描述: 动态链接库应用示范程序 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     4 /* 程序编写: 纵横软件制作中心雨亦奇 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     5 /* 编写时间: 2001-06-28             */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     6 /************************************/</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     7 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     8 #include "stdio.h" /* 包含标准输入输出文件 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     9 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    10 #include "dlfcn.h" /* 包含动态链接功能接口文件 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    11 #define SOFILE "./my.so" /* 指定动态链接库名称 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    12 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    13 #define SHARED /* 定义宏,确认共享,以便引用动态函数 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    14 #include "datetime.h" /* 包含用户接口文件 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    15 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    16 main()</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    17 {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    18 DATETYPE d;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    19 TIMETYPE t;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    20 void *dp;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    21 char *error;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    22 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    23 puts("动态链接库应用示范");</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    24 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    25 dp=dlopen(SOFILE,RTLD_LAZY); /* 打开动态链接库 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    26 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    27 if (dp==NULL) /* 若打开失败则退出 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    28 {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    29   fputs(dlerror(),stderr);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    30   exit(1);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    31 }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    32 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    33 getdate=dlsym(dp,"getdate"); /* 定位取日期函数 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    34 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    35 error=dlerror(); /* 检测错误 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    36 if (error) /* 若出错则退出 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    37 {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    38   fputs(error,stderr);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    39   exit(1);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    40 }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    41 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    42 getdate(&amp;d); /* 调用此共享函数 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    43 printf("当前日期: %04d-%02d-%02d\n",d.year,d.mon,d.day);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    44 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    45 gettime=dlsym(dp,"gettime"); /* 定位取时间函数 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    46 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    47 error=dlerror(); /* 检测错误 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    48 if (error) /* 若出错则退出 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    49 {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    50   fputs(error,stderr);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    51   exit(1);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    52 }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    53 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    54 gettime(&amp;t); /* 调用此共享函数 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    55 printf("当前时间: %02d:%02d:%02d\n",t.hour,t.min,t.sec);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    56 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    57 dlclose(dp); /* 关闭共享库 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    58 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    59 exit(0); /* 成功返回 */</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    60 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    61 } </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
---------------------------------------------------------------------- </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    程序说明:</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第8行: 包含标准输入输出头文件,因为程序中使用了printf,puts,fputs等标准输入输出函数,需要让编译器根据头文件中函数的原型,检查一下语法;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第10-11行: 包含动态链接库功能头文件,并定义动态链接库名称;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第13-14行: 定义宏SHARED以便引用14行的头文件datetime.h中的动态函数说明;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第25行: 用dlopen打开SOFILE共享库,返回句柄dp;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第27-31行: 检测dp是否为空,为空则显示错误后退出;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第33行: 用dlsym取得getdate函数动态地址;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第35-40行: 如果dlerror返回值不为空,则dlsym执行出错,程序显示错误后退出;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第42-43行: 执行getdate调用,输出当前日期;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第45行: 用dlsym取得gettime函数动态地址;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第47-52行: 如果dlerror返回值不为空,则dlsym执行出错,程序显示错误后退出;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第54-55行: 执行gettime调用,输出当前时间;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第57行: 用dlclose关闭dp所指示的动态链接库;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第59行: 程序退出,返回0值。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    2.2.2 编写维护文件</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    维护文件makefile内容如下:</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
----------------------------------------------------------------------</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     1 # makefile : 纵横软件制作中心雨亦奇编写, 2001-06-28.</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     2 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     3 all : dy</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     4 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     5 DYSRC = dy.c</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     6 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     7 DYTGT = $(DYSRC:.c=.o)</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     8 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
     9 %.o : %.c</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    10 cc -c $?</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    11 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    12 # 动态库应用示范程序</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    13 dy : $(DYTGT)</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    14 cc -rdynamic -s -o $@ $(DYTGT) -ldl</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14" bgcolor="#c0c0c0">  
    15 </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
----------------------------------------------------------------------</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    维护文件说明:</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第3行: 定义所有需要维护的模块;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第5行: 定义源程序;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第7行: 定义目标文件;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第9-10行: 定义.o文件依赖于.c文件,维护代码为“cc -c 变动的源文件名”;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    第13-14行: 定义dy依赖于变量DYTGT指示的值,维护代码中采用-rdynamic选项以指定输出文件为动态链接的方式，选项-s指定删除目标文件中的符号表,最后的选项-ldl则指示装配程序ld需要装载dl函数库。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    2.2.3 运行make命令</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    运行make后将产生执行文件dy，运行后将产生如下类似信息：</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    动态链接库应用示范</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    当前日期: 2001-06-28</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    当前时间: 10:06:21</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    当删除my.so文件时,将出现以下信息:</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    动态链接库应用示范</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">  
    my.so: cannot open shared object file: 文件或目录不存在</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"><b>3、小结</b></td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> 
    LINUX创建与使用动态链接库并不是一件难事。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> 
    编译函数源程序时选用-shared选项即可创建动态链接库，注意应以.so后缀命名，最好放到公用库目录(如/lib,/usr/lib等)下面，并要写好用户接口文件，以便其它用户共享。</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> 
    使用动态链接库，源程序中要包含dlfcn.h头文件，写程序时注意dlopen等函数的正确调用，编译时要采用-rdynamic选项与-ldl选项，以产生可调用动态链接库的执行代码。</td></tr></tbody></table> 
    点击这里<a href="http://www.ccw.com.cn/htm/app/down/linuxdllsrc.tgz"><font color="#0000ff">下载源程序</font></a>。<img src ="http://www.cppblog.com/mydriverc/aggbug/33162.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-29 01:21 <a href="http://www.cppblog.com/mydriverc/articles/33162.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>跟我一起写 Makefile</title><link>http://www.cppblog.com/mydriverc/articles/33161.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 28 Sep 2007 17:20:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/33161.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/33161.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/33161.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/33161.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/33161.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 陈皓概述——什么是makefile？或许很多Winodws的程序员都不知道这个东西，因为那些Windows的IDE都为你做了这个工作，但我觉得要作一个好的和professional的程序员，makefile还是要懂。这就好像现在有这么多的HTML的编辑器，但如果你想成为一个专业人士，你还是要了解HTML的标识的含义。特别在Unix下的软件编译，你就不能不自己写makefile了...&nbsp;&nbsp;<a href='http://www.cppblog.com/mydriverc/articles/33161.html'>阅读全文</a><img src ="http://www.cppblog.com/mydriverc/aggbug/33161.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-29 01:20 <a href="http://www.cppblog.com/mydriverc/articles/33161.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GCC编译选项</title><link>http://www.cppblog.com/mydriverc/articles/33144.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 28 Sep 2007 15:29:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/33144.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/33144.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/33144.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/33144.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/33144.html</trackback:ping><description><![CDATA[
		<p>gcc 编译选项,自己翻译的.- -<br />                                       </p>
		<p>gcc 编译选项,自己翻译的. </p>
		<p>-o 设定输出文件名 <br />-c 只编译,不连接. <br />-E 只做预编译. <br />-pipe 在多个编译过程之间使用管道. <br />--version 显示版本. <br />-static 静态连接. <br />-ansi C 模式下支持所有 ISO C90 标准的 C 程序, C++ 模式下去除对 GNU C++ 扩展的支持(GNU扩展会与 ISO C++ 冲突) <br />-std=
确定编译语言的标准,目前只在编译 C 和 C++ 时有效 -fno-asm 不将 "asm" "inline" "typeof"
作为关键字,可以用他们做变量名等. -funsigned-char 将"char"的数据类型设为"unsigned",即无符号. <br />-fsigned-char 正好相反,将"char"设为"signed". <br />-fsyntax-only 只检查语法错误,不做其他任何事. <br />-pedantic 显示所有的 ISO C 和 ISO C++ 的警告,并且拒绝所有使用禁止扩展的程序 <br />-Wall 显示所有警告 <br />-g 将编译时的调试信息保存到本地文件中( stabs,COFF,XCOFF,DWARF) <br />-ggdb 为 GDB 产生调试信息,包含 GDB 的扩展. <br />-ggdb(level) 设定产生何种等级的调试信息, level 为 1-3, 1 最少,3 最多. <br />-ftime-reprot 统计编译消耗的时间并显示报告. <br />-fmem-report 显示所有的静态内存分配. <br />-ftest-coverages 为 gcov工具产生数据文件. <br />gcc 编译选项,翻译出来用起来方便. </p>
		<p>VC编译选项（转载）- -<br />Tag： 编程                                           <br />/***********************************************************************************************/<br />VC编译选项 csdnb3a [原作] </p>
		<p>关键字 VC编译选项 出处 <br />-优化- <br />/O1 最小化空间 minimize space <br />/Op[-] 改善浮点数一致性 improve floating-pt consistency <br />/O2 最大化速度 maximize speed <br />/Os 优选代码空间 favor code space <br />/Oa 假设没有别名 assume no aliasing <br />/Ot 优选代码速度 favor code speed <br />/Ob 内联展开（默认 n=0） inline expansion (default n=0) <br />/Ow 假设交叉函数别名 assume cross-function aliasing <br />/Od 禁用优化（默认值） disable optimizations (default) <br />/Ox 最大化选项。(/Ogityb2 /Gs) maximum opts. (/Ogityb1 /Gs) <br />/Og 启用全局优化 enable global optimization <br />/Oy[-] 启用框架指针省略 enable frame pointer omission <br />/Oi 启用内建函数 enable intrinsic functions </p>
		<p>-代码生成- <br />/G3 为 80386 进行优化 optimize for 80386 <br />/G4 为 80486 进行优化 optimize for 80486 <br />/GR[-] 启用 C++ RTTI enable C++ RTTI <br />/G5 为 Pentium 进行优化 optimize for Pentium <br />/G6 为 Pentium Pro 进行优化 optimize for Pentium Pro <br />/GX[-] 启用 C++ 异常处理（与 /EHsc 相同） enable C++ EH (same as /EHsc) <br />/EHs 启用同步 C++ 异常处理 enable synchronous C++ EH <br />/GD 为 Windows DLL 进行优化 optimize for Windows DLL <br />/GB 为混合模型进行优化（默认） optimize for blended model (default) <br />/EHa 启用异步 C++ 异常处理 enable asynchronous C++ EH <br />/Gd __cdecl 调用约定 __cdecl calling convention <br />/EHc extern“C”默认为 nothrow extern "C" defaults to nothrow <br />/Gr __fastcall 调用约定 __fastcall calling convention <br />/Gi[-] 启用增量编译 enable incremental compilation <br />/Gz __stdcall 调用约定 __stdcall calling convention <br />/Gm[-] 启用最小重新生成 enable minimal rebuild <br />/GA 为 Windows 应用程序进行优化 optimize for Windows Application <br />/Gf 启用字符串池 enable string pooling <br />/QIfdiv[-] 启用 Pentium FDIV 修复 enable Pentium FDIV fix <br />/GF 启用只读字符串池 enable read-only string pooling <br />/QI0f[-] 启用 Pentium 0x0f 修复 enable Pentium 0x0f fix <br />/Gy 分隔链接器函数 separate functions for linker <br />/GZ 启用运行时调试检查 enable runtime debug checks <br />/Gh 启用钩子函数调用 enable hook function call <br />/Ge 对所有函数强制堆栈检查 force stack checking for all funcs <br />/Gs[num] 禁用堆栈检查调用 disable stack checking calls </p>
		<p>-输出文件- <br />/Fa[file] 命名程序集列表文件 name assembly listing file <br />/Fo 命名对象文件 name object file <br />/FA[sc] 配置程序集列表 configure assembly listing <br />/Fp 命名预编译头文件 name precompiled header file <br />/Fd[file] 命名 .PDB 文件 name .PDB file <br />/Fr[file] 命名源浏览器文件 name source browser file <br />/Fe 命名可执行文件 name executable file <br />/FR[file] 命名扩展 .SBR 文件 name extended .SBR file <br />/Fm[file] 命名映射文件 name map file </p>
		<p>-预处理器- <br />/FI 命名强制包含文件 name forced include file <br />/C 不吸取注释 don't strip comments <br />/U 移除预定义宏 remove predefined macro <br />/D{=|#} 定义宏 define macro <br />/u 移除所有预定义宏 remove all predefined macros <br />/E 将预处理定向到标准输出 preprocess to stdout <br />/I 添加到包含文件的搜索路径 add to include search path <br />/EP 将预处理定向到标准输出，不要带行号 preprocess to stdout, no #line <br />/X 忽略“标准位置” ignore "standard places" <br />/P 预处理到文件 preprocess to file </p>
		<p>-语言- <br />/Zi 启用调试信息 enable debugging information <br />/Zl 忽略 .OBJ 中的默认库名 omit default library name in .OBJ <br />/ZI 启用调试信息的“编辑并继续”功能 enable Edit and Continue debug info <br />/Zg 生成函数原型 generate function prototypes <br />/Z7 启用旧式调试信息 enable old-style debug info <br />/Zs 只进行语法检查 syntax check only <br />/Zd 仅要行号调试信息 line number debugging info only <br />/vd{0|1} 禁用/启用 vtordisp disable/enable vtordisp <br />/Zp[n] 在 n 字节边界上包装结构 pack structs on n-byte boundary <br />/vm 指向成员的指针类型 type of pointers to members <br />/Za 禁用扩展（暗指 /Op） disable extensions (implies /Op) <br />/noBool 禁用“bool”关键字 disable "bool" keyword <br />/Ze 启用扩展（默认） enable extensions (default) </p>
		<p>- 杂项 - <br />/?, /help 打印此帮助消息 print this help message <br />/c 只编译，不链接 compile only, no link <br />/W 设置警告等级（默认 n=1） set warning level (default n=1) <br />/H 最大化外部名称长度 max external name length <br />/J 默认 char 类型是 unsigned default char type is unsigned <br />/nologo 取消显示版权消息 suppress copyright message <br />/WX 将警告视为错误 treat warnings as errors <br />/Tc 将文件编译为 .c compile file as .c <br />/Yc[file] 创建 .PCH 文件 create .PCH file <br />/Tp 将文件编译为 .cpp compile file as .cpp <br />/Yd 将调试信息放在每个 .OBJ 中 put debug info in every .OBJ <br />/TC 将所有文件编译为 .c compile all files as .c <br />/TP 将所有文件编译为 .cpp compile all files as .cpp <br />/Yu[file] 使用 .PCH 文件 use .PCH file <br />/V 设置版本字符串 set version string <br />/YX[file] 自动的 .PCH 文件 automatic .PCH <br />/w 禁用所有警告 disable all warnings <br />/Zm 最大内存分配（默认为 %） max memory alloc (% of default) </p>
		<p>-链接- <br />/MD 与 MSVCRT.LIB 链接 link with MSVCRT.LIB <br />/MDd 与 MSVCRTD.LIB 调试库链接 link with MSVCRTD.LIB debug lib <br />/ML 与 LIBC.LIB 链接 link with LIBC.LIB <br />/MLd 与 LIBCD.LIB 调试库链接 link with LIBCD.LIB debug lib <br />/MT 与 LIBCMT.LIB 链接 link with LIBCMT.LIB <br />/MTd 与 LIBCMTD.LIB 调试库链接 link with LIBCMTD.LIB debug lib <br />/LD 创建 .DLL Create .DLL <br />/F 设置堆栈大小 set stack size <br />/LDd 创建 .DLL 调试库 Create .DLL debug libary <br />/link [链接器选项和库] [linker options and libraries]</p>
<img src ="http://www.cppblog.com/mydriverc/aggbug/33144.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-28 23:29 <a href="http://www.cppblog.com/mydriverc/articles/33144.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>qmake概念</title><link>http://www.cppblog.com/mydriverc/articles/32728.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 23 Sep 2007 08:44:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/32728.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/32728.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/32728.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/32728.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/32728.html</trackback:ping><description><![CDATA[
		<h3>介绍qmake</h3>
		<p>
				<em>qmake</em>是用来为不同的平台的开发项目创建makefile的Trolltech开发一个易于使用的工具。<em>qmake</em>简化了makefile的生成，所以为了创建一个makefile只需要一个只有几行信息的文件。<em>qmake</em>可以供任何一个软件项目使用，而不用管它是不是用Qt写的，尽管它包含了为支持Qt开发所拥有的额外的特征。</p>
		<p>
				<em>qmake</em>基于一个项目文件这样的信息来生成makefile。项目文件可以由开发者生成。项目文件通常很简单，但是如果需要它是非常完善的。不用修改项目文件，<em>qmake</em>也可以为为Microsoft
Visual Studio生成项目。</p>
		<h3>
				<a name="2">
				</a>qmake的概念</h3>
		<h4>
				<a name="2-1">
				</a>QMAKESPEC环境变量</h4>
		<p>举例来说，如果你在Windows下使用Microsoft Visual
Studio，然后你需要把QMAKESPEC环境变量设置为<em>win32-msvc</em>。如果你在Solaris上使用gcc，你需要把QMAKESPEC环境变量设置为<em>solaris-g++</em>。</p>
		<p>
在qt/mkspecs中的每一个目录里面，都有一个包含了平台和编译器特定信息的<em>qmake.conf</em>文件。这些设置适用于你要使用<em>qmake</em>的任何项目，请不要修改它，除非你是一个专家。例如，假如你所有的应用程序都必须和一个特定的库连接，你可以把这个信息添加到相应的<em>qmake.conf</em>文件中。</p>
		<h4>
				<a name="2-2">
				</a>项目(.pro)文件</h4>
		<p>
一个项目文件是用来告诉<em>qmake</em>关于为这个应用程序创建makefile所需要的细节。例如，一个源文件和头文件的列表、任何应用程序特定配置、例如一个必需要连接的额外库、或者一个额外的包含路径，都应该放到项目文件中。</p>
		<h5>
				<a name="2-2-1">
				</a>“#”注释</h5>
		<p>
你可以为项目文件添加注释。注释由“#”符号开始，一直到这一行的结束。</p>
		<h4>
				<a name="2-3">
				</a>模板</h4>
		<p>
模板变量告诉<em>qmake</em>为这个应用程序生成哪种makefile。下面是可供使用的选择：</p>
		<ul>
				<li>
						<p>app -
建立一个应用程序的makefile。这是默认值，所以如果模板没有被指定，这个将被使用。</p>
				</li>
				<li>
						<p>lib - 建立一个库的makefile。</p>
				</li>
				<li>
						<p>vcapp - 建立一个应用程序的Visual Studio项目文件。</p>
				</li>
				<li>
						<p>vclib - 建立一个库的Visual Studio项目文件。</p>
				</li>
				<li>
						<p>subdirs -
这是一个特殊的模板，它可以创建一个能够进入特定目录并且为一个项目文件生成makefile并且为它调用make的makefile。</p>
				</li>
		</ul>
		<h5>
				<a name="2-3-1">
				</a>“app”模板</h5>
		<p>
“app”模板告诉<em>qmake</em>为建立一个应用程序生成一个makefile。当使用这个模板时，下面这些<em>qmake</em>系统变量是被承认的。你应该在你的.pro文件中使用它们来为你的应用程序指定特定信息。</p>
		<ul>
				<li>
						<p>HEADERS - 应用程序中的所有头文件的列表。</p>
				</li>
				<li>
						<p>SOURCES - 应用程序中的所有源文件的列表。</p>
				</li>
				<li>
						<p>FORMS -
应用程序中的所有.ui文件（由<em>Qt设计器</em>生成）的列表。</p>
				</li>
				<li>
						<p>LEXSOURCES - 应用程序中的所有lex源文件的列表。</p>
				</li>
				<li>
						<p>YACCSOURCES - 应用程序中的所有yacc源文件的列表。</p>
				</li>
				<li>
						<p>TARGET -
可执行应用程序的名称。默认值为项目文件的名称。（如果需要扩展名，会被自动加上。）</p>
				</li>
				<li>
						<p>DESTDIR - 放置可执行程序目标的目录。</p>
				</li>
				<li>
						<p>DEFINES - 应用程序所需的额外的预处理程序定义的列表。</p>
				</li>
				<li>
						<p>INCLUDEPATH - 应用程序所需的额外的包含路径的列表。</p>
				</li>
				<li>
						<p>DEPENDPATH - 应用程序所依赖的搜索路径。</p>
				</li>
				<li>
						<p>VPATH - 寻找补充文件的搜索路径。</p>
				</li>
				<li>
						<p>DEF_FILE - 只有Windows需要：应用程序所要连接的.def文件。</p>
				</li>
				<li>
						<p>RC_FILE - 只有Windows需要：应用程序的资源文件。</p>
				</li>
				<li>
						<p>RES_FILE - 只有Windows需要：应用程序所要连接的资源文件。</p>
				</li>
		</ul>
		<p>
你只需要使用那些你已经有值的系统变量，例如，如果你不需要任何额外的INCLUDEPATH，那么你就不需要指定它，<em>qmake</em>会为所需的提供默认值。例如，一个实例项目文件也许就像这样：</p>
		<pre>TEMPLATE = app<br />DESTDIR  = c:\helloapp<br />HEADERS += hello.h<br />SOURCES += hello.cpp <br />SOURCES += main.cpp<br />DEFINES += QT_DLL<br />CONFIG  += qt warn_on release<br /></pre>
		<p>
如果条目是单值的，比如template或者目的目录，我们是用“=”，但如果是多值条目，我们使用“+=”来为这个类型<em>添加</em>现有的条目。使用“=”会用新值替换原有的值，例如，如果我们写了<tt>DEFINES=QT_DLL</tt>，其它所有的定义都将被删除。</p>
		<h5>
				<a name="2-3-2">
				</a>“lib”模板</h5>
		<p>
“lib”模板告诉<em>qmake</em>为建立一个库而生成makefile。当使用这个模板时，除了“app”模板中提到系统变量，还有一个<em>VERSION</em>是被支持的。你需要在为库指定特定信息的.pro文件中使用它们。</p>
		<ul>
				<li>
						<p>VERSION - 目标库的版本号，比如，2.3.1。</p>
				</li>
		</ul>
		<h5>
				<a name="2-3-3">
				</a>“subdirs”模板</h5>
		<p>
“subdirs”模板告诉qmake生成一个makefile，它可以进入到特定子目录并为这个目录中的项目文件生成makefile并且为它调用make。</p>
		<p>
在这个模板中只有一个系统变量<em>SUBDIRS</em>可以被识别。这个变量中包含了所要处理的含有项目文件的子目录的列表。这个项目文件的名称是和子目录同名的，这样<em>qmake</em>就可以发现它。例如，如果子目里是“myapp”，那么在这个目录中的项目文件应该被叫做<em>myapp.pro</em>。</p>
		<h4>
				<a name="2-4">
				</a>CONFIG变量</h4>
		<p>
配置变量指定了编译器所要使用的选项和所需要被连接的库。配置变量中可以添加任何东西，但只有下面这些选项可以被qmake识别。</p>
		<p>下面这些选项控制着使用哪些编译器标志：</p>
		<ul>
				<li>
						<p>release -
应用程序将以release模式连编。如果“debug”被指定，它将被忽略。</p>
				</li>
				<li>
						<p>debug - 应用程序将以debug模式连编。</p>
				</li>
				<li>
						<p>warn_on -
编译器会输出尽可能多的警告信息。如果“warn_off”被指定，它将被忽略。</p>
				</li>
				<li>
						<p>warn_off - 编译器会输出尽可能少的警告信息。</p>
				</li>
		</ul>
		<p>下面这些选项定义了所要连编的库/应用程序的类型：</p>
		<ul>
				<li>
						<p>qt - 应用程序是一个Qt应用程序，并且Qt库将会被连接。</p>
				</li>
				<li>
						<p>thread - 应用程序是一个多线程应用程序。</p>
				</li>
				<li>
						<p>x11 - 应用程序是一个X11应用程序或库。</p>
				</li>
				<li>
						<p>windows -
只用于“app”模板：应用程序是一个Windows下的窗口应用程序。</p>
				</li>
				<li>
						<p>console -
只用于“app”模板：应用程序是一个Windows下的控制台应用程序。</p>
				</li>
				<li>
						<p>dll - 只用于“lib”模板：库是一个共享库（dll）。</p>
				</li>
				<li>
						<p>staticlib - 只用于“lib”模板：库是一个静态库。</p>
				</li>
				<li>
						<p>plugin -
只用于“lib”模板：库是一个插件，这将会使dll选项生效。</p>
				</li>
		</ul>
		<p>
例如，如果你的应用程序使用Qt库，并且你想把它连编为一个可调试的多线程的应用程序，你的项目文件应该会有下面这行：</p>
		<pre>    CONFIG += qt thread debug<br /></pre>
		<p>
注意，你必须使用“+=”，不要使用“=”，否则<em>qmake</em>就不能正确使用连编Qt的设置了，比如没法获得所编译的Qt库的类型了。</p>
<img src ="http://www.cppblog.com/mydriverc/aggbug/32728.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-23 16:44 <a href="http://www.cppblog.com/mydriverc/articles/32728.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用qmake快速生成makefile</title><link>http://www.cppblog.com/mydriverc/articles/32720.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 23 Sep 2007 07:35:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/32720.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/32720.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/32720.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/32720.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/32720.html</trackback:ping><description><![CDATA[
 用qmake快速生成makefile <br /><br />
摘要 <br />
qmake是Trolltech公司创建的用来为不同的平台和编译器书写Makefile的工具。是qt工具包的一部分.在Unix&amp;linux
上写程式的人大概都碰过Makefile。用 make 来开发和编译程式的确很方便，可是要写出一个
Makefile就不简单了,手写Makefile是比较困难并且容易出错的，这阻挡了很多一部分的linux爱好者加入linux程序开发的阵营。
(2004-05-17 02:14:46) <br /><br />
-------------------------------------------------------------------------------- <br />
By lanf, 出处：http://mylottery.cosoft.org.cn/news/show.php?type=linuxprg&amp;id=1081842801 <br /><br />
作者：孙高勇 <br />
1.简介: <br />
qmake是Trolltech公司创建的用来为不同的平台和编译器书写Makefile的工具。是qt工具包的一部分.在Unix&amp;linux
上写程式的人大概都碰过Makefile。用 make 来开发和编译程式的确很方便，可是要写出一个
Makefile就不简单了,手写Makefile是比较困难并且容易出错的，这阻挡了很多一部分的linux爱好者加入linux程序开发的阵营。 <br />
虽然Open Source Software也有GNU Automake和GNU
Autoconf两个软件可以生成makefile文件,但是对于一个简单的项目,使用Automake和Autoconf就有点杀鸡也用宰牛刀了.使用
qmake完全可以符合你的要求.Trolltech公司使用qmake作为Qt库和Qt所提供的工具的主要连编工具。 <br />
2.安装qmake <br />
在linux平台上,安装完qt以及相关的qt工具包,qmake就已经被安装好了.你唯一要注意的就是QTDIR值的设定,这个必须设置到Qt被安装到的地方。如:/usr/lib/qt3/,以及qmake可执行文件的路径加到PATH路径中. <br />
3. 一个简单的例子 <br />
用vi写个hello.c ， <br />
#include <br />
int main(int argc, char** argv) <br />
{ <br />
printf(\"Hello, world!\\n\"); <br />
return 0; <br />
} <br />
创建qmake需要的项目文件(hello.pro), <br />
SOURCES = hello.cpp <br />
CONFIG += qt warn_on release <br />
Makefile可以像这样由\".pro\"文件生成： <br />
qmake -o Makefile hello.pro <br />
现在你的目录下已经产生了一个 Makefile 文件，输入\"make\" 指令就可以开始编译 hello.c 成执行文件，执行 ./hello 和 world 打声招呼吧！打开这个Makefile文件看看，是不是很专业啊！ <br />
4.高级操作技巧 <br />
当然，在实际使用过程中，我们的工程不可能象这个程序这样简单的，它可能有多个目录，多个头文件，多个源文件，需要链接器它不同的链接库等等情况。别急，
让我和你慢慢道来。这些都是非常容易用qmake来实现的。我们从一个更加复杂的项目文件为例和你详细的讲诉qmake的高级技巧： <br />
项目文件示例： <br />
SOURCES += myqt.cpp <br />
SOURCES += main.cpp <br />
HEADERS += myqt.h <br />
FORMS += xsimform.ui <br />
TEMPLATE = lib <br />
CONFIG += debug \\ <br />
warn_on \\ <br />
qt \\ <br />
thread \\ <br />
x11 \\ <br />
plugin <br />
TARGET = ../bin/panel_qt <br />
INCLUDEPATH = ../../../../xsim \\ <br />
../../../../xsim/IMdkit <br />
DEFINES = BDB_VERSION4 \\ <br />
OS_LINUX <br />
从这个文件可以知道，SOURCES变量指向项目中的源文件，当项目中有多个源文件时，我们需对项目中的每一个源文件都这样做，直到结束： <br />
SOURCES += hello.cpp <br />
SOURCES += main.cpp <br />
当然，如果你喜欢使用像Make一样风格的语法，你也可以写成这样，一行写一个源文件，并用反斜线结尾，然后再起新的一行： <br />
SOURCES = hello.cpp \\ <br />
main.cpp <br />
HEADERS变量指向项目中的头文件，多个头文件的时候，和多个源文件的解决方法一致。 <br />
FORMS变量指向项目中使用到的窗体文件(qtdesign设计的.ui文件)，qmake也注意了Qt的特殊需求，可以自动的包含moc和uic的连编规则。没有的话或者非qt程序可以不写。 <br />
TEMPLATE变量告诉qmake为这个应用程序生成哪种makefile。下面是可供使用的选择： <br />
app - 建立一个应用程序的makefile。这是默认值，所以如果模板没有被指定，这个将被使用。 <br />
lib - 建立一个链接库的makefile。 <br />
vcapp - 建立一个应用程序的Visual Studio项目文件。 <br />
vclib - 建立一个库的Visual Studio项目文件。 <br />
subdirs - 这是一个特殊的模板，它可以创建一个能够进入特定目录并且为一个项目文件生成makefile并且为它调用make的mkefile。 <br />
CONFIG变量变量指定了编译器所要使用的选项和所需要被连接的库。配置变量中可以添加任何东西，但只有下面这些选项可以被qmake识别。 <br />
下面这些选项控制着使用哪些编译器标志： <br />
release - 应用程序将以release模式连编。如果\"debug\"被指定，它将被忽略。 <br />
debug - 应用程序将以debug模式连编。 <br />
warn_on - 编译器会输出尽可能多的警告信息。如果\"warn_off\"被指定，它将被忽略。 <br />
warn_off - 编译器会输出尽可能少的警告信息。 <br />
下面这些选项定义了所要连编的库/应用程序的类型： <br />
qt - 应用程序是一个Qt应用程序，并且Qt库将会被连接。 <br />
thread - 应用程序是一个多线程应用程序。 <br />
x11 - 应用程序是一个X11应用程序或库。 <br />
windows - 只用于\"app\"模板：应用程序是一个Windows下的窗口应用程序。 <br />
console - 只用于\"app\"模板：应用程序是一个Windows下的控制台应用程序。 <br />
dll - 只用于\"lib\"模板：库是一个共享库（dll）。 <br />
staticlib - 只用于\"lib\"模板：库是一个静态库。 <br />
plugin - 只用于\"lib\"模板：库是一个插件，这将会使dll选项生效。 <br />
TARGET变量指定生成的二进制代码的路径和文件名，如果建立的是一个链接库的话，它会在文件名前面自动加上\"lib\"和在最后自动加上\".so\". <br />
我们在使用过程中可能会使用到另外的一些函数库，链接库等。函数库的头文件指定使用INCLUDEPATH变量，其它链接库的指定可以通过LIBS 变量来指定，例LIBS += -lmath -L/usr/local/lib <br />
DEFINES变量的指定就如同make的-D选项一样。 <br />
结束语 <br />
Autoconf 和 Automake
功能十分强大，但对于普通用户来说，太过复杂。qmake方便、简单、快捷，是一个轻量级的makefile生成工具，虽然它是qt工具包的一部分，但它
也完全可以用来进行其它程序makefile文件的生成，对于大多数人来说，它已经是非常的够用了。你也可以从qt提供的许多现存的源程序中找到相关的.
pro项目文件，它们是学习qmake 更多技巧的最佳范例。 <br />
这篇简介只用到了 qmake 的一些皮毛罢了，希望这篇文件能帮助你对产生 Makefile有个简单的依据。 <img src ="http://www.cppblog.com/mydriverc/aggbug/32720.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-23 15:35 <a href="http://www.cppblog.com/mydriverc/articles/32720.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>QT入门 二 建立菜单</title><link>http://www.cppblog.com/mydriverc/articles/32719.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 23 Sep 2007 07:30:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/32719.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/32719.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/32719.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/32719.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/32719.html</trackback:ping><description><![CDATA[#ifndef WIN_H<br />#define WIN_H<br /><br />#include &lt;qmainwindow.h&gt;<br /><br />class QAction;<br />class QPopupMenu;<br />class Mywin: public QMainWindow<br />{<br />    Q_OBJECT<br />    public:<br />    Mywin(QWidget *parent=0,const char * name=0);<br />    <br />    private:<br />       void createActions();<br />       void createMenus();<br />    <br />    private slots:<br />    void newFile();<br />    <br />    private:<br />       QAction *newAct;<br />       QPopupMenu *fileMenu;<br />};<br /><br />#endif<br /><br /><br />#include "win.h"<br />#include &lt;qapplication.h&gt;<br />#include &lt;qaction.h&gt;<br />#include &lt;qmessagebox.h&gt;<br />#include &lt;qpopupmenu.h&gt;<br />#include &lt;qmenubar.h&gt;<br />Mywin::Mywin(QWidget *parent,const char * name):QMainWindow(parent,name)<br />{<br />   createActions();  <br />   createMenus();<br />}<br /><br />void Mywin::createActions()<br />{<br />  newAct=new QAction(NULL,tr("&amp;New"),0,this);<br /> // newAct-&gt;setStatusTip(tr("Create a new file"));<br />  connect(newAct,SIGNAL(activated()),this,SLOT(newFile()));<br />}<br />void Mywin::createMenus()<br />{#include "win.h"<br />#include &lt;qapplication.h&gt;<br />#include &lt;qaction.h&gt;<br />#include &lt;qmessagebox.h&gt;<br />#include &lt;qpopupmenu.h&gt;<br />#include &lt;qmenubar.h&gt;<br />Mywin::Mywin(QWidget *parent,const char * name):QMainWindow(parent,name)<br />{<br />   createActions();  <br />   createMenus();<br />}<br /><br />void Mywin::createActions()<br />{<br />  newAct=new QAction(NULL,tr("&amp;New"),0,this);<br />  connect(newAct,SIGNAL(activated()),this,SLOT(newFile()));<br />}<br /><br />void Mywin::createMenus()<br />{<br />    fileMenu=new QPopupMenu(this);<br />    newAct-&gt;addTo(fileMenu);<br />    <br />    <br />    menuBar()-&gt;insertItem(tr("&amp;File"),fileMenu);<br />}<br /><br />void Mywin::newFile()<br />{<br />    QMessageBox::warning(this,tr("Hello"),tr("Hello \n World!"),QMessageBox::Yes|QMessageBox::Default,QMessageBox::No,QMessageBox::Cancel|QMessageBox::Escape);     <br />}<br /><br /> <br /><br />#include &lt;qapplication.h&gt;<br />#include "win.h"<br />int main(int argc,char* argv[])<br />{<br />     QApplication app(argc,argv);<br /><br />     Mywin *mywin=new Mywin;<br />     app.setMainWidget(mywin);<br />     mywin-&gt;show();<br />    return app.exec();<br />}<br />1<br /><br /><br /><br /><br /><br />1 newAct=new QAction(NULL,tr("&amp;New"),0,this);<br />  connect(newAct,SIGNAL(activated()),this,SLOT(newFile()));<br />  建立一个菜单项,并将点击信号连接到槽newFile()<br />2 fileMenu=new QPopupMenu(this);<br />
  newAct-&gt;addTo(fileMenu);<br />
  建立一个菜单<br />3 menuBar()返回一个MenuBar<br />  menuBar()-&gt;insertItem(tr("&amp;File"),fileMenu);<br />  将菜单添加到MenuBar上.<br /><img src ="http://www.cppblog.com/mydriverc/aggbug/32719.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-23 15:30 <a href="http://www.cppblog.com/mydriverc/articles/32719.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>QT 人门 1</title><link>http://www.cppblog.com/mydriverc/articles/32696.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sat, 22 Sep 2007 18:04:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/32696.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/32696.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/32696.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/32696.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/32696.html</trackback:ping><description><![CDATA[ QT designer 1 建立一个文件夹 2 在该文件夹下,新建一个项目try1.pro 3 建立try1.cpp 和 win.h 4 Save All 5 在终端进入这个文件夹 6 qmake -project 7 qmake 8 make 9 ./try1 建立一个主窗口 #ifndef WIN_H #define WIN_H #include class Mywin:public QMainWindow { }; #endif #include #include "win.h" int main(int argc,char* argv[]) { QApplication app(argc,argv); Mywin *mywin=new Mywin; app.setMainWidget(mywin); mywin-&gt;show(); return app.exec(); }<img src ="http://www.cppblog.com/mydriverc/aggbug/32696.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-09-23 02:04 <a href="http://www.cppblog.com/mydriverc/articles/32696.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>QT编译---我自己的</title><link>http://www.cppblog.com/mydriverc/articles/30900.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 26 Aug 2007 17:03:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/30900.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/30900.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/30900.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/30900.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/30900.html</trackback:ping><description><![CDATA[最简单就是<br />qmake -project<br />qmake<br />make<br />三条命令<br /><img src ="http://www.cppblog.com/mydriverc/aggbug/30900.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-27 01:03 <a href="http://www.cppblog.com/mydriverc/articles/30900.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LINUX下创建和编译Qt项目</title><link>http://www.cppblog.com/mydriverc/articles/29953.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Mon, 13 Aug 2007 16:35:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29953.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29953.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29953.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29953.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29953.html</trackback:ping><description><![CDATA[
		<p>LINUX下创建和编译Qt项目</p>
		<p>1,用Qt designer创建 <font color="#00ff00">BaseClass</font>.ui 文件 和 <font color="#00ff00">BaseClass</font>.ui.h （如有必要）。</p>
		<p>2,由.ui文件生成相应的 .h 和 .cpp 文件：<br />uic -o <font color="#00ff00">BaseClass</font>.h <font color="#00ff00">BaseClass</font>.ui<br />uic -o <font color="#00ff00">BaseClass</font>.cpp -i(-impl) <font color="#00ff00">BaseClass</font>.h <font color="#00ff00">BaseClass</font>.ui</p>
		<p>3,生成子类的 .h 和 .cpp 文件（如有必要）：<br />uic -o <font color="#00ff00">SubClass</font>.h -subdecl <font color="#00ff00">SubClass</font><font color="#00ff00">BaseClass</font><font color="#00ff00">BaseClass</font>.ui<br />uic -o <font color="#00ff00">SubClass</font>.cpp -subimpl <font color="#00ff00">Subclass</font><font color="#00ff00">SubClass</font>.h <font color="#00ff00">BaseClass</font>.ui</p>
		<p>4,生成moc文件：<br />moc -o moc_<font color="#00ff00">BaseClass</font>.cpp <font color="#00ff00">BaseClass</font>.h<br />moc -o moc_<font color="#00ff00">SubClass</font>.cpp <font color="#00ff00">SubClass</font>.h</p>
		<p>5,生成项目文件.pro：<br />qmake -o <font color="#00ff00">Project</font>.pro -project<br />如不指定文件名，则文件名默认为当前目录的名字。</p>
		<p>6,生成Makefile<br />qmake</p>
		<p>7,编译链接：make</p>
		<p>注：Linux Redhat 9.0, Qt3.3。绿色可根据实际项目自行更改。</p>
		<p>参见：<br />Integrating Qt Designer Files Into Your Project</p>
		<p>
				<a href="http://docsrv.caldera.com:8457/en/QtTutorial/chap5_4.html">http://docsrv.caldera.com:8457/en/QtTutorial/chap5_4.html</a> </p>
		<br />
		<br />
		<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=933964</p>
<img src ="http://www.cppblog.com/mydriverc/aggbug/29953.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-14 00:35 <a href="http://www.cppblog.com/mydriverc/articles/29953.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用QtE实现SBC一241OX上的LED控制</title><link>http://www.cppblog.com/mydriverc/articles/29951.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Mon, 13 Aug 2007 16:30:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29951.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29951.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29951.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29951.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29951.html</trackback:ping><description><![CDATA[源：单片机及嵌入式系统应用  作者：西安交通大学 宋飞 赵广社<br />　<br />摘
要：QtE(Qt／Embedded)是挪威Trolltech公司专门为嵌入式系统设计的图形用户界面(GUI)的一个工具包。本文通过QtE编程，完
成对基于ARM9的SBC-2410X开发板上LED的控制，并详细说明从程序设计、移植到运行的过程。程序能很好的运行在开发板上，验证了程序的有效性
和可靠性。结果说明用QtE开发的应用程序界面清晰美观，操作简单方便。<br /><p>关键词：QtE ARM9 GUI SBC-2410X</p><br /><p>引言 </p><br /><p>近
年来，由于ARM(Advanced RISC
Machines)在性能、功耗、成本和体积上的优势，使得它在嵌入式系统中的发展如日中天，它在工业控制、航空航天、军事领域、消费电子、智能家电和视
频监控等方面都发挥了重大的作用。很多人都迫切地想学习ARM，以笔者的经验，从最基本的LED控制学起，是一个不错的选择。</p><br /><p>如
果不熟悉Linux，可能对QtE的概念知之甚少，它是挪威Trolltech公司为各种系统设计的图形用户界面工具包，采用c++语言编程。QtE的优
点有：跨平台，可以方便的连接数据库，可以将程序与Java集成等。其实，QtE在一些高端的移动手持设备中早已深入人心，Troll—tech公司在
QtE的基础上开发了一个应用环境Qtopia，目前已有很多公司采用Qtopia来开发他们的主流PDA。当然，Qt也有它的缺点，既不能提供IDE
(集成开发环境)，但这个缺点在强大的Linux下显得微不足道。使用gcc／g++，加上Qt的开发工具：Qt
Designer，tmake／qmake，UIC等，开发可视化程序将变得十分容易。</p><br /><p>要学习ARM，选择一款好的开发板是必不
可少的。SBC一2410X是广州友善之臂公司设计的一款基于ARM9的开发板，操作系统是韩国的mizi-linux。SBC-2410X不同于
uClinux，它的接口丰富，且支持MMU、QtE2．3．7，同时提供了控制LED的命令行方式。</p><br /><p>1 系统需求</p><br /><p>◇完全安装RedHat9．0。<br />◇在Linux下建立QtE编译环境。<br />◇开发板上已经加载LED设备驱动程序。</p><br /><p>注意：你所建立的QtE版本必须与开发板所支持的版本一致。</p><br /><p>2 程序设计</p><br /><p>在Linux
下，用系统自带的Qt Designer来编写程序。Qt
Designer是一个优秀的可视化开发工具，用它来设计十分容易。它的界面类似于Delphi，但使用C++语言编写应用程序。Qt程序设计里的一个亮
点就是SignaI-_Slot机制，它有点类似于VC++里的消息机制。当一个组件发出Signal时，其他一个组件或多个组件可以通过Slot接收发
来的SignaI，组件本身也可以接收自己的Signal，这样处理一个事件将变得非常容易。Qt
Designer的优点不仅在于它可以十分方便的设计图形用户界面，还表现在可以用连接工具很容易的把Signals和Slots连接起来。用户自己设计
的程序添加在**．ui．h中。</p><br /><p>打开Qt Deslgner，选择File／new
C++Project，命名为led．pro并保存。接着选择File／new-Dialog，并在此设计自己的用户界面。最后选择File／new
C++Main—File，系统自动生成一个main．cpp文件。图形用户界面如图1所示，Initlalize按钮主要完成LED设备的打开，定时器
的关闭和其他一些初始化工作；Begim按钮则针对LED Display
Mode的开始；Exit按钮为退出应用程序。左面的组合框包括4个RadioButton，对应4个LED的亮或灭；右面的组合框包括6个
RadioButton(对应不同的闪烁模式和闪烁快慢)和1个LCDNumber(用来显示闪烁的时间间隔)。本程序建立的Signal—S1ot连接
有许多，下面对几个重要的连接和函数进行说明。</p><br /><p><img src="http://www.guangdongdz.com/special_column/techimages/200612610411293403.jpg" /></p><br /><p>(1)connect(initPushButton．SIGNAL(clicked())，this．SLOT(initial()))</p><br /><p>当Initialize
按钮按下时，LedForm对话框接收此信号，并执行initial()函数。Linux操作系统把所有的设备都看成文件，因此对设备的操作控制可以通过
open()、ioctl()等函数实现。在initial()函数中就是调用了open()函数来打开LED设备的。</p><br /><p>(2)connect(okPushButton，SIGNAL(clicked())，this，SLOT(modeSel()))</p><br /><p>当Begin
按钮按下时，LedForm对话框接收此信号，并执行modeSel()函数。在modeSel()函数中开启定时器，并创建另外一个连接用来循环显示
LED。在命令行方式中，LED的循环显示要用到死循环，并用键盘中断来控制循环；而在Qt或其他可视化语言中，则要用到定时器来控制循环。</p><br /><p>(3)函数open()</p><br /><p>在中声明，含义为打开设备文件。打开成功返回0；失败返回一1。</p><br /><p>(4)函数ioctl()</p><br /><p>在中声明，控制I／O端口。例如：<br />Int fd=open(”／dev／leds”，0)；<br />ioctl(fd，0，3)； ／／让LED4灭</p><br /><p>(5)函数select()</p><br /><p>在中声明，含义为经过多长时间访问一次设备。</p><br /><p>3 程序移植与运行</p><br /><p>要想使程序运行在开发板的Qtopia中，则必须在建立的QtE环境下编译，并设置好环境变量。终端进入所建立的QtE目录后，环境变量或运行环境变量和脚本设置如下：</p><br /><p>export QTDIR=$PWD／qt<br />export QPEDlR=$PWD／qtopia<br />export TMAKEDIR=$PWD／tmake<br />export TMAKEPATH=$TMAKEDIR／lib／qws／linux-armg++<br />export PATH=$QTDIR／bin：$QPEDIR／bin：$TMAKEDIR／<br />bin：$PATH</p><br /><p>值得注意的一点是：用Linux自带的Qt Designer创建的工程文件只适用于qmake，而笔者建立的QtE环境只能用tmake来生成makefile，因此需要修改led．pro文件，让它适用于tmake，修改如下：</p><br /><p>TEMPLATE =app<br />CoNFIG +=qtopia warn_on release<br />SoURCES +=main．epp<br />INTERFACES =led．ui<br />TARGET =led</p><br /><p>以上的工作做完后，就可以编译了。编译成功后生成可执行的二进制文件led，然而要使程序运行在开发板上的qtopia桌面上，还需要一个桌面文件led．desktop和自定义的一个图标led．png。桌面文件如下：</p><br /><p>[Desktop Entry]<br />Comment=a LED Control Program<br />Exee=led<br />Icon=led<br />Type=Application<br />Name=LED Control</p><br /><p>然
后，把led复制到SBC-2410X下／opt／qtopia／bin目录下，led．png复制到／opt／qtopia／pics目录下，led．
desktop复制到／0pt／qtopia／apps／Applications目录下。最后，用ehmod a+X
led改变文件led的权限，重新启动系统，便可看到led．png的图标显示在qtopia桌面上，点击此图标就可运行程序了。</p><br /><p>4 部分源程序及注释</p><br /><p>由于源程序代码比较长，这里就不全部列出了，仅给出重要部分的代码及注释，以供读者参考。</p><br /><p>(1) 程序的初始化<br />int fd： ／／LED设备文件句柄<br />int type=1； ／／默认方式为计数器<br />double period=0．2； ／／默认时间间隔<br />QTimer*t=new QTimer(Q″timer″)；／／创建定时器<br />void LedForm：：initial(){<br />fd=open(″／dev／leds″，0)； ／／打开LED设备<br />／／其他一些初始化语句<br />一&gt;stop()； ／／关闭定时器<br />～</p><br /><p>(2) LED显示方式的设置<br />void LedForm：：push-leds(void){<br />static unsigned step；<br />unsigned led—bitmap；<br />inti；<br />switch(type){<br />case 0： ／／方式0：跑马灯<br />if(step&gt;=14){step=0；}<br />if(step&lt;7){led_bitmap=1&lt;else{led_bitmap=1&lt;&lt;(14一step)；}<br />break；<br />case 1： ／／方式1：计数器<br />if(step&gt;255){step=0；)<br />Ied_bitmap=step!<br />break；<br />case 2： ／／方式2：停止<br />led_bitmap=0：<br />break;<br />step十十;<br />for(i=0；i&lt;8；l++){<br />ioctl(fd，led_bitmap＆1，i)；<br />led_bitmap&gt;&gt;=1；<br />}<br />}</p><br /><p>(3) LED设备的读取及调用LED显示程序<br />void LedForm：：ledDisplay(){<br />fd_set rds；<br />struct timeval step；<br />FD_ZERO(&amp;rds)；<br />step．tv_sec=period；<br />step．tv_usec=(pefiod—step．W_sec)*1000000L；<br />select(fd，&amp;rds，NULL，NULL，&amp;step)；<br />push_leds()；<br />}</p><br /><p>(4)循环显示的实现<br />void LedForm：：modeSel(){<br />t--&gt;start(O)；／／启动定时器，并立即发出Signal- timeout()<br />connect(t，SIGNAL(timeout())，this．SLOT(1edDisplay()))；<br />}</p><br /><p>结 语</p><br /><p>随
着通信行业的迅猛发展，移动手持设备必将成为人们工作、学习和生活的主流，用Qt设计移动手持设备的GUI有着得天独厚的优势。目前，Qt应用于全世界上
百个软件开发项目中。在我国，Qt的发展也有星星之火可以燎原之势。本文通过一个最基础的小程序向读者演示了怎样使用Qt开发应用程序，以及怎样建立Qt
与ARM的联系，希望对读者学习ARM和Qt起到一定的帮助作用。</p><img src ="http://www.cppblog.com/mydriverc/aggbug/29951.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-14 00:30 <a href="http://www.cppblog.com/mydriverc/articles/29951.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux私有数据编程</title><link>http://www.cppblog.com/mydriverc/articles/29747.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 10 Aug 2007 17:47:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29747.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29747.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29747.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29747.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29747.html</trackback:ping><description><![CDATA[ 
在单线程程序中，经常要用全局变量实现共享数据。在多线程环境下，由于数据空间是共享的，因此全局变量也是各线程共有。但有时在应用程序设计过程中有必要
提供线程私有的全局变量，仅在某个线程中有效，却可以跨多个函数进行访问，比如程序可能需要每个线程维护一个链表，要使用相同的函数操作，最简单的办法就
是使用同名而不同变量地址的线程相关数据结构。这样的数据结构就是私有数据（TSD）<br />    <br />    程序就是演示这样的数据结构。创建了两个新的线程，分别把自己的ID写入私有数据，然后互不干扰的输出。代码如下：<p>#include&lt;stdio.h&gt;<br />#include&lt;stdlib.h&gt;<br />#include&lt;unistd.h&gt;<br />#include&lt;pthread.h&gt;<br /> <br /><b>pthread_key_t key;</b><br /> <br />void echomsg(int t)<br />{<br /> printf("destructor excuted in thread %d,param=%d\n",pthread_self(),t);    <br /> <br />}</p><p>void child()<br />{<br /> int tid;<br /> <br /> tid = pthread_self(); <br />   printf("thread %d enter\n",tid);<br /> sleep(1);<br />   pthread_setspecific(key,(void *)tid);<br />   printf("thread %d returns %d\n",tid,pthread_getspecific(key));<br /> sleep(1);<br />}</p>int main()<br />{<br /> pthread_t tid1,tid2;<br /> <br />   printf("Hello\n");<br />   <br /><b>   pthread_key_create(&amp;key,(void *)echomsg);</b><br />   pthread_create(&amp;tid1,NULL,(void *)child,NULL);<br />   pthread_create(&amp;tid2,NULL,(void *)child,NULL);<br />   <br />   pthread_join(tid1,NULL);<br /> pthread_join(tid2,NULL);<br /><b>   pthread_key_delete(key);</b><br />   <br />   printf("main thread exit\n");   <br />   return 0;<br />}<br /><img src ="http://www.cppblog.com/mydriverc/aggbug/29747.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-11 01:47 <a href="http://www.cppblog.com/mydriverc/articles/29747.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>取消线程</title><link>http://www.cppblog.com/mydriverc/articles/29746.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 10 Aug 2007 17:42:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29746.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29746.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29746.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29746.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29746.html</trackback:ping><description><![CDATA[一般情况下,线程在其主函数退出时自动终止,但同时也可以因为接收到另一个线程发来的取消请求而终止.<br />
线程取消的方法是 向目标线程发出一个cancel信号,但如何处理cancel信号则是由目标线程自己决定,或者忽略,或者立即终止,或者继续运行到cancelation-point(取消点),由不通的cancellation状态决定.<br />
竭诚接收到cancel信号的默认处理是继续运行到取消点,也就是说摄制一个canceled状态,线程继续运行,只有运行到cancelation-point的时候才会退出.<br /><br />
取消点:根据posix的标准,pthread_join,pthread_testcancel,pthread_cand_wait,
pthread_cond_timedwait,sem_wait,sigwait等函数以及read,write等会引起堵塞的调用都是
cancelation-point,而其他的pthread函数都不会引起cancelation动作,但pthread_cancel的手册说明,
linuxthread的库与C库结合的并不是很好,因而目前的C函数库都不是cancelation-point,<b>但cancel信号会使线程从堵塞的
系统调用中退出.并设置EINTR错误码,因此可以在需要作为cancelation-point的系统调用前后调用
pthread_testcancel,从而达到POSIX的标准,例如<br />
pthread_testcancel();<br />
retcode=read(fd,buffer,length);<br />
pthread_testcancel();</b><br /><br />
程序设计的考虑:<br />
如果线程处于无限的循环中且循环没有执行到取消点的必然路径,则线程无法由于外部的其他线程的取消请求而终止,因此,在这样的循环体中的必经路径上加上pthread_testcancel()的调用.<img src ="http://www.cppblog.com/mydriverc/aggbug/29746.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-11 01:42 <a href="http://www.cppblog.com/mydriverc/articles/29746.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux环境进程间通信系列（五）：共享内存</title><link>http://www.cppblog.com/mydriverc/articles/29741.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 10 Aug 2007 17:00:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29741.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29741.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29741.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29741.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29741.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 共享内存可以说是最有用的进程间通信方式，也是最快的				IPC				形式。两个不同进程										A						、						B						共享内存的意思是，同一块物理内存被映射到进程						A						、						B														各自的进程地址空间。进程				A				可以即时看到进程				B...&nbsp;&nbsp;<a href='http://www.cppblog.com/mydriverc/articles/29741.html'>阅读全文</a><img src ="http://www.cppblog.com/mydriverc/aggbug/29741.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-11 01:00 <a href="http://www.cppblog.com/mydriverc/articles/29741.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>inux下使用系统调用实现进程后台运行 </title><link>http://www.cppblog.com/mydriverc/articles/29613.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Wed, 08 Aug 2007 16:57:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29613.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29613.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29613.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29613.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29613.html</trackback:ping><description><![CDATA[
		<p>其实很简单，就是把<br />if(fork()==0)<br />{<br />    execve(...);<br />}<br />else<br />{<br />wait(state);<br />}<br />结构中的else去掉就可以了<br />下面是一个示例<br />#include &lt;stdio.h&gt;<br />#include &lt;sys/types.h&gt;<br />#include &lt;unistd.h&gt;<br />#include &lt;wait.h&gt;<br />#include &lt;errno.h&gt;<br />#include &lt;sys/stat.h&gt;<br />#include &lt;string.h&gt;</p>
		<p>int main(int argc,char** argv)<br />{<br />  int * status;<br />  int gc;<br />  char command[64]="./clu";<br />  char comarg[64]="";<br />  argv[1]=comarg;<br />  while(1)<br />    {<br />      if((gc=getchar())=='g')<br /> {<br />   if(fork()==0)<br />     {<br />       if(execve(command,argv,0)==-1)<br />  {<br />    printf("process error is %s\n",strerror(errno));<br />  }<br />       printf("process is ok\n");<br />     }<br /> }<br />      if(gc=='k')<br /> {<br />   printf("to be continue\n");<br /> }<br />      if(gc=='e')<br /> {<br />   return 1;<br /> }<br />    }<br /> }<br /><br />功能是这样的，启动以后会从键盘接受字符，如果是g就运行预先指定好的程序（在这里是一个叫clu的程序），如果是k就打印to be continue，如果是e就退出<br />下面是这个叫clu的程序的代码<br />#include &lt;stdio.h&gt;<br />#include &lt;time.h&gt;</p>
		<p>int main()<br />{<br />  long begin=0;<br />  long end=24000;<br />  long finish=0;<br />  long loop1,loop2;<br />  time_t flag;<br />  time_t nowtime1,nowtime2;<br />  if((flag=time(&amp;nowtime1))==-1)exit(1);<br />  for(loop1=begin;loop1&lt;end;loop1++)<br />    {<br />      for(loop2=begin;loop2&lt;end;loop2++)<br /> {<br />   if((loop1%2)==0)<br />     {<br />       if((loop2%2)==0)<br />  {<br />    if(finish&gt;65535)<br />      finish=0;<br />    finish+=((loop1/2+loop2/2)%2)%2;<br />  }<br />     }<br /> }<br />    }<br />  if((flag=time(&amp;nowtime2))==-1)exit(1);<br />  printf("%ld\n",finish);<br />  printf("proccess time %ldsec\n",nowtime2-nowtime1);<br />}<br />这个程序本身没有任何意义，只是纯粹的延误一下时间，输出一下运行的时间，便于测试，在我的AMD2500+上，大概是5秒，你可以试试哦^_^<br /><br />要测试这个程序，可以在程序运行时输入g，但是这个计算不会马上结束，在屏幕还没有输出的时候再输入k，就会先输出k的内容，后台clu的结果出来后才输出到屏幕，也就是clu在后台运行了</p>
<img src ="http://www.cppblog.com/mydriverc/aggbug/29613.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-09 00:57 <a href="http://www.cppblog.com/mydriverc/articles/29613.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux信号机制之信号阻塞</title><link>http://www.cppblog.com/mydriverc/articles/29237.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Thu, 02 Aug 2007 15:19:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29237.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29237.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29237.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29237.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29237.html</trackback:ping><description><![CDATA[    每个进程都有一个用来描述哪些信号传送来将被阻塞的阻塞的信号集，如果某种信号在某个进程的阻塞信号集中，则传送到该进程的此种信号将会被阻塞。信号阻塞操作主要有以下三个函数：<br />
     <br />
     #include&lt;signal.h&gt;<br />
     int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);<br />
     int sigpending(sigset_t *set);<br />
     int sigsuspend(const sigset_t *mask);<br />
     <br />
     sigprocmask系统调用用于改变当前阻塞信号集，它具体的功能主要取决于第一个参数how。how的取值如下：<br />
            -----------------------------------------------------------------------------------------------------<br />
                    参数how         |                                                动作<br />
            ------------------------------------------------------------------------------------------------------<br />
               SIG_BLOCK       |       将set指向的信号集中的信号添加到当前阻塞信号集中<br />
            ------------------------------------------------------------------------------------------------------<br />
           SIG_UNBLOCK      |       从当前阻塞信号集中移除set指向的信号集中的信号，<br />
                                                       |       移除一个阻塞信号集中不存在的信号那也是合法的<br />
            ------------------------------------------------------------------------------------------------------<br />
            SIG_SETMASK      |       指定set所指向的信号集为当前阻塞信号集<br />
           --------------------------------------------------------------------------------------------------------<br />
       如果sigprocmask的第三个参数oldset非空，则oldset保存旧的信号阻塞集。<br />
       <br />
      sigpending(…)获得当前已传送到进程，却被阻塞的所有信号，在set指向的信号集中保存未决（阻塞）的信号。<br />
    sigsuspend(const sigset_t
*mask)，<b>在接收到某个信号之前，临时用mask替换进程的信号掩码，并挂起进程，直到收到信号为止。sigsuspend返回后，即系统在接受到信
号后,进程将恢复调用前的信号掩码,调用信号处理函数完毕后进程将继续执行。</b>始终返回-1，并将errno设置为EINTR。<br />
      <br />
      下面这个小程序测试了信号的阻塞以及信号集操作：<br />
#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
#include &lt;signal.h&gt;<br />
#include &lt;unistd.h&gt;<br /><br />
#define BLOCK_SIG      SIGRTMIN+10<br />
/*信号处理函数*/<br />
static void sig_handler(int signum,struct siginfo *sig_info,void *unused)<br />
{<br />
      printf("\n&lt;! receive signal %d !&gt;\n",signum);<br />
      printf("-----------------&lt; Signal information &gt;--------------------\n");<br />
      printf("\tsi_signo :\t%d\n",sig_info-&gt;si_signo);//信号值<br />
      printf("\tsi_errno :\t%d\n",sig_info-&gt;si_errno);//error值<br />
      printf("\tsi_code  :\t%d\n",sig_info-&gt;si_code); //信号产生的原因<br />
      printf("\tsi_int     :\t%d\n",sig_info-&gt;si_int); //4 bytes 整型      <br />
      <br />
}<br /><br />
int main(int argc,char**argv)<br />
{<br />
      sigset_t new_mask,old_mask,pending_mask;<br />
      struct sigaction act;<br />
      <br />
      printf("Block precess PID : %d\n",getpid());<br />
      /*<br />
       *设置struct sigaction结构<br />
       */<br />
      sigemptyset(&amp;act.sa_mask);<br />
      act.sa_flags=SA_SIGINFO;<br />
      act.sa_sigaction=(void *)sig_handler;<br />
          /*<br />
       *安装信号<br />
       */<br />
      if(sigaction(BLOCK_SIG,&amp;act,NULL))<br />
            printf("install siganl SIGRTMIN+10 error!\n");<br />
          /*<br />
       *设置新的屏蔽信号集，阻塞 SIGRTMIN+10 信号！<br />
       */<br />
      sigemptyset(&amp;new_mask);<br />
      sigaddset(&amp;new_mask,BLOCK_SIG);<br />
      if(sigprocmask(SIG_BLOCK,&amp;new_mask,&amp;old_mask))<br />
            printf("block signal SIGRTMIN+10 error!\n");<br /><br />
      sleep(30);//等待信号SIGRTMIN+10唤醒，但因为上面设置了此信号阻塞，因此此处不能从睡眠中唤醒进程<br /><br />
      printf("\nNow begin to get pending mask and unblock SIGRTMIN_10\n");<br />
      sigpending(&amp;pending_mask);     //获得当前已传送到进程，却被阻塞的所有信号<br />
      if(sigismember(&amp;pending_mask,BLOCK_SIG))<br />
            printf("\nsignal SIGRTMIN+10 is pending!\n");<br />
      /*<br />
        *恢复原来的信号屏蔽集,此前被阻塞的信号触发<br />
        */<br />
      sigprocmask(SIG_SETMASK,&amp;old_mask,NULL);<br />
      printf("\nsignal unblocked!\n");<br /><br />
      /*<br />
       *进程进入睡眠，直到有信号唤醒进程，或时间到！<br />
       */<br />
      sleep(30);<br />
      <br />
      exit(0);<br />
}<br />
      <br />
       配合上篇中介绍的信号发送小程序，你可以测试一下程序的执行结果！<img src ="http://www.cppblog.com/mydriverc/aggbug/29237.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-02 23:19 <a href="http://www.cppblog.com/mydriverc/articles/29237.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux信号机制之sigaction结构体浅析</title><link>http://www.cppblog.com/mydriverc/articles/29159.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Wed, 01 Aug 2007 16:34:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29159.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29159.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29159.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29159.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29159.html</trackback:ping><description><![CDATA[     信号安装函数sigaction(int signum,const struct sigaction *act,struct
sigaction
*oldact)的第二个参数是一个指向sigaction结构的指针（结构体名称与函数名一样，千万别弄混淆了）。在结构sigaction的实例中，
指定了对特定信号的处理，信号所传递的信息，信号处理函数执行过程中应屏蔽掉哪些函数等。当然，此指针也可以为NULL，进程会以默认方式处理信号。以下
就简单介绍一下sigaction结构以及一般的用法。
<p>        对于内核头文件而言，struct sigaction 结构体定义在kernel/include/asm/signal.h,此头文件又被kernel/include/linux/signal.h包含。<br />
        对于用户空间的头文件而言，struct sigaction定义在
/usr/include/bits/sigaction.h,此头文件又被/usr/include/signal.h包含，所以应用程序中如果用到此
结构，只要#include
&lt;signal.h&gt;即可。注意内核中的定义和应用程序中的定义是不一样的，内核空间的sigaction结构只支持函数类型为
__sighandler_t的信号处理函数，不能处理信号传递的额外信息。具体定义如下：</p><p>……<br />
/* Type of a signal handler.   */<br />
typedef void (*__sighandler_t)(int);</p><p>……<br />
#ifdef __KERNEL__<br />
struct old_sigaction {<br />
          __sighandler_t sa_handler;<br />
         old_sigset_t sa_mask;<br />
         unsigned long sa_flags;<br />
         void (*sa_restorer)(void);<br />
};</p><p>struct sigaction {<br />
         __sighandler_t sa_handler;<br />
        unsigned long sa_flags;<br />
        void (*sa_restorer)(void);<br />
        sigset_t sa_mask;   /* mask last for extensibility */<br />
};</p><p>struct k_sigaction {<br />
        struct sigaction sa;<br />
};</p><p>#else<br />
/* Here we must cater to libcs that poke about in kernel headers.   */</p><p>struct sigaction {<br />
          union {<br />
                  __sighandler_t _sa_handler;<br />
                  void (*_sa_sigaction)(int, struct siginfo *, void *);<br />
          } _u;<br />
          sigset_t sa_mask;<br />
          unsigned long sa_flags;<br />
          void (*sa_restorer)(void);<br />
};</p><p>#define sa_handler   _u._sa_handler<br />
#define sa_sigaction _u._sa_sigaction</p><p>#endif /* __KERNEL__ */</p><p>sa_handler的原型是一个参数为int，返回类型为void的函数指针。参数即为信号值，所以信号不能传递除信号值之外的任何信息;</p><p>sa_sigaction的原型是一个带三个参数，类型分别为int，struct siginfo *，void
*,返回类型为void的函数指针。第一个参数为信号值;第二个参数是一个指向struct
siginfo结构的指针，此结构中包含信号携带的数据值;第三个参数没有使用。</p><p>sa_mask指定在信号处理程序执行过程中，哪些信号应当被阻塞。默认当前信号本身被阻塞。</p><p>sa_flags包含了许多标志位，比较重要的一个是SA_SIGINFO，当设定了该标志位时，表示信号附带的参数可以传递到信号处理函数中。即
使sa_sigaction指定信号处理函数，如果不设置SA_SIGINFO，信号处理函数同样不能得到信号传递过来的数据，在信号处理函数中对这些信
息的访问都将导致段错误。</p><p>sa_restorer已过时，POSIX不支持它，不应再使用。</p><p>        因此，当你的信号需要接收附加信息的时候，你必须给sa_sigaction赋信号处理函数指针，同时还要给sa_flags赋SA_SIGINFO,类似下面的代码：<br />
     #include &lt;signal.h&gt;<br />
     ……<br />
     void sig_handler_with_arg(int sig,siginfo_t *sig_info,void *unused){……}<br />
    <br />
     int main(int argc,char **argv)<br />
     {<br />
              struct sigaction sig_act;<br />
              ……<br />
              sigemptyset(&amp;sig_act.sa_mask);<br />
              sig_act.sa_sigaction=sig_handler_with_arg;<br />
              sig_act.sa_flags=SA_SIGINFO;<br />
  <br />
               ……<br />
     }<br />
        如果你的应用程序只需要接收信号，而不需要接收额外信息，那你需要的设置的是sa_handler,而不是sa_sigaction,你的程序可能类似下面的代码：<br />
     #include &lt;signal.h&gt;<br />
     ……<br />
     void sig_handler(int sig){……}<br />
    <br />
     int main(int argc,char **argv)<br />
     {<br />
              struct sigaction sig_act;<br />
              ……<br />
              sigemptyset(&amp;sig_act.sa_mask);<br />
              sig_act.sa_handler=sig_handler;<br />
              sig_act.sa_flags=0;<br />
  <br />
               ……<br />
      }</p><p>      如果需要更详细说明，请参阅sigaction的man手册。</p><img src ="http://www.cppblog.com/mydriverc/aggbug/29159.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-02 00:34 <a href="http://www.cppblog.com/mydriverc/articles/29159.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下的信号处理</title><link>http://www.cppblog.com/mydriverc/articles/29158.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Wed, 01 Aug 2007 16:25:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29158.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29158.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29158.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29158.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29158.html</trackback:ping><description><![CDATA[
		<p>一个最简单的信号信号处理例子:<br />#include &lt;signal.h&gt;<br />#include &lt;sys/types.h&gt;<br />#include &lt;unistd.h&gt;<br />#include &lt;stdio.h&gt;</p>
		<p>void signal_rec(int signum, siginfo_t *info, void *myop)<br />{<br /> printf("receive signal %d\n", signum);<br /> sleep(5); <br />}</p>
		<p>int main(int argc,char **argv)<br />{<br /> struct sigaction act;<br /> int sig,i;<br /> <br /> sigemptyset(&amp;act.sa_mask);<br /> act.sa_flags = SA_SIGINFO;<br /> act.sa_sigaction = signal_rec;<br /> <br /> for(i=1; i&lt;argc; i++){ //argv[0]为调用程序名<br />  sig = atoi(argv[i]);<br />  if(<b>sigaction</b>(sig, &amp;act, NULL) &lt;0){<br />   printf("install signal error\n");<br />  } <br /> }<br /> <br /> while(1){<br />  sleep(2);<br />  printf("wait for the signal\n");<br /> }<br />}<br />用法：<br />将文件保存为test.c，执行make test，编译生成test文件。<br />./test 36 39&amp;<br />命令的意思是注册信号36,39。命令结果会返回pid，假设为2625。<br />通过另外一个终端向2625进程发送信号。例如:kill -s 36 2625<br />则我们可以看到signal_rec函数被执行了。<br />当我们发送40信号，我们会发送程序终止了，这是因为对于实时信号，默认的操作是终止。<br />关于信号：<br /> Linux
支持的信号分为可靠信号和不可靠信号。此处的可靠与不可靠不是指系统是不是可靠的可靠，而是对于可靠信息来说，每一次信号发实都每被加到信号链中，所以是
可靠的；而对于不可靠信号来说，如果进程之前已经接收到该信号，则不会被加到信号链中，因此对于此次发送的信号来说，对于该进程来说是不知道的，所就是说
信号丢失了，因此是不可靠的。<br /> 不可靠信号主要是在早期信号机制上的信号。一般来说，信号值小于SIGRTMIN的信号为不可靠信号，不可靠信号又称为非实时信号。可靠信号又称为实时信号。实时信号是SIGRTMIN和SIGRTMAX间的所有信号。<br /> 我们可以通过kill -l查看SIGRTMIN和SIGRTMAX的值。在Debian系统和Redhat上面，一般SIGRTMIN=33,SIGRTMAX=64。<br /> Linux既支持新的信号安装函数sigation以及信号发送函数sigqueue，又支持早期的signal信号安装函数和kill信号发送函数。<br /> 信号的可靠与不可靠只与信号值有关，与信号的发送和安装函数无关，也就是说在Linux下即使使用sigaction和sigqueue也不可能将不可靠信号变为可靠信号。<br />信号发送函数主要有以下几个：kill,raise,sigqueue,alarm,settimer,abort。<br />信号安装函数主要有signal和sigaction。sigaction主要用于与sigqueue系统调用配合使用，主要用于实时信号处理。<br />信号集操作主要有以下几个函数：sigemptyset,sigfillset,sigaddset,sigdelset,sigismember。<br />信号的阻塞的未决主要有以下几个函数：sigprocmask,sigsuspend,sigpending。<br />最后以一个读串口设备时用到的信号处理作为结尾(在上次的初始化串口设备中已经出现过)：<br />在读取设备数据或者进行网络应用的时候，为了防止程序进入死锁，我们需要设置超时操作，即比如我们读串口设备，尝试一定时间后仍然没有响应，则可能设备没有正常工作。那么在超时以后我们需要退出，否则程序就锁住了。<br />我们现在使用SIGALRM信号来进行这个处理(这种方式并不是最好的办法)：<br />我们假设有一个标记是否超时的全局变量caught_alrm，默认为0，为1时则表示超时。<br />static volatile sig_atomic_t caught_alrm;<br />static void sig_alrm(int signo){//信号处理函数，设置超时全局变量为1。<br />        caught_alrm = 1;<br />        return;<br />}<br />int a_function{<br /> ……<br />    if(signal(SIGALRM, sig_alrm) == SIG_ERR){<br />     syslog(LOG_ERR,"signal error in function:%s",__FUNCTION__);<br />        return -1;<br />    }<br />    caught_alrm = 0;<br />    alarm(expalarm);<br />    do{<br />     //do something<br />     //if the work is finished,call alarm(0) to clean the timer.<br />    }while(caught_alrm == 0);<br />}<br />注：<br />1、本文为整理以前的工作笔记，如果您要转载，请注意来源为尔雅，作者覃士国。如果您有任何问题欢迎交流：<a href="mailto:shiguo.qin@gmail.com">shiguo.qin@gmail.com</a><br />2、
此文仅仅是一则笔记，如果您正在寻找关于Linux信号编程方面的资料，可以参考郑彦兴的一篇文章:http://www-
128.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html。此文详细描述了很多关于信
号处理中的结构等内容，是学习信号编程的一篇不错的教程式文章。</p>
		<p> </p>
<img src ="http://www.cppblog.com/mydriverc/aggbug/29158.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-02 00:25 <a href="http://www.cppblog.com/mydriverc/articles/29158.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>僵尸进程的产生和避免</title><link>http://www.cppblog.com/mydriverc/articles/29114.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Tue, 31 Jul 2007 16:24:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29114.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29114.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29114.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29114.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29114.html</trackback:ping><description><![CDATA[  
在fork()/execve()过程中，假设子进程结束时父进程仍存在，而父进程fork()之前既没安装SIGCHLD信号处理函数调用
waitpid()等待子进程结束，又没有显式忽略该信号，则子进程成为僵尸进程，无法正常结束，此时即使是root身份kill
-9也不能杀死僵尸进程。补救办法是杀死僵尸进程的父进程(僵尸进程的父进程必然存在)，僵尸进程成为”孤儿进程”，过继给1号进程init，init始
终会负责清理僵尸进程。 <p>产生原因：<br />
1.在子进程终止后到父进程调用wait()前的时间里，子进程被称为zombie。<br />
2.网络原因有时会引起僵死进程。<br /><br />
解决方法：<br />
1.设置SIGCLD信号为SIG_IGN，系统将不产生僵死进程。<br />
2.用两次fork()，而且使紧跟的子进程直接退出，是的孙子进程成为孤儿进程，从而init进程将负责清除这个孤儿进程。</p><p><font size="4"><strong>wait的函数原型是：</strong></font><br />
　　<br />
　　#include /* 提供类型pid_t的定义 */<br />
　　#include<br />
　　 pid_t wait(int *status)<br />
　　<br />
　　进程一旦调用了wait，就立即阻塞自己，由wait自动分析是否当前进程的某个子进程已经退出，如果让它找到了这样一个已经变成僵尸的子进程，
wait就会收集这个子进程的信息，并把它彻底销毁后返回；如果没有找到这样一个子进程，wait就会一直阻塞在这里，直到有一个出现为止。<br />
　　<br />
　　参数status用来保存被收集进程退出时的一些状态，它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意，只想把这个僵尸进程消灭掉，（事实上绝大多数情况下，我们都会这样想），我们就可以设定这个参数为NULL，就象下面这样：<br />
　　<br />
　　pid = wait(NULL);<br />
　　<br />
　　如果成功，wait会返回被收集的子进程的进程ID，如果调用进程没有子进程，调用就会失败，此时wait返回-1，同时errno被置为ECHILD。<br />
　</p><p><font size="4"><strong>waitpid的函数原型是：</strong></font></p><p><br />
　　简介<br />
　　waitpid系统调用在Linux函数库中的原型是：<br />
　　<br />
　　#include /* 提供类型pid_t的定义 */<br />
　　#include<br />
　　pid_t waitpid(pid_t pid,int *status,int options)</p><p></p><p>从本质上讲，系统调用waitpid和wait的作用是完全相同的，但waitpid多出了两个可由用户控制的参数pid和options，从而为我们编程提供了另一种更灵活的方式。下面我们就来详细介绍一下这两个参数：<br />
　　<br />
　　● pid<br />
　　<br />
　　从参数的名字pid和类型pid_t中就可以看出，这里需要的是一个进程ID。但当pid取不同的值时，在这里有不同的意义。<br />
　　<br />
　　pid&gt;0时，只等待进程ID等于pid的子进程，不管其它已经有多少子进程运行结束退出了，只要指定的子进程还没有结束，waitpid就会一直等下去。<br />
　　<br />
　　pid=-1时，等待任何一个子进程退出，没有任何限制，此时waitpid和wait的作用一模一样。<br />
　　<br />
　　pid=0时，等待同一个进程组中的任何子进程，如果子进程已经加入了别的进程组，waitpid不会对它做任何理睬。<br />
　　<br />
　　pid&lt;-1时，等待一个指定进程组中的任何子进程，这个进程组的ID等于pid的绝对值。<br /><br />
　　● options<br /><br />
　　options提供了一些额外的选项来控制waitpid，目前在Linux中只支持WNOHANG和WUNTRACED两个选项，这是两个常数，可以用"|"运算符把它们连接起来使用，比如：</p><p><br />
　　ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);</p><p><br />
　　如果我们不想使用它们，也可以把options设为0，如：</p><p><br />
　　ret=waitpid(-1,NULL,0);<br />
　　<br />
　　如果使用了WNOHANG参数调用waitpid，即使没有子进程退出，它也会立即返回，不会像wait那样永远等下去。<br />
　　<br />
　　而WUNTRACED参数，由于涉及到一些跟踪调试方面的知识，加之极少用到，这里就不多费笔墨了，有兴趣的读者可以自行查阅相关材料。<br />
　　<br />
　　看到这里，聪明的读者可能已经看出端倪了--wait不就是经过包装的waitpid吗？没错，察看&lt;内核源码目录&gt;/include/unistd.h文件349-352行就会发现以下程序段：<br />
　　<br />
　　static inline pid_t wait(int * wait_stat)<br />
　　{<br />
　　 return waitpid(-1,wait_stat,0);<br />
　　}<br />
　　<br /><br />
　　<strong>返回值和错误<br /></strong>　　<br />
　　waitpid的返回值比wait稍微复杂一些，一共有3种情况：<br />
　　<br />
　　● 当正常返回的时候，waitpid返回收集到的子进程的进程ID；<br />
　　<br />
　　● 如果设置了选项WNOHANG，而调用中waitpid发现没有已退出的子进程可收集，则返回0；<br />
　　<br />
　　● 如果调用中出错，则返回-1，这时errno会被设置成相应的值以指示错误所在；<br />
　　<br />
　　当pid所指示的子进程不存在，或此进程存在，但不是调用进程的子进程，waitpid就会出错返回，这时errno被设置为ECHILD</p><p></p><p><strong><font size="4">其它：</font></strong></p><p>调用 wait＆waitpid 来处理终止的子进程：</p><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" /><span style="color: rgb(0, 0, 0);">pid_t wait(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"></span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);"> statloc);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" />pid_t waitpid(pid_t pid, </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"></span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);">statloc, </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> options);</span></div><p>两个函数都返回两个值：函数的返回值和终止的子进程ID，而子进程终止的状态则是通过statloc指针返回的。<br /></p><p>     wait＆waitpid 的区别是显而易见的，wait等待第一个终止的子进程，而waitpid则可以指定等待特定的子进程。这样的区别可能会在下面这种情况时表现得更加明显：<br />
        
当同时有5个客户连上服务器，也就是说有五个子进程分别对应了5个客户，此时，五个客户几乎在同时请求终止，这样一来，几乎同时，五个FIN发向服务器，
同样的，五个SIGCHLD信号到达服务器，然而，UNIX的信号往往是不会排队的，显然这样一来，信号处理函数将只会执行一次，残留剩余四个子进程作为
僵尸进程驻留在内核空间。此时，正确的解决办法是利用waitpid(-1, &amp;stat,
WNOHANG)防止留下僵尸进程。其中的pid为－1表明等待第一个终止的子进程，而WNOHANG选择项通知内核在没有已终止进程项时不要阻塞。 </p><p></p><p><strong><font size="4">wait＆waitpid 区别</font></strong></p><p>       waitpid提供了wait函数不能实现的3个功能:<br /></p><ul><li>waitpid等待特定的子进程, 而wait则返回任一终止状态的子进程; </li><li>waitpid提供了一个wait的非阻塞版本; </li><li>waitpid支持作业控制(以WUNTRACED选项).</li></ul><font size="4"><strong>用于检查wait和waitpid两个函数返回终止状态的宏:</strong></font><br /><p>这两个函数返回的子进程状态都保存在statloc指针中, 用以下3个宏可以检查该状态:<br /></p><ul><li>WIFEXITED(status): 若为正常终止, 则为真. 此时可执行</li></ul><ul><ul><li>WEXITSTATUS(status): 取子进程传送给exit或_exit参数的低8位. </li></ul></ul><ul><li>WIFSIGNALED(status): 若为异常终止, 则为真. 此时可执行</li></ul><ul><ul><li>WTERMSIG(status):   取使子进程终止的信号编号. </li></ul></ul><ul><li>WIFSTOPPED(status): 若为当前暂停子进程, 则为真. 此时可执行</li></ul>
                        WSTOPSIG(status): 取使子进程暂停的信号编号<img src ="http://www.cppblog.com/mydriverc/aggbug/29114.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-01 00:24 <a href="http://www.cppblog.com/mydriverc/articles/29114.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WIFEXITED和WEXITSTATUS</title><link>http://www.cppblog.com/mydriverc/articles/29113.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Tue, 31 Jul 2007 16:17:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29113.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29113.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29113.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29113.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29113.html</trackback:ping><description><![CDATA[两个<p><nobr id="key1" style="border-bottom: 2px dotted; text-decoration: underline; color: rgb(102, 0, 255); background-color: transparent;" onclick="return kwC();" target="_blank" oncontextmenu="return false;" onmouseover="kwE(event,1, this);" onmouseout="kwL(event, this);" onmousemove="kwM(1);">检验</nobr>子进程退出的正常还是非正常和返回值的宏。</p><br />wait(&amp;status)<br />if(WIFEXITED(status))<br />{<br />  printf("%d",WEXITSTATUS(status));<br />}<br /><img src ="http://www.cppblog.com/mydriverc/aggbug/29113.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-01 00:17 <a href="http://www.cppblog.com/mydriverc/articles/29113.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux安全攻略—僵尸进程</title><link>http://www.cppblog.com/mydriverc/articles/29111.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Tue, 31 Jul 2007 16:07:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29111.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29111.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29111.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29111.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29111.html</trackback:ping><description><![CDATA[
		<font id="zoom" class="f14">
				<br />    好了，我现在让大家就看看我们神秘僵尸进程是什么样子的？下面我来用c写出父进程建立子进程的代码：<br />/**********************************子进程正常出生和灭亡过程****************************************/<br />#include &lt;sys/types.h&gt;<br />#include &lt;unistd.h&gt;<br />main()<br />{<br />    <br />    fork(); /*开始创建一个子进程*/</font>
		<p>
				<font id="zoom" class="f14">    if(fork()&gt;0) /* 如果是父进程 */</font>
		</p>
		<p>
				<font id="zoom" class="f14">       wait(NULL);    /* 收集僵尸进程 */<br />}</font>
		</p>
		<p>
				<font id="zoom" class="f14">    大家看完代码后，就会觉得这个代码正是我刚才讲的进程一生是怎么样的，进程死后，一定要为他收尸，否则他就会编程僵尸进程。下面就让我们来看看未能把死去的进程收尸会变成什么样子？</font>
		</p>
		<p>
				<font id="zoom" class="f14">/**********************************僵尸进程******zombie.c*****************************************************/</font>
		</p>
		<p>
				<font id="zoom" class="f14">#include &lt;sys/types.h&gt;<br />#include &lt;unistd.h&gt;<br />main()<br />{</font>
		</p>
		<p>
				<font id="zoom" class="f14">       fork(); /*开始创建一个子进程*/</font>
		</p>
		<p>
				<font id="zoom" class="f14">    if(fork&gt;0) /* 如果是父进程 */</font>
		</p>
		<p>
				<font id="zoom" class="f14">       sleep(30);    /* 休眠30秒，这段时间里，父进程什么也干不了 */<br />       wait(NULL);    /* 收起僵尸进程 */</font>
		</p>
		<p>
				<font id="zoom" class="f14">   /*因为父进程死了，子进程也得一起陪葬，那么我们就让父进程睡眠30秒，在这30秒内我们就可以看到僵尸进程。<br />     30秒过后，父进程醒来后就得死，所以到那时子进程也就死去。*/<br />}</font>
		</p>
		<p>
				<font id="zoom" class="f14">
						<br />/********************************************************************************************/</font>
		</p>
		<p>
				<font id="zoom" class="f14">    为了让大家更清楚的看到僵尸进程我们把这个僵尸进程代码编译，然后执行，<br />#gcc -o zombie zombie.c</font>
		</p>
		<p>
				<font id="zoom" class="f14">编译成功后我们开始执行这个程序因为代码里的sleep(30)是让父进程睡眠30秒，如果我们在前台执行我们这个程序，那么就不能执行其他shell命令了，所以这里我们在执行的命令后面加一个&amp; 代表后台运行的意思。<br />#./zomber &amp;</font>
		</p>
		<p>
				<font id="zoom" class="f14">好了，我们在30秒内执行查看进程命令来看看我们这个zombie进程是什么样的？<br />#ps -aux |grep zombie</font>
		</p>
		<p>
				<font id="zoom" class="f14">这个命令是显示有关zombie的所有信息。具体操作请看图（2）。</font>
		</p>
		<p>
				<font id="zoom" class="f14">   
看到这里后，聪明的朋友会问，既然子进程是由父进程创造出来的，那么如果一个父程序执行完退出后，子进程不管是不是僵尸进程也直接就退出了，因为父进程死
了嘛。那么这也不会造成多大危害啊？如果大家这么认为那可就错了，当我们刚才用ps命令观察进程的执行状态时，看到某些进程的状态栏为defunct，这
就是所谓的“僵尸”进程。“僵尸”进程是一个早已死亡的进程，但在进程表（processs
table）中仍占了一个位置（slot）。由于进程表的容量是有限的，所以，defunct进程不仅占用系统的内存资源，影响系统的性能，而且如果其数
目太多，还会导致系统瘫痪，具体请看下面的代码：<br />/***********************************无限创建子进程********************************************/</font>
		</p>
		<p>
				<font id="zoom" class="f14">#include &lt;sys/types.h&gt;<br />#include &lt;unistd.h&gt;<br />main()<br />{<br />       for (;;)  /*制作一个死循环*/<br />       <br />         fork(); /*开始创建一个子进程*/</font>
		</p>
		<p>
				<font id="zoom" class="f14">}</font>
		</p>
		<p>
				<font id="zoom" class="f14">
						<br />/********************************************************************************************/</font>
		</p>
		<p>
				<font id="zoom" class="f14">懂C语言的朋友会知道for(;;)是一个死循环。那么这仅有2行代码的程序就可以把你的linux瞬间死机，因为他的作用是无限制的在内存里增加新的子进程，一个系统根据内存的容量会分配进程的最大限制，一旦同时运行的进程数超符合，那么你的机器必然会死机。<br />  
我这里分别用了2个用户进行测试过，一个是普通权限的用户，一个是Root用户，测试结果是“任何用户只要执行这个程序都会让机器死掉”。原因就是默认的
Linux系统没有对普通用户的使用最大进程进行限制。所以这样对系统造成很大一个威胁，那么我们如何避免普通用户非法执行过多资源或进程导致系统拖死，
所以我们的给除了Root的用户做一下限制。</font>
		</p>
		<p>
				<font id="zoom" class="f14">
						<br />对普通用户进行限制：</font>
		</p>
		<p>
				<font id="zoom" class="f14">第1步：首先进到Linux终端用vi编辑/etc/security /limits.conf文件，在里面加入： </font>
		</p>
		<p>
				<font id="zoom" class="f14">　　* hard core 0 <br />　　* hard rss 5000 <br />　　* hard nproc 20 <br />  <br /> 
这里的* 代表除了Root的所有用户，(* hard core 0) 是禁止core files“core 0”，(* hard rss
5000) 是限制内存使用为5MB“rss  5000”, (* hard nproc 20 )是限制进程数为“nproc
50“。大家可以根据自己系统内存大小进行合理配置。</font>
		</p>
		<p>
				<font id="zoom" class="f14">第2步：用vi编辑/etc/pam.d/login文件，然后加上下面这行保存退出就可以。 </font>
		</p>
		<p>
				<font id="zoom" class="f14">　　session required /lib/security/pam_limits.so </font>
		</p>
		<p>
				<font id="zoom" class="f14">    好了，现在我们已经对普通用户限制了进程和内存使用极限，修改完配置以后大家可以用Root和普通用户分别进行测试，结果当然是普通用户执行完我们刚才做的程序不会死机了。</font>
		</p>
		<p>
				<font id="zoom" class="f14">    一个系统的安全性不取决与打不打补丁，虽然打补丁很重要，但是对系统做出相应的配置也是相当重要的。<br /></font>
		</p>
    
     
      　<img src ="http://www.cppblog.com/mydriverc/aggbug/29111.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-01 00:07 <a href="http://www.cppblog.com/mydriverc/articles/29111.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于linux 僵尸进程</title><link>http://www.cppblog.com/mydriverc/articles/29109.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Tue, 31 Jul 2007 16:05:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29109.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29109.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29109.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29109.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29109.html</trackback:ping><description><![CDATA[
		<div id="text91890" style="font-size: 13px;">　　僵尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程.(zombie)进程 <br /><br />　　怎样产生僵尸进程的： <br /><br />　　一个进程在调用exit命令结束自己的生命的时候，其实它并没有真正的被销毁，而是留下一个称为僵尸进程（Zombie）的数据结构（系统调用 exit，它的作用是使进程退出，但也仅仅限于将一个正常的进程变成一个僵尸进程，并不能将其完全销毁）。<br /><br />　
　在Linux进程的状态中，僵尸进程是非常特殊的一种，它已经放弃了几乎所有内存空间，没有任何可执行代码，也不能被调度，仅仅在进程列表中保留一个位
置，记载该进程的退出状态等信息供其他进程收集，除此之外，僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸，如果他的父进程没安装
SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束，又没有显式忽略该信号，那么它就一直保持僵尸状态，如果这时父进程结束了，
那么init进程自动会接手这个子进程，为它收尸，它还是能被清除的。但是如果如果父进程是一个循环，不会结束，那么子进程就会一直保持僵尸状态，这就是
为什么系统中有时会有很多的僵尸进程。 <br /><br />　　怎么查看僵尸进程： <br /><br />　　利用命令ps，可以看到有标记为Z的进程就是僵尸进程。 <br /><br />　　怎样来清除僵尸进程： <br /><br />　
　1.改写父进程，在子进程死后要为它收尸。具体做法是接管SIGCHLD信号。子进程死后，会发送SIGCHLD信号给父进程，父进程收到此信号后，执
行waitpid()函数为子进程收尸。这是基于这样的原理：就算父进程没有调用wait，内核也会向它发送SIGCHLD消息，尽管对的默认处理是忽
略，如果想响应这个消息，可以设置一个处理函数。 <br /><br />　　2.把父进程杀掉。父进程死后，僵尸进程成为"孤儿进程"，过继给1号进程init，init始终会负责清理僵尸进程．它产生的所有僵尸进程也跟着消失。</div>
		<p> </p>
		<p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
		<p>在fork
()/execve()过程中，假设子进程结束时父进程仍存在，而父进程fork()之前既没安装SIGCHLD信号处理函数调用waitpid()等待
子进程结束，又没有显式忽略该信号，则子进程成为僵尸进程，无法正常结束，此时即使是root身份kill
-9也不能杀死僵尸进程。补救办法是杀死僵尸进程的父进程(僵尸进程的父进程必然存在)，僵尸进程成为"孤儿进程"，过继给1号进程init，init始
终会负责清理僵尸进程。  <br /><br />=========================================== <br />在Linux中可以用 <br />ps auwx  <br />发现僵尸进程 <br /><br />a all w/ tty, including other users 所有窗口和终端，包括其他用户的进程 <br />u user-oriented 面向用户(用户友好) <br />-w,w wide output 宽格式输出 <br />x processes w/o controlling ttys  <br /><br />在僵尸进程后面 会标注 <br /><br /><br /><br /><br />ps axf  <br />看进程树，以树形方式现实进程列表 <br /><br /><br />ps axm  <br />会把线程列出来,在linux下进程和线程是统一的，是轻量级进程的两种方式。 <br /><br />ps axu  <br />显示进程的详细状态 <br />=========================================== <br />killall <br />kill -15 <br />kill -9 <br />一般都不能杀掉 defunct进程 <br />用了kill -15,kill -9以后 之后反而会多出更多的僵尸进程 <br /><br />kill -kill pid <br />fuser -k pid  <br /><br /><br />可以考虑杀死他的parent process， <br />kill -9 他的parent process <br />=========================================== <br />一个已经终止,但是其父进程尚未对其进行善后处理（获取终止子进程的有关信息、释放它仍占用的资源）的进程被称为僵死进程(Zombie Process)。 <br /><br />避免zombie的方法： <br />1)在SVR4中，如果调用signal或sigset将SIGCHLD的配置设置为忽略,则不会产生僵死子进程。另外,使用SVR4版的sigaction,则可设置SA_NOCLDWAIT标志以避免子进程僵死。 <br /><br />Linux中也可使用这个，在一个程序的开始调用这个函数 <br /><br />signal(SIGCHLD,SIG_IGN);  <br />  <br /><br /><br /><br /><br />2)调用fork两次。程序8 - 5 实现了这一点。 <br />3)用waitpid等待子进程返回.  <br /><br />=========================================== <br /><br />zombie进程是僵死进程。防止它的办法，一是用wait,waitpid之类的函数获得 <br /><br />进程的终止状态，以释放资源。另一个是fork两次  <br />=========================================== <br />defunct进程只是在process table里还有一个记录，其他的资源没有占用，除非你的系统的process个数的限制已经快超过了，zombie进程不会有更多的坏处。 <br />可能唯一的方法就是reboot系统可以消除zombie进程。 <br />=========================================== <br /><br /><br />任何程序都有僵尸状态，它占用一点内存资源(也就是进程表里还有一个记录)，仅仅是表象而已不必害怕。如果程序有问题有机会遇见，解决大批量僵尸简单有效的办法是重起。kill是无任何效果的 <br /><br /><br />fork与zombie/defunct" <br /><br />在Unix
下的一些进程的运作方式。当一个进程死亡时,它并不是完全的消失了。进程终止,它不再运行,但是还有一些残留的小东西等待父进程收回。这些残留的东西包括
子进程的返回值和其他的一些东西。当父进程 fork() 一个子进程后,它必须用 wait() 或者 waitpid()
等待子进程退出。正是这个 wait() 动作来让子进程的残留物消失。 <br /><br />自然的，在上述规则之外有个例外:父进程可以忽略 SIGCLD 软中断而不必要 wait()。可以这样做到(在支持它的系统上,比如Linux): <br /><br />main() <br />{ <br />signal(SIGCLD, SIG_IGN); /* now I don't have to wait()! */ <br />. <br />. <br />fork(); <br />fork(); <br />fork(); /* Rabbits, rabbits, rabbits! */ <br /><br />｝ <br /><br />现在，子进程死亡时父进程没有 wait()，通常用 ps 可以看到它被显示为“”。它将永远保持这样 直到 父进程 wait()，或者按以下方法处理。 <br /><br />这
里是你必须知道的另一个规则:当父进程在它wait()子进程之前死亡了(假定它没有忽略 SIGCLD),子进程将把 init(pid
1)进程作为它的父进程。如果子进程工作得很好并能够控制，这并不是问题。但如果子进程已经是
defunct，我们就有了一点小麻烦。看，原先的父进程不可能再 wait()，因为它已经消亡了。这样，init 怎么知道 wait() 这些
zombie 进程。 <br /><br />答案：不可预料的。在一些系统上，init周期性的破坏掉它所有的defunct进程。在另外一些系统中,它干
脆拒绝成为任何defunct进程的父进程，而是马上毁灭它们。如果你使用上述系统的一种,可以写一个简单的循环，用属于init的defunct进程填
满进程表。这大概不会令你的系统管理员很高兴吧? <br /><br />你的任务：确定你的父进程不要忽略 SIGCLD，也不要 wait() 它
fork() 的所有进程。不过，你也未必 要 总是这样做（比如，你要起一个 daemon 或是别的什么东西）,但是你必须小心编程，如果你是一个
fork() 的新手。另外，也不要在心理上有任何束缚。 <br /><br />总结： <br /><b>子进程成为 defunct 直到父进程 wait()，除非父进程忽略了 SIGCLD 。 <br />更进一步，父进程没有 wait() 就消亡（仍假设父进程没有忽略 SIGCLD ）的子进程（活动的或者 defunct）成为 init 的子进程，init 用重手法处理它们。</b></p>
		<p> </p>
		<p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
		<p> </p>
<img src ="http://www.cppblog.com/mydriverc/aggbug/29109.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-01 00:05 <a href="http://www.cppblog.com/mydriverc/articles/29109.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>_exit和exit的区别</title><link>http://www.cppblog.com/mydriverc/articles/29106.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Tue, 31 Jul 2007 15:34:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29106.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29106.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29106.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29106.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29106.html</trackback:ping><description><![CDATA[
		<span class="tpc_content">  exit()在结束调用它的进程之前，要进行如下步骤：<br />1.cleanup()；<br />2.在atexit()注册的函数；<br />最后调用_exit()函数。。。<br /><br />‘exit()’与‘_exit()’有不少区别在使用‘fork()’，特别是‘vfork()’时变得很 <br />突出。 <br /><br />‘exit()’与‘_exit()’的基本区别在于前一个调用实施与调用库里用户状态结构 <br />(user-mode constructs)有关的清除工作(clean-up)，而且调用用户自定义的清除程序 <br />(译者注：自定义清除程序由atexit函数定义，可定义多次，并以倒序执行)，相对 <br />应，后一个函数只为进程实施内核清除工作。 <br /><br />在由‘fork()’创建的子进程分支里，正常情况下使用‘exit()’是不正确的，这是 <br />因为使用它会导致标准输入输出(译者注：stdio: Standard Input Output)的缓冲区被 <br />清空两次，而且临时文件被出乎意料的删除(译者注：临时文件由tmpfile函数创建 <br />在系统临时目录下，文件名由系统随机生成)。在C++程序中情况会更糟，因为静 <br />态目标(static objects)的析构函数(destructors)可以被错误地执行。(还有一些特殊情 <br />况，比如守护程序，它们的*父进程*需要调用‘_exit()’而不是子进程；适用于绝 <br />大多数情况的基本规则是，‘exit()’在每一次进入‘main’函数后只调用一次。) <br /><br />在由‘vfork()’创建的子进程分支里，‘exit()’的使用将更加危险，因为它将影响 <br />*父*进程的状态<br /><br /><br /><br />_exit终止调用进程，但不关闭文件，不清除输出缓存，也不调用出口函数。<br />exit函数将终止调用进程。在退出程序之前，所有文件关闭，缓冲输出内容<br />将刷新定义，并调用所有已刷新的“出口函数”（由atexit定义）。</span>
<img src ="http://www.cppblog.com/mydriverc/aggbug/29106.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-31 23:34 <a href="http://www.cppblog.com/mydriverc/articles/29106.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>eixt  _exit区别</title><link>http://www.cppblog.com/mydriverc/articles/29105.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Tue, 31 Jul 2007 15:32:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29105.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29105.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29105.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29105.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29105.html</trackback:ping><description><![CDATA[#include &lt;unistd.h&gt;<br />int main()<br />{<br /> printf("output begin\n");<br /> printf("content in buffer");<br /> _exit(0);<br />}<br />仅输出 output begin<br /><br />#include &lt;unistd.h&gt;<br />
int main()<br />
{<br />
 printf("output begin\n");<br />
 printf("content in buffer<b>\n</b>");<br />
 _exit(0);<br />
}<br />输出 <br />output begin<br />content in buffer<br /><br />使用exit(0)<br />都是输出<br />output begin<br />
content in buffer<br /><br />在linux种,标准输入和输出都是作为文件来处理的,他们在打开以后也都有自己的缓冲区<br /><br />缓冲区--<b>只有达到一定的数量,或者遇到特殊的符号,例如换行符,文件结束符, 将缓冲区的内容一次写入文件</b><br /><img src ="http://www.cppblog.com/mydriverc/aggbug/29105.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-31 23:32 <a href="http://www.cppblog.com/mydriverc/articles/29105.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>