linux下修改进程名称 prctl


http://blog.chinaunix.net/uid-29482215-id-4120748.html

 

在编写网络服务器程序时,为了响应客户端的请求,我们经常需要新建进程来处理业务流程;而且又是为了关闭某个非法请求或者关闭长连接的客户端,这时就需要杀死进程 killall  proc_name。 但是在新建进程时,子进程名与父进程名相同。因此需要由进程名及参数来区分客户端连接。
   在linux中prctl可以满足这个要求,下满是man手册:
      PR_SET_NAME (since Linux 2.6.9)
              Set the process name for the calling process, using the value in
              the location pointed to by (char *) arg2.  The name can be up to
              16  bytes  long,  and  should  be null terminated if it contains
              fewer bytes.

    但是prctl修改的进程名,只能是16个字节(包括'\0')。下面是修改的代码(changetitle.c):

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <sys/prctl.h>
  3. int main(int argc, char *argv[], char *envp[])
  4. {
  5.     char *new_name = "abcdefghijklmnopqrstuvwxyz";
  6.     
  7.     getchar();
  8.     prctl(PR_SET_NAME, new_name);
  9.     getchar();
  10.     return 0;
  11. }
    当新名称长度大于16时就会截断,上面的新名字截断后是 abcdefghijklmno。这对于我们来说是有缺陷的。而且 通过ps -aux 查看,进程名称并没有改变,改变的只是/prco/$(PID)/stat和
/prco/$(PID)/status的值,而/prco/$(PID)/cmdline并没有改变。这种方式使用起来也是不方便的。
    下面介绍另一种方式,可以与上面的方式互补。
    首先看一下main函数的原型:int main(int argc, char *argv[]);
        argv[0]存放的是终端执行的程序名称也就是进程名。argv[1...argc-1]存放的是命令行参数。
        linux中main()还有一个隐藏参数就是环境变量信息,存放了运行时所需要的环境变量。
        我们可以通过以下来访问这个变量

点击(此处)折叠或打开

  1. extern char **environ;
      argv与environ是连续存放在栈区的。下面代码可以查看参数信息:

点击(此处)折叠或打开

#include<stdio.h>
extern char** environ;
int main(int argc, char *argv[])
{
        int i;
        for (i = 0; i < argc; ++i)
        {
                printf("argv[%d](0x%x):%s\n" , i , argv[i], argv[i]);
        }

        printf("\n\n");

        char** t = environ;
        for(i=0;environ[i];i++)
        {
                printf("environ[%d] (0x:%x) is %s \n", i, environ[i], environ[i]);
        }
        printf("i: %d \n",i);
        return 0;
}

  

(gdb) p argv[0]
$11 = 0x7fffffffe7d7 "/home/bwei/c/a.out"
(gdb) p argv[1]
$12 = 0x0
(gdb) p argv[2]
$13 = 0x7fffffffe7ea "build=/home/C3/platform/trunk/projects/stream_serv/build"
(gdb) p argv[3]
$14 = 0x7fffffffe823 "HOSTNAME=localhost.localdomain"
(gdb) p argv[4]
$15 = 0x7fffffffe842 "SHELL=/bin/bash"
(gdb) p argv[5]
$16 = 0x7fffffffe852 "TERM=xterm"

 

(gdb) p t
$7 = (char **) 0x7fffffffe598
(gdb) p t[0]
$8 = 0x7fffffffe7ea "build=/home/C3/platform/trunk/projects/stream_serv/build"

 

 

        通过上面可以看出,我们只需要修改argv[0]所指向的内存空间的内容,就可以修改进程名。但是如果新名称比argv[0]的长度小,我们可以直接修改,并把多余的部分请0,如果新名称
比argv[0]长我们需要两步:
            1、申请新内存保存环境变量信息和argv[1...argc-1]参数信息
            2、修改argv[0],将新名称往后到environ的最后一项清0
        以下是参考代码:

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdarg.h>
  4. #include <string.h>
  5. #include <stdlib.h>
  6. #include <sys/prctl.h>
  7. # define MAXLINE 2048
  8. extern char **environ;
  9. static char **g_main_Argv = NULL; /* pointer to argument vector */
  10. static char *g_main_LastArgv = NULL; /* end of argv */
  11. void setproctitle_init(int argc, char **argv, char **envp)
  12. {
  13.     int i;
  14.     for (i = 0; envp[i] != NULL; i++) // calc envp num
  15.         continue;
  16.     environ = (char **) malloc(sizeof (char *) * (i + 1)); // malloc envp pointer
  17.     
  18.     for (i = 0; envp[i] != NULL; i++)
  19.     {
  20.         environ[i] = malloc(sizeof(char) * strlen(envp[i]));
  21.         strcpy(environ[i], envp[i]);
  22.     }
  23.     environ[i] = NULL;
  24.     g_main_Argv = argv;
  25.     if (i > 0)
  26.       g_main_LastArgv = envp[i - 1] + strlen(envp[i - 1]);
  27.     else
  28.       g_main_LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
  29. }
  30. void setproctitle(const char *fmt, ...)
  31. {
  32.     char *p;
  33.     int i;
  34.     char buf[MAXLINE];
  35.     extern char **g_main_Argv;
  36.     extern char *g_main_LastArgv;
  37.     va_list ap;
  38.     p = buf;
  39.     va_start(ap, fmt);
  40.     vsprintf(p, fmt, ap);
  41.     va_end(ap);
  42.     i = strlen(buf);
  43.     if (i > g_main_LastArgv - g_main_Argv[0] - 2)
  44.     {
  45.         i = g_main_LastArgv - g_main_Argv[0] - 2;
  46.         buf[i] = '\0';
  47.     }
  48.     (void) strcpy(g_main_Argv[0], buf);
  49.     p = &g_main_Argv[0][i];
  50.     while (p < g_main_LastArgv)
  51.         *p++ = '\0';
  52.     g_main_Argv[1] = NULL;
  53.     prctl(PR_SET_NAME,buf);
  54. }
  55. int main(int argc, char *argv[])
  56. {
  57.     char argv_buf[MAXLINE] = {0}; // save argv paramters
  58.     for(int i = 1; i < argc; i++)
  59.     {
  60.         strcat(argv_buf, argv[i]);
  61.         strcat(argv_buf, " ");
  62.     }
  63.     setproctitle_init(argc, argv, environ);
  64.     setproctitle("%s@%s %s", "new_name", "ip", argv_buf);
  65.     for (int i = 0; environ[i] != NULL; i++)
  66.         free(environ[i]);
  67.     getchar();
  68.     return 0;
  69. }
      上面的代码使用了prctl和修改argv[0]两种修改方法的结合,通过ps -a 、 ps -ef  、ps -aux、 top 等等命令都只能查询到新进程名,/proc/$PID/ 下的文件也显示了新进程名的信息。
       应用场景:
         1、标识父子进程名称,防止被误杀
         2、构造假的进程名及参数,引导非法进入人员到蜜罐系统,取证


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM