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;
相关推荐
努力的小T7 小时前
使用 Docker 部署 Apache Spark 集群教程
linux·运维·服务器·docker·容器·spark·云计算
枫叶落雨2228 小时前
08-Elasticsearch
运维·jenkins
爆更小小刘9 小时前
Linux下基本指令(4)
linux·运维·服务器
我码玄黄9 小时前
解决本地模拟IP的DHCP冲突问题
linux·运维
Self-Discipline10 小时前
Linux arm64 IOMMU总结
linux·运维·服务器
我言秋日胜春朝★10 小时前
【Linux】命名管道------Linux进程间通信的桥梁
linux·运维·服务器
Dontla10 小时前
华为昇腾服务器(固件版本查询、驱动版本查询、CANN版本查询)
运维·服务器·chrome
wenchun00110 小时前
【并发压测】高并发下Linux流量监控
linux·运维·服务器
从后端到QT10 小时前
ubuntu磁盘清理垃圾文件
linux·运维·ubuntu