云联天下首页 |  登陆 |  注册 |  密码找回 |  关于我们 | 加入收藏 
首页技术资料网站建设→Apache APR可移植运行库简介(3) 【字号: 】 【背景色 杏仁黄 秋叶褐 胭脂红 芥末绿 天蓝 雪青 灰 银河白(默认色)

Apache APR可移植运行库简介(3)

网址来源:http://www.kehui.net发布时间: 2006-12-07 00:00:16
转载请注明来源:http://blog.csdn.net/tingya
1.4 应用APR

我们首先make install一下,比如我们在Makefile中指定prefix=$(APR)/dist,则make install后,在$(APR)/dist下会发现4个子目录,分别为bin、lib、include和build,其中我们感兴趣的只有include和lib。下面是一个APR app的例子project。

该工程的目录组织如下:

$(apr_path)

dist

    - lib

    - include

 - examples

    - apr_app

      - Make.properties

      - Makefile

      - apr_app.c

我们的Make.properties文件内容如下:

#

# The APR app demo

#

CC              = gcc -Wall

BASEDIR         = $(HOME)/apr-1.1.1/examples/apr_app

APRDIR          = $(HOME)/apr-1.1.1

APRVER          = 1

APRINCL         = $(APRDIR)/dist/include/apr-$(APRVER)

APRLIB          = $(APRDIR)/dist/lib

DEFS            = -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS -D_DEBUG_

LIBS            = -L$(APRLIB) -lapr-$(APRVER)

                  -lpthread -lxnet -lposix4 -ldl -lkstat -lnsl -lkvm -lz -lelf -lm -lsocket –ladm

INCL            = -I$(APRINCL)

CFLAGS          = $(DEFS) $(INCL)

Makefile文件内容如下:

include Make.properties

TARGET  = apr_app

OBJS    = apr_app.o

all: $(TARGET)

$(TARGET): $(OBJS)

        $(CC) ${CFLAGS} -o $@ $(OBJS) ${LIBS}

clean:

        rm -f core $(TARGET) $(OBJS)

apr_app.c文件采用的是$(apr_path)/test目录下的proc_child.c文件。编译运行一切OK。

1.5 APR的可移植性

正如前面所描述,APR的目前的首要目标就是设计为一个跨平台的通用库,因此在APR的整个设计过程中无不体现了可移植的思想,APR附带一个简短的设计文档,文字言简意赅,其中很多的移植设计思想都值得我们所借鉴,主要从四个方面谈。

1.5.1APR类型

为了支持可移植性,APR中的一个策略就是尽量使用APR自定义的类型来代替平台相关类型。这样的好处很多,比如便于代码移植,避免数据间进行不必要的类型转换(如果你不使用APR自定义的数据类型,你在使用某些APR提供的接口时,就需要进行一些参数的类型转换);自定义数据类型的名字更加具有自描述性,提高代码可读性。APR提供的基本自定义数据类型包括apr_byte_t,apr_int16_t,apr_uint16_t,apr_size_t等等。通常情况下这些类型都定义在apr.h中,不过你找遍整个APR包也不会找到apr.h这个文件,不过include目录下倒是存在类似于apr.h的apr.h.in和apr.hw,这两个文件是生成apr.h的模版,在apr.h.in中通用APR类型定义如下:

typedef unsigned char              apr_byte_t;

typedef @short_value@           apr_int16_t;

typedef unsigned @short_value@       apr_uint16_t;

typedef @int_value@              apr_int32_t;

typedef unsigned @int_value@           apr_uint32_t;

typedef @long_value@            apr_int64_t;

typedef unsigned @long_value@         apr_uint64_t;

typedef @size_t_value@            apr_size_t;

typedef @ssize_t_value@         apr_ssize_t;

typedef @off_t_value@            apr_off_t;

typedef @socklen_t_value@              apr_socklen_t;

@xxx@变量的值是可变的,不同的平台其值可能不一样。其值由configure配置过程自动生成,configue脚本中设置@xxx@变量的部分大致如下:

