当前位置

setproctitle

从 javaeye 的 robbin 那儿看到 rails 的这个技巧,觉得还是挺实用的,于是就下令在 web.py 上实现类似的功能。

经过 douyuan 同学的一番折腾,然后我也做了几个小实验,最后结论如下:

  • FreeBSD 下有 setproctitle(3) 函数来完成这个功能
  • Linux 对应的是系统调用 prctl(2), option 是 PR_SET_NAME, 宏定义是 15。注意 15 调用是从 2.6.9 引入的,虽然在 2.6.9 的内核头文件 linux/prctl.h 里没有 PR_SET_NAME 的宏定义,但仍然可以直接用 15 来成功调用... 换句话说,RHEL4/CentOS4的系统是可以利用这个特性的

对于 Linux 来说,prctl 并没有修改 argv/cmdline 的值,而是 /proc 文件系统里的内容(comm)。用 top 看是可以看到修改结果的,如果用 ps 命令,必须用参数 c 或者增加 output 格式 comm 才能看到修改结果,比如命令 ps axc (详情请参考 manual)

这样看在 FreeBSD or Linux 2.6.9+ 上用 ctypes 就可以搞定我想要的功能了,但对于更古老的 Linux 内核甚至别的操作系统怎么处理呢?看起来就只能去修改 argv[0] 的内容了,不过修改 argv[0] 也不是一件那么容易的事情,Linux 下运行例程:

  1. #include <unistd.h>
  2.  
  3. extern char **environ;
  4. int main(int argc, char *argv[])
  5. {
  6.     int i;
  7.     for (i = 0; i < argc; i++) {
  8.         printf("%p, %s\n", argv[i], argv[i]);
  9.     }
  10.     for (i = 0; environ[i]; i++) {
  11.         printf("%p, %s\n", environ[i], environ[i]);
  12.     }
  13. }

可以看到命令行参数,以及 environ 环境变量,是存储在一整块连续的内存上的(据说 Solaris 也是这样),如果向 argv[0] 里复制内容过长,有可能造成 environ 被破坏,进而对程序运行产生影响。正确的做法是先把 environ 内容复制出来,再修改 environ[] 数组里的指针地址到新的位置,然后就可以对 argv[0] 为所欲为了。nginx 的 os/unix/ngx_setproctitle.c 也是这么工作的。

换个角度,argv/environ 的这种存储方式使得如果知道所有 argv 的长度之和,从 environ[0] 向前偏移这个数字就是 argv[0] 的地址。这样看写一个 python extension 完成这个功能是可行的——虽然无法在模块里直接获得 argv[0] 地址,但可以取到 environ[0] 的地址,argv 的总长度则可以从 sys.argv 里计算出来然后作为参数传入... 这种方法潜在的问题也不少,比如在一个 python 进程里多次调用该怎么办,可似乎也只能这么来做了。

UPDATE: 发现 prctl 的一个问题是,它最多能设 15 字节长度的 comm, 这就导致可用性降低,也许最终还是要改 argv

Topic: