当前位置:web 服务器 > Apache >>

Apache源代码分析——模块的加载

tingya | 18 一月, 2005 22:50

  本文分析了Apache中关于模块的加载过程

  /////////////////////////////////////////////////////////////

  //Apache源代码分析——配置命令执行过程

  //张中庆于西安交通大学软件所

  //tingya$stu,xjtu,edu,cn,将$换成@,,换成.防止地址被收集

  //转载请保留出处

  //最初出自西安交通大学兵马俑Linux版

  //////////////////////////////////////////////////////////////

  阅读本文之前,请先阅读Apache源代码分析——关于模块结构的几个重要概念一文

  ///////////////////////////////////////////////////////////////////////////////////////

  在了解了上面的四个重要的概念后,我们现在来看看Apache中是如何进行模块加载的。

  Apache中对模块的装载处理的相关函数主要集中于mod_so.c中,其本身也是一个子模块,该子模块用来动态状态其余的模块。在mod_so.c文件的开始位置有很长的一段文件描述,将该描述翻译成中文如下:

  该模块(指Mod_so)主要用来在运行的时候动态装入Apache模块,这意味着对服务器可以进行功能扩展而不需要重新对源代码进行编译,甚至根本不需要停止服务器。我们所需要做的仅仅是给服务器发送信号HUP或者AP_SIG_GRACEFUL通知服务器重新载入模块。

  当然,在动态加载之前,你首先必须将你的模块编译成为动态链接库,然后更新你所指定的配置文件,通常情况下是httpd.conf。这样Apache核心就可以在启动的时候调用你的模块了。

  将模块编译成为共享受库的最简单的方法就是在配置中使用ShareModule命令,而不是使用AddModule命令,而且你必须将文件的扩展名称从‘.o’改变为‘.so’。比如如果我们想将status模块变为共享库,在配置文件中我们只需要将

  AddModule modules/standard/mod_status.o

  更改为

  SharedModule modules/standard/mod_status.so

  一旦更改完毕,运行配置文件同时进行编译。现在Apache的httpd的二进制文件中并没有包含mod_status模块,。。。。

  为了使用共享模块,将.so文件拷贝到适当的目录中。你可能需要在服务器根目录下创建一个名称为“modules”的目录,比如“/usr/local/httpd/modules”。

  下面的事情就是编辑你的conf/httpd.conf文件,同时增加一行“LoadModule”命令。比如:

  LoadModule status_module modules/mod_status.so

  该命令的第一个参数是模块的名称,名称可以在module_source的最后找到。第二个选项是模块所处的路径,这个路径是相对于服务器路径而言。

  如果服务器还在运行的时候,你就编辑LoadModule命令,那么你可以通过发送信号HUP或者AP_SIG_GRACEFUL给服务器,一旦接受到该信号,Apache将重新装载模块,而不需要重新启动服务器。

  下面我们将来看看Apache是如何动态装载模块的。在分析每个模块之前,我们首先需要分析的就是模块的数据结构以及该模块能够处理的命令表。对于mod_so模块也不例外。mod_so模块的结构和其中的命令表格如下所示:

  static const command_rec so_cmds[] = {

  AP_INIT_TAKE2("LoadModule", load_module, NULL, RSRC_CONF | EXEC_ON_READ,

  "a module name and the name of a shared object file to load it from"),

  AP_INIT_ITERATE("LoadFile", load_file, NULL, RSRC_CONF | EXEC_ON_READ,

  "shared object file or library to load into the server at runtime"),

  { NULL }

  };

  module AP_MODULE_DECLARE_DATA so_module = {

  STANDARD20_MODULE_STUFF,

  NULL, /* create per-dir config */

  NULL, /* merge per-dir config */

  so_sconf_create, /* server config */

  NULL, /* merge server config */

  so_cmds, /* command apr_table_t */

  NULL /* register hooks */

  };

  模块中能够处理的所有命令都保存在so_cmds中,从命令表中可以看出,mod_so模块可以处理的命令只有“LoadModule”和“LoadFile”,相应的处理函数分别为load_module和loadfile。下面我们首先来看load_module函数的实现,该函数也是模块装载处理的入口。

  static const char *load_module(cmd_parms *cmd, void *dummy,

  const char *modname, const char *filename)

  该函数用来将共享对象载入到服务器的地址空间中。其中,cmd和dummy是所有的命令处理程序都必须具有的,其用于Apache核心给模块传递相应的信息。modname是需要状态的模块的路径名称。

  函数首先要做的事情就是在现有的所有的模块中查找是否已经存在需要载入的模块,如果该模块已经载入,则什么都不处理,否则则对其进行装载。遍历的模块包括动态装载的和静态编译两种。

  对于动态载入模块,函数首先得到模块so_module中的模块配置信息,这个通过宏ap_get_module_config来实现。

  在server_rec结构中,成员module_config是一个一维向量结构,专门用来存储各个模块针对本服务器的配置信息,向量结构中的每一个元素对应存储一个模块针对本服务器的所有配置信息,结构如下所示:

  从上面的结构图中可以看出,如果要获取第i个即索引为i的模块针对本服务器的配置信息的话,可以通过下面的表示式来获取:module_config[i],事实上模块的索引由模块的module_index决定,因此式子展开为:module_config[(m)->module_index]。这实际上真是宏ap_get_module_config展开后的结果,在Apache中,该宏定义为

  #define ap_get_module_config(v,m)

  (((void **)(v))[(m)->module_index])

  根据上面的描述,我们不难理解该宏的含义。不过通过该宏获取的信息各个模块是千差万别,因此返回的类型只能是void**,因此如果需要使用最好进行转换。就so_module模块而言,其每个元素都是so_server_conf类型的。该类型非常简单,其内部就是一个简单的数组:

  typedef struct so_server_conf {

  apr_array_header_t *loaded_modules;

  } so_server_conf;

  模块对应的配置信息实际上都保存在数组loaded_modules中。数组中的每个元素是moduleinfo结构,因此函数需要做的实质上就是遍历查找loaded_modules数组,判断其中是否存在给定的模块,比较只需要通过模块名称进行。下面的代码完成的就是查找动态加载模块:

  sconf = (so_server_conf *)ap_get_module_config(cmd->server->module_config,

  &so_module);

  modie = (moduleinfo *)sconf->loaded_modules->elts;

  for (i = 0; i < sconf->loaded_modules->nelts; i++) {

  modi = &modie[i];

  if (modi->name != NULL && strcmp(modi->name, modname) == 0) {

  ap_log_perror(APLOG_MARK, APLOG_WARNING, 0,

  cmd->pool, "module %s is already loaded, skipping",

  modname);

  return NULL;

  }

  }

  对动态加载模块检查完毕后,Apache将检查静态链接模块数组ap_preloaded_modules。在ap_preloaded_modules数组中查找指定模块相对简单。对于每一个模块,Apache必须保证其文件名是以“mod_”开始的,比如mod_so.c、mod_alias.c等等,如果命名格式不是这样,函数将认为模块不是合法的模块。函数的名称最终通过宏STANDARD20_MODULE_STUFF以__FILE__反映到module结构的name属性中,因此函数只需要判断name是否合法就可以了。

  如果需要载入的模块没有被载入,则函数首先在动态载入模块数组sconf->loaded_modules中压入一个新的module元素,同时将该结构的名称置为modname,既而函数调用apr_dso_load将文件载入到Apache的地址空间中,同时调用apr_dso_sym获取动态链接库中的module结构,返回的结构保存在modsym中。一旦得到modsym,我们就相当于得到了该模块的module结构了,用modp表示,同时该模块内的动态加载句柄dynamic_load_handle设置为dlopen取得的句柄。

  在真正的使用即激活加载模块之前,Apache必须确保加载的模块确实是Apache模块,为此Apache在模块结构中设定了magic字段,通过检查magic字段,apache确定加载的模块是否是apache模块。对于Apache2.0而言,该值为“AP2.0”。另外apache还可以通过结构中的version来判断模块的兼容性。如果加载的是合法的2.0模块,函数将立即调用ap_add_loaded_module将模块激活,所谓的激活无非就是将模块放入ap_top_modules链表中。

  此外apache含需要在配置内存池pconf中注册cleanup处理函数。这样当我们重新启动或者关闭服务器的时候,cleanup函数将自动调用将共享模块卸载;最后我们需要做的就是为模块运行配置过程。

  ///////////////////////////////////////////////////////////////////////////////////////

  APR_DECLARE(apr_status_t) apr_dso_load(apr_dso_handle_t **res_handle,

  const char *path, apr_pool_t *ctx);

  该函数用
Apache
IIS
Nginx
Tomcat
如果你遇到web 服务器难题:
访问www.zzzyk.com 试试
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,