nginx源码分析configure脚本详解

nginx源码分析——configure脚本

一、前言

在分析源码时,经常可以看到类似 #if (NGX_PCRE) .... #endif 这样的代码段,这样的设计可以在不改动源码的情况下,通过简单的定义宏的方式来实现功能的打开与关闭,但是在nginx/src目录下始终没有找到宏 NGX_PCRE 对应的 #define 语句。

在之前介绍event模块的时候,讲到init_cycle函数中对cycle进行了初始化,其中很重要一步操作就是讲包含所有module信息的数组拷贝到这个cycle对应的结构中(nginx/src/core/ngx_module.c),文件中函数用到的包含module名称的数组ngx_module_names在源码中也没找到定义、初始化。

上述两个疑问的答案应该在对nginx源码编译前执行的./auto/configure命令,因为该命令的输出中显示了对一些函数、头文件的检测,所以就将重点分析放到nginx/auto/configure文件中。

二、configure脚本

由于nginx拥有丰富的功能选项,因此有经验的使用者都会采用直接源码编译、安装的方式。在编译前,需要通过执行如下命令来完成源代码的编译。

cd nginx; ./auto/configure --with-pcre && make

其中./auto/configure --with-pcre 就是需要在源码中启用NGX_PCRE宏,但是如何实现的呢?

打开nginx/auto/configure文件,发现该文件是一个shell脚本,并调用了一些其他文件

################## nginx/auto/configure #######################
#!/bin/sh

# Copyright (C) Igor Sysoev
# Copyright (C) Nginx, Inc.

LC_ALL=C
export LC_ALL

#执行auto/options文件中的命令,行中的“.”表示在当前的sh环境中执行auto/options
#文件中的命令(与source命令效果一样)不同于sh命令会导致创建子进程,由于configure
#文件中命令与options的命令都在同一sh环境下,所以变量在两个文件中都是彼此可见的
. auto/options   #设置变量,并将之置空
. auto/init     #初始化一些变量诸如:NGX_AUTO_HEADERS_H=$NGX_OBJS/ngx_auto_headers.h
. auto/sources   #设置核心模块、平台代码对应的源文件

test -d $NGX_OBJS || mkdir -p $NGX_OBJS

echo > $NGX_AUTO_HEADERS_H
echo > $NGX_AUTOCONF_ERR

echo "#define NGX_CONFIGURE \"$NGX_CONFIGURE\"" > $NGX_AUTO_CONFIG_H

if [ $NGX_DEBUG = YES ]; then
  have=NGX_DEBUG . auto/have   #设置NGX_DEBUG=1
fi

.....

. auto/cc/conf   #检查编译器相关选项

if [ "$NGX_PLATFORM" != win32 ]; then
  . auto/headers   #检查相关头文件,并将结果输出到ngx_auto_headers.h文件中
fi

. auto/os/conf   #检查系统平台所需的函数

if [ "$NGX_PLATFORM" != win32 ]; then
  . auto/unix   #检查unix环境下一些文件、函数
fi  

. auto/threads

#统计要编译到nginx中的模块信息,创建并初始化ngx_module_t *ngx_modules[]和
#char *ngx_module_names[]两个数组中(这两个数组在init_cycle中被调用)存放
#到之前创建的nginx/objs/ngx_modules.c文件中
. auto/modules
. auto/lib/conf

.......

#定义变量NGX_SBIN_PATH的值为"\"$NGX_SBIN_PATH\""
have=NGX_SBIN_PATH value="\"$NGX_SBIN_PATH\"" . auto/define
have=NGX_CONF_PATH value="\"$NGX_CONF_PATH\"" . auto/define
have=NGX_PID_PATH value="\"$NGX_PID_PATH\"" . auto/define

......

上面简要介绍了nginx/auto/configure文件中的一些内容,configure并没有把工作全部集中于该文件内部,而是提供了一个框架,具体的工作交由auto/threads、auto/headers等文件来完成,并且采用 . auto/conf 这种调用方式,这样可以做到变量共享;这种做法既简化了configure文件的编写,也不同类型的检查工作拆分开,便于编写、维护。下面就来解答第一部分提出的两个问题:

NGX_PCRE宏定义,这类的宏定义可以在nginx/objs/ngx_auto_config.h中看到,这个文件是由have=$ngx_have_feature . auto/have这样的语句生成的。

################ nginx/auto/have ##############
cat << END >> $NGX_AUTO_CONFIG_H

#ifndef $have
#define $have 1
#endif

END

