ngx_os_init
声明在 src/os/unix/ngx_os.h
ngx_int_t ngx_os_init(ngx_log_t *log);
定义在 src\os\unix\ngx_posix_init.c
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 if (ngx_init_setproctitle(log) != NGX_OK) { return NGX_ERROR; } ngx_pagesize = getpagesize(); ngx_cacheline_size = NGX_CPU_CACHE_LINE; for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ } #if (NGX_HAVE_SC_NPROCESSORS_ONLN) if (ngx_ncpu == 0) { ngx_ncpu = sysconf(_SC_NPROCESSORS_ONLN); } #endif if (ngx_ncpu < 1) { ngx_ncpu = 1; } #if (NGX_HAVE_LEVEL1_DCACHE_LINESIZE) size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE); if (size > 0) { ngx_cacheline_size = size; } #endif ngx_cpuinfo(); 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; #if (NGX_HAVE_INHERITED_NONBLOCK || NGX_HAVE_ACCEPT4) ngx_inherited_nonblocking = 1; #else ngx_inherited_nonblocking = 0; #endif tp = ngx_timeofday(); srandom(((unsigned) ngx_pid << 16) ^ tp->sec ^ tp->msec); return NGX_OK; }
初始化操作系统相关的参数和配置,为 Nginx 的运行环境做好准备
代码逻辑分析
操作系统特定初始化:
- 如果定义了
NGX_HAVE_OS_SPECIFIC_INIT
,调用ngx_os_specific_init
进行特定操作系统的初始化。- 如果失败,直接返回错误。
进程标题初始化:
- 调用
ngx_init_setproctitle
初始化进程标题设置功能。- 如果失败,直接返回错误。
系统参数初始化:
- 获取系统页面大小(
getpagesize
)并计算页面大小的对数(ngx_pagesize_shift
)。- 初始化 CPU 缓存行大小(
ngx_cacheline_size
),如果支持_SC_LEVEL1_DCACHE_LINESIZE
,则从系统获取缓存行大小。CPU 核心数初始化:
- 如果定义了
NGX_HAVE_SC_NPROCESSORS_ONLN
,通过sysconf(_SC_NPROCESSORS_ONLN)
获取 CPU 核心数。- 如果核心数小于 1,则默认设置为 1。
CPU 信息初始化:
- 调用
ngx_cpuinfo
获取 CPU 相关信息。文件描述符限制:
- 使用
getrlimit
获取当前进程的最大文件描述符限制。- 如果获取失败,记录错误日志并返回错误。
非阻塞套接字标志:
- 根据是否支持
NGX_HAVE_INHERITED_NONBLOCK
或NGX_HAVE_ACCEPT4
,设置ngx_inherited_nonblocking
标志。随机数种子初始化:
- 使用当前进程 ID、时间戳(秒和毫秒)生成随机数种子。
返回成功:
- 如果所有初始化步骤都成功,返回
NGX_OK
。详解
函数签名
ngx_int_t ngx_os_init(ngx_log_t *log)
参数:
ngx_log_t *log
:日志对象指针,用于记录错误或调试信息。返回值:
**
ngx_int_t
**Nginx 自定义的一个整数类型
NGX_OK
:表示函数执行成功,所有初始化步骤均顺利完成。
NGX_ERROR
:表示函数执行失败,某些初始化步骤未能完成
局部变量声明ngx_time_t *tp; ngx_uint_t n;
#if (NGX_HAVE_LEVEL1_DCACHE_LINESIZE)
long size;
#endif
tp
:指向时间结构体ngx_time_t
的指针,用于获取当前时间。
n
:无符号整数,用于计算页面大小的对数。
size
(条件编译):长整型变量,用于存储 CPU 缓存行大小。
NGX_HAVE_LEVEL1_DCACHE_LINESIZE
是 Nginx 源码中的一个宏,用于指示当前系统是否支持获取 CPU 一级缓存(L1 Data Cache)的缓存行大小
定义在 objs/ngx_auto_config.h 中
#ifndef NGX_HAVE_LEVEL1_DCACHE_LINESIZE #define NGX_HAVE_LEVEL1_DCACHE_LINESIZE 1 #endif
操作系统特定初始化
#if (NGX_HAVE_OS_SPECIFIC_INIT) if (ngx_os_specific_init(log) != NGX_OK) { return NGX_ERROR; } #endif
NGX_HAVE_OS_SPECIFIC_INIT
:- 这是一个宏,表示是否需要执行特定操作系统的初始化逻辑。
ngx_os_specific_init
:- 调用特定于操作系统的初始化函数。如果初始化失败,直接返回
NGX_ERROR
。- 意图:通过条件编译,支持不同操作系统的定制化初始化逻辑,增强跨平台兼容性。
NGX_HAVE_OS_SPECIFIC_INIT
定义在 src\os\unix\ngx_linux_config.h
#define NGX_HAVE_OS_SPECIFIC_INIT 1
使用 gcc -E 处理条件编译后,确认一下我当前 Ubuntu 环境下的实际情况
gcc -E src/os/unix/ngx_posix_init.c \ -I src/core \ -I src/event \ -I src/event/modules \ -I src/os/unix \ -I objs \ > ngx_posix_init_preprocessed.c
在输出文件中查找 ngx_os_init 函数
ngx_int_t ngx_os_init(ngx_log_t *log) { ngx_time_t *tp; ngx_uint_t n; long size; if (ngx_os_specific_init(log) != 0) { return -1; } if (ngx_init_setproctitle(log) != 0) { return -1; } ngx_pagesize = getpagesize(); ngx_cacheline_size = 64; for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { } if (ngx_ncpu == 0) { ngx_ncpu = sysconf( # 60 "src/os/unix/ngx_posix_init.c" 3 4 _SC_NPROCESSORS_ONLN # 60 "src/os/unix/ngx_posix_init.c" ); } if (ngx_ncpu < 1) { ngx_ncpu = 1; } size = sysconf( # 69 "src/os/unix/ngx_posix_init.c" 3 4 _SC_LEVEL1_DCACHE_LINESIZE # 69 "src/os/unix/ngx_posix_init.c" ); if (size > 0) { ngx_cacheline_size = size; } ngx_cpuinfo(); if (getrlimit( # 77 "src/os/unix/ngx_posix_init.c" 3 4 RLIMIT_NOFILE # 77 "src/os/unix/ngx_posix_init.c" , &rlmt) == -1) { if ((log)->log_level >= 2) ngx_log_error_core(2, log, # 78 "src/os/unix/ngx_posix_init.c" 3 4 (*__errno_location ()) # 78 "src/os/unix/ngx_posix_init.c" , "getrlimit(RLIMIT_NOFILE) failed") ; return -1; } ngx_max_sockets = (ngx_int_t) rlmt.rlim_cur; ngx_inherited_nonblocking = 1; tp = (ngx_time_t *) ngx_cached_time; srandom(((unsigned) ngx_pid << 16) ^ tp->sec ^ tp->msec); return 0; }
ngx_os_specific_init
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_os_specific_init函数-CSDN博客
进程标题初始化if (ngx_init_setproctitle(log) != NGX_OK) { return NGX_ERROR; }
ngx_init_setproctitle
:初始化进程标题设置功能,允许修改进程的命令行标题(如ps
命令中显示的内容)- 意图:方便管理员通过工具查看 Nginx 进程的状态(如主进程、工作进程等)
ngx_init_setproctitle
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_init_setproctitle函数-CSDN博客
系统页面大小初始化
ngx_pagesize = getpagesize(); ngx_cacheline_size = NGX_CPU_CACHE_LINE; for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ }
getpagesize()
:获取系统页面大小(单位为字节),例如 4KB。ngx_pagesize
:全局变量,存储页面大小。ngx_cacheline_size
:全局变量,存储 CPU 缓存行大小,默认值为NGX_CPU_CACHE_LINE
。for
循环 :计算页面大小的对数(ngx_pagesize_shift
),即页面大小是 2 的多少次幂。例如,4KB 的页面大小对应ngx_pagesize_shift = 12
。- 意图:页面大小和缓存行大小是性能优化的重要参数,直接影响内存分配和 CPU 缓存效率。
getpagesize
在 C 语言中,
getpagesize()
函数用于获取系统内存页的大小(以字节为单位)函数原型
int getpagesize(void);
返回值
- 返回值是一个整数,表示系统内存页的大小(以字节为单位)
- 例如,在许多现代系统上,返回值通常是 4096 字节(即 4KB)
需要包含以下头文件:
#include <unistd.h>
NGX_CPU_CACHE_LINE
定义在 objs/ngx_auto_config.h
#ifndef NGX_CPU_CACHE_LINE #define NGX_CPU_CACHE_LINE 64 #endif
CPU 核心数初始化
#if (NGX_HAVE_SC_NPROCESSORS_ONLN) if (ngx_ncpu == 0) { ngx_ncpu = sysconf(_SC_NPROCESSORS_ONLN); } #endif if (ngx_ncpu < 1) { ngx_ncpu = 1; }
sysconf(_SC_NPROCESSORS_ONLN)
:获取当前系统在线的 CPU 核心数。ngx_ncpu
:全局变量,存储 CPU 核心数。- 默认值处理:如果核心数小于 1,则设置为 1(防止异常情况)。
- 设计意图:根据 CPU 核心数动态调整工作线程的数量,充分利用多核优势。
NGX_HAVE_SC_NPROCESSORS_ONLN
定义在 objs/ngx_auto_config.h
#ifndef NGX_HAVE_SC_NPROCESSORS_ONLN #define NGX_HAVE_SC_NPROCESSORS_ONLN 1 #endif
用于指示当前系统是否支持通过
sysconf(_SC_NPROCESSORS_ONLN)
获取在线 CPU 核心数。它的定义与否取决于目标操作系统和硬件平台的特性不同的操作系统对
sysconf(_SC_NPROCESSORS_ONLN)
的支持情况不同:
- Linux :完全支持
sysconf(_SC_NPROCESSORS_ONLN)
,因此该宏通常会被定义。- Windows :Windows 不支持 POSIX 标准的
sysconf
函数,因此该宏不会被定义
sysconf
函数原型
long sysconf(int name);
返回值:
- 成功时,返回与
name
参数对应的系统配置值。- 如果发生错误(例如参数无效或功能不支持),返回
-1
。参数:
name
:指定要查询的系统配置选项。它是一个常量,表示不同的系统属性。
_SC_NPROCESSORS_ONLN
是其中一个常量,表示系统中当前在线的 CPU 核心数。
CPU 缓存行大小初始化#if (NGX_HAVE_LEVEL1_DCACHE_LINESIZE)
size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
if (size > 0) {
ngx_cacheline_size = size;
}
#endif
sysconf(_SC_LEVEL1_DCACHE_LINESIZE)
:获取一级缓存行大小。更新
ngx_cacheline_size
:如果获取到的值有效,则更新全局变量。意图:缓存行大小影响内存对齐和性能优化,动态获取可以适配不同的硬件架构。
NGX_HAVE_LEVEL1_DCACHE_LINESIZE
定义在 objs/ngx_auto_config.h
#ifndef NGX_HAVE_LEVEL1_DCACHE_LINESIZE #define NGX_HAVE_LEVEL1_DCACHE_LINESIZE 1 #endif
CPU 信息初始化
ngx_cpuinfo();
ngx_cpuinfo
:获取 CPU 的详细信息(如型号、特性等),并存储在全局变量中。
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;
getrlimit
:获取当前进程的最大文件描述符限制。rlmt
:存储限制值的结构体。ngx_max_sockets
:全局变量,存储最大文件描述符数量。- 错误处理 :如果获取失败,记录错误日志并返回
NGX_ERROR
。- 意图:文件描述符限制决定了 Nginx 能同时处理的最大连接数,合理设置可以避免资源耗尽。
getrlimit
getrlimit
是一个 POSIX 标准函数,用于获取当前进程的资源限制。它允许程序查询操作系统对某种资源的硬限制(hard limit)和软限制(soft limit)
函数原型
int getrlimit(int resource, struct rlimit *rlim);
返回值:
- 成功时返回
0
。- 失败时返回
-1
,并设置errno
表示错误原因。(1)
resource
参数
类型 :
int
含义:指定要查询的资源类型。
常用值:
RLIMIT_NOFILE
:表示文件描述符的最大数量(Number of Open Files)。- 其他可能的值包括:
RLIMIT_CPU
:CPU 时间限制。RLIMIT_DATA
:数据段大小限制。RLIMIT_STACK
:栈大小限制。意图:
- 在这里,
RLIMIT_NOFILE
被传递给getrlimit
,表示我们关心的是文件描述符的数量限制。(2)
rlim
参数
- 类型 :
struct rlimit *
- 含义 :指向一个
rlimit
结构体的指针,用于存储查询结果。
rlimit
结构体定义struct rlimit { rlim_t rlim_cur; // 软限制(Soft Limit) rlim_t rlim_max; // 硬限制(Hard Limit) };
rlim_cur
:
- 当前生效的限制值(软限制)。
- 进程可以动态调整软限制,但不能超过硬限制。
rlim_max
:
- 最大允许的限制值(硬限制)。
- 只有超级用户(root)才能修改硬限制。
意图
获取文件描述符限制
文件描述符是操作系统用于管理打开文件、套接字等资源的抽象句柄。
每个进程都有一个文件描述符表,其大小由
RLIMIT_NOFILE
决定。通过调用
getrlimit(RLIMIT_NOFILE, &rlmt)
,Nginx 获取当前进程的文件描述符限制:
rlmt.rlim_cur
:当前允许的最大文件描述符数量(软限制)。
rlmt.rlim_max
:理论上允许的最大文件描述符数量(硬限制)。
非阻塞套接字标志初始化#if (NGX_HAVE_INHERITED_NONBLOCK || NGX_HAVE_ACCEPT4)
ngx_inherited_nonblocking = 1;
#else
ngx_inherited_nonblocking = 0;
#endif
ngx_inherited_nonblocking
:全局变量,指示是否支持继承非阻塞套接字。条件编译 :根据是否支持
NGX_HAVE_INHERITED_NONBLOCK
或NGX_HAVE_ACCEPT4
设置标志。意图:非阻塞套接字是高并发网络编程的基础,确保套接字行为一致。
NGX_HAVE_INHERITED_NONBLOCK
含义:
- 表示当前系统是否支持继承非阻塞套接字(Inherited Non-blocking Sockets)。
- 如果定义了该宏,则表示系统允许父进程创建的套接字在子进程中保持非阻塞状态。
背景:
- 在传统的网络编程中,套接字的阻塞或非阻塞状态是由每个进程独立管理的。
- 如果系统支持继承非阻塞套接字,则父进程设置的非阻塞状态可以直接被子进程继承,而无需额外的系统调用。
优点:
- 减少了系统调用的开销,提高了性能。
- 简化了多进程模型中的套接字管理逻辑。
NGX_HAVE_ACCEPT4
含义:
- 表示当前系统是否支持
accept4
系统调用。accept4
是 Linux 内核 2.6.28 引入的一个扩展版本的accept
系统调用,允许在接收新连接时直接设置套接字选项(如非阻塞模式)。背景:
- 传统的
accept
系统调用仅返回一个新的套接字文件描述符,但无法直接设置套接字选项。- 使用
accept4
可以在接收连接的同时设置套接字为非阻塞模式或其他选项,从而减少额外的系统调用。优点:
提高了性能,减少了系统调用次数。
简化了代码逻辑,避免了在
accept
后手动调用fcntl
或其他函数来设置套接字选项。
随机数种子初始化tp = ngx_timeofday(); srandom(((unsigned) ngx_pid << 16) ^ tp->sec ^ tp->msec);
ngx_timeofday
:获取当前时间戳。
srandom
:设置随机数种子。种子生成公式 :
(进程 ID << 16) ^ 当前秒数 ^ 当前毫秒数
。意图:随机数种子用于生成唯一的随机数序列,避免每次运行时生成相同的随机数。
ngx_timeofday
定义在 src/core/ngx_times.h
#define ngx_timeofday() (ngx_time_t *) ngx_cached_time
srandom
:
srandom
是标准库函数,用于设置随机数生成器的种子。种子值决定了随机数序列的初始状态。如果种子值不同,生成的随机数序列也会不同。
返回成功return NGX_OK;
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_os_init 函数
若云止水2025-02-22 19:41
相关推荐
努力的小T7 小时前
使用 Docker 部署 Apache Spark 集群教程枫叶落雨2228 小时前
08-Elasticsearch爆更小小刘9 小时前
Linux下基本指令(4)我码玄黄9 小时前
解决本地模拟IP的DHCP冲突问题Self-Discipline10 小时前
Linux arm64 IOMMU总结我言秋日胜春朝★10 小时前
【Linux】命名管道------Linux进程间通信的桥梁Dontla10 小时前
华为昇腾服务器(固件版本查询、驱动版本查询、CANN版本查询)wenchun00110 小时前
【并发压测】高并发下Linux流量监控从后端到QT10 小时前
ubuntu磁盘清理垃圾文件