使用的版本为nginx版本为1.18.0
编译平台为ubuntu14.04 x86环境。
目标平台为marvell平台 64位arm处理器环境。
首先去除编译器的自检流程,./configure 执行时会检查当期编译的工具链是否有效,同时根据当前平台生成部分头文件,但因为需要执行交叉编译,部分检查手段就会存在异常,故而会在执行configure生成Makefile时就会报错,需要提前排除。
21 # exit 1
因脚本中执行 auto/feature 脚本中会使用 C C 交 叉 编 译 工 具 进 行 生 成 CC 交叉编译工具进行 生成 CC交叉编译工具进行生成NGX_AUTOTEST 可执行文件尝试执行,但因为跨平台,势必无法执行成功,故而返回的ngx_found变量必为no,会导致退出,故而需要注释。
该sizeof脚本会根据入参要判断的变量,比如int,long等,通过生成源文件,执行printf打印出大小,得到对应变量在当前平台的值大小,通过ngx_size,以及ngx_max_value等参数返回,得到一些平台相关变量的占位大小,以及最大值和范围等等,抽取脚本中的核心环节如下。
29 printf("%d", (int) sizeof($ngx_type));
调用的地方在auto/unix中,grep得到的清单如下:
./auto/unix:618:ngx_type="int"; . auto/types/sizeof
./auto/unix:620:ngx_type="long"; . auto/types/sizeof
./auto/unix:622:ngx_type="long long"; . auto/types/sizeof
./auto/unix:624:ngx_type="void *"; . auto/types/sizeof; ngx_ptr_size=$ngx_size
./auto/unix:636:. auto/types/sizeof
./auto/unix:651:ngx_type="size_t"; . auto/types/sizeof
./auto/unix:655:ngx_type="off_t"; . auto/types/sizeof
./auto/unix:659:ngx_type="time_t"; . auto/types/sizeof
该脚本中得到ngx_size后,会把名称和ngx_size或者ngx_max_value大小传给auto/types/value脚本,并以宏定义的形式写入ngx_auto_config.h脚本。从unix脚本中看,主要包括void *,sig_atomic_t,和time_t,size_t,off_t,time_t等,还是比较多的。这些对应的宏,以NGX_SIZE_T_LEN为例,还是在不少地方有使用到的。如下是对整个工程目录grep NGX_SIZE_T_LEN得到的结果
./auto/unix:653:ngx_param=NGX_SIZE_T_LEN; ngx_value=$ngx_max_len; .
auto/types/value
./src/mail/ngx_mail_proxy_module.c:358: + 1 + NGX_SIZE_T_LEN
+ 1 + 2;
./src/mail/ngx_mail_proxy_module.c:377: line.len = s->login.len + 1 + 1 +
NGX_SIZE_T_LEN + 1 + 2;
./src/core/ngx_log.c:600: + NGX_SIZE_T_LEN
./src/http/ngx_http_variables.c:787: v->data = ngx_pnalloc(r->pool,
NGX_SIZE_T_LEN);
./src/http/modules/ngx_http_image_filter_module.c:610: + 2 *
NGX_SIZE_T_LEN;
./src/http/modules/ngx_http_log_module.c:246: { ngx_string("request_length"),
NGX_SIZE_T_LEN,
./src/http/modules/ngx_http_scgi_module.c:773: b = ngx_create_temp_buf(r->pool,
NGX_SIZE_T_LEN + 1 + len + 1);
./objs/ngx_auto_config.h:211:#ifndef NGX_SIZE_T_LEN
./objs/ngx_auto_config.h:212:#define NGX_SIZE_T_LEN
(sizeof("-9223372036854775808") - 1)
部分网上介绍说直接将$CC替换gcc,如果编译机和目标机的平台位数不一致的话,会产生问题,比如上述涉及到size相关的代码,其行为就会变得不可预知。同时将ngx_size变量写死,也是会产生一样的问题。
所以合理的方式是,参考auto/types/sizeof脚本中的判断方式,在目标平台上得到nginx所需的各个通过auto/types/sizeof脚本得到大小的变量的实际大小。
此处可能需要结合各人交叉编译的目标平台的实际结果进行判断,个人编写了如下源文件,可交叉编译放在目标平台上执行后查看结果。用于替换后续生成的ngx_auto_config.h源文件中的值。在执行./configure生成makefile时为保证通过,可以先修改$CC为gcc,后续再按如上方式进行替换。
[源文件待补充]
在我本地64位的目标机芯片上执行结果如下
# ./a.out
name NGX_PTR_SIZE size 8
name NGX_SIG_ATOMIC_T_SIZE size 4
name NGX_SIZE_T_SIZE size 8
name NGX_OFF_T_SIZE size 8
name NGX_TIME_T_SIZE size 8
for 4 :
ngx_max_value 2147483647
ngx_max_len (sizeof("-2147483648") - 1)
for 8 :
ngx_max_value 9223372036854775807LL
ngx_max_len (sizeof("-9223372036854775808") - 1)
后续可与执行./configure后生成的ngx_auto_config.h中对照,看是否有需要修改之处
[这里可补充后续生成的结果]]
依赖库如开头所言,有三个,如下为当期编译中使用的依赖库版本,分别对要对依赖库也进行交叉编译的准备和流程做分析:
pcre-8.43
openssl-1.1.1d
zlib-1.2.11
pcre库有对应的nginx在编译时可以包含的选项。
--with-pcre=DIR #用于指定pcre库的源码位置
--with-pcre-opt=OPTIONS #可以指定编译时加入的选项
实际最终在obj/Makefile中生成的编译pcre的执行的脚本如下
cd /mnt/hd2/home/dir/nginxbuild/pcre-8.43 \
&& if [ -f Makefile ]; then $(MAKE) distclean; fi \
&& CFLAGS="-O2 -fomit-frame-pointer -pipe " \
但是—with-pcre-opt选项影响的是只 CFLAGS的选项,而不影响configure的参数,即无法配置交叉编译。
故而需要在上述位置进行手动修改,通过–host参数配置pcre库对应的交叉编译平台,注意此为通过./configure生成makefile后执行的,后续第四环节对生成的Makefile再做修改时会再提及。
1293:/mnt/hd2/home /nginxbuild/pcre-8.43/Makefile: objs/Makefile
…
1297:./configure --host=aarch64-linux-gnu --disable-shared
执行configure前,保证下载了源码文件,并解压到对应位置,并将对应位置写入DIR即可。主要是生成obj/Makefile后需再做修改
Nginx提供与openssl相关选项
--with-openssl #指定openssl的使用路径
--with-openssl-opt=OPTIONS #指定config使用的参数
比如带上了第三步调用configure生成makefile的参数,执行./configure命令配置nginx后,在obj/Makefile里面可以看到生成如下与openssl相关的目标和执行命令
/mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/openssl-1.1.1d/.openssl/include/openssl/ssl.h: objs/Makefile
cd /mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/openssl-1.1.1d \
&& if [ -f Makefile ]; then $(MAKE) clean; fi \
&& ./config
--prefix=/mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/openssl-1.1.1d/.openssl no-shared
no-threads linux-armv4 --cross-compile-prefix=aarch64-marvell-linux-gnu-
--prefix=/mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/openssl-1.1.1d/.openssl no-asm
no-ssl2 -DOPENSSL_USE_IPV6 -DOPENSSL_NO_DYNAMIC_ENGINE \
&& $(MAKE) \
&& $(MAKE) install_sw LIBDIR=lib
但是openssl源文件的config文件其实不支持接受指定平台参数,虽然此处交叉编译arm平台,故选择linux-armv4参数,即使指定,仍然会使用默认自身检测到的平台,在我的ubuntu14.04上就是如下地址:
Operating system: x86_64-whatever-linux2
Failure! build file wasn't produced.
Please read INSTALL and associated NOTES files. You may also have to look over
your available compiler tool chain or change your configuration.
target already defined - linux-x86_64 (offending arg: linux-armv4)
虽然–cross-compile-prefix参数的指定使得CC和AR等命令是正常的,但是在编译中会遇到-m64缺失的问题,原因因为认为目标平台是x86平台,gcc 使用-m64参数,用于确定编译的目标平台为64位还是32位,但是交叉编译工具链大概率不支持(我本地测试的两款是不支持的)
故而需要在执行最终编译前先进入到openssl的目录,提前执行Configure命令配置平台,Configure命令是接受平台参数的
指定生成目录直接为当前目录下的.openssl目录,因为为交叉编译,不应该install到系统目录下。
./Configure linux-armv4 --cross-compile-prefix=aarch64-marvell-linux-gnu-
--prefix=/mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/openssl-1.1.1d/.openssl no-shared
no-asm no-ssl2 -DOPENSSL_USE_IPV6 -DOPENSSL_NO_DYNAMIC_ENGINE
以保证后续编译器使用正确的选项执行。这里暂不对openssl的配置命令做详述,有兴趣可以查阅相关资料。
zlib库最简单,只需要解压好,并放置在对应目录,带上 –with-zlib参数即可。
先一睹完整命令(实际使用注意需要在同一行中))
./configure
--crossbuild=aarch-marvell-linux-gnu
--with-cc=/opt/marvell_i686_gnu/bin//aarch64-marvell-linux-gnu-gcc
--with-cpp=/opt/marvell_i686_gnu/bin//aarch64-marvell-linux-gnu-g++
--with-pcre=/mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/pcre-8.43
--with-openssl=/mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/openssl-1.1.1d
--with-openssl-opt="no-shared no-threads linux-armv4 --cross-compile-prefix=aarch64-marvell-linux-gnu- --prefix=/mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/openssl-1.1.1d/.openssl no-asm no-ssl2 -DOPENSSL_USE_IPV6 -DOPENSSL_NO_DYNAMIC_ENGINE"
--with-zlib=/mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/zlib-1.2.11
--prefix=/mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/output/
--with-http_ssl_module
--with-http_v2_module
--without-http_upstream_zone_module
--with-cc-opt="-D_GNU_SOURCE"
上述配置项主要包括五个部分:1、交叉编译配置和工具链配置。2、依赖库配置。3、安装路径配置。4、部分http模块配置。5、部分编译错误处理。对1,2,3部分不再赘述,针对4,5的理由,做简单说明。
故而需要携带–with-http_ssl_module 和 --with-http_v2_module 选项。
编译过程中可能遇到如下两类未定义问题
struct in6_pktinfo 结构体未定义
accept4 函数未定义
因为其定义依赖于GNU_SOURCE宏的包裹,故而需要添加。以accpet4的man手册为例。
SYNOPSIS
#include /* See NOTES */
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include
int accept4(int sockfd, struct sockaddr *addr,
socklen_t *addrlen, int flags);
明确说明调用accept4要定义_GNU_SOURCE宏,故而携带–with-cc-opt="-D_GNU_SOURCE" 可以解决该问题。
该选项为解决如下编译问题
src/core/ngx_rwlock.c:125:2: error: #error ngx_atomic_cmp_set() is not defined!
执行./configure之后,最核心的生成产物在obj目录下,包括Makefile和相关头文件,针对这些产物还涉及到几个修改步骤。。注意后续每次执行./configure,这些内容都会重新生成,故而还是需要按照下述步骤再做一次修改。
之前提到的pcre库修改,需修改为2a中1297行描述的内容,执行configure时带上指定host平台的参数。
需在ngx_auto_config.h添加如下宏定义,以解决后续可能出现的编译报错。
#ifndef NGX_SYS_NERR
#define NGX_SYS_NERR 132
#endif
#ifndef NGX_HAVE_SYSVSHM
#define NGX_HAVE_SYSVSHM 1
#endif
如1b描述,需根据目标平台的实际情况,对ngx_auto_config.h中的部分相关宏参数做修改,比如NGX_PTR_SIZE等,这个需要根据自己实际情况做修改,无法给出一个通用的解决方案。如果目标平台与编译平台的位数一致,该点可能不需要改动。
带 pthread参数,才能索引到部分线程函数比如 pthread_atfork的定义,否则会报部分线程函数实现未定义错误。
修改在如下行
381 -ldl -lcrypt -pthread
/mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/pcre-8.43/.libs/libpcre.a
/mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/openssl-1.1.1d/.openssl/lib/libssl.a
/mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/openssl-1.1.1d/.openssl/lib /libcrypto.a
-ldl /mnt/hd2/home/cpl/yac/nginxbuild_20210515_raw/zlib-1.2.11/libz.a \
最后执行make;make install即可生成对应目标平台的nginx产物。
《深入理解Nginx:模块开发与架构解析》
https://blog.csdn.net/u011641885/article/details/49863723
https://blog.csdn.net/whahu1989/article/details/101567517
https://www.dazhuanlan.com/2019/12/09/5dee69b4131f9/