nginx源码分析

手把手教你彻底认识nginx的auto/feature脚本

2019-07-24  本文已影响0人  郑尔多斯

回顾

来,看下图:

辅助脚本
图中还剩下auto/featureauto/include脚本没有分析,其他的几个辅助脚本已经分析完了。
那么本文就详细分析一下一个非常非常重要的脚本,auto/feature.

写在分析之前

为什么说auto/feature脚本非常重要呢?
因为该脚本对于不同的宿主系统,可以测试当前的宿主系统是否支持某个特性。nginx会根据测试结果选用不同的实现方式。
比如auto/feature可以测试当前系统是否支持epoll,如果支持,nginx就使用这种高效率的io多路复用机制。如果不支持,那么就可能选用select或者poll机制等。总之,无论如何,总会让你的机器能够成功的运行nginx.

auto/feature 脚本

测试宿主系统是否支持某个特性。

输入参数

ngx_feature: 要测试的特性名称,该参数主要用于向终端和NGX_AUTOCONF_ERR文件中输出日志,便于后面的分析。

ngx_feature_name: define的变量名

ngx_feature_run: 表示对编译之后文件的处理方式

ngx_feature_incs:包含c源文件所需的头文件。下面的ngx_feature_path参数是这些头文件所在的目录

ngx_feature_path:包含了编译程序时设置的头文件目录

ngx_feature_libs:编译源文件时依赖的库文件

ngx_feature_test:测试当前特性时要执行的代码

输出参数

ngx_found: 表示是否拥有该特性,如果为yes表示有该特性。
若为no,表示没有该特性。

功能

测试是否支持某个特性。

示例

我们以测试epoll特性的代码为例来说明auto/feature的用法。

# epoll, EPOLLET version

ngx_feature="epoll"
ngx_feature_name="NGX_HAVE_EPOLL"
ngx_feature_run=yes
ngx_feature_incs="#include <sys/epoll.h>"
ngx_feature_path=
ngx_feature_libs=
ngx_feature_test="int efd = 0, fd = 1, n;
                  struct epoll_event ee;
                  ee.events = EPOLLIN|EPOLLOUT|EPOLLET;
                  ee.data.ptr = NULL;
                  efd = epoll_create(100);
                  if (efd == -1) return 1;"
. auto/feature

if [ $ngx_found = yes ]; then
    have=NGX_HAVE_CLEAR_EVENT . auto/have
    CORE_SRCS="$CORE_SRCS $EPOLL_SRCS"
    EVENT_MODULES="$EVENT_MODULES $EPOLL_MODULE"
    EVENT_FOUND=YES
fi
脚本内容
echo $ngx_n "checking for $ngx_feature ...$ngx_c"

cat << END >> $NGX_AUTOCONF_ERR

----------------------------------------
checking for $ngx_feature

END

ngx_found=no

if test -n "$ngx_feature_name"; then
    ngx_have_feature=`echo $ngx_feature_name \
                   | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ`
fi

if test -n "$ngx_feature_path"; then
    for ngx_temp in $ngx_feature_path; do
        ngx_feature_inc_path="$ngx_feature_inc_path -I $ngx_temp"
    done
fi

cat << END > $NGX_AUTOTEST.c

#include <sys/types.h>
$NGX_INCLUDE_UNISTD_H
$ngx_feature_incs

int main() {
    $ngx_feature_test;
    return 0;
}

END


ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS $ngx_feature_inc_path \
          -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_TEST_LD_OPT $ngx_feature_libs"

ngx_feature_inc_path=

eval "/bin/sh -c \"$ngx_test\" >> $NGX_AUTOCONF_ERR 2>&1"


if [ -x $NGX_AUTOTEST ]; then

    case "$ngx_feature_run" in

        yes)
            # /bin/sh is used to intercept "Killed" or "Abort trap" messages
            if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
                echo " found"
                ngx_found=yes

                if test -n "$ngx_feature_name"; then
                    have=$ngx_have_feature . auto/have
                fi

            else
                echo " found but is not working"
            fi
        ;;

        value)
            # /bin/sh is used to intercept "Killed" or "Abort trap" messages
            if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
                echo " found"
                ngx_found=yes

                cat << END >> $NGX_AUTO_CONFIG_H