文件中<<符号是来告知shell标准输入来自一对分隔符(可以使字符串、数字等类型,只要保证开头和结尾的一致)中间的内容;所以,have文件中的命令就是利用cat将分隔符END之间的5行内容追加到$NGX_AUTO_CONFIG_H文件中。效果如下:

#ifndef NGX_PCRE
#define NGX_PCRE 1
#endif

这就回答了第一个问题。

auto/configure文件中有一行. auto/modules, 这个文件中定义了要注册到nginx中各个模块的信息以及对应的源文件,然后遍历文件中包含所有定义模块名称的变量modules,自动生成ngx_module_t *ngx_modules[]和char *ngx_module_names[]两个数组,并写入到 $NGX_MODULES_C文件中。这就解释了第二个问题中两个数组是从哪里定义的问题了。

############## nginx/auto/modules ################
......
modules="$modules $MISC_MODULES"

cat << END                  > $NGX_MODULES_C

#include <ngx_config.h>
#include <ngx_core.h>

$NGX_PRAGMA

END

#声明模块为全局变量
for mod in $modules
do
  echo "extern ngx_module_t $mod;"     >> $NGX_MODULES_C
done

#定义并初始化ngx_module_t *ngx_modules[] 数组,然后输出重定向到$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
};

END

#定义并初始化char *ngx_module_names[]数组,然后输出重定向到$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
.......

nginx/auto/modules这个文件生成的两个数组用于cycle的初始化,因此如果开发者开发的模块要添加到nginx中,一定要记得修改nginx/auto/modules这个文件,否则是不会被编译到nginx中的(当然不会生效啦)。

三、方法、函数检查验证

由于nginx跨平台的特性,因此增加了很多位于不同平台下的函数(这些与平台有关的函数、方法也是通过宏定义的方式来决定是否调用),nginx在configure的时候需要找出当前环境中支持的方法、函数,然后将这些支持的方法、函数用宏定义的方式来确定下来。

################# nginx/auto/unix ###############
......
#定义当前feature的各个元素
ngx_feature="poll()"
ngx_feature_name=
ngx_feature_run=no
ngx_feature_incs="#include <poll.h>"
ngx_feature_path=
ngx_feature_libs=
#测试当前feature是否可用的代码段
ngx_feature_test="int n; struct pollfd pl;
         pl.fd = 0;
         pl.events = 0;
         pl.revents = 0;
         n = poll(&pl, 1, 0);
         if (n == -1) return 1"
#利用上面定义的各个变量测试poll()函数这个feature是否可用
. auto/feature

#如果上述的测试结果表明该feature不可用,就将相应的宏设置为NONE
if [ $ngx_found = no ]; then
  EVENT_POLL=NONE
fi
......
################# nginx/auto/feature ###############
......

#利用cat命令将END分隔符之间的内容(与测试feature的代码段组合成的一个简单的C程序)
#写到 $NGX_AUTOTEST.c文件中
cat << END > $NGX_AUTOTEST.c

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

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

END

#将编译链接$NGX_AUTOTEST.c的命令赋值给ngx_test变量
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=

#执行ngx_test变量指向的编译、链接指令,完成对$NGX_AUTOTEST.c的编译、链接生成可执行程序NGX_AUTOTEST
eval "/bin/sh -c \"$ngx_test\" >> $NGX_AUTOCONF_ERR 2>&1"

#检查是否成功生成可执行程序$NGX_AUTOTEST
if [ -x $NGX_AUTOTEST ]; then
   #根据feature的类型,采用不同的方案验证feature的可行性
  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
        #执行成功,将ngx_found设成yes,表示该feature可用
        echo " found"
        ngx_found=yes

        #调用auto/have文件,在$NGX_AUTO_CONFIG_H 文件中,设置该feature对应的宏的值为1(启用该feature)
        if test -n "$ngx_feature_name"; then
          have=$ngx_have_feature . auto/have
        fi

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