AC_CHECK_SIZEOF(char, 1)

AC_CHECK_SIZEOF(short, 2)

AC_CHECK_SIZEOF(int, 4)

AC_CHECK_SIZEOF(long, 4)

AC_CHECK_SIZEOF(long long, 8)

 

if test "$ac_cv_sizeof_short" = "2"; then

    short_value=short

fi

if test "$ac_cv_sizeof_int" = "4"; then

    int_value=int

fi

if test "$ac_cv_sizeof_int" = "8"; then

    int64_value="int"

    long_value=int

elif test "$ac_cv_sizeof_long" = "8"; then

    int64_value="long"

    long_value=long

elif test "$ac_cv_sizeof_long_long" = "8"; then

    int64_value="long long"

    long_value="long long"

elif test "$ac_cv_sizeof_longlong" = "8"; then

    int64_value="__int64"

    long_value="__int64"

else

    AC_ERROR([could not detect a 64-bit integer type])

fi

if test "$ac_cv_type_size_t" = "yes"; then

    size_t_value="size_t"

else

    size_t_value="apr_int32_t"

fi

if test "$ac_cv_type_ssize_t" = "yes"; then

    ssize_t_value="ssize_t"

else

    ssize_t_value="apr_int32_t"

fi

.. ..

Configure的具体的细节并不是本书描述的细节,如果你想了解更多细节,你可以去阅读GNU的AutoConf、AutoMake等使用手册。

不同的操作系统中各个变量的值如下表所示:
































































































变量类型


硬件平台


I686


I386


alpha


IA64


M68k


MIPS


Sparc


Spar64


apr_byte_t


1


1


1


1


1


1


1


1


apr_int16_t


2


2


2


2


2


2


2


2


apr_uint16_t


2


2


2


2


2


2


2


2


apr_int32_t


4


4


4


4


4


4


4


4


apr_uint32_t


4


4


4


4


4


4


4


4


apr_int64_t


4


4


8


8


4


4


4


4


apr_uint64_t


4


4


8


8


4


4


4


4


不过不同的操作系统中,定义各不相同,在Red Hat 9.0 Linux中,生成的定义如下:

typedef short                     apr_int16_t;        //16位整数

typedef unsigned short         apr_uint16_t;             //16位无符号整数

typedef int                         apr_int32_t;        //32位整数

typedef unsigned int              apr_uint32_t;             //32位无符号整数

typedef long long                            apr_int64_t;        //64位整数

typedef unsigned long long            apr_uint64_t;             //64位无符号整数

 

typedef size_t                          apr_size_t;          //

typedef ssize_t                        apr_ssize_t;

typedef off64_t                        apr_off_t;

typedef socklen_t                           apr_socklen_t;     //套接字长度

通用数据类型的另外一个定义之处就是文件apr_portable.h中,APR中提供了通用的数据类型以及对应的操作系统依赖类型如下表:

























































































通用类型


含义


Win32类型


BEOS类型


UNIX


apr_os_file_t


文件类型


HANDLE


int


Int


apr_os_dir_t


目录类型


HANDLE


dir


DIR


apr_os_sock_t


套接字类型


SOCKET


int


int


apr_os_proc_mutex_t


进程互斥锁


HANDLE


apr_os_proc_mutex_t


pthread_mutex_t


apr_os_thread_t


线程类型


HANDLE


thread_id


pthread_t


apr_os_proc_t


进程类型


HANDLE


thread_id


pid_t


apr_os_threadkey_t


线程key类型


DWORD


int


pthread_key_t


apr_os_imp_time_t


 


FILETIME


struct timeval


struct timeval


apr_os_exp_time_t


 


SYSTEMTIME


struct tm


tm


apr_os_dso_handle_t


DSO加载


HANDLE


image_id


void*


apr_os_shm_t


共享内存


HANDLE


void*


void*