#ifndef $ngx_feature_name
#define $ngx_feature_name  `$NGX_AUTOTEST`
#endif

END
            else
                echo " found but is not working"
            fi
        ;;

        bug)
            # /bin/sh is used to intercept "Killed" or "Abort trap" messages
            if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
                echo " not found"

            else
                echo " found"
                ngx_found=yes

                if test -n "$ngx_feature_name"; then
                    have=$ngx_have_feature . auto/have
                fi
            fi
        ;;

        *)
            echo " found"
            ngx_found=yes

            if test -n "$ngx_feature_name"; then
                have=$ngx_have_feature . auto/have
            fi
        ;;

    esac

else
    echo " not found"

    echo "----------"    >> $NGX_AUTOCONF_ERR
    cat $NGX_AUTOTEST.c  >> $NGX_AUTOCONF_ERR
    echo "----------"    >> $NGX_AUTOCONF_ERR
    echo $ngx_test       >> $NGX_AUTOCONF_ERR
    echo "----------"    >> $NGX_AUTOCONF_ERR
fi

rm $NGX_AUTOTEST*
脚本分析

我们以示例中测试epoll特性的代码分析这个脚本。

1).向终端和NGX_AUTOCONF_ERR中输出测试信息

echo $ngx_n "checking for $ngx_feature ...$ngx_c"

cat << END >> $NGX_AUTOCONF_ERR

----------------------------------------
checking for $ngx_feature

END

我们之前分析的几乎所有脚本文件的第一步都是这个功能。主要是方便查找错误使用。

2). 初始化一些变量

ngx_found=no

if test -n "$ngx_feature_name"; then
    ngx_have_feature=`echo $ngx_feature_name \
                   | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ`
fi

上面首先初始化一个ngx_found,这个变量表示的是当前测试的特性是否存在。
接着,根据传进来的ngx_feature_name参数生成一个ngx_have_feature变量。
生成规则:把ngx_feature_name的小写字母全部换成大写字母。所以对于这个示例来说,最终的ngx_have_feature的值为NGX_HAVE_EPOLL

3).设置头文件目录

if test -n "$ngx_feature_path"; then
    for ngx_temp in $ngx_feature_path; do
        ngx_feature_inc_path="$ngx_feature_inc_path -I $ngx_temp"
    done

根据传进来的入参ngx_feature_path设置编译测试程序所需要的头文件。生成结果保存到ngx_feature_inc_path变量中。

4).生成自动测试程序
根据传进来的ngx_feature_test参数生成一个测试特性的c源文件。

cat << END > $NGX_AUTOTEST.c

#include <sys/types.h>
$NGX_INCLUDE_UNISTD_H
$ngx_feature_incs

int main() {
    $ngx_feature_test;
    return 0;
}

END

本例生成的c源文件代码如下:

#include <sys/types.h>
#include <unistd.h>
#include <sys/epoll.h>

int main() {
    int efd = 0, fd = 1, n;
    struct epoll_event ee;
    ee.events = EPOLLIN|EPOLLOUT|EPOLLET;
    ee.data.ptr = NULL;
    efd = epoll_create(100);
    if (efd == -1) return 1;;
    return 0;
}

这个c源文件也是一个很简单的代码。

5).编译自动测试程序

ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS $ngx_feature_inc_path \
          -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_TEST_LD_OPT $ngx_feature_libs"

ngx_feature_inc_path=

eval "/bin/sh -c \"$ngx_test\" >> $NGX_AUTOCONF_ERR 2>&1"

上面的编译指令ngx_test的值如下:

gcc  -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -o objs/autotest objs/autotest.c

编译之后生成的目标文件是objs/autotest文件。

6).执行自动测试程序
我们先看一下如果没有编译成功的话,会执行什么东东。

