1.需求
如nginx这种,父进程可以fork出多个子进程,程序在同一台机器上运行多次产生多个进程,多个进程使用不同的conf从而监听不同的端口。
这样,在ps和top时,会有区分父子进程名的需求而不是都叫nginx;对于多个nginx的父进程希望知道进程的具体任务,这可以用conf区分。就像nginx实际处理一样:
nginx: master process /usr/local/nginx/sbin/nginx
nginx: worker process。
24987 ubuntu 20 0 31088 388 300 S 0.0 0.0 0:00.00 threadbin
24988 ubuntu 20 0 31088 388 300 S 0.0 0.0 0:00.00 threadbin
24989 ubuntu 20 0 31088 388 300 S 0.0 0.0 0:00.00 threadbin
24990 ubuntu 20 0 31088 388 300 S 0.0 0.0 0:00.00 threadbin
看哪个进程在休眠或者等待事件、哪个进程自旋锁死锁一直cpu 100%、负载均衡哪些进程很闲哪些很忙、对于不同任务的线程哪一类任务占用cpu内存最多(涉及到调线程数)但看线程号很不直观。
2.方法
在查阅了网上,以及看了nginx源码后,方法:
prctl(PR_SET_NAME,"your_new_name",0,0,0)和设置argv[0]。
初步试一下:
top结果:
26424 ubuntu 20 0 4204 356 276 S 0.0 0.0 0:00.00 notchanged
26424 ubuntu 20 0 4204 356 276 S 0.0 0.0 0:00.00 yesImChanged
ps aux结果:
ubuntu 26547 0.0 0.0 4204 356 pts/6 S+ 10:57 0:00 ./notchanged
ubuntu 26547 0.0 0.0 4204 356 pts/6 S+ 10:57 0:00 y/notchanged
可见top和ps aux修改名称都生效了,但是top时prctl生效后结果,ps aux是修改argv[0]的结果。此外,如果ps auxc多加一个c那么就是prctl的结果;而prctl最多支持16字节名称;修改argv[0]过长会破坏环境变量。这些下面着重深究。
3.argv和环境变量的位置
argv和环境变量位置的关系,argv和环境变量在内存中是紧紧相连的,验证下:
输出:
argvs:
./a.out,address=0x7ffc575bb832,strlen=7,pdiff=0 //environ开始
xixi,address=0x7ffc575bb83a,strlen=4,pdiff=8 //pdiff是前一段strlen加字符串尾1,8=7+1
haha,address=0x7ffc575bb83f,strlen=4,pdiff=5 //同上
XDG_SESSION_ID=300,ppc address=0x7ffc575bb478,pc address=0x7ffc575bb844,strlen=18,pdiff=5 //environ开始,同上
TERM=xterm,ppc address=0x7ffc575bb480,pc address=0x7ffc575bb857,strlen=10,pdiff=19 //同上
SHELL=/bin/bash,ppc address=0x7ffc575bb488,pc address=0x7ffc575bb862,strlen=15,pdiff=11 //同上
HISTSIZE=1000,ppc address=0x7ffc575bb490,pc address=0x7ffc575bb872,strlen=13,pdiff=16 //同上
可见的确如此。并且通过pmap可以看到argv起始位置在栈区之后,就像《unix高级环境编程》说的一样。
4.ps和top是怎么获得文件名的
在我的ubuntu上 dpkg -S /bin/ps之后,apt-get source procps下载源码。
有几行:
proc_t *restrict const t;
t->cmdline = file2strvec(path, "cmdline"); //path是/proc/pid/或者/proc/pid/task/tid
......
{"cmd", "CMD", pr_args, sr_cmd, 27, ARG, DEC, PO|UNLIMITED},
......
endp += escaped_copy(endp, *pp->cmdline, OUTBUF_SIZE, &rightward);
......
char **cmdline, // (special) command line string vector (/proc/#/cmdline),cmdline定义
大约(因为理清某种c源代码的调用关系比较麻烦,这里是根据看到的局部现象推测)ps是从proc中读取到cmdline字符串再打印的。测试下:
cat /proc/15440/cmdline
y/notchanged
cat /proc/15440/task/15440/cmdline
y/notchanged
proc中的cmdline的确跟随argv[0]一起改了,ok,证据链闭环。
再看下top的,这下学聪明点了,直接一路grep COMMAND到底:
case P_CMD:
makeVAR(forest_display(q, p)); //调forest_display生成输出结果
......
const char *which = (CHKw(q, Show_CMDLIN)) ? *p->cmdline : p->cmd; //默认返回cmd而非cmdline
......
#define Show_CMDLIN 0x000080 // 'c' - show cmdline vs. name
......
char cmd[16]; // stat,status basename of executable file in call to exec(2),cmd定义
这里除了上面的cmdline(就是proc下cmdline,看现象是argv[0]),还有了个cmd,并且cmd是默认值。
函数stat2proc里有一行注释,Reads /proc/*/stat files, being careful not to trip over processes with,一行代码,memcpy(P->cmd, S, num);
测试下proc下的stat文件:
cat /proc/20528/stat
20528 (notchanged) S 15260 20528 15260 34831 20528 1077960704 189 0 0 0 0 0 0 0 20 0 1 0 672583548 4304896 89 18446744073709551615 4194304 4196588 140721140003936 140721140003208 139868578957600 0 0 0 0 1 0 0 17 0 0 0 0 0 0 6295056 6295648 13897728 140721140008994 140721140009017 140721140009017 140721140010987 0
cat /proc/20528/stat
20528 (yesImChanged) S 15260 20528 15260 34831 20528 1077960704 193 0 0 0 0 0 0 0 20 0 1 0 672583548 4304896 89 18446744073709551615 4194304 4196588 140721140003936 140721140003208 139868578957600 0 0 0 0 1 0 0 17 0 0 0 0 0 0 6295056 6295648 13897728 140721140008994 140721140009017 140721140009017 140721140010987 0
top的证据链也完毕了。
总之,默认情况下的ps和top都是从proc下获取命令名的,一个是从cmdline,一个从stat。而且cmdline似乎是被argv[0]影响,stat能给被prctl改变。
5.proc是如何更新cmdline和stat的
proc再linux内核代码fs/proc下。c文件名对应proc对应文件名,相当工整。
余下内容见下篇。
如nginx这种,父进程可以fork出多个子进程,程序在同一台机器上运行多次产生多个进程,多个进程使用不同的conf从而监听不同的端口。
这样,在ps和top时,会有区分父子进程名的需求而不是都叫nginx;对于多个nginx的父进程希望知道进程的具体任务,这可以用conf区分。就像nginx实际处理一样:
nginx: master process /usr/local/nginx/sbin/nginx
nginx: worker process。
同理,对于多线程也有修改名称的必要,如下一个最简单的线程top示例:
#include <cstdio>
#include <unistd.h>
#include <pthread.h>
void *func(void *arg){
sleep(60);
return NULL;
}
pthread_t p1,p2,p3;
int main(){
pthread_create(&p1,NULL,func,NULL);
pthread_create(&p2,NULL,func,NULL);
pthread_create(&p3,NULL,func,NULL);
sleep(65);
return 0;
}
24987 ubuntu 20 0 31088 388 300 S 0.0 0.0 0:00.00 threadbin
24988 ubuntu 20 0 31088 388 300 S 0.0 0.0 0:00.00 threadbin
24989 ubuntu 20 0 31088 388 300 S 0.0 0.0 0:00.00 threadbin
24990 ubuntu 20 0 31088 388 300 S 0.0 0.0 0:00.00 threadbin
看哪个进程在休眠或者等待事件、哪个进程自旋锁死锁一直cpu 100%、负载均衡哪些进程很闲哪些很忙、对于不同任务的线程哪一类任务占用cpu内存最多(涉及到调线程数)但看线程号很不直观。
2.方法
在查阅了网上,以及看了nginx源码后,方法:
prctl(PR_SET_NAME,"your_new_name",0,0,0)和设置argv[0]。
初步试一下:
#include <cstdio>
#include <errno.h>
#include <sys/prctl.h>
#include <unistd.h>
int main(int argc,char **argv){
printf("before argv[0]:%s\n",argv[0]);
sleep(20);
if(prctl(PR_SET_NAME,"yesImChanged",0,0,0) != 0) //用函数设置为yesImChanged
perror(NULL);
printf("changed done\n");
argv[0][0] = 'y'; //修改argv[0]第一个字节.为y
printf("after argv[0]:%s\n",argv[0]);
sleep(20);
}
top结果:
26424 ubuntu 20 0 4204 356 276 S 0.0 0.0 0:00.00 notchanged
26424 ubuntu 20 0 4204 356 276 S 0.0 0.0 0:00.00 yesImChanged
ps aux结果:
ubuntu 26547 0.0 0.0 4204 356 pts/6 S+ 10:57 0:00 ./notchanged
ubuntu 26547 0.0 0.0 4204 356 pts/6 S+ 10:57 0:00 y/notchanged
可见top和ps aux修改名称都生效了,但是top时prctl生效后结果,ps aux是修改argv[0]的结果。此外,如果ps auxc多加一个c那么就是prctl的结果;而prctl最多支持16字节名称;修改argv[0]过长会破坏环境变量。这些下面着重深究。
3.argv和环境变量的位置
argv和环境变量位置的关系,argv和环境变量在内存中是紧紧相连的,验证下:
#include <cstdio>
#include <cstring>
extern char **environ;
int main(int argc,char **argv){
char **ppc = environ;
char *lastp = argv[0];
int i = 0;
printf("argvs:\n");
while(i < argc){
printf("%s,address=%p,strlen=%d,pdiff=%d\n",argv[i],argv[i],strlen(argv[i]),(int)(argv[i] - lastp));
lastp = argv[i];
i++;
}
printf("\n");
while(*ppc != NULL){
printf("%s,ppc address=%p,pc address=%p,strlen=%d,pdiff=%d\n",*ppc,ppc,*ppc,strlen(*ppc),(int)(*ppc - lastp));
lastp = *ppc;
ppc++;
}
}
输出:
argvs:
./a.out,address=0x7ffc575bb832,strlen=7,pdiff=0 //environ开始
xixi,address=0x7ffc575bb83a,strlen=4,pdiff=8 //pdiff是前一段strlen加字符串尾1,8=7+1
haha,address=0x7ffc575bb83f,strlen=4,pdiff=5 //同上
XDG_SESSION_ID=300,ppc address=0x7ffc575bb478,pc address=0x7ffc575bb844,strlen=18,pdiff=5 //environ开始,同上
TERM=xterm,ppc address=0x7ffc575bb480,pc address=0x7ffc575bb857,strlen=10,pdiff=19 //同上
SHELL=/bin/bash,ppc address=0x7ffc575bb488,pc address=0x7ffc575bb862,strlen=15,pdiff=11 //同上
HISTSIZE=1000,ppc address=0x7ffc575bb490,pc address=0x7ffc575bb872,strlen=13,pdiff=16 //同上
可见的确如此。并且通过pmap可以看到argv起始位置在栈区之后,就像《unix高级环境编程》说的一样。
4.ps和top是怎么获得文件名的
在我的ubuntu上 dpkg -S /bin/ps之后,apt-get source procps下载源码。
有几行:
proc_t *restrict const t;
t->cmdline = file2strvec(path, "cmdline"); //path是/proc/pid/或者/proc/pid/task/tid
......
{"cmd", "CMD", pr_args, sr_cmd, 27, ARG, DEC, PO|UNLIMITED},
......
endp += escaped_copy(endp, *pp->cmdline, OUTBUF_SIZE, &rightward);
......
char **cmdline, // (special) command line string vector (/proc/#/cmdline),cmdline定义
大约(因为理清某种c源代码的调用关系比较麻烦,这里是根据看到的局部现象推测)ps是从proc中读取到cmdline字符串再打印的。测试下:
cat /proc/15440/cmdline
y/notchanged
cat /proc/15440/task/15440/cmdline
y/notchanged
proc中的cmdline的确跟随argv[0]一起改了,ok,证据链闭环。
再看下top的,这下学聪明点了,直接一路grep COMMAND到底:
case P_CMD:
makeVAR(forest_display(q, p)); //调forest_display生成输出结果
......
const char *which = (CHKw(q, Show_CMDLIN)) ? *p->cmdline : p->cmd; //默认返回cmd而非cmdline
......
#define Show_CMDLIN 0x000080 // 'c' - show cmdline vs. name
......
char cmd[16]; // stat,status basename of executable file in call to exec(2),cmd定义
这里除了上面的cmdline(就是proc下cmdline,看现象是argv[0]),还有了个cmd,并且cmd是默认值。
函数stat2proc里有一行注释,Reads /proc/*/stat files, being careful not to trip over processes with,一行代码,memcpy(P->cmd, S, num);
测试下proc下的stat文件:
cat /proc/20528/stat
20528 (notchanged) S 15260 20528 15260 34831 20528 1077960704 189 0 0 0 0 0 0 0 20 0 1 0 672583548 4304896 89 18446744073709551615 4194304 4196588 140721140003936 140721140003208 139868578957600 0 0 0 0 1 0 0 17 0 0 0 0 0 0 6295056 6295648 13897728 140721140008994 140721140009017 140721140009017 140721140010987 0
cat /proc/20528/stat
20528 (yesImChanged) S 15260 20528 15260 34831 20528 1077960704 193 0 0 0 0 0 0 0 20 0 1 0 672583548 4304896 89 18446744073709551615 4194304 4196588 140721140003936 140721140003208 139868578957600 0 0 0 0 1 0 0 17 0 0 0 0 0 0 6295056 6295648 13897728 140721140008994 140721140009017 140721140009017 140721140010987 0
top的证据链也完毕了。
总之,默认情况下的ps和top都是从proc下获取命令名的,一个是从cmdline,一个从stat。而且cmdline似乎是被argv[0]影响,stat能给被prctl改变。
5.proc是如何更新cmdline和stat的
proc再linux内核代码fs/proc下。c文件名对应proc对应文件名,相当工整。
余下内容见下篇。