手把手教你彻底认识nginx的auto/feature脚本
回顾
来,看下图:
图中还剩下
auto/feature
和auto/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源码分析
)。