echo " not found"

    echo "----------"    >> $NGX_AUTOCONF_ERR
    cat $NGX_AUTOTEST.c  >> $NGX_AUTOCONF_ERR
    echo "----------"    >> $NGX_AUTOCONF_ERR
    echo $ngx_test       >> $NGX_AUTOCONF_ERR
    echo "----------"    >> $NGX_AUTOCONF_ERR

其实这也是nginx惯用的手法,就是把这些测试信息保存到NGX_AUTOCONF_ERR错误日志文件中,便于查找错误原因。

7).如果编译成功了该咋办呢?
这部分代码也是比较长,我们分别讨论。
根据ngx_feature_run的值,可以分为如下几部分:

①.ngx_feature_run 的值为yes:

yes)
            if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
                echo " found"
                ngx_found=yes

                if test -n "$ngx_feature_name"; then
                    have=$ngx_have_feature . auto/have
                fi

            else
                echo " found but is not working"
            fi
        ;;

如果执行成功的话,会把ngx_found设置为yes。这个变量在后面会用到。
然后调用auto/have脚本在NGX_AUTO_CONFIG_H文件中增加一个宏定义。

②.ngx_feature_run 的值为value:
在这种情况下,编译只有的源文件执行结果是一个数值。最终生成一个宏定义代表该数值。

value)
            # /bin/sh is used to intercept "Killed" or "Abort trap" messages
            if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
                echo " found"
                ngx_found=yes

                cat << END >> $NGX_AUTO_CONFIG_H

#ifndef $ngx_feature_name
#define $ngx_feature_name  `$NGX_AUTOTEST`
#endif

END
            else
                echo " found but is not working"
            fi
        ;;

这种情况是计算一个结果,然后把结果通过宏的方式添加到NGX_AUTO_CONFIG_H文件中。

③.ngx_feature_run的值为bug.

 bug)
            # /bin/sh is used to intercept "Killed" or "Abort trap" messages
            if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
                echo " not found"

            else
                echo " found"
                ngx_found=yes

                if test -n "$ngx_feature_name"; then
                    have=$ngx_have_feature . auto/have
                fi
            fi
        ;;

这种情况的处理方式和yes的处理方式刚好相反。

④.ngx_feature_run为其他值。

*)
            echo " found"
            ngx_found=yes

            if test -n "$ngx_feature_name"; then
                have=$ngx_have_feature . auto/have
            fi
        ;;

只是简单的增加一个宏定义。

8). 删除临时文件

rm $NGX_AUTOTEST*

9).脚本执行完之后的工作
我们上面已经写到了,auto/feature输出了一个参数ngx_found,这个参数表示当前宿主系统是否含有此特性。
我们看一下当测试完epoll之后的处理。

if [ $ngx_found = yes ]; then
    have=NGX_HAVE_CLEAR_EVENT . auto/have
    CORE_SRCS="$CORE_SRCS $EPOLL_SRCS"
    EVENT_MODULES="$EVENT_MODULES $EPOLL_MODULE"
    EVENT_FOUND=YES
fi

这里就是一些特殊的处理,当宿主系统包含该特性之后的一些后续逻辑。

调试方法

由于在脚本中很多地方都调用了auto/feature脚本,如果我们想调试的话,不是特别方便,可以通过在源代码中添加下面的代码进行调试:

 if  test -n "$ngx_feature_name" ;then
 # 这里的NGX_SYS_NERR是我们想要调试的特性
 #我们可以在里层的if语句中完整各种功能进行调试
    if [ "$ngx_feature_name" = "NGX_SYS_NERR" ];then
        cat $NGX_AUTOTEST.c;
        echo `$NGX_AUTOTEST`;
    fi
fi

总结

我们详细的分析了auto/feature脚本的功能。这个脚本的作用就是测试在当前的宿主系统上面是否支持某个特性。

后面的文章我们会接着分析nginx的源码,敬请期待。顺便关注我的个公众号(Nginx源码分析)。

上一篇下一篇

猜你喜欢

热点阅读