先说说需求,原来的业务程序使用单进程多线程结构,守护使用的shell脚本,工作起来倒是没有什么大的问题。直到某处远程升级后出现小小的配置问题,进程运行就死掉,看门狗没人喂,守护脚本又判断不了出了什么问题,只是傻傻的重启。我只好想象着它一遍遍的重启,然后驱车300KM,到现场1分钟解决问题,白白花费了大半日功夫,一路风吹日晒。为了满足自己宅在办公室就能操控世界的伟大理想,我决定想个办法解决一下。最简单的就是自己fork来监控进程状态啦,为了保存旧的兼容性,脚本只允许一个同名进程,只好牺牲下自己,改个名欢快地跑起来。
网上传的最多的就是:
法一:
1
|
int prctl(PR_SET_NAME, name);
|
通过这个函数可以将当前进程的名称修改为 name 的内容。高高兴兴的改完,ps一下,名字还是一样的呢……经过一番搜索,/proc/$pid/stat等几个文件里面名字确实变了。某文章看到只有使用 ps -L 才能看到,达不到想要的效果。
法二:
父子进程管理,nginx一直做的很棒,它家的进程名字都自带小尾巴。代码中有名为set_proctitle函数用来修改进程名。一搜果然出来一些分析博文,linux的ps命令实际是以argv[0]处的值来作为进程的title的,因此只需要修改argv[0]处的值即可。原理如下图,一图胜千言。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
#include <stdarg.h>
#include <sys/prctl.h>
#define MAXLINE 2048
extern char **environ;
static char **g_main_argv = NULL; /* pointer to argument vector */
static char *g_main_last_argv = NULL; /* end of argv */
//备份原始变量,以免修改argv[0]的时候覆盖了environ变量
void setproctitle_init(int argc, char **argv, char **envp) {
int i;
//计算环境变量链表长度,重新申请空间,然后把变量字符串也拷贝过去
for(i = 0; envp[i] != NULL; i++) // calc envp num
continue;
environ = (char **) malloc(sizeof(char *) * (i + 1)); // malloc envp pointer
for(i = 0; envp[i] != NULL; i++) {
environ[i] = (char*)malloc(sizeof(char) * strlen(envp[i]));
strcpy(environ[i], envp[i]);
}
environ[i] = NULL;
g_main_argv = argv;
if(i > 0)
g_main_last_argv = envp[i - 1] + strlen(envp[i - 1]);
else
g_main_last_argv = argv[argc - 1] + strlen(argv[argc - 1]);
}
void setproctitle(const char *fmt, ...) {
char *p;
int i;
char buf[MAXLINE];
va_list ap;
p = buf;
va_start(ap, fmt);
vsprintf(p, fmt, ap);
va_end(ap);
i = strlen(buf);
if(i > g_main_last_argv - g_main_argv[0] - 2) {
i = g_main_last_argv - g_main_argv[0] - 2;
buf[i] = '\0';
}
(void) strcpy(g_main_argv[0], buf);
p = &g_main_argv[0][i];
while(p < g_main_last_argv)
*p++ = '\0';
g_main_argv[1] = NULL;
prctl(PR_SET_NAME, buf);
}
|
其实如果修改后的名字不比原来长,直接修改argv[0]也是可以的。在redis的源代码中还有一个实现,原理自然一样。翻阅一下,发现bsd库自带这个函数(“▔□▔) “)