一旦定义了这些通用的数据类型,APR不再使用系统类型,而是上述的APR类型。不过由于系统底层仍然使用系统类型,因此在使用通用类型的时候一项必须的工作就是用实际的类型来真正替代通用类型,比如apr_os_file_t,如果是Win32平台,则必须转换为HANDLE。对于上面表格的每一个通用数据类型,Apache都提供两个函数支持这种转换:

APR_DECLARE(apr_status_t) apr_os_XXX_get(…);

APR_DECLARE(apr_status_t) apr_os_XXX_put(…);

get函数用于将通用的数据类型转换为特定操作系统类型;而put函数则是将特定操作系统类型转换为通用数据类型。比如对于file类型,则对应的函数为:

APR_DECLARE(apr_status_t) apr_os_file_get(apr_os_file_t *thefile,

apr_file_t *file);

APR_DECLARE(apr_status_t) apr_os_file_put(apr_file_t **file,

                                          apr_os_file_t *thefile,

                                          apr_int32_t flags, apr_pool_t *cont);

前者将通用的文件类型apr_os_file_t转换为特定操作系统类型apr_file_t,后者则是将apr_file_t转换为apr_os_file_t。

在后面的分析中我们可以看到,对于每一个组件类型,比如apr_file_t中都会包含系统定义类型,APR类型都是围绕系统类型扩充起来的,比如apr_file_t,在Unix中为:

struct apr_file_t

{

    int filedes;              //UNIX下的实际的文件系统类型

    … …

}

而在Window中则是:

struct apr_file_t

{

    HANDLE filedes;     //Window下的实际的文件系统类型

    ……

}

因此apr_os_file_get函数无非就是返回结构中的文件系统类型,而apr_os_file_put函数则无非就是根据系统文件类型创建apr_file_t类型。

类似的例子还有apr_os_thread_get和apr_os_thread_put等等。

1.5.2函数

APR中函数可以分为两大类:内部函数和外部接口函数。顾名思义,内部函数仅限于APR内部使用,外部无法调用;而外部结构函数则作为API结构,由外部程序调用。

1.5.2.1内部函数

APR中所有的内部函数都以static进行修饰。通常理解static只是指静态存储的概念,事实上在里面static包含了两方面的含义。
      1)、在固定地址上的分配,这意味着变量是在一个特殊的静态区域上创建的,而不是每次函数调用的时候在堆栈上动态创建的,这是static的静态存储的概念。

2)、另一方面,static能够控制变量和函数对于连接器的可见性。一个static变量或者函数,对于特定的编译单元来说总是本地范围的,这个范围在C语言中通常是指当前文件,超过这个范围的文件或者函数是不可以看到static变量和函数的,因此编译器也无法访问到这些变量和函数,它们对编译器是不可见的。因此内部函数是不允许被直接调用的,任何直接调用都导致“尚未定义”的错误。不过潜在的好处就是,内部函数的修改不影响API接口。

static的这两种用法APR中都存在,但是第二种用法较多。

1.5.2.2外部API函数

对于APR用户而言,它们能够调用的只能是APR提供的API。要识别APR中提供的API非常的简单,如果函数是外部API,那么它的返回值总是用APR_DECLARE或者APR_DECLARE_NONSTD进行包装,比如:

APR_DECLARE(apr_hash_t *) apr_hash_make(apr_pool_t *pool);

APR_DECLARE(int) apr_fnmatch_test(const char *pattern);

APR_DECLARE和APR_DECLARE_NONSTD是两个宏定义,它们在apr.h中定义如下:

#define APR_DECLARE(type)            type

#define APR_DECLARE_NONSTD(type)     type

APR_DECLARE和APR_DECLARE_NONSTD到底是什么意思呢?为什么要将返回类型封装为宏呢?在apr.h中有这样的解释:

/**

 * The public APR functions are declared with APR_DECLARE(), so they may

 * use the most appropriate calling convention. Public APR functions with

 * variable arguments must use APR_DECLARE_NONSTD().

 *

 * @remark Both the declaration and implementations must use the same macro.

 * @example

 */

