Ubuntu 下 nginx-1.24.0 源码分析 (1)
main 函数在
src\core\nginx.c
int ngx_cdecl
main(int argc, char *const *argv)
{ngx_buf_t *b;ngx_log_t *log;ngx_uint_t i;ngx_cycle_t *cycle, init_cycle;ngx_conf_dump_t *cd;ngx_core_conf_t *ccf;ngx_debug_init();
进入 main 函数
最开始是局部变量的声明
然后是
ngx_debug_init();
接下来是 :
if (ngx_strerror_init() != NGX_OK) {return 1;}
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_strerror_init()函数-CSDN博客
接下来是 :
if (ngx_get_options(argc, argv) != NGX_OK) {return 1;}
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_get_options函数-CSDN博客
当前这次执行的是:
sudo ./nginx
除了 程序名 没有其他参数
所以
argc=1
于是在 ngx_get_options 函数中
static ngx_int_t
ngx_get_options(int argc, char *const *argv)
{u_char *p;ngx_int_t i;for (i = 1; i < argc; i++) {p = (u_char *) argv[i];if (*p++ != '-') {ngx_log_stderr(0, "invalid option: \"%s\"", argv[i]);return NGX_ERROR;}while (*p) {switch (*p++) {case '?':case 'h':ngx_show_version = 1;ngx_show_help = 1;break;case 'v':ngx_show_version = 1;break;case 'V':ngx_show_version = 1;ngx_show_configure = 1;break;case 't':ngx_test_config = 1;break;case 'T':ngx_test_config = 1;ngx_dump_config = 1;break;case 'q':ngx_quiet_mode = 1;break;case 'p':if (*p) {ngx_prefix = p;goto next;}if (argv[++i]) {ngx_prefix = (u_char *) argv[i];goto next;}ngx_log_stderr(0, "option \"-p\" requires directory name");return NGX_ERROR;case 'e':if (*p) {ngx_error_log = p;} else if (argv[++i]) {ngx_error_log = (u_char *) argv[i];} else {ngx_log_stderr(0, "option \"-e\" requires file name");return NGX_ERROR;}if (ngx_strcmp(ngx_error_log, "stderr") == 0) {ngx_error_log = (u_char *) "";}goto next;case 'c':if (*p) {ngx_conf_file = p;goto next;}if (argv[++i]) {ngx_conf_file = (u_char *) argv[i];goto next;}ngx_log_stderr(0, "option \"-c\" requires file name");return NGX_ERROR;case 'g':if (*p) {ngx_conf_params = p;goto next;}if (argv[++i]) {ngx_conf_params = (u_char *) argv[i];goto next;}ngx_log_stderr(0, "option \"-g\" requires parameter");return NGX_ERROR;case 's':if (*p) {ngx_signal = (char *) p;} else if (argv[++i]) {ngx_signal = argv[i];} else {ngx_log_stderr(0, "option \"-s\" requires parameter");return NGX_ERROR;}if (ngx_strcmp(ngx_signal, "stop") == 0|| ngx_strcmp(ngx_signal, "quit") == 0|| ngx_strcmp(ngx_signal, "reopen") == 0|| ngx_strcmp(ngx_signal, "reload") == 0){ngx_process = NGX_PROCESS_SIGNALLER;goto next;}ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal);return NGX_ERROR;default:ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1));return NGX_ERROR;}}next:continue;}return NGX_OK;
}
进入不了 for 循环
直接走到
return NGX_OK;
返回到 main 函数中
接下来是 :
if (ngx_show_version) {ngx_show_version_info();if (!ngx_test_config) {return 0;}}
ngx_show_version 未设置,此时是 0
于是跳过这段代码
接下来是 :
/* TODO */ ngx_max_sockets = -1;
初始化全局变量 ngx_max_sockets
ngx_max_sockets
是一个全局变量,用于存储 Nginx 能够处理的最大文件描述符(socket)数量。
文件描述符是操作系统用于管理打开的文件、socket 等资源的标识符。Nginx 作为一个高性能的 Web 服务器,需要处理大量的并发连接,因此文件描述符的数量对性能有重要影响
初始值为 -1
的意义
-
将
ngx_max_sockets
初始化为-1
表示在程序启动时,还没有确定实际的最大文件描述符数量。 -
-1
通常用作一个初始值或无效值,表示该变量尚未被正确初始化或配置。 -
在后续的代码中,Nginx 会根据操作系统的限制和配置文件中的设置来更新
ngx_max_sockets
的值。
接下来是 :
ngx_time_init();
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_time_init 函数-CSDN博客
接下来是 :
#if (NGX_PCRE)ngx_regex_init();
#endif
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_regex_init 函数-CSDN博客
接下来是 :
ngx_pid = ngx_getpid();ngx_parent = ngx_getppid();
获取当前进程的进程ID(ngx_pid
)和父进程的进程ID(ngx_parent
)。
这在后续的进程管理中很有用。
在 src/os/unix/ngx_process.h 中:
#define ngx_getpid getpid
#define ngx_getppid getppid
getpid
和getppid
是 C 语言中用于获取进程 ID 的函数,定义在<unistd.h>
头文件中getpid():获取当前进程的进程ID
getppid():获取当前进程的父进程ID父进程是创建当前进程的进程(如通过fork())。
若父进程终止,子进程的PPID会被重置为init进程(PID=1)
接下来是:
log = ngx_log_init(ngx_prefix, ngx_error_log);if (log == NULL) {return 1;}
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_log_init 函数-CSDN博客
此次调用 ngx_log_init 时
prefix 和 error_log 都还没有设置此时还是 null
进入 ngx_log_init
首先是
ngx_log_t *
ngx_log_init(u_char *prefix, u_char *error_log)
{u_char *p, *name;size_t nlen, plen;ngx_log.file = &ngx_log_file;ngx_log.log_level = NGX_LOG_NOTICE;
-
将全局日志对象
ngx_log
的文件指针指向ngx_log_file
。 -
设置默认日志级别为
NGX_LOG_NOTICE
(通知级别)
if (error_log == NULL) {error_log = (u_char *) NGX_ERROR_LOG_PATH;}name = error_log;nlen = ngx_strlen(name);
此时 error_log 还是null
于是进入这个 if 条件中
把默认值 NGX_ERROR_LOG_PATH 赋值给 error_log
这个默认值是在 执行configure命令时定义的一个宏,它的值由 configure 命令的配置项 --error-log-path 指定
if (nlen == 0) {ngx_log_file.fd = ngx_stderr;return &ngx_log;}p = NULL;
nlen 不是0
跳过这个 if 条件
if (name[0] != '/') {
检查路径是否以 /
开头
是以 /
开头
所以当前 name 已经是绝对路径了,不需要拼接前缀
ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND,NGX_FILE_CREATE_OR_OPEN,NGX_FILE_DEFAULT_ACCESS);
以追加的方式打开 name 指向的 日志文件
此时返回的 fd 是 4
if (ngx_log_file.fd == NGX_INVALID_FILE) {ngx_log_stderr(ngx_errno,"[alert] could not open error log file: "ngx_open_file_n " \"%s\" failed", name);
NGX_INVALID_FILE 的值是 -1 无效的文件描述符,表示上一步的打开文件失败
现在这个条件不成立,跳过这段代码
if (p) {ngx_free(p);}return &ngx_log;
由于之前条件不成立,所以 没有用到 p
最后返回 ngx_log 的地址
回到 main 函数中
接下来是:
ngx_ssl_init(log);
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_ssl_init 函数-CSDN博客
接下来是:
ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));
把 init_cycle 的每个字节都初始化为 0
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_cycle_t 类型-CSDN博客
init_cycle.log = log;
将之前 初始化的 日志对象的地址 记录在 log 字段
ngx_cycle = &init_cycle;
现在 ngx_cycle 是指向 init_cycle 的指针了
init_cycle.pool = ngx_create_pool(1024, log);
创建一个内存池要求的内存大小是 1024 字节,然后将地址记录到 init_cycle 的 pool 字段进行管理
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_create_pool函数-CSDN博客
进入 ngx_create_pool
ngx_pool_t *
ngx_create_pool(size_t size, ngx_log_t *log)
{ngx_pool_t *p;p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);if (p == NULL) {return NULL;}
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_memalign函数-CSDN博客
使用 ngx_memalign 来分配内存
NGX_POOL_ALIGNMENT 是要求的对齐边界,值为 16
进入 ngx_memalign
void *
ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)
{void *p;int err;err = posix_memalign(&p, alignment, size);
调用 posix_memalign 来分配一块对齐的内存
posix_memalign 函数-CSDN博客
此次返回值为 0
也就是分配内存成功了
p=0x5aa55f9765a0
这个地址是按 16 对齐的
if (err) {ngx_log_error(NGX_LOG_EMERG, log, err,"posix_memalign(%uz, %uz) failed", alignment, size);p = NULL;}
条件不成立,跳过这段代码
return p;
把分配的地址返回到
ngx_create_pool 函数中
回到 ngx_create_pool
接下来是:
if (p == NULL) {return NULL;}
条件不成立,跳过这段代码
p->d.last = (u_char *) p + sizeof(ngx_pool_t);p->d.end = (u_char *) p + size;p->d.next = NULL;p->d.failed = 0;size = size - sizeof(ngx_pool_t);p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;p->current = p;p->chain = NULL;p->large = NULL;p->cleanup = NULL;p->log = log;return p;
赋值然后返回 内存池 地址
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_create_pool函数-CSDN博客
当前 size = 944 , NGX_MAX_ALLOC_FROM_POOL = -1
所以 p->max=944
回到 main 函数中
接下来是:
if (init_cycle.pool == NULL) {return 1;}
条件不成立,跳过这段代码
if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) {return 1;}
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_save_argv函数-CSDN博客
if (ngx_process_options(&init_cycle) != NGX_OK) {return 1;}
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_process_options-CSDN博客
进入 ngx_process_options
static ngx_int_t
ngx_process_options(ngx_cycle_t *cycle)
{u_char *p;size_t len;if (ngx_prefix) {
ngx_prefix 还是 null
所以跳过这个 if 代码段进入 else
接下来是:
ngx_str_set(&cycle->conf_prefix, NGX_CONF_PREFIX);
将 默认的配置文件路径前缀设置给 cycle->conf_prefix
ngx_str_set(&cycle->prefix, NGX_PREFIX);
将 默认的路径前缀设置给 cycle->prefix
if (ngx_conf_file) {cycle->conf_file.len = ngx_strlen(ngx_conf_file);cycle->conf_file.data = ngx_conf_file;} else {ngx_str_set(&cycle->conf_file, NGX_CONF_PATH);}
此次启动没有指定 配置文件路径
所以 ngx_conf_file 在这里还是 null
进入 else 中,将 默认的配置文件路径设置给 cycle->conf_file 进行管理
接下来是:
if (ngx_conf_full_name(cycle, &cycle->conf_file, 0) != NGX_OK) {return NGX_ERROR;}
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_conf_full_name 函数-CSDN博客
cycle->conf_file 保存的配置文件路径可能是 相对路径,需要 调用 ngx_conf_full_name 来拼接路径前缀 然后形成完成的路径
进入 ngx_conf_full_name 中
ngx_int_t
ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name, ngx_uint_t conf_prefix)
{ngx_str_t *prefix;prefix = conf_prefix ? &cycle->conf_prefix : &cycle->prefix;return ngx_get_full_name(cycle->pool, prefix, name);
}
这里传进来的 conf_prefix 的值是 0
所以 prefix = &cycle->prefix; 选择 cycle->prefix (/usr/local/nginx/) 作为配置文件路径前缀
然后调用 ngx_get_full_name 来拼接路径
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_get_full_name 函数-CSDN博客
进入 ngx_get_full_name
ngx_int_t
ngx_get_full_name(ngx_pool_t *pool, ngx_str_t *prefix, ngx_str_t *name)
{size_t len;u_char *p, *n;ngx_int_t rc;rc = ngx_test_full_name(name);if (rc == NGX_OK) {return rc;}
调用 ngx_test_full_name 来判断 name (/home/wsd/桌面/nginx/conf/nginx.conf) 是否是完整的路径
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_test_full_name-CSDN博客
进入 ngx_test_full_name
static ngx_int_t
ngx_test_full_name(ngx_str_t *name)
{
#if (NGX_WIN32)u_char c0, c1;c0 = name->data[0];if (name->len < 2) {if (c0 == '/') {return 2;}return NGX_DECLINED;}c1 = name->data[1];if (c1 == ':') {c0 |= 0x20;if ((c0 >= 'a' && c0 <= 'z')) {return NGX_OK;}return NGX_DECLINED;}if (c1 == '/') {return NGX_OK;}if (c0 == '/') {return 2;}return NGX_DECLINED;#elseif (name->data[0] == '/') {return NGX_OK;}return NGX_DECLINED;#endif
}
当前环境是 Ubuntu #if (NGX_WIN32) 不成立
所以接下来执行的是:
if (name->data[0] == '/') {return NGX_OK;}return NGX_DECLINED;
条件成立 name 的第一个字符是 /
被认为是 绝对路径
返回 NGX_OK
回到 ngx_get_full_name
rc = ngx_test_full_name(name);if (rc == NGX_OK) {return rc;}
rc 得到的返回值是 NGX_OK
条件成立,把这个 NGX_OK 返回到 ngx_conf_full_name
return ngx_get_full_name(cycle->pool, prefix, name);
这个结果继续向上返回到 ngx_process_options
回到 ngx_process_options
接下来是:
if (ngx_conf_full_name(cycle, &cycle->conf_file, 0) != NGX_OK) {return NGX_ERROR;}
于是 这里 调用 ngx_conf_full_name 得到 返回值 NGX_OK
条件不成立
接下来是:
for (p = cycle->conf_file.data + cycle->conf_file.len - 1;p > cycle->conf_file.data;p--){if (ngx_path_separator(*p)) {cycle->conf_prefix.len = p - cycle->conf_file.data + 1;cycle->conf_prefix.data = cycle->conf_file.data;break;}}
cycle->conf_file.data 是配置文件路径,
cycle->conf_file.data + cycle->conf_file.len - 1 指向这个路径字符串的最后一个字节
从最后一个字符向第一个字符遍历
if (ngx_path_separator(*p)) { 判断当前这个字符是否是分隔符 /
这个 分隔符后面是 配置文件的文件名,前面是配置文件的目录
将 cycle->conf_file.data 的地址赋给cycle->conf_prefix.data
从这个地址开始 len 个字符是 配置文件所在的目录
于是
cycle->conf_file.data(0x5bfbfeaf3e50) = cycle->conf_prefix.data(0x5bfbfeaf3e50)
这2个地址一样
cycle->conf_file.len=38
cycle->conf_file.data=/home/wsd/桌面/nginx/conf/nginx.conf
cycle->conf_prefix.len=28
cycle->conf_prefix.data=/home/wsd/桌面/nginx/conf/
就是 cycle->conf_file.data 的前 28 个字节
接下来是:
if (ngx_error_log) {cycle->error_log.len = ngx_strlen(ngx_error_log);cycle->error_log.data = ngx_error_log;} else {ngx_str_set(&cycle->error_log, NGX_ERROR_LOG_PATH);}
当前 ngx_error_log=null 还没有设置它
所以进入 else
把 默认值 NGX_ERROR_LOG_PATH(/home/wsd/桌面/nginx/LOG/error.log)
设置给 cycle->error_log
接下来是:
if (ngx_conf_params) {cycle->conf_param.len = ngx_strlen(ngx_conf_params);cycle->conf_param.data = ngx_conf_params;}
ngx_conf_params 没有设置是 null
所以跳过这段代码
if (ngx_test_config) {cycle->log->log_level = NGX_LOG_INFO;}
ngx_test_config 是 0
所以跳过这段代码
return NGX_OK;
返回结果
回到 main 函数中
接下来是:
if (ngx_os_init(log) != NGX_OK) {return 1;}
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_os_init 函数-CSDN博客
进入 ngx_os_init
ngx_int_t
ngx_os_init(ngx_log_t *log)
{ngx_time_t *tp;ngx_uint_t n;
#if (NGX_HAVE_LEVEL1_DCACHE_LINESIZE)long size;
#endif#if (NGX_HAVE_OS_SPECIFIC_INIT)if (ngx_os_specific_init(log) != NGX_OK) {return NGX_ERROR;}
#endif
调用 ngx_os_specific_init 函数
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_os_specific_init函数-CSDN博客
进入 ngx_os_specific_init
ngx_int_t
ngx_os_specific_init(ngx_log_t *log)
{struct utsname u;if (uname(&u) == -1) {ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "uname() failed");return NGX_ERROR;}
调用 uname 获取系统信息
这次获得的系统信息如下:
// 操作系统名称
u.sysname=Linux
//主机名
u.nodename=wsd-vm//操作系统发行版本
u.release=6.8.0-52-generic// 操作系统版本信息
u.version=#53~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Wed Jan 15 19:18:46 UTC 2// 硬件架构
u.machine=x86_64
接下来是:
(void) ngx_cpystrn(ngx_linux_kern_ostype, (u_char *) u.sysname,sizeof(ngx_linux_kern_ostype));(void) ngx_cpystrn(ngx_linux_kern_osrelease, (u_char *) u.release,sizeof(ngx_linux_kern_osrelease));
复制 u.sysname 和 u.release 存储到全局变量 ngx_linux_kern_ostype 和 ngx_linux_kern_osrelease
ngx_os_io = ngx_linux_io;return NGX_OK;
设置 ngx_os_io
然后返回结果
回到 ngx_os_init
接下来是:
if (ngx_init_setproctitle(log) != NGX_OK) {return NGX_ERROR;}
调用 ngx_init_setproctitle
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_init_setproctitle函数-CSDN博客
进入 ngx_init_setproctitle
ngx_int_t
ngx_init_setproctitle(ngx_log_t *log)
{u_char *p;size_t size;ngx_uint_t i;size = 0;for (i = 0; environ[i]; i++) {size += ngx_strlen(environ[i]) + 1;}
遍历 环境变量
ngx_strlen(environ[i]) + 1;
是当前这个环境变量占用的内存字节数量(字符串长度 + 字符串结束标志 '\0')
累加到 size 中,最终 size 的值是整个环境变量占据的字节数量
这次的执行情况:
i=0
environ[0]="COLORTERM=truecolor"
ngx_strlen(environ[0]) + 1 =20
size=20i=1
environ[1]="LANGUAGE=zh_CN:en"
ngx_strlen(environ[1]) + 1 =18
size=38i=2
environ[2]="LC_ADDRESS=zh_CN.UTF-8"
ngx_strlen(environ[2]) + 1 =23
size=61i=3
environ[3]="LC_NAME=zh_CN.UTF-8"
ngx_strlen(environ[3]) + 1 =20
size=81i=4
environ[4]="LC_MONETARY=zh_CN.UTF-8"
ngx_strlen(environ[4]) + 1 =24
size=105i=5
environ[5]="XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.SUZ122"
ngx_strlen(environ[5]) + 1 =54
size=159i=6
environ[6]="LC_PAPER=zh_CN.UTF-8"
ngx_strlen(environ[6]) + 1 =21
size=180i=7
environ[7]="LANG=zh_CN.UTF-8"
ngx_strlen(environ[7]) + 1 =17
size=197i=8
environ[8]="LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:"
ngx_strlen(environ[8]) + 1 =1519
size=1716i=9
environ[9]="XDG_CURRENT_DESKTOP=ubuntu:GNOME"
ngx_strlen(environ[9]) + 1 =33
size=1749i=10
environ[10]="TERM=xterm-256color"
ngx_strlen(environ[10]) + 1 =20
size=1769i=11
environ[11]="LC_IDENTIFICATION=zh_CN.UTF-8"
ngx_strlen(environ[11]) + 1 =30
size=1799i=12
environ[12]="DISPLAY=:0"
ngx_strlen(environ[12]) + 1 =11
size=1810i=13
environ[13]="LC_TELEPHONE=zh_CN.UTF-8"
ngx_strlen(environ[13]) + 1 =25
size=1835i=14
environ[14]="LC_MEASUREMENT=zh_CN.UTF-8"
ngx_strlen(environ[14]) + 1 =27
size=1862i=15
environ[15]="LC_TIME=zh_CN.UTF-8"
ngx_strlen(environ[15]) + 1 =20
size=1882i=16
environ[16]="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
ngx_strlen(environ[16]) + 1 =76
size=1958i=17
environ[17]="LC_NUMERIC=zh_CN.UTF-8"
ngx_strlen(environ[17]) + 1 =23
size=1981i=18
environ[18]="MAIL=/var/mail/root"
ngx_strlen(environ[18]) + 1 =20
size=2001i=19
environ[19]="LOGNAME=root"
ngx_strlen(environ[19]) + 1 =13
size=2014i=20
environ[20]="USER=root"
ngx_strlen(environ[20]) + 1 =10
size=2024i=21
environ[21]="HOME=/root"
ngx_strlen(environ[21]) + 1 =11
size=2035i=22
environ[22]="SHELL=/bin/bash"
ngx_strlen(environ[22]) + 1 =16
size=2051i=23
environ[23]="SUDO_COMMAND=./nginx"
ngx_strlen(environ[23]) + 1 =21
size=2072i=24
environ[24]="SUDO_USER=wsd"
ngx_strlen(environ[24]) + 1 =14
size=2086i=25
environ[25]="SUDO_UID=1000"
ngx_strlen(environ[25]) + 1 =14
size=2100i=26
environ[26]="SUDO_GID=1000"
ngx_strlen(environ[26]) + 1 =14
size=2114
接下来是:
p = ngx_alloc(size, log);if (p == NULL) {return NGX_ERROR;}
为环境变量分配内存,环境变量将要迁移到新分配的内存中
此次运行的情况:
p = ngx_alloc(size=2114, log);
p=0x654273b10f20
接下来是:
ngx_os_argv_last = ngx_os_argv[0];
ngx_os_argv_last
将用于标记命令行参数可用的边界
ngx_os_argv[0]
是存储参数的那片内存的首地址
接下来是:
for (i = 0; ngx_os_argv[i]; i++) {if (ngx_os_argv_last == ngx_os_argv[i]) {ngx_os_argv_last = ngx_os_argv[i] + ngx_strlen(ngx_os_argv[i]) + 1;}}
循环,每次循环获取下一个参数的首地址,直到这个地址为 null,
null 是标记,意味着到此为止,后面没有更多的参数了
ngx_os_argv[i] 是参数首地址,ngx_strlen(ngx_os_argv[i]) 是参数的长度,最后再 + 1,就越过了这个参数的最后一个字节成了下一个参数的最后一个字节
所以下一次循环时 ngx_os_argv[i] 是下一个参数的首地址 等于 上一次循环被重新赋值的 ngx_os_argv_last
这样 ngx_os_argv_last
的值在不断的向后移动,直到它等于存贮参数的内存的最后一个字节的下一个字节(也是环境变量的第一个字节)
此次的执行情况是:
i=0
ngx_os_argv_last(0x7ffecc4747a6) == ngx_os_argv[0](0x7ffecc4747a6)
ngx_os_argv_last(0x7ffecc4747ae) = ngx_os_argv[0] + ngx_strlen(ngx_os_argv[0])(7) + 1;
因为此次运行时没有给命令行参数,所以 只有一个参数,也就是运行的程序名
接下来是:
for (i = 0; environ[i]; i++) {if (ngx_os_argv_last == environ[i]) {size = ngx_strlen(environ[i]) + 1;ngx_os_argv_last = environ[i] + size;ngx_cpystrn(p, (u_char *) environ[i], size);environ[i] = (char *) p;p += size;}}
现在 ngx_os_argv_last
等于环境变量的首地址
循环逻辑与上一个循环相同
ngx_os_argv_last
逐渐向后移
每次遍历还会将 环境变量依次迁移到新分配的内存
此次的运行情况:
i=0
ngx_os_argv_last(=0x7ffde3e967ae) == environ[0](=0x7ffde3e967ae)
size(=20) = ngx_strlen(environ[0])(=19) + 1;
ngx_os_argv_last(=0x7ffde3e967c2) = environ[0](=0x7ffde3e967ae) + size(=20);
ngx_cpystrn(0x62a807451f20, 0x7ffde3e967ae, 20);
environ[0](=0x62a807451f20) = (char *) p(=0x62a807451f20);
p(=0x62a807451f34) += size(=20);i=1
ngx_os_argv_last(=0x7ffde3e967c2) == environ[1](=0x7ffde3e967c2)
size(=18) = ngx_strlen(environ[1])(=17) + 1;
ngx_os_argv_last(=0x7ffde3e967d4) = environ[1](=0x7ffde3e967c2) + size(=18);
ngx_cpystrn(0x62a807451f34, 0x7ffde3e967c2, 18);
environ[1](=0x62a807451f34) = (char *) p(=0x62a807451f34);
p(=0x62a807451f46) += size(=18);i=2
ngx_os_argv_last(=0x7ffde3e967d4) == environ[2](=0x7ffde3e967d4)
size(=23) = ngx_strlen(environ[2])(=22) + 1;
ngx_os_argv_last(=0x7ffde3e967eb) = environ[2](=0x7ffde3e967d4) + size(=23);
ngx_cpystrn(0x62a807451f46, 0x7ffde3e967d4, 23);
environ[2](=0x62a807451f46) = (char *) p(=0x62a807451f46);
p(=0x62a807451f5d) += size(=23);i=3
ngx_os_argv_last(=0x7ffde3e967eb) == environ[3](=0x7ffde3e967eb)
size(=20) = ngx_strlen(environ[3])(=19) + 1;
ngx_os_argv_last(=0x7ffde3e967ff) = environ[3](=0x7ffde3e967eb) + size(=20);
ngx_cpystrn(0x62a807451f5d, 0x7ffde3e967eb, 20);
environ[3](=0x62a807451f5d) = (char *) p(=0x62a807451f5d);
p(=0x62a807451f71) += size(=20);i=4
ngx_os_argv_last(=0x7ffde3e967ff) == environ[4](=0x7ffde3e967ff)
size(=24) = ngx_strlen(environ[4])(=23) + 1;
ngx_os_argv_last(=0x7ffde3e96817) = environ[4](=0x7ffde3e967ff) + size(=24);
ngx_cpystrn(0x62a807451f71, 0x7ffde3e967ff, 24);
environ[4](=0x62a807451f71) = (char *) p(=0x62a807451f71);
p(=0x62a807451f89) += size(=24);i=5
ngx_os_argv_last(=0x7ffde3e96817) == environ[5](=0x7ffde3e96817)
size(=54) = ngx_strlen(environ[5])(=53) + 1;
ngx_os_argv_last(=0x7ffde3e9684d) = environ[5](=0x7ffde3e96817) + size(=54);
ngx_cpystrn(0x62a807451f89, 0x7ffde3e96817, 54);
environ[5](=0x62a807451f89) = (char *) p(=0x62a807451f89);
p(=0x62a807451fbf) += size(=54);i=6
ngx_os_argv_last(=0x7ffde3e9684d) == environ[6](=0x7ffde3e9684d)
size(=21) = ngx_strlen(environ[6])(=20) + 1;
ngx_os_argv_last(=0x7ffde3e96862) = environ[6](=0x7ffde3e9684d) + size(=21);
ngx_cpystrn(0x62a807451fbf, 0x7ffde3e9684d, 21);
environ[6](=0x62a807451fbf) = (char *) p(=0x62a807451fbf);
p(=0x62a807451fd4) += size(=21);i=7
ngx_os_argv_last(=0x7ffde3e96862) == environ[7](=0x7ffde3e96862)
size(=17) = ngx_strlen(environ[7])(=16) + 1;
ngx_os_argv_last(=0x7ffde3e96873) = environ[7](=0x7ffde3e96862) + size(=17);
ngx_cpystrn(0x62a807451fd4, 0x7ffde3e96862, 17);
environ[7](=0x62a807451fd4) = (char *) p(=0x62a807451fd4);
p(=0x62a807451fe5) += size(=17);i=8
ngx_os_argv_last(=0x7ffde3e96873) == environ[8](=0x7ffde3e96873)
size(=1519) = ngx_strlen(environ[8])(=1518) + 1;
ngx_os_argv_last(=0x7ffde3e96e62) = environ[8](=0x7ffde3e96873) + size(=1519);
ngx_cpystrn(0x62a807451fe5, 0x7ffde3e96873, 1519);
environ[8](=0x62a807451fe5) = (char *) p(=0x62a807451fe5);
p(=0x62a8074525d4) += size(=1519);i=9
ngx_os_argv_last(=0x7ffde3e96e62) == environ[9](=0x7ffde3e96e62)
size(=33) = ngx_strlen(environ[9])(=32) + 1;
ngx_os_argv_last(=0x7ffde3e96e83) = environ[9](=0x7ffde3e96e62) + size(=33);
ngx_cpystrn(0x62a8074525d4, 0x7ffde3e96e62, 33);
environ[9](=0x62a8074525d4) = (char *) p(=0x62a8074525d4);
p(=0x62a8074525f5) += size(=33);i=10
ngx_os_argv_last(=0x7ffde3e96e83) == environ[10](=0x7ffde3e96e83)
size(=20) = ngx_strlen(environ[10])(=19) + 1;
ngx_os_argv_last(=0x7ffde3e96e97) = environ[10](=0x7ffde3e96e83) + size(=20);
ngx_cpystrn(0x62a8074525f5, 0x7ffde3e96e83, 20);
environ[10](=0x62a8074525f5) = (char *) p(=0x62a8074525f5);
p(=0x62a807452609) += size(=20);i=11
ngx_os_argv_last(=0x7ffde3e96e97) == environ[11](=0x7ffde3e96e97)
size(=30) = ngx_strlen(environ[11])(=29) + 1;
ngx_os_argv_last(=0x7ffde3e96eb5) = environ[11](=0x7ffde3e96e97) + size(=30);
ngx_cpystrn(0x62a807452609, 0x7ffde3e96e97, 30);
environ[11](=0x62a807452609) = (char *) p(=0x62a807452609);
p(=0x62a807452627) += size(=30);i=12
ngx_os_argv_last(=0x7ffde3e96eb5) == environ[12](=0x7ffde3e96eb5)
size(=11) = ngx_strlen(environ[12])(=10) + 1;
ngx_os_argv_last(=0x7ffde3e96ec0) = environ[12](=0x7ffde3e96eb5) + size(=11);
ngx_cpystrn(0x62a807452627, 0x7ffde3e96eb5, 11);
environ[12](=0x62a807452627) = (char *) p(=0x62a807452627);
p(=0x62a807452632) += size(=11);i=13
ngx_os_argv_last(=0x7ffde3e96ec0) == environ[13](=0x7ffde3e96ec0)
size(=25) = ngx_strlen(environ[13])(=24) + 1;
ngx_os_argv_last(=0x7ffde3e96ed9) = environ[13](=0x7ffde3e96ec0) + size(=25);
ngx_cpystrn(0x62a807452632, 0x7ffde3e96ec0, 25);
environ[13](=0x62a807452632) = (char *) p(=0x62a807452632);
p(=0x62a80745264b) += size(=25);i=14
ngx_os_argv_last(=0x7ffde3e96ed9) == environ[14](=0x7ffde3e96ed9)
size(=27) = ngx_strlen(environ[14])(=26) + 1;
ngx_os_argv_last(=0x7ffde3e96ef4) = environ[14](=0x7ffde3e96ed9) + size(=27);
ngx_cpystrn(0x62a80745264b, 0x7ffde3e96ed9, 27);
environ[14](=0x62a80745264b) = (char *) p(=0x62a80745264b);
p(=0x62a807452666) += size(=27);i=15
ngx_os_argv_last(=0x7ffde3e96ef4) == environ[15](=0x7ffde3e96ef4)
size(=20) = ngx_strlen(environ[15])(=19) + 1;
ngx_os_argv_last(=0x7ffde3e96f08) = environ[15](=0x7ffde3e96ef4) + size(=20);
ngx_cpystrn(0x62a807452666, 0x7ffde3e96ef4, 20);
environ[15](=0x62a807452666) = (char *) p(=0x62a807452666);
p(=0x62a80745267a) += size(=20);i=16
ngx_os_argv_last(=0x7ffde3e96f08) == environ[16](=0x7ffde3e96f08)
size(=76) = ngx_strlen(environ[16])(=75) + 1;
ngx_os_argv_last(=0x7ffde3e96f54) = environ[16](=0x7ffde3e96f08) + size(=76);
ngx_cpystrn(0x62a80745267a, 0x7ffde3e96f08, 76);
environ[16](=0x62a80745267a) = (char *) p(=0x62a80745267a);
p(=0x62a8074526c6) += size(=76);i=17
ngx_os_argv_last(=0x7ffde3e96f54) == environ[17](=0x7ffde3e96f54)
size(=23) = ngx_strlen(environ[17])(=22) + 1;
ngx_os_argv_last(=0x7ffde3e96f6b) = environ[17](=0x7ffde3e96f54) + size(=23);
ngx_cpystrn(0x62a8074526c6, 0x7ffde3e96f54, 23);
environ[17](=0x62a8074526c6) = (char *) p(=0x62a8074526c6);
p(=0x62a8074526dd) += size(=23);i=18
ngx_os_argv_last(=0x7ffde3e96f6b) == environ[18](=0x7ffde3e96f6b)
size(=20) = ngx_strlen(environ[18])(=19) + 1;
ngx_os_argv_last(=0x7ffde3e96f7f) = environ[18](=0x7ffde3e96f6b) + size(=20);
ngx_cpystrn(0x62a8074526dd, 0x7ffde3e96f6b, 20);
environ[18](=0x62a8074526dd) = (char *) p(=0x62a8074526dd);
p(=0x62a8074526f1) += size(=20);i=19
ngx_os_argv_last(=0x7ffde3e96f7f) == environ[19](=0x7ffde3e96f7f)
size(=13) = ngx_strlen(environ[19])(=12) + 1;
ngx_os_argv_last(=0x7ffde3e96f8c) = environ[19](=0x7ffde3e96f7f) + size(=13);
ngx_cpystrn(0x62a8074526f1, 0x7ffde3e96f7f, 13);
environ[19](=0x62a8074526f1) = (char *) p(=0x62a8074526f1);
p(=0x62a8074526fe) += size(=13);i=20
ngx_os_argv_last(=0x7ffde3e96f8c) == environ[20](=0x7ffde3e96f8c)
size(=10) = ngx_strlen(environ[20])(=9) + 1;
ngx_os_argv_last(=0x7ffde3e96f96) = environ[20](=0x7ffde3e96f8c) + size(=10);
ngx_cpystrn(0x62a8074526fe, 0x7ffde3e96f8c, 10);
environ[20](=0x62a8074526fe) = (char *) p(=0x62a8074526fe);
p(=0x62a807452708) += size(=10);i=21
ngx_os_argv_last(=0x7ffde3e96f96) == environ[21](=0x7ffde3e96f96)
size(=11) = ngx_strlen(environ[21])(=10) + 1;
ngx_os_argv_last(=0x7ffde3e96fa1) = environ[21](=0x7ffde3e96f96) + size(=11);
ngx_cpystrn(0x62a807452708, 0x7ffde3e96f96, 11);
environ[21](=0x62a807452708) = (char *) p(=0x62a807452708);
p(=0x62a807452713) += size(=11);i=22
ngx_os_argv_last(=0x7ffde3e96fa1) == environ[22](=0x7ffde3e96fa1)
size(=16) = ngx_strlen(environ[22])(=15) + 1;
ngx_os_argv_last(=0x7ffde3e96fb1) = environ[22](=0x7ffde3e96fa1) + size(=16);
ngx_cpystrn(0x62a807452713, 0x7ffde3e96fa1, 16);
environ[22](=0x62a807452713) = (char *) p(=0x62a807452713);
p(=0x62a807452723) += size(=16);i=23
ngx_os_argv_last(=0x7ffde3e96fb1) == environ[23](=0x7ffde3e96fb1)
size(=21) = ngx_strlen(environ[23])(=20) + 1;
ngx_os_argv_last(=0x7ffde3e96fc6) = environ[23](=0x7ffde3e96fb1) + size(=21);
ngx_cpystrn(0x62a807452723, 0x7ffde3e96fb1, 21);
environ[23](=0x62a807452723) = (char *) p(=0x62a807452723);
p(=0x62a807452738) += size(=21);i=24
ngx_os_argv_last(=0x7ffde3e96fc6) == environ[24](=0x7ffde3e96fc6)
size(=14) = ngx_strlen(environ[24])(=13) + 1;
ngx_os_argv_last(=0x7ffde3e96fd4) = environ[24](=0x7ffde3e96fc6) + size(=14);
ngx_cpystrn(0x62a807452738, 0x7ffde3e96fc6, 14);
environ[24](=0x62a807452738) = (char *) p(=0x62a807452738);
p(=0x62a807452746) += size(=14);i=25
ngx_os_argv_last(=0x7ffde3e96fd4) == environ[25](=0x7ffde3e96fd4)
size(=14) = ngx_strlen(environ[25])(=13) + 1;
ngx_os_argv_last(=0x7ffde3e96fe2) = environ[25](=0x7ffde3e96fd4) + size(=14);
ngx_cpystrn(0x62a807452746, 0x7ffde3e96fd4, 14);
environ[25](=0x62a807452746) = (char *) p(=0x62a807452746);
p(=0x62a807452754) += size(=14);i=26
ngx_os_argv_last(=0x7ffde3e96fe2) == environ[26](=0x7ffde3e96fe2)
size(=14) = ngx_strlen(environ[26])(=13) + 1;
ngx_os_argv_last(=0x7ffde3e96ff0) = environ[26](=0x7ffde3e96fe2) + size(=14);
ngx_cpystrn(0x62a807452754, 0x7ffde3e96fe2, 14);
environ[26](=0x62a807452754) = (char *) p(=0x62a807452754);
p(=0x62a807452762) += size(=14);
接下来是:
ngx_os_argv_last--;return NGX_OK;
ngx_os_argv_last 的值 -1,指向原来存放环境变量的那片内存的最后一个字节
然后返回
回到 ngx_os_init
接下来是:
ngx_pagesize = getpagesize();
获取 当前系统一页的大小是多少字节
此次的情况
ngx_pagesize=4096
ngx_cacheline_size = NGX_CPU_CACHE_LINE;
存储 CPU 缓存行大小,默认值为 NGX_CPU_CACHE_LINE
目前的情况:
ngx_cacheline_size=64
for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ }
计算页面大小的对数(ngx_pagesize_shift
),即页面大小是 2 的多少次幂。
例如,4KB 的页面大小对应 ngx_pagesize_shift = 12
此次的情况:
ngx_pagesize_shift=12
if (ngx_ncpu == 0) {ngx_ncpu = sysconf(_SC_NPROCESSORS_ONLN);}
获取当前系统在线的 CPU 核心数
此次的情况是:
ngx_ncpu=2
if (ngx_ncpu < 1) {ngx_ncpu = 1;}
条件不成立,跳过这段代码
size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);if (size > 0) {ngx_cacheline_size = size;}
sysconf(_SC_LEVEL1_DCACHE_LINESIZE)
:获取一级缓存行大小
成功就更新 ngx_cacheline_size ,之前的值是默认值
此次的情况:
获取成功,但获得的值和默认值一样
ngx_cacheline_size=64
接下来是:
ngx_cpuinfo();
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_cpuinfo 函数-CSDN博客
接下来是:
if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {ngx_log_error(NGX_LOG_ALERT, log, errno,"getrlimit(RLIMIT_NOFILE) failed");return NGX_ERROR;}ngx_max_sockets = (ngx_int_t) rlmt.rlim_cur;
获取文件描述符数量的限制
此次的情况是:
ngx_max_sockets=1024
#if (NGX_HAVE_INHERITED_NONBLOCK || NGX_HAVE_ACCEPT4)ngx_inherited_nonblocking = 1;
#elsengx_inherited_nonblocking = 0;
#endif
ngx_inherited_nonblocking
:全局变量,指示当前环境是否支持继承非阻塞套接字
当前情况:
ngx_inherited_nonblocking=1
tp = ngx_timeofday();srandom(((unsigned) ngx_pid << 16) ^ tp->sec ^ tp->msec);return NGX_OK;
获取当前的时间缓存 用来 设置随机数种子
返回 NGX_OK
回到 main 函数中
接下来是:
if (ngx_crc32_table_init() != NGX_OK) {return 1;}
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_crc32_table_init 函数-CSDN博客
ngx_slab_sizes_init();
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_slab_sizes_init 函数-CSDN博客
进入 ngx_slab_sizes_init
void
ngx_slab_sizes_init(void)
{ngx_uint_t n;ngx_slab_max_size = ngx_pagesize / 2;ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t));for (n = ngx_slab_exact_size; n >>= 1; ngx_slab_exact_shift++) {/* void */}
}
此次的情况是:
ngx_slab_max_size=2048
ngx_slab_exact_size=64
ngx_slab_exact_shift=6
回到 main 函数中
接下来是:
if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {return 1;}
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_add_inherited_sockets函数-CSDN博客
进入 ngx_add_inherited_sockets
static ngx_int_t
ngx_add_inherited_sockets(ngx_cycle_t *cycle)
{u_char *p, *v, *inherited;ngx_int_t s;ngx_listening_t *ls;inherited = (u_char *) getenv(NGINX_VAR);
此次
inherited 是 null,没有需要继承的 套接字描述符
if (inherited == NULL) {return NGX_OK;}
返回 NGX_OK
回到 main 函数中
接下来是:
if (ngx_preinit_modules() != NGX_OK) {return 1;}
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_preinit_modules 函数-CSDN博客
进入 ngx_preinit_modules
ngx_int_t
ngx_preinit_modules(void)
{ngx_uint_t i;for (i = 0; ngx_modules[i]; i++) {ngx_modules[i]->index = i;ngx_modules[i]->name = ngx_module_names[i];}ngx_modules_n = i;ngx_max_module = ngx_modules_n + NGX_MAX_DYNAMIC_MODULES;return NGX_OK;
}
进入循环,遍历 ngx_modules 数组
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_modules-CSDN博客
为每个模块的 index 和 name 字段赋值
此次的情况是:
ngx_modules[0]->index = 0
ngx_modules[0]->name = ngx_core_module
ngx_modules[1]->index = 1
ngx_modules[1]->name = ngx_errlog_module
ngx_modules[2]->index = 2
ngx_modules[2]->name = ngx_conf_module
ngx_modules[3]->index = 3
ngx_modules[3]->name = ngx_openssl_module
ngx_modules[4]->index = 4
ngx_modules[4]->name = ngx_regex_module
ngx_modules[5]->index = 5
ngx_modules[5]->name = ngx_events_module
ngx_modules[6]->index = 6
ngx_modules[6]->name = ngx_event_core_module
ngx_modules[7]->index = 7
ngx_modules[7]->name = ngx_epoll_module
ngx_modules[8]->index = 8
ngx_modules[8]->name = ngx_http_module
ngx_modules[9]->index = 9
ngx_modules[9]->name = ngx_http_core_module
ngx_modules[10]->index = 10
ngx_modules[10]->name = ngx_http_log_module
ngx_modules[11]->index = 11
ngx_modules[11]->name = ngx_http_upstream_module
ngx_modules[12]->index = 12
ngx_modules[12]->name = ngx_http_v2_module
ngx_modules[13]->index = 13
ngx_modules[13]->name = ngx_http_static_module
ngx_modules[14]->index = 14
ngx_modules[14]->name = ngx_http_gzip_static_module
ngx_modules[15]->index = 15
ngx_modules[15]->name = ngx_http_autoindex_module
ngx_modules[16]->index = 16
ngx_modules[16]->name = ngx_http_index_module
ngx_modules[17]->index = 17
ngx_modules[17]->name = ngx_http_mirror_module
ngx_modules[18]->index = 18
ngx_modules[18]->name = ngx_http_try_files_module
ngx_modules[19]->index = 19
ngx_modules[19]->name = ngx_http_auth_basic_module
ngx_modules[20]->index = 20
ngx_modules[20]->name = ngx_http_access_module
ngx_modules[21]->index = 21
ngx_modules[21]->name = ngx_http_limit_conn_module
ngx_modules[22]->index = 22
ngx_modules[22]->name = ngx_http_limit_req_module
ngx_modules[23]->index = 23
ngx_modules[23]->name = ngx_http_geo_module
ngx_modules[24]->index = 24
ngx_modules[24]->name = ngx_http_map_module
ngx_modules[25]->index = 25
ngx_modules[25]->name = ngx_http_split_clients_module
ngx_modules[26]->index = 26
ngx_modules[26]->name = ngx_http_referer_module
ngx_modules[27]->index = 27
ngx_modules[27]->name = ngx_http_rewrite_module
ngx_modules[28]->index = 28
ngx_modules[28]->name = ngx_http_ssl_module
ngx_modules[29]->index = 29
ngx_modules[29]->name = ngx_http_proxy_module
ngx_modules[30]->index = 30
ngx_modules[30]->name = ngx_http_fastcgi_module
ngx_modules[31]->index = 31
ngx_modules[31]->name = ngx_http_uwsgi_module
ngx_modules[32]->index = 32
ngx_modules[32]->name = ngx_http_scgi_module
ngx_modules[33]->index = 33
ngx_modules[33]->name = ngx_http_grpc_module
ngx_modules[34]->index = 34
ngx_modules[34]->name = ngx_http_memcached_module
ngx_modules[35]->index = 35
ngx_modules[35]->name = ngx_http_empty_gif_module
ngx_modules[36]->index = 36
ngx_modules[36]->name = ngx_http_browser_module
ngx_modules[37]->index = 37
ngx_modules[37]->name = ngx_http_upstream_hash_module
ngx_modules[38]->index = 38
ngx_modules[38]->name = ngx_http_upstream_ip_hash_module
ngx_modules[39]->index = 39
ngx_modules[39]->name = ngx_http_upstream_least_conn_module
ngx_modules[40]->index = 40
ngx_modules[40]->name = ngx_http_upstream_random_module
ngx_modules[41]->index = 41
ngx_modules[41]->name = ngx_http_upstream_keepalive_module
ngx_modules[42]->index = 42
ngx_modules[42]->name = ngx_http_upstream_zone_module
ngx_modules[43]->index = 43
ngx_modules[43]->name = ngx_http_write_filter_module
ngx_modules[44]->index = 44
ngx_modules[44]->name = ngx_http_header_filter_module
ngx_modules[45]->index = 45
ngx_modules[45]->name = ngx_http_chunked_filter_module
ngx_modules[46]->index = 46
ngx_modules[46]->name = ngx_http_v2_filter_module
ngx_modules[47]->index = 47
ngx_modules[47]->name = ngx_http_range_header_filter_module
ngx_modules[48]->index = 48
ngx_modules[48]->name = ngx_http_gzip_filter_module
ngx_modules[49]->index = 49
ngx_modules[49]->name = ngx_http_postpone_filter_module
ngx_modules[50]->index = 50
ngx_modules[50]->name = ngx_http_ssi_filter_module
ngx_modules[51]->index = 51
ngx_modules[51]->name = ngx_http_charset_filter_module
ngx_modules[52]->index = 52
ngx_modules[52]->name = ngx_http_userid_filter_module
ngx_modules[53]->index = 53
ngx_modules[53]->name = ngx_http_headers_filter_module
ngx_modules[54]->index = 54
ngx_modules[54]->name = ngx_http_copy_filter_module
ngx_modules[55]->index = 55
ngx_modules[55]->name = ngx_http_range_body_filter_module
ngx_modules[56]->index = 56
ngx_modules[56]->name = ngx_http_not_modified_filter_module
ngx_modules[57]->index = 57
ngx_modules[57]->name = ngx_stream_module
ngx_modules[58]->index = 58
ngx_modules[58]->name = ngx_stream_core_module
ngx_modules[59]->index = 59
ngx_modules[59]->name = ngx_stream_log_module
ngx_modules[60]->index = 60
ngx_modules[60]->name = ngx_stream_proxy_module
ngx_modules[61]->index = 61
ngx_modules[61]->name = ngx_stream_upstream_module
ngx_modules[62]->index = 62
ngx_modules[62]->name = ngx_stream_write_filter_module
ngx_modules[63]->index = 63
ngx_modules[63]->name = ngx_stream_ssl_module
ngx_modules[64]->index = 64
ngx_modules[64]->name = ngx_stream_limit_conn_module
ngx_modules[65]->index = 65
ngx_modules[65]->name = ngx_stream_access_module
ngx_modules[66]->index = 66
ngx_modules[66]->name = ngx_stream_geo_module
ngx_modules[67]->index = 67
ngx_modules[67]->name = ngx_stream_map_module
ngx_modules[68]->index = 68
ngx_modules[68]->name = ngx_stream_split_clients_module
ngx_modules[69]->index = 69
ngx_modules[69]->name = ngx_stream_return_module
ngx_modules[70]->index = 70
ngx_modules[70]->name = ngx_stream_set_module
ngx_modules[71]->index = 71
ngx_modules[71]->name = ngx_stream_upstream_hash_module
ngx_modules[72]->index = 72
ngx_modules[72]->name = ngx_stream_upstream_least_conn_module
ngx_modules[73]->index = 73
ngx_modules[73]->name = ngx_stream_upstream_random_module
ngx_modules[74]->index = 74
ngx_modules[74]->name = ngx_stream_upstream_zone_module
ngx_modules_n 静态模块的总数
ngx_max_module 表示Nginx支持的最大模块数量
NGX_MAX_DYNAMIC_MODULES 动态加载模块的最大数量
ngx_modules_n=75
ngx_max_module(=203) = ngx_modules_n(75) + NGX_MAX_DYNAMIC_MODULES(128)
回到 main 函数中
接下来是:
cycle = ngx_init_cycle(&init_cycle);if (cycle == NULL) {if (ngx_test_config) {ngx_log_stderr(0, "configuration file %s test failed",init_cycle.conf_file.data);}return 1;}
调用 ngx_init_cycle
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_init_cycle 函数-CSDN博客
进入 ngx_init_cycle
ngx_cycle_t *
ngx_init_cycle(ngx_cycle_t *old_cycle)
{void *rv;char **senv;ngx_uint_t i, n;ngx_log_t *log;ngx_time_t *tp;ngx_conf_t conf;ngx_pool_t *pool;ngx_cycle_t *cycle, **old;ngx_shm_zone_t *shm_zone, *oshm_zone;ngx_list_part_t *part, *opart;ngx_open_file_t *file;ngx_listening_t *ls, *nls;ngx_core_conf_t *ccf, *old_ccf;ngx_core_module_t *module;char hostname[NGX_MAXHOSTNAMELEN];ngx_timezone_update();/* force localtime update with a new timezone */tp = ngx_timeofday();tp->sec = 0;ngx_time_update();
更新时间
log = old_cycle->log;
获取之前的 log 对象,暂时还是用它来记录 log
pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);if (pool == NULL) {return NULL;}pool->log = log;
创建一个新的内存池,然后设置它使用的 log
此次的情况是:
pool = ngx_create_pool(16384, log)
pool= 0x5f33e4db67b0 (地址)
接下来是:
cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t));if (cycle == NULL) {ngx_destroy_pool(pool);return NULL;}
此次的情况是:
cycle = ngx_pcalloc(pool, 648);
cycle= 0x5c67408e7800
cycle->pool = pool;cycle->log = log;cycle->old_cycle = old_cycle;
cycle->conf_prefix.len = old_cycle->conf_prefix.len;cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix);if (cycle->conf_prefix.data == NULL) {ngx_destroy_pool(pool);return NULL;}
给字段赋值
此次的情况是:
cycle->conf_prefix.len =28
cycle->conf_prefix.data=/home/wsd/桌面/nginx/conf/
cycle->prefix.len = old_cycle->prefix.len;cycle->prefix.data = ngx_pstrdup(pool, &old_cycle->prefix);if (cycle->prefix.data == NULL) {ngx_destroy_pool(pool);return NULL;}
此次的情况是:
cycle->prefix.len = 17)
cycle->prefix.data= /usr/local/nginx/
cycle->error_log.len = old_cycle->error_log.len;cycle->error_log.data = ngx_pnalloc(pool, old_cycle->error_log.len + 1);if (cycle->error_log.data == NULL) {ngx_destroy_pool(pool);return NULL;}ngx_cpystrn(cycle->error_log.data, old_cycle->error_log.data,old_cycle->error_log.len + 1);
此次的情况是:
cycle->error_log.len=36
cycle->error_log.data=/home/wsd/桌面/nginx/LOG/error.log
cycle->conf_file.len = old_cycle->conf_file.len;cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1);if (cycle->conf_file.data == NULL) {ngx_destroy_pool(pool);return NULL;}ngx_cpystrn(cycle->conf_file.data, old_cycle->conf_file.data,old_cycle->conf_file.len + 1);
此次的情况是:
cycle->conf_file.len=38
cycle->conf_file.data=/home/wsd/桌面/nginx/conf/nginx.conf
cycle->conf_param.len = old_cycle->conf_param.len;cycle->conf_param.data = ngx_pstrdup(pool, &old_cycle->conf_param);if (cycle->conf_param.data == NULL) {ngx_destroy_pool(pool);return NULL;}
此次的情况是:
cycle->conf_param.len=0
cycle->conf_param.data 是 null运行时没有给命令行参数
n = old_cycle->paths.nelts ? old_cycle->paths.nelts : 10;
此次的情况是:
n = 0 ? 0 : 10;
n=10
if (ngx_array_init(&cycle->paths, pool, n, sizeof(ngx_path_t *))!= NGX_OK){ngx_destroy_pool(pool);return NULL;}
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_array_init 函数-CSDN博客
ngx_memzero(cycle->paths.elts, n * sizeof(ngx_path_t *));
将 paths
数组的前 n
个元素清零
if (ngx_array_init(&cycle->config_dump, pool, 1, sizeof(ngx_conf_dump_t))!= NGX_OK){ngx_destroy_pool(pool);return NULL;}
初始化数组 config_dump
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_conf_dump_t-CSDN博客
ngx_rbtree_init(&cycle->config_dump_rbtree, &cycle->config_dump_sentinel,ngx_str_rbtree_insert_value);
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_rbtree_init-CSDN博客
if (old_cycle->open_files.part.nelts) {n = old_cycle->open_files.part.nelts;for (part = old_cycle->open_files.part.next; part; part = part->next) {n += part->nelts;}} else {n = 20;}
此次运行的情况是:
old_cycle->open_files.part.nelts=0
所以进入 else
n=20
if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t))!= NGX_OK){ngx_destroy_pool(pool);return NULL;}
初始化新周期的 open_files
列表。
此次运行的情况是:
ngx_list_init(&cycle->open_files, pool, n=20, sizeof(ngx_open_file_t)=40)
if (old_cycle->shared_memory.part.nelts) {n = old_cycle->shared_memory.part.nelts;for (part = old_cycle->shared_memory.part.next; part; part = part->next){n += part->nelts;}} else {n = 1;}
设置新周期的 shared_memory 的元素数量
此次运行的情况是:
old_cycle->shared_memory.part.nelts=0
所以 进入else
n=1
if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t))!= NGX_OK){ngx_destroy_pool(pool);return NULL;}
初始化 shared_memory
此次运行的情况是:
ngx_list_init(&cycle->shared_memory, pool, n=1, sizeof(ngx_shm_zone_t)=88 )
n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10;
此次运行的情况是:
n(=10) = old_cycle->listening.nelts(=0) ? old_cycle->listening.nelts(=0) : 10
if (ngx_array_init(&cycle->listening, pool, n, sizeof(ngx_listening_t))!= NGX_OK){ngx_destroy_pool(pool);return NULL;}
初始化 cycle->listening
数组
此次运行的情况是:
ngx_array_init(&cycle->listening, pool, n(=10), sizeof(ngx_listening_t)(=296))
ngx_memzero(cycle->listening.elts, n * sizeof(ngx_listening_t));
将 cycle->listening
的
内存区域清 0
ngx_queue_init(&cycle->reusable_connections_queue);
cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));if (cycle->conf_ctx == NULL) {ngx_destroy_pool(pool);return NULL;}
从内存池 pool 中分配一个指针数组 conf_ctx,每个元素对应一个模块的配置结构指针。
if (gethostname(hostname, NGX_MAXHOSTNAMELEN) == -1) {ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "gethostname() failed");ngx_destroy_pool(pool);return NULL;}
调用 gethostname()
系统调用获取本地主机名,存储到 hostname
缓冲区。
hostname[NGX_MAXHOSTNAMELEN - 1] = '\0';cycle->hostname.len = ngx_strlen(hostname);
确保 hostname
以 \0
结尾
计算主机名的实际长度(不含终止符),存储到 cycle->hostname.len
cycle->hostname.data = ngx_pnalloc(pool, cycle->hostname.len);if (cycle->hostname.data == NULL) {ngx_destroy_pool(pool);return NULL;}
从内存池 pool
分配内存,存储主机名的副本
ngx_strlow(cycle->hostname.data, (u_char *) hostname, cycle->hostname.len);
将主机名转换为全小写,存储到 cycle->hostname.data
此次运行的情况是:
hostname=wsd-vm
cycle->hostname.len=6
cycle->hostname.data = ngx_pnalloc(pool, 6)
cycle->hostname.data=0x6165d5b590d0 地址
cycle->hostname.data = wsd-vm
if (ngx_cycle_modules(cycle) != NGX_OK) {ngx_destroy_pool(pool);return NULL;}
调用 ngx_cycle_modules 初始化 cycle->modules 数组,该数组包含所有核心模块的指针
for (i = 0; cycle->modules[i]; i++) {if (cycle->modules[i]->type != NGX_CORE_MODULE) {continue;}
遍历所有核心模块
module = cycle->modules[i]->ctx;
获取核心模块的配置
相关文章:

Ubuntu 下 nginx-1.24.0 源码分析 (1)
main 函数在 src\core\nginx.c int ngx_cdecl main(int argc, char *const *argv) {ngx_buf_t *b;ngx_log_t *log;ngx_uint_t i;ngx_cycle_t *cycle, init_cycle;ngx_conf_dump_t *cd;ngx_core_conf_t *ccf;ngx_debug_init(); 进入 main 函数 最…...

2025数据存储技术风向标:解析数据湖与数据仓库的实战效能差距
一、技术演进的十字路口 当前全球数据量正以每年65%的复合增长率激增,IDC预测到2027年企业将面临日均处理500TB数据的挑战。在这样的背景下,传统数据仓库与新兴数据湖的博弈进入白热化阶段。Gartner最新报告显示,采用混合架构的企业数据运营效…...

探索高性能AI识别和边缘计算 | NVIDIA Jetson Orin Nano 8GB 开发套件的全面测评
随着边缘计算和人工智能技术的迅速发展,性能强大的嵌入式AI开发板成为开发者和企业关注的焦点。NVIDIA近期推出的Jetson Orin Nano 8GB开发套件,凭借其40 TOPS算力、高效的Ampere架构GPU以及出色的边缘AI能力,引起了广泛关注。本文将从配置性…...

数据结构 常见的排序算法
🌻个人主页:路飞雪吖~ 🌠专栏:数据结构 目录 🌻个人主页:路飞雪吖~ 一、插入排序 🌟直接插入排序 🌟希尔排序 二、选择排序 🌟选择排序 🌟堆排序…...

ES索引知识
索引是数据的载体,存储了文档和映射的信息 索引是具有相同结构的文档的合集体。 设置索引,不仅仅是设置索引名字,还有索引的一些配置,比如:分片和副本,刷新频率,搜索结果的最大参数,…...

FreeRTOS第17篇:FreeRTOS链表实现细节05_MiniListItem_t:FreeRTOS内存优化
文/指尖动听知识库-星愿 文章为付费内容,商业行为,禁止私自转载及抄袭,违者必究!!! 文章专栏:深入FreeRTOS内核:从原理到实战的嵌入式开发指南 1 为什么需要迷你列表项? 在嵌入式系统中,内存资源极其宝贵。FreeRTOS为满足不同场景需求,设计了标准列表项(ListItem_…...

Golang | Gin(简洁版)
文章目录 安装使用RESTful API响应页面获取请求参数路由讲解中间件 安装使用 Gin 是一个 golang 的微框架,封装比较优雅,API 友好,源代码比较明确。具有快速灵活,容错方便等特点。其实对于 golang 而言,web 框架的依赖…...

RAG外挂知识库
目录 RAG的工作流程 python实现RAG 1.引入相关库及相关准备工作 函数 1. 加载并读取文档 2. 文档分割 3. embedding 4. 向集合中添加文档 5. 用户输入内容 6. 查询集合中的文档 7. 构建Prompt并生成答案 主流程 附录 函数解释 1. open() 函数语法 2.client.embe…...

Rust语言:开启高效编程之旅
目录 一、Rust 语言初相识 二、Rust 语言的独特魅力 2.1 内存安全:消除隐患的护盾 2.2 高性能:与 C/C++ 并肩的实力 2.3 强大的并发性:多线程编程的利器 2.4 跨平台性:适配多环境的优势 三、快速上手 Rust 3.1 环境搭建:为开发做准备 3.2 第一个 R…...

蓝桥杯备考:图论初解
1:图的定义 我们学了线性表和树的结构,那什么是图呢? 线性表是一个串一个是一对一的结构 树是一对多的,每个结点可以有多个孩子,但只能有一个父亲 而我们今天学的图!就是多对多的结构了 V表示的是图的顶点集…...

Codeforces Round 502 E. The Supersonic Rocket 凸包、kmp
题目链接 题目大意 平面上给定两个点集,判定两个点集分别形成的凸多边形能否通过旋转、平移重合。 点集大小 ≤ \leq ≤ 1 0 5 10^{5} 105,坐标范围 [0, 1 0 8 10^{8} 108 ]. 思路 题意很明显,先求出凸包再判断两凸包是否同构。这里用…...

机器人匹诺曹机制,真话假话平衡机制
摘要: 本文聚焦于机器人所采用的一种“匹诺曹机制”,该机制旨在以大概率保持“虚拟鼻子”(一种象征虚假程度的概念)不会过长,通过在对话中夹杂真话与假话来实现。文章深入探讨了这一机制的原理,分析其背后的…...

用Python分割并高效处理PDF大文件
在处理大型PDF文件时,将它们分解成更小、更易于管理的块通常是有益的。这个过程称为分区,它可以提高处理效率,并使分析或操作文档变得更容易。在本文中,我们将讨论如何使用Python和为Unstructured.io库将PDF文件划分为更小的部分。…...

【RAG】混合检索(Hybrid Search) 提高检索精度
1.问题:向量检索也易混淆,而关键字会更精准 在实际生产中,传统的关键字检索(稀疏表示)与向量检索(稠密表示)各有利弊。 举个具体例子,比如文档中包含很长的专有名词, 关…...

CTFHub-FastCGI协议/Redis协议
将木马进行base64编码 <?php eval($_GET[cmd]);?> 打开kali虚拟机,使用虚拟机中Gopherus-master工具 Gopherus-master工具安装 git clone https://github.com/tarunkant/Gopherus.git 进入工具目录 cd Gopherus 使用工具 python2 "位置" --expl…...

【算法day4】最长回文子串——动态规划方法
最长回文子串 给你一个字符串 s,找到 s 中最长的 回文 子串。 https://leetcode.cn/problems/longest-palindromic-substring/submissions/607962358/ 动态规划: 回文串即是从前面开始读和从后面开始读,读出来的字符串均相同的字符串&#…...

C++之“string”类的模拟实现
🌹个人主页🌹:喜欢草莓熊的bear 🌹专栏🌹:C入门 前言 hello ,大家又来跟着bear学习了。一起奔向更好的自己,上篇博客已经讲清楚了string的一些功能的使用。我们就实现一些主要的功…...

请谈谈 HTTP 中的安全策略,如何防范常见的Web攻击(如XSS、CSRF)?
一、Web安全核心防御机制 (一)XSS攻击防御(跨站脚本攻击) 1. 原理与分类 存储型XSS:恶意脚本被持久化存储在服务端(如数据库)反射型XSS:脚本通过URL参数或表单提交触发执行…...

Python Flask 渲染静态程动态页面
Python Flask 渲染静态程动态页面 Python Flask 渲染静态程动态页面 Python Flask 渲染静态程动态页面 对网页应用程序来说,静态内容是重要的,因为它们包括 CSS 和 JavaScript 文件。静态文件可以直接由网页服务器提供。如果我们在我们的项目中创建一个…...

Unity大型游戏开发全流程指南
一、开发流程与核心步骤 1. 项目规划与设计阶段 需求分析 明确游戏类型(MMORPG/开放世界/竞技等)、核心玩法(战斗/建造/社交)、目标平台(PC/移动/主机)示例:MMORPG需规划角色成长树、副本Boss…...

Unity场景制作
一、关于后处理效果 然后可在后处理组件中添加各种效果 ACES : 电影感的强对比效果 添加了ACES后场景明显变暗,所以可以提高曝光度 Post-exposure 二、添加雾效 在Window的项目栏中选择Render中的Lighting 在环境属性中的其他设置中可勾选雾效,为场景中添…...

PCIE接口
PCIE接口 PIC接口介绍PIC总线结构PCI总线特点PCI总线的主要性能PIC的历程 PCIE接口介绍PCIe接口总线位宽PCIE速率GT/s和Gbps区别PCIE带宽计算 PCIE架构PCIe体系结构端到端的差分数据传递PCIe总线的层次结构事务层数据链路层物理层PCIe层级结构及功能框图 PCIe链路初始化PCIe链路…...

Leetcode 3479. Fruits Into Baskets III
Leetcode 3479. Fruits Into Baskets III 1. 解题思路2. 代码实现 题目链接:3479. Fruits Into Baskets III 1. 解题思路 这一题思路本质上就是考察每一个水果被考察时找到第一个满足条件且未被使用的basket。 因此,我们只需要将basket按照其capacit…...

小程序 -- uni-app开发微信小程序环境搭建(HBuilder X+微信开发者工具)
目录 前言 一 软件部分 1. 微信开发者工具 2. HBuilder X 开发工具 二 配置部分 1. 关于 HBuilder X 配置 2. 关于 微信开发工具 配置 三 运行项目 1. 新建项目 2. 代码编写 3. 内置浏览器 编译 4. 配置小程序 AppID获取 注意 四 实现效果 前言 uni-app开发小程…...

深度学习PyTorch之13种模型精度评估公式及调用方法
深度学习pytorch之22种损失函数数学公式和代码定义 深度学习pytorch之19种优化算法(optimizer)解析 深度学习pytorch之4种归一化方法(Normalization)原理公式解析和参数使用 深度学习pytorch之简单方法自定义9类卷积即插即用 实时…...

《云原生监控体系构建实录:从Prometheus到Grafana的观测革命》
PrometheusGrafana部署配置 Prometheus安装 下载Prometheus服务端 Download | PrometheusAn open-source monitoring system with a dimensional data model, flexible query language, efficient time series database and modern alerting approach.https://prometheus.io/…...

GHCTF2025--Web
upload?SSTI! import os import refrom flask import Flask, request, jsonify,render_template_string,send_from_directory, abort,redirect from werkzeug.utils import secure_filename import os from werkzeug.utils import secure_filenameapp Flask(__name__)# 配置…...

NO.32十六届蓝桥杯备战|函数|库函数|自定义函数|实参|形参|传参(C++)
函数是什么 数学中我们其实就⻅过函数的概念,⽐如:⼀次函数 y kx b ,k和b都是常数,给⼀个任意的x ,就得到⼀个 y 值。其实在C/C语⾔中就引⼊了函数(function)的概念,有些翻译为&a…...

计算机视觉算法实战——老虎个体识别(主页有源码)
✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ 1. 领域介绍 老虎个体识别是计算机视觉中的一个重要应用领域,旨在通过分析老虎的独特条纹图案,自动识别和区…...

【移动WEB开发】rem适配布局
目录 1. rem基础 2.媒体查询 2.1 语法规范 2.2 媒体查询rem 2.3 引入资源(理解) 3. less基础 3.1 维护css的弊端 3.2 less介绍 3.3 less变量 3.4 less编译 3.5 less嵌套 3.6 less运算 4. rem适配方案 4.1 rem实际开发 4.2 技术使用 4.3 …...