nginx的这种demo验证机制,既做到了检查当前系统是否拥有对应的方法、函数,也验证了拥有的方法、函数是否提供了期望的功能。这种情况也提醒我们,运行nginx的生产环境与编译nginx的开发环境要保持一致。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • nginx超时设置详细介绍

    nginx常用的超时配置说明 client_header_timeout 语法 client_header_timeout time 默认值 60s 上下文 http server 说明 指定等待client发送一个请求头的超时时间(例如:GET / HTTP/1.1).仅当在一次read中,没有收到请求头,才会算成超时.如果在超时时间内,client没发送任何东西,nginx返回HTTP状态码408("Request timed out") client_body_timeout 语

  • 详解Linux中Nginx反向代理下的tomcat集群

    Nginx具有反向代理(注意和正向代理的区别)和负载均衡等特点. 这次Nginx安装在 192.168.1.108 这台linux 机器上.安装Nginx 先要装openssl库,gcc,PCRE,zlib库等. Tomcat 安装在192.168.1.168 和 192.168.1.178 这两台机器上.客户端通过访问192.168.1.108 反向代理访问到192.168.1.168 和 192.168.1.178 里Tomcat 部署的工程内容.  1.Linux 下安装Nginx (机器

  • CentOS 7中利用yum安装Nginx的方法教程

    前言 因为最近在筹备Docker系列课程,其中涉及到在CentOS 7中安装Nginx.遇到一些问题,所以想着将过程总结分享出来供大家参考学习,下面来看看详细的介绍: 当使用以下命令安装Nginx时,发现无法安装成功. yum install -y nginx 需要做一点处理. 安装Nginx源 执行以下命令: rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noar

  • Nginx+lua 实现调用.so文件

    最近在和智能硬件部门一起,做一个室内定位的服务,该服务根据手机端传过来的beacon设备列表,根据一定的算法计算出具体的商场,并将商场ID和beason设备列表作为参数,调用.so文件中的计算方法,得出位置数据(坐标:x.y.z),返回给手机端. 因为服务对QPS要求比较高,并且都是纯查询操作,于是决定使用Nginx+lua+Redis的架构(该架构在公司内部已成主流,比较成熟).下面我将对lua调用.so文件的方式,做一下介绍. lua调用.so文件,主要有两种方式: 1.Lua直接调用动态链

  • Nginx 实现灰度发布的三种方法总结

    Nginx 实现灰度发布的三种方法总结 灰度发布的主要原理是访问路由的控制,重点是保证每次访问的是同一个节点. 方式一:通过调节负载均衡权重 负载均衡 建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽.增加吞吐量.加强网络数据处理能力.提高网络的灵活性和可用性. 负载均衡,英文名称为Load Balance,其意思就是分摊到多个操作单元上进行执行,例如Web服务器.FTP服务器.企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务. 简单配置如下: ht

  • 详解nginx服务器http重定向到https的正确写法

    http重定向到https使用了nginx的重定向命令.那么应该如何写重定向?之前老版本的nginx可能使用了以下类似的格式. rewrite ^/(.*)$ http://domain.com/$1 permanent; 或者 rewrite ^ http://domain.com$request_uri? permanent; 现在nginx新版本已经换了种写法,上面这些已经不再推荐. 下面是nginx http页面重定向到https页面最新支持的写法: server { listen 80

  • nginx服务器通过配置来解决API的跨域问题

    前言 最近在采用jquery ajax调用http请求时,发现了一系列问题: 如采用firebug调试API请求(这个API是自己服务器的应用),看到服务器明明返回200状态,response返回数据也是json格式,但ajax返回的error. 在排除json数据格式不正确的原因之后,发现了ajax error函数返回"networkerror failed to execute 'send' on 'xmlhttprequest' failed to load 'http //"

  • nginx源码分析configure脚本详解

    nginx源码分析--configure脚本 一.前言 在分析源码时,经常可以看到类似 #if (NGX_PCRE) .... #endif 这样的代码段,这样的设计可以在不改动源码的情况下,通过简单的定义宏的方式来实现功能的打开与关闭,但是在nginx/src目录下始终没有找到宏 NGX_PCRE 对应的 #define 语句. 在之前介绍event模块的时候,讲到init_cycle函数中对cycle进行了初始化,其中很重要一步操作就是讲包含所有module信息的数组拷贝到这个cycle对应

  • nginx源码分析线程池详解

    nginx源码分析线程池详解 一.前言 nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影响.但是经常会有人问道,nginx为什么不采用多线程模型(这个除了之前一篇文章讲到的情况,别的只有去问作者了,HAHA).其实,nginx代码中提供了一个thread_pool(线程池)的核心模块来处理多任务的.下面就本人对该thread_pool这个模块的理解来跟大家做些分享(文中错误.不足还请大家指出,谢谢) 二.thread_

  • java集合类源码分析之Set详解

    Set集合与List一样,都是继承自Collection接口,常用的实现类有HashSet和TreeSet.值得注意的是,HashSet是通过HashMap来实现的而TreeSet是通过TreeMap来实现的,所以HashSet和TreeSet都没有自己的数据结构,具体可以归纳如下: •Set集合中的元素不能重复,即元素唯一 •HashSet按元素的哈希值存储,所以是无序的,并且最多允许一个null对象 •TreeSet按元素的大小存储,所以是有序的,并且不允许null对象 •Set集合没有ge

  • JAVA 枚举单例模式及源码分析的实例详解

    JAVA 枚举单例模式及源码分析的实例详解 单例模式的实现有很多种,网上也分析了如今实现单利模式最好用枚举,好处不外乎三点: 1.线程安全 2.不会因为序列化而产生新实例 3.防止反射攻击但是貌似没有一篇文章解释ENUM单例如何实现了上述三点,请高手解释一下这三点: 关于第一点线程安全,从反编译后的类源码中可以看出也是通过类加载机制保证的,应该是这样吧(解决) 关于第二点序列化问题,有一篇文章说枚举类自己实现了readResolve()方法,所以抗序列化,这个方法是当前类自己实现的(解决) 关于

  • Java集合框架源码分析之LinkedHashMap详解

    LinkedHashMap简介 LinkedHashMap是HashMap的子类,与HashMap有着同样的存储结构,但它加入了一个双向链表的头结点,将所有put到LinkedHashmap的节点一一串成了一个双向循环链表,因此它保留了节点插入的顺序,可以使节点的输出顺序与输入顺序相同. LinkedHashMap可以用来实现LRU算法(这会在下面的源码中进行分析). LinkedHashMap同样是非线程安全的,只在单线程环境下使用. LinkedHashMap源码剖析 LinkedHashM

  • Vue编译器源码分析compileToFunctions作用详解

    目录 引言 Vue.prototype.$mount函数体 源码出处 options.delimiters & options.comments compileToFunctions函数逐行分析 createFunction 函数源码 引言 Vue编译器源码分析 接上篇文章我们来分析:compileToFunctions的作用. 经过前面的讲解,我们已经知道了 compileToFunctions 的真正来源你可能会问为什么要弄的这么复杂?为了搞清楚这个问题,我们还需要继续接触完整的代码. 下面

  • jQuery源码分析之Callbacks详解

    代码的本质突出顺序.有序这一概念,尤其在javascript--毕竟javascript是单线程引擎. javascript拥有函数式编程的特性,而又因为javascript单线程引擎,我们的函数总是需要有序的执行.优秀代码常常 把函数切割成各自的模块,然后在某一特定条件下执行,既然这些函数是有序的执行,那么我们为什么不编写一个统一管理的对象,来帮助我们管理这些函数--于是,Callbacks(回调函数)诞生. 什么是Callbacks javascript中充斥着函数编程,例如最简单的wind

  • Django restframework 源码分析之认证详解

    前言 最近学习了 django 的一个 restframework 框架,对于里面的执行流程产生了兴趣,经过昨天一晚上初步搞清楚了执行流程(部分方法还不太清楚),于是想详细的总结一下当来一个请求时,在该框架里面是如何执行的? 启动项目时 昨天在调试django时,发现在 APIView 中打的断点没有断下来,而是打在 View 中的断点断下来了,调试了很多次,最后发现,在 django 项目启动时,会首先加载 urls 中的文件,执行 views 中类的 as_view方法,其实是继承自 API

  • 通过JDK源码分析关闭钩子详解

    关闭钩子 用户关闭关闭程序,需要做一些善后的清理工作,但问题是,某些用户不会按照推荐的方法关闭应用程序,肯能导致善后工作无法进行.像tomcat调用server的start方法启动容器,然后会逐级调用start.当发出关闭命令是会启动关闭功能,但是关闭可能会有一些意外产生,导致应用程序没有进入到我们制定的关闭方法去.如何解决这个问题呢,使得即使有意外也能正常进入关闭流程. 好在java提供了一种优雅的方式去解决这种问题.使得关闭的善后处理的代码能执行.java的关闭钩子能确保总是执行,无论用户如

  • Java源码解析之TypeVariable详解

    TypeVariable,类型变量,描述类型,表示泛指任意或相关一类类型,也可以说狭义上的泛型(泛指某一类类型),一般用大写字母作为变量,比如K.V.E等. 源码 public interface TypeVariable<D extends GenericDeclaration> extends Type { //获得泛型的上限,若未明确声明上边界则默认为Object Type[] getBounds(); //获取声明该类型变量实体(即获得类.方法或构造器名) D getGenericDe

随机推荐