/** APR_DECLARE(rettype) apr_func(args)

 * @see APR_DECLARE_NONSTD @see APR_DECLARE_DATA

 * @remark Note that when APR compiles the library itself, it passes the

 * symbol -DAPR_DECLARE_EXPORT to the compiler on some platforms (e.g. Win32)

 * to export public symbols from the dynamic library build.

 * The user must define the APR_DECLARE_STATIC when compiling to target

 * the static APR library on some platforms (e.g. Win32.) The public symbols

 * are neither exported nor imported when APR_DECLARE_STATIC is defined.

 * By default, compiling an application and including the APR public

 * headers, without defining APR_DECLARE_STATIC, will prepare the code to be

 * linked to the dynamic library.

 */

#define APR_DECLARE(type)            type

/**

 * The public APR functions using variable arguments are declared with

 * APR_DECLARE_NONSTD(), as they must follow the C language calling convention.

 * @see APR_DECLARE @see APR_DECLARE_DATA

 * @remark Both the declaration and implementations must use the same macro.

 * @example

 */

/** APR_DECLARE_NONSTD(rettype) apr_func(args, ...);

 */

#define APR_DECLARE_NONSTD(type)     type

从上面的解释中我们可以看出“APR的固定个数参数公共函数的声明形式APR_DECLARE(rettype) apr_func(args);而非固定个数参数的公共函数的声明形式为APR_DECLARE_NONSTD(rettype) apr_func(args, ...);”。

apr.h文件中解释了这么做就是为了在不同平台上编译时使用“the most appropriate calling convention”,这里的“calling convention”是一术语,翻译过来叫“调用约定”。 我们知道函数调用是通过栈操作来完成的,在栈操作过程中需要函数的调用者和被调用者在下面的两个问题上做出协调,达成协议:

a) 当参数个数多于一个时,按照什么顺序把参数压入堆栈

b) 函数调用后,由谁来把堆栈恢复原来状态

c) 产生函数修饰名的方法

在像C/C++这样的中、高级语言中,使用“调用约定”来说明这两个问题。常见的调用约定有:__stdcall、__cdecl、__fastcall、thiscall和naked call

__stdcall调用约定:函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈。

__cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。注意:对于可变参数的成员函数,始终使用__cdecl的转换方式

__fastcall调用约定规定通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈)。

thiscall仅仅应用于"C++"成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。

naked call采用上述调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。

另外不同的调用约定对于函数内部修饰名处理的方法也不一样。所谓修饰名是C或者C++函数在内部编译和链接的时候产生的唯一的标识名称。

对于C语言而言,__stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个"@"符号和其参数的字节数,格式为_functionname@number,例如 :function(int a, int b),其修饰名为:_function@8

__cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname。

__fastcall调用约定在输出函数名前加上一个"@"符号,后面也是一个"@"符号和其参数的字节数,格式为@functionname@number。

如果是C++,不同调用约定处理要稍微复杂一点。由于Apache是基于C语言开发,因此本处不再描述。

1.5.2.3内存池参数

关于函数的最后一个问题就是它的参数,如果函数内部需要分配空间,那么你就可以看到参数的参数中肯定包含一个apr_pool_t参数,比如:

APR_DECLARE(apr_status_t) apr_shm_attach(apr_shm_t **m,

                                         const char *filename,

                                         apr_pool_t *pool);

由于Apache服务器所具有的一些特性,APR中并没有使用普通的malloc/free内存管理策略,而是使用了自行设计的内存池管理策略。APR中所有的需要的内存都不再直接使用malloc分配,然后首先分配一块足够大的内存块,然后每次需要的时候再从中获取;当内存不再使用的时候也不是直接调用free,而是直接归还给内存池。只有当内存池本身被释放的时候,这些内存才真正的被free给操作系统。Apache中使用apr_pool_t描述一个内存池,因此毫无疑问,由于这种特殊的内存分配策略,对于任何一个函数,如果你需要使用内存,那么你就应该指定内存所源自的内存池。这就是为什么大部分函数参数中都具有apr_pool_t的原因。关于内存池的详细细节,我们在第二章详细讨论。



