由于学习本文需要Nginx
源码及搭建相关的编译环境,且本文与前面的文章有先后呼应关系,所以建议大家按以下文章顺序阅读
本文将介绍模块配置脚本相关的内容,内容非常重要,需要我们仔细研读。
auto/module
是极其重要的一个脚本,auto/modules
里面会多次复用到这个脚本,先来看看里面实现了什么功能。
一开始根据ngx_module_type
的值来设置ngx_var
的值,区分出了HTTP
类型模块和其他模块。
代码如下:
case $ngx_module_type in
HTTP_*) ngx_var=HTTP ;;
*) ngx_var=$ngx_module_type ;;
esac
接下来是一个分支if elif
语句,下面的代码是当ngx_module_link
的值为DYNAMIC
的情况。
if [ "$ngx_module_link" = DYNAMIC ]; then
for
循环,该循环的意义是如果ngx_module_name
是有一系列字符串组成的话,就将第一个的值取出来放进去ngx_module
中,结束循环 for ngx_module in $ngx_module_name; do
# extract the first name
break
done
DYNAMIC_MODULES
的值后面添加ngx_module
的值 eval ${ngx_module}_SRCS=\"$ngx_module_srcs\"
eval ${ngx_module}_MODULES=\"$ngx_module_name\"
${ngx_module}_SRCS
的值赋为ngx_module_srcs
的值,将${ngx_module}_MODULES
的值赋为ngx_module_name
的值 DYNAMIC_MODULES="$DYNAMIC_MODULES $ngx_module"
Shell
中,-z
表示检查字符串是否为空,-a
表示与操作,-o
表示或操作,下面一段代码就是通过判断然后给${ngx_module}_ORDER
赋值 if [ -z "$ngx_module_order" -a \
\( "$ngx_module_type" = "HTTP_FILTER" \
-o "$ngx_module_type" = "HTTP_AUX_FILTER" \) ]
then
eval ${ngx_module}_ORDER=\"$ngx_module_name \
ngx_http_copy_filter_module\"
else
eval ${ngx_module}_ORDER=\"$ngx_module_order\"
fi
Shell
中,-n
表示检查字符串是否不为空,和-z
刚好相反。下面代码就是先判断如果ngx_module_incs
不为空,则在CORE_INCS
的值后面添加ngx_module_incs
的值 if test -n "$ngx_module_incs"; then
CORE_INCS="$CORE_INCS $ngx_module_incs"
fi
ngx_module_deps
的值不为空,则在CORE_INCS
的值后面添加ngx_module_incs
的值 if test -n "$ngx_module_deps"; then
NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_module_deps"
fi
libs
变量,然后对ngx_module_libs
进行for
循环,根据每一次取出的lib
值的不同执行不通的操作,代码比较简单,这里就不做详细分析了 libs=
for lib in $ngx_module_libs
do
case $lib in
LIBXSLT | LIBGD | GEOIP | PERL)
libs="$libs \$NGX_LIB_$lib"
if eval [ "\$USE_${lib}" = NO ] ; then
eval USE_${lib}=DYNAMIC
fi
;;
PCRE | OPENSSL | ZLIB)
eval USE_${lib}=YES
;;
MD5 | SHA1)
# obsolete
;;
*)
libs="$libs $lib"
;;
esac
done
eval ${ngx_module}_LIBS=\'$libs\'
下面的代码是当ngx_module_link
的值为YES
的情况。
elif [ "$ngx_module_link" = YES ]; then
${ngx_module_type}_MODULES
的后面添加ngx_module_name
的值。需要注意的是后面用了两个$
符号,一个是获取ngx_module_type
的值,另一个是获取${ngx_module_type}_MODULES
的值 eval ${ngx_module_type}_MODULES=\"\$${ngx_module_type}_MODULES \
$ngx_module_name\"
eval ${ngx_var}_SRCS=\"\$${ngx_var}_SRCS $ngx_module_srcs\"
if test -n "$ngx_module_incs"; then
eval ${ngx_var}_INCS=\"\$${ngx_var}_INCS $ngx_module_incs\"
fi
if test -n "$ngx_module_deps"; then
eval ${ngx_var}_DEPS=\"\$${ngx_var}_DEPS $ngx_module_deps\"
fi
for
循环和上一个的比较类似,大家自行看一下里面做了什么就行 for lib in $ngx_module_libs
do
case $lib in
PCRE | OPENSSL | ZLIB | LIBXSLT | LIBGD | PERL | GEOIP)
eval USE_${lib}=YES
;;
MD5 | SHA1)
# obsolete
;;
*)
CORE_LIBS="$CORE_LIBS $lib"
;;
esac
done
下面的代码是当ngx_module_link
的值为ADDON
的情况。和前两个的流程基本是一样的,这里就不作详细分析了,读者可以自行阅读代码
elif [ "$ngx_module_link" = ADDON ]; then
eval ${ngx_module_type}_MODULES=\"\$${ngx_module_type}_MODULES \
$ngx_module_name\"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_module_srcs"
if test -n "$ngx_module_incs"; then
eval ${ngx_var}_INCS=\"\$${ngx_var}_INCS $ngx_module_incs\"
fi
if test -n "$ngx_module_deps"; then
NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_module_deps"
fi
for lib in $ngx_module_libs
do
case $lib in
PCRE | OPENSSL | ZLIB | LIBXSLT | LIBGD | PERL | GEOIP)
eval USE_${lib}=YES
;;
MD5 | SHA1)
# obsolete
;;
*)
CORE_LIBS="$CORE_LIBS $lib"
;;
esac
done
fi
本节重点讲了auto/module
脚本的内容,它会在auto/modules
里面被多次复用,有点像之前的auto/feature
脚本,不同的是auto/module
是没有直接往文件里面写入任何内容的,最终都只影响到一些变量而已,而auto/feature
将修改的内容直接写入了文件中。
代码如下,比较简单,初始化一堆变量,大家可以自行阅读代码
if [ $EVENT_SELECT = NO -a $EVENT_FOUND = NO ]; then
EVENT_SELECT=YES
fi
if [ $EVENT_SELECT = YES ]; then
have=NGX_HAVE_SELECT . auto/have
CORE_SRCS="$CORE_SRCS $SELECT_SRCS"
EVENT_MODULES="$EVENT_MODULES $SELECT_MODULE"
fi
if [ $EVENT_POLL = NO -a $EVENT_FOUND = NO ]; then
EVENT_POLL=YES
fi
if [ $EVENT_POLL = YES ]; then
have=NGX_HAVE_POLL . auto/have
CORE_SRCS="$CORE_SRCS $POLL_SRCS"
EVENT_MODULES="$EVENT_MODULES $POLL_MODULE"
fi
if [ $NGX_TEST_BUILD_DEVPOLL = YES ]; then
have=NGX_HAVE_DEVPOLL . auto/have
have=NGX_TEST_BUILD_DEVPOLL . auto/have
EVENT_MODULES="$EVENT_MODULES $DEVPOLL_MODULE"
CORE_SRCS="$CORE_SRCS $DEVPOLL_SRCS"
fi
if [ $NGX_TEST_BUILD_EVENTPORT = YES ]; then
have=NGX_HAVE_EVENTPORT . auto/have
have=NGX_TEST_BUILD_EVENTPORT . auto/have
EVENT_MODULES="$EVENT_MODULES $EVENTPORT_MODULE"
CORE_SRCS="$CORE_SRCS $EVENTPORT_SRCS"
fi
if [ $NGX_TEST_BUILD_EPOLL = YES ]; then
have=NGX_HAVE_EPOLL . auto/have
have=NGX_HAVE_EPOLLRDHUP . auto/have
have=NGX_HAVE_EPOLLEXCLUSIVE . auto/have
have=NGX_HAVE_EVENTFD . auto/have
have=NGX_TEST_BUILD_EPOLL . auto/have
EVENT_MODULES="$EVENT_MODULES $EPOLL_MODULE"
CORE_SRCS="$CORE_SRCS $EPOLL_SRCS"
fi
if [ $NGX_TEST_BUILD_SOLARIS_SENDFILEV = YES ]; then
have=NGX_TEST_BUILD_SOLARIS_SENDFILEV . auto/have
CORE_SRCS="$CORE_SRCS $SOLARIS_SENDFILEV_SRCS"
fi
这里选一个典型的调用auto/module
的例子进行分析,由于后续的大部分代码都是重复地调用auto/module
,所以这里只分析一个就行,其它的原理大致相同,读者可以自行去阅读源代码
代码如下:
ngx_module_type=HTTP
if :; then
ngx_module_name="ngx_http_module \
ngx_http_core_module \
ngx_http_log_module \
ngx_http_upstream_module"
ngx_module_incs="src/http src/http/modules"
ngx_module_deps="src/http/ngx_http.h \
src/http/ngx_http_request.h \
src/http/ngx_http_config.h \
src/http/ngx_http_core_module.h \
src/http/ngx_http_cache.h \
src/http/ngx_http_variables.h \
src/http/ngx_http_script.h \
src/http/ngx_http_upstream.h \
src/http/ngx_http_upstream_round_robin.h"
ngx_module_srcs="src/http/ngx_http.c \
src/http/ngx_http_core_module.c \
src/http/ngx_http_special_response.c \
src/http/ngx_http_request.c \
src/http/ngx_http_parse.c \
src/http/modules/ngx_http_log_module.c \
src/http/ngx_http_request_body.c \
src/http/ngx_http_variables.c \
src/http/ngx_http_script.c \
src/http/ngx_http_upstream.c \
src/http/ngx_http_upstream_round_robin.c"
ngx_module_libs=
ngx_module_link=YES
. auto/module
fi
分析如下:
ngx_module_type
被设置为了HTTP
,所以auto/module
里的ngx_var
被设置成了该值,即HTTP
ngx_module_link
被设置为了YES
,所以后续会调用auto/module
里的YES
分支auto/module
的ngx_module_link=YES
分支的内容展开,代码和注释如下: # 最开始HTTP_MODULES为空
eval HTTP_MODULES="ngx_http_module \
ngx_http_core_module \
ngx_http_log_module \
ngx_http_upstream_module"
# 最开始HTTP_SRCS为空
eval HTTP_SRCS="src/http/ngx_http.c \
src/http/ngx_http_core_module.c \
src/http/ngx_http_special_response.c \
src/http/ngx_http_request.c \
src/http/ngx_http_parse.c \
src/http/modules/ngx_http_log_module.c \
src/http/ngx_http_request_body.c \
src/http/ngx_http_variables.c \
src/http/ngx_http_script.c \
src/http/ngx_http_upstream.c \
src/http/ngx_http_upstream_round_robin.c"
# 最开始HTTP_INCS为空
eval HTTP_INCS="src/http src/http/modules"
# 最开始HTTP_DEPS为空
eval HTTP_DEPS="src/http/ngx_http.h \
src/http/ngx_http_request.h \
src/http/ngx_http_config.h \
src/http/ngx_http_core_module.h \
src/http/ngx_http_cache.h \
src/http/ngx_http_variables.h \
src/http/ngx_http_script.h \
src/http/ngx_http_upstream.h \
src/http/ngx_http_upstream_round_robin.h"
# 由于ngx_module_libs为空,这段不执行,执行了也很容易理解,就是根据分支选择对应的操作而已
for lib in $ngx_module_libs
do
case $lib in
PCRE | OPENSSL | ZLIB | LIBXSLT | LIBGD | PERL | GEOIP)
eval USE_${lib}=YES
;;
MD5 | SHA1)
# obsolete
;;
*)
CORE_LIBS="$CORE_LIBS $lib"
;;
esac
done
之前做了一系列的准备,获取到了一系列的变量,但是把这些变量留着Shell
脚本里面抱窝是没用的,必须把它输出到源文件、头文件、或者是Makefile
文件里面参与编译才行,接下来介绍auto/modules
最后一部分的内容,也就是生成模块源文件的内容。
NGX_MODULES_C
的值在这篇文章《Nginx编译配置脚本篇(2)- Makefile初始化脚本auto/init》里已经介绍过了,默认情况下NGX_MODULES_C
被赋值为了objs/ngx_modules.c
。这一段内容很简单,输出两个头文件到NGX_MODULES_C
文件里去。NGX_PRAGMA
的值一般为空,无视就行。
cat << END > $NGX_MODULES_C
#include <ngx_config.h>
#include <ngx_core.h>
$NGX_PRAGMA
END
这部分是输出变量的声明到文件里,为什么要声明变量是因为下面要用到,这是c语言的知识在这里不作分析。
代码如下:
for mod in $modules
do
echo "extern ngx_module_t $mod;" >> $NGX_MODULES_C
done
modules
的值是在前面初始化的,用于赋值modules
的变量是通过反复调用auto/module
一个一个生成的,这里就不分析是如何生成的了,前面已经举例分析过了。摘录出来代码如下:
modules="$CORE_MODULES $EVENT_MODULES"
if [ $USE_THREADS = YES ]; then
modules="$modules $THREAD_POOL_MODULE"
fi
if [ $HTTP = YES ]; then
modules="$modules $HTTP_MODULES $HTTP_FILTER_MODULES \
$HTTP_AUX_FILTER_MODULES $HTTP_INIT_FILTER_MODULES"
NGX_ADDON_DEPS="$NGX_ADDON_DEPS \$(HTTP_DEPS)"
fi
if [ $MAIL = YES ]; then
modules="$modules $MAIL_MODULES"
if [ $STREAM = YES ]; then
modules="$modules $STREAM_MODULES"
modules="$modules $MISC_MODULES"
输出到文件中的内容大致如下:
extern ngx_module_t ngx_core_module;
extern ngx_module_t ngx_errlog_module;
extern ngx_module_t ngx_conf_module;
extern ngx_module_t ngx_epoll_module;
extern ngx_module_t ngx_http_module;
...
extern ngx_module_t ngx_http_not_modified_filter_module;
代码比较简单,可以数组的成员就是前面那些模块,这也就是为什么前面需要声明这些模块变量(因为这些模块在源代码中是四散分布的,不是集中写一起的,Nginx
这种做法大大降低了添加新模块的难度,需要自己添加模块时只需要很轻易就可以通过脚本文件自动将模块生成到NGX_MODULES_C
里进行统一管理了)
echo >> $NGX_MODULES_C
echo 'ngx_module_t *ngx_modules[] = {' >> $NGX_MODULES_C
for mod in $modules
do
echo " &$mod," >> $NGX_MODULES_C
done
cat << END >> $NGX_MODULES_C
NULL
};
输出到文件中的内容大致如下:
ngx_module_t *ngx_modules[] = {
&ngx_core_module,
&ngx_errlog_module,
&ngx_conf_module,
&ngx_epoll_module,
&ngx_http_module,
...
&ngx_http_not_modified_filter_module,
NULL
};
这个和前面的内容基本一样,不过这里输出的是模块名,也就是前面那个模块变量的字符串形式,代码如下,很简单,这里就不多作分析了
echo 'char *ngx_module_names[] = {' >> $NGX_MODULES_C
for mod in $modules
do
echo " \"$mod\"," >> $NGX_MODULES_C
done
cat << END >> $NGX_MODULES_C
NULL
};
END
输出到文件中的内容大致如下:
char *ngx_module_names[] = {
"ngx_core_module",
"ngx_errlog_module",
"ngx_conf_module",
"ngx_epoll_module",
"ngx_http_module",
...
"ngx_http_not_modified_filter_module",
NULL
};
本文主要介绍了与模块配置脚本相关的内容,先是介绍了auto/module
脚本,该脚本的主要任务是根据传入参数往对应的变量里面添加信息,这些变量最终会在auto/modules
脚本里被使用到。auto/modules
的任务就是不停地调用auto/module
脚本,然后将生成的模块信息写入NGX_MODULES_C
(默认为objs/ngx_modules.c
)文件里,这样就生成了对后续编译过程尤为重要的模块文件,该文件存在的意义就是整合散落在不同源文件里的各个模块,以达到统一启动各个模块的效果(即只有存在于objs/ngx_modules.c
文件里的模块才会在最终启动完成后被加载进去)。