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

发布于:2025-02-25 ⋅ 阅读:(18) ⋅ 点赞:(0)

ngx_init_setproctitle

声明在 src/os/unix/ngx_setproctitle.h

ngx_int_t ngx_init_setproctitle(ngx_log_t *log);

定义在 src/os/unix/ngx_setproctitle.c

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;
    }
 
    p = ngx_alloc(size, log);
    if (p == NULL) {
        return NGX_ERROR;
    }
 
    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;
        }
    }
 
    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--;
 
    return NGX_OK;
}

ngx_init_setproctitle 是一个用于初始化进程标题(process title)修改功能的函数。

它的主要目的是通过重新分配和整理环境变量的存储空间,为后续修改进程标题腾出连续的内存区域

在类 Unix 系统中,每个进程都有一个标题(process title),通常显示在 pstop 命令中

动态修改进程标题,以便更好地描述进程的状态 

例如,主进程可能会显示为 master process,而工作进程可能会显示为 worker process

当一个进程启动时,操作系统会为该进程分配一块连续的内存区域,用于存储以下内容

命令行参数  环境变量

这些数据通常存储在进程地址空间的高地址区域(靠近栈的底部),并且是连续的

操作系统会将进程标题初始化为命令行参数的内容(即 argv[0] 及其后续参数)

例如,假设启动了一个进程:

$ ./my_program arg1 arg2

那么,操作系统的内存布局可能如下:

+-------------------+-------------------+-------------------+
| argv[0]           | argv[1]           | environ           |
+-------------------+-------------------+-------------------+
^                   ^                   ^
|                   |                   |
argv[0]             argv[1]             environ[0]

进程标题就是 argv[0] 的内容(即 "./my_program"

现在需要修改 argv[0] 的内容,修改后的内容如果比原来的内容需要的内存空间更多的话就会覆盖掉后面的内容,导致数据丢失,所以需要重新分配存储空间

主要逻辑 
原始内存布局:
+----------------+----------------+----------------+----------------+
| argv[0]        | argv[1]        | ... (argv)     | environ[0]     | environ[1] ...
+----------------+----------------+----------------+----------------+
↑                ↑                ↑                ↑
ngx_os_argv[0]   ngx_os_argv[1]   ...              environ[0]

迁移后布局:
+----------------+----------------+----------------+----------------+
| argv[0]        | argv[1]        | ... (argv)     | [空闲区域]     | 
+----------------+----------------+----------------+----------------+
                                                                    ↑
                                                          ngx_os_argv_last(可用的最后一个字节的位置) 
                                                    [空闲区域]原environ区域(现可安全覆盖)

新分配的内存块p:
+----------------+----------------+----------------+
| environ[0]     | environ[1]     | ... (environ)  |
+----------------+----------------+----------------+
↑                ↑
environ[0]新指向   environ[1]新指向

ngx_int_t
ngx_init_setproctitle(ngx_log_t *log)
{
    u_char      *p;
    size_t       size;
    ngx_uint_t   i;
 
    size = 0;
  • 作用 :定义变量并初始化。
    • p:指向新分配的内存区域,用于存储环境变量。
    • size:用于计算环境变量的总大小。
    • i:循环计数器。
    • 初始化 size 为 0,表示尚未计算任何环境变量的大小。
    for (i = 0; environ[i]; i++) {
        size += ngx_strlen(environ[i]) + 1;
    }

 

  • 作用 :遍历全局变量 environ(存储环境变量的数组),计算所有环境变量字符串的总长度。
    • environ[i]:当前环境变量字符串。
    • ngx_strlen(environ[i]) + 1:计算当前环境变量的长度,并加上终止符 \0 的长度。
    • 最终得到的 size 是所有环境变量占用的总字节数。

   

    p = ngx_alloc(size, log);
    if (p == NULL) {
        return NGX_ERROR;
    }
  • 作用 :分配一块连续的内存空间,大小为 size
    • 如果分配失败,返回错误码 NGX_ERROR,表示初始化失败。
    ngx_os_argv_last = ngx_os_argv[0];

 

  • 作用 :初始化 ngx_os_argv_last,指向命令行参数的第一个元素。
    • ngx_os_argv 是存储命令行参数的数组。
    • ngx_os_argv_last 将用于标记命令行参数和环境变量的末尾地址
    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;
        }
    }

 

  • 作用 :找到命令行参数的末尾地址
    • 遍历 ngx_os_argv 数组,逐个检查每个命令行参数
    • 每次更新 ngx_os_argv_last 为当前参数的末尾地址(包括终止符 \0
    • 这一步确保 ngx_os_argv_last 指向命令行参数的最后一个有效字符之后的位置
    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;
        }
    }

 

  • 作用 :将环境变量复制到新分配的内存区域。
    • 遍历 environ 数组,逐个处理每个环境变量。
    • 如果当前环境变量的地址等于 ngx_os_argv_last,说明它位于命令行参数的末尾。
    • 计算当前环境变量的长度(包括终止符 \0)。
    • 使用 ngx_cpystrn 将环境变量复制到新内存区域。
    • 更新 environ[i] 指向新内存中的位置。
    • 移动指针 p 到下一个可用位置。
    ngx_os_argv_last--;

 

  • 作用 :调整 ngx_os_argv_last 的位置。
    • ngx_os_argv_last 回退一个字节,使其指向最后一个有效字符的位置。
    • 这是为了确保后续修改进程标题时,不会覆盖无效的内存区域。
    return NGX_OK;
}

 作用 :返回成功状态。

  • 如果所有操作成功完成,返回 NGX_OK,表示初始化成功