关于作者
张中庆,目前主要的研究方向是嵌入式浏览器,移动中间件以及大规模服务器设计。目前正在进行Apache的源代码分析,计划出版《Apache源代码全景分析》上下册。Apache系列文章为本书的草案部分,对Apache感兴趣的朋友可以通过flydish1234 at sina.com.cn与之联系!




如果你觉得本文不错,请点击文后的“推荐本文”链接!!








tingya 

相关新闻
v 配置Apache2+PHP5+MYSQL5 2006-12-07 00:00:16
v Window.ShowModalDialog使用手册 2006-12-07 00:00:16
v 图片无缝滚动的完美解决 2006-12-07 00:00:16
v CSDN社区结贴给分器 2006-12-07 00:00:16
v 获取被拖动的对象的ID 2006-12-07 00:00:16
v (Javascript)prototype的一个优势也是缺点 2006-12-07 00:00:16
v 浅谈PHP开发团队管理及程序员做人问题! 2006-12-07 00:00:16
v Web开发技术的历史发展简介 2006-12-07 00:00:16
v 多选下拉菜单的一个实现方案 by unifly 2006-12-07 00:00:16
v NickLee.Framework.V1.9.4.3 2006-12-07 00:00:16
  最新新闻
智慧家居
智慧家居颠覆传统智能家居
智慧云谷让智能家居变成有智慧的
智慧云谷引领智慧家居新生活
科技改变生活 智慧云谷智慧家居系
智慧家居领航者,智慧云谷助你玩
智能家居如何赢得市场美誉度?
智慧云谷智慧家居:创业者有无限
WiFi智能家居你还在用?这样的智
互联网+助推智能家居产业
智慧云谷为您打造真正的智能家居
智能家居产业需要的不是单品,而
新家如何选择开关?智慧云谷iWis
智能传感器-世界首款“智”为你的
智慧云谷开关智能安防智能空气质
智能开关品牌,如何选择智能开关
秋季干燥,智慧家居温湿度传感器
传感器助力智慧家居 感知爱家
iWiscloud智能触摸开关缔造家居装

  最新帖子
 ※室内空气污染的危害及  [sensor]
 ※超声波风速传感器在生  [sensor]
 ※这么冷清  [gabc111]
 ※手机APP操作有问题  [ssy11407]
 ※智慧云谷智慧家居将在  [cici]
 ※上传下载  [cici]
 ※下载智慧家居  [apple2008]
 ※秋季干燥,智慧家居温  [apple2008]
 ※智慧家居紧扣热点 安全  [apple2008]
 ※办公大楼如何智慧化管  [apple2008]
 ※智慧云谷工业自控的优  [apple2008]
 ※传感器助力智慧家居 感  [apple2008]
 ※智能开关品牌,如何选  [apple2008]
 ※智慧云谷开关智能安防  [apple2008]
 ※没有专业人员,如何安  [apple2008]
 ※烟台智慧云谷董事长任  [apple2008]
 ※互联网+助推智能家居产  [apple2008]
 ※WiFi智能家居你还在用  [apple2008]
 ※智慧云谷智慧家居:创  [apple2008]
 ※智能家居如何赢得市场  [apple2008]
钯碳回收 硝酸银回收 银浆回收 银焊条回收 回收银浆 氯化钯回收 氯化钯回收 氧化钯回收 回收硝酸钯 钯水回收价格 海绵钯回收 钯炭回收价格 回收镀金板 深圳钯碳回收 镇江氯化钯回收 杭州钯浆回收 银浆回收多少钱 回收钯碳公司 硝酸银的价格 那里有回收金 氯化钯回收价格 江苏擦银布回收 硝酸银价格 德州钯粉回收 银铜回收 回收钯粉 回收铂碳催化剂 佛山钯碳回收 金盐回收价格 海绵钯回收 钯碳高价回收 钯回收价格 钯炭回收