Ubuntu 下 nginx-1.24.0 源码分析 - ngx_os_init 函数

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 的运行环境做好准备

代码逻辑分析

  1. 操作系统特定初始化

    • 如果定义了 NGX_HAVE_OS_SPECIFIC_INIT,调用 ngx_os_specific_init 进行特定操作系统的初始化。
    • 如果失败,直接返回错误。
  2. 进程标题初始化

    • 调用 ngx_init_setproctitle 初始化进程标题设置功能。
    • 如果失败,直接返回错误。
  3. 系统参数初始化

    • 获取系统页面大小(getpagesize)并计算页面大小的对数(ngx_pagesize_shift)。
    • 初始化 CPU 缓存行大小(ngx_cacheline_size),如果支持 _SC_LEVEL1_DCACHE_LINESIZE,则从系统获取缓存行大小。
  4. CPU 核心数初始化

    • 如果定义了 NGX_HAVE_SC_NPROCESSORS_ONLN,通过 sysconf(_SC_NPROCESSORS_ONLN) 获取 CPU 核心数。
    • 如果核心数小于 1,则默认设置为 1。
  5. CPU 信息初始化

    • 调用 ngx_cpuinfo 获取 CPU 相关信息。
  6. 文件描述符限制

    • 使用 getrlimit 获取当前进程的最大文件描述符限制。
    • 如果获取失败,记录错误日志并返回错误。
  7. 非阻塞套接字标志

    • 根据是否支持 NGX_HAVE_INHERITED_NONBLOCKNGX_HAVE_ACCEPT4,设置 ngx_inherited_nonblocking 标志。
  8. 随机数种子初始化

    • 使用当前进程 ID、时间戳(秒和毫秒)生成随机数种子。
  9. 返回成功

    • 如果所有初始化步骤都成功,返回 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_NONBLOCKNGX_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;
相关推荐
荣--1 小时前
一键部署不是为了省时间 —— 它是把"买来的 PaaS"变成"自己的平台"的拐点
运维·zabbix·工程化·一键部署·平台化·边界设计
江华森2 小时前
动手实战学 Docker — 从零到集群编排完全指南
运维
Avan_菜菜18 小时前
FRP 内网穿透完整实战:从 HTTP 映射到 HTTPS 自签代理
运维·nginx·https
SelectDB2 天前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
XIAOHEZIcode3 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220704 天前
如何搭建本地yum源(上)
运维
ping某5 天前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
大树887 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠7 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质7 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务