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