Nginx编译配置脚本篇(8)- 模块配置脚本auto/modules

卢伟志
2023-12-01

1、相关文章

由于学习本文需要Nginx源码及搭建相关的编译环境,且本文与前面的文章有先后呼应关系,所以建议大家按以下文章顺序阅读

2、前言

本文将介绍模块配置脚本相关的内容,内容非常重要,需要我们仔细研读。

3、auto/module脚本文件详解

auto/module是极其重要的一个脚本,auto/modules里面会多次复用到这个脚本,先来看看里面实现了什么功能。

3.1、设置ngx_var的值

一开始根据ngx_module_type的值来设置ngx_var的值,区分出了HTTP类型模块和其他模块。
代码如下:

case $ngx_module_type in
    HTTP_*) ngx_var=HTTP ;;
    *)      ngx_var=$ngx_module_type ;;
esac

3.2、当ngx_module_link的值为DYNAMIC的情况

接下来是一个分支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\'

3.3、当ngx_module_link的值为YES的情况

下面的代码是当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

3.4、当ngx_module_link的值为YES的情况

下面的代码是当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

3.5、小结

本节重点讲了auto/module脚本的内容,它会在auto/modules里面被多次复用,有点像之前的auto/feature脚本,不同的是auto/module是没有直接往文件里面写入任何内容的,最终都只影响到一些变量而已,而auto/feature将修改的内容直接写入了文件中。

4、auto/modules脚本文件详解

4.1、初始化一堆变量

代码如下,比较简单,初始化一堆变量,大家可以自行阅读代码

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

4.2、分析一个调用auto/module的典型例子

这里选一个典型的调用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/modulengx_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

4.3、将之前的内容输出到文件中

之前做了一系列的准备,获取到了一系列的变量,但是把这些变量留着Shell脚本里面抱窝是没用的,必须把它输出到源文件、头文件、或者是Makefile文件里面参与编译才行,接下来介绍auto/modules最后一部分的内容,也就是生成模块源文件的内容。

4.3.1、输出头文件到NGX_MODULES_C文件里

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

4.3.2、输出变量声明到NGX_MODULES_C文件里

这部分是输出变量的声明到文件里,为什么要声明变量是因为下面要用到,这是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;

4.3.3、输出模块数组到NGX_MODULES_C文件里

代码比较简单,可以数组的成员就是前面那些模块,这也就是为什么前面需要声明这些模块变量(因为这些模块在源代码中是四散分布的,不是集中写一起的,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
};

4.3.4、输出模块名数组到NGX_MODULES_C文件里

这个和前面的内容基本一样,不过这里输出的是模块名,也就是前面那个模块变量的字符串形式,代码如下,很简单,这里就不多作分析了

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
};

5、总结

本文主要介绍了与模块配置脚本相关的内容,先是介绍了auto/module脚本,该脚本的主要任务是根据传入参数往对应的变量里面添加信息,这些变量最终会在auto/modules脚本里被使用到。auto/modules的任务就是不停地调用auto/module脚本,然后将生成的模块信息写入NGX_MODULES_C(默认为objs/ngx_modules.c)文件里,这样就生成了对后续编译过程尤为重要的模块文件,该文件存在的意义就是整合散落在不同源文件里的各个模块,以达到统一启动各个模块的效果(即只有存在于objs/ngx_modules.c文件里的模块才会在最终启动完成后被加载进去)。

 类似资料: