nginx 进程名
使用 nginx 的过程中,我们经常看到 nginx 的进程名是不同的,如下:
$ ps -ef |grep nginx
smaug 1183 1115 0 05:46 pts/2 00:00:00 grep --color=auto nginx
root 14201 1 0 2019 ? 00:00:00 nginx: master process ./sbin/nginx
nobody 28887 14201 0 Oct14 ? 00:00:00 nginx: worker process
nobody 28888 14201 0 Oct14 ? 00:00:00 nginx: worker process
可以看到 nginx 的进程名是不同的,那么它是怎么做到的呢?
argv[0]
首先来看一下 C 语言中的 main 函数的定义:
int main(int argc, char *argv[]);
这个应该大家都是比较熟悉的,argc 表示命令行参数个数, argv 保存了各个命令行参数的内容。其中 argv[0]
表示的是进程的名字,这就是修改进程名的关键点所在。
只需要修改 argv[0] 的值即可完成修改进程名。
hello world
下面以程序员经典入门代码为例说明:
// filename: hello_world_setproctitle.cc
// build: g++ hello_world_setproctitle.cc -o hello_world_setproctitle
#include <cstdio>
#include <cstring>
int main(int argc, char *argv[]) {
printf("hello world\n");
while (true) {
// block here
char c = getchar();
}
return 0;
}
编译运行:
g++ hello_world_setproctitle.cc -o hello_world_setproctitle
./hello_world_setproctitle
查看一下进程名:
# ps -ef |grep hello_world
root 26356 12289 0 14:17 pts/3 00:00:00 ./hello_world_setproctitle
root 26366 20422 0 14:18 pts/1 00:00:00 grep --color=auto hello_world
可以看到进程名是 hello_world_setproctitle
,接下来我们修改一下 argv[0] 的值,代码如下:
// filename: hello_world_setproctitle.cc
// build: g++ hello_world_setproctitle.cc -o hello_world_setproctitle
#include <cstdio>
#include <cstring>
int main(int argc, char *argv[]) {
// new process name
const char* new_name = "new_new_hello_world_setproctitle";
strcpy(argv[0], new_name);
printf("hello world\n");
while (true) {
// block here
char c = getchar();
}
return 0;
}
编译运行之后,查看进程名:
# ps -ef |grep hello_world
root 26750 12289 0 14:23 pts/3 00:00:00 new_new_hello_world_setproctitle
root 26754 20422 0 14:23 pts/1 00:00:00 grep --color=auto hello_world
可以看到进程名已经修改为 new_new_hello_world_setproctitle
了。
是不是很简单?
不过上面的代码是有一定的风险的,如果新的进程名超过了原来 argv[0] 的长度,就可能会影响到后面的 environ 的内容。
环境变量 environ
C 语言中 main 函数的定义还有一个:
int main(int argc, char *argv[], char *envp[]);
这个版本提供了第三个参数,大多数 Unix 系统支持,但是 POSIX.1 不建议这么做,如果要访问环境变量建议使用 getenv
和 putenv
接口。这里就不展开讲了。
envp 这个参数表示环境变量,每一个进程都有与之相关的环境变量,其中每个字符串都以(name=value)形式定义,并且 envp 的地址紧跟在 argv 之后。
hello world environ
接下来我们打印一下 envp 这个参数的值,基于上面的代码,简单修改一下:
// filename: hello_world_setproctitle.cc
// build: g++ hello_world_setproctitle.cc -o hello_world_setproctitle
#include <cstdio>
#include <cstring>
int main(int argc, char *argv[], char *envp[]) {
int i;
for (i = 0; i < argc; ++i) {
printf("mem:%p len:%d argv[%d]: %s\n", argv[i], strlen(argv[i]), i, argv[i]);
}
printf("\n");
for (i = 0; envp[i] != NULL; ++i) {
printf("mem:%p len:%d envp[%d]: %s\n", envp[i], strlen(envp[i]), i, envp[i]);
}
const char* new_name = "new_new_hello_world_setproctitle";
strcpy(argv[0], new_name);
printf("hello world\n");
while (true) {
// block here
char c = getchar();
}
return 0;
}
上面的代码同时也打印了每个参数的地址以及长度,编译并执行:
# ./hello_world_setproctitle 1 22
mem:0x7ffc84cf7544 len:26 argv[0]: ./hello_world_setproctitle
mem:0x7ffc84cf755f len:1 argv[1]: 1
mem:0x7ffc84cf7561 len:2 argv[2]: 22
mem:0x7ffc84cf7564 len:19 envp[0]: XDG_SESSION_ID=2554
mem:0x7ffc84cf7578 len:13 envp[1]: HOSTNAME=Jiao
mem:0x7ffc84cf7586 len:23 envp[2]: SELINUX_ROLE_REQUESTED=
mem:0x7ffc84cf759e len:19 envp[3]: TERM=xterm-256color
mem:0x7ffc84cf75b2 len:15 envp[4]: SHELL=/bin/bash
mem:0x7ffc84cf75c2 len:13 envp[5]: HISTSIZE=1000
mem:0x7ffc84cf75d0 len:31 envp[6]: SSH_CLIENT=192.168.1.3 61311 22
mem:0x7ffc84cf75f0 len:26 envp[7]: SELINUX_USE_CURRENT_RANGE=
mem:0x7ffc84cf760b len:75 envp[8]: ANDROID_NDK_TOOLCHAIN_ROOT=/root/smaug/software/android-ndk-r16b/toolchains
mem:0x7ffc84cf7657 len:12 envp[9]: OLDPWD=/root
mem:0x7ffc84cf7664