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

回顾

来,看下图:

辅助脚本

图中还剩下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源码分析)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容