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

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,表示初始化成功
相关推荐
HIT_Weston4 小时前
93、【Ubuntu】【Hugo】搭建私人博客:面包屑(一)
linux·运维·ubuntu
cuijiecheng20184 小时前
Linux下Beyond Compare过期
linux·运维·服务器
喵叔哟4 小时前
20.部署与运维
运维·docker·容器·.net
HIT_Weston5 小时前
92、【Ubuntu】【Hugo】搭建私人博客:侧边导航栏(六)
linux·运维·ubuntu
CodeAllen嵌入式5 小时前
Windows 11 本地安装 WSL 支持 Ubuntu 24.04 完整指南
linux·运维·ubuntu
RisunJan8 小时前
Linux命令-ipcs命令(报告进程间通信(IPC)设施状态的实用工具)
linux·运维·服务器
HABuo9 小时前
【Linux进程(四)】进程切换&环境变量深入剖析
linux·运维·服务器·c语言·c++·ubuntu·centos
橘颂TA9 小时前
【Linux】死锁四条件的底层逻辑:从锁冲突到 STL 组件的线程安全实践(Ⅵ)
linux·运维·服务器·c++·死锁
黄焖鸡能干四碗10 小时前
智能制造工业大数据应用及探索方案(PPT文件)
大数据·运维·人工智能·制造·需求分析