守護進程的創建-keepalived源碼解析

  在keepalived處理中,包含三個進程,keepalived進程以及兩個子進程check和vrrp。

    keepalived主要功能是:負責主進程的啓動和維護,全局配置文件的加載解析等。

    check主要功能是:負責healthcheck(健康檢查),包含了各種健康檢查方式,以及對應的配置解析(包括LVS的配置解析)

    vrrp主要功能是:VRRPD子進程, VRRPD協議子進程就是來實現VRRP協議的,以及對於配置解析(keepalived主配置文件)


  keepalived默認運行方式爲守護進程運行方式,這裏分析下keepalived中守護進程是如何創建的(core/daemon.c)。

pid_t
xdaemon(int nochdir, int noclose, int exitflag)
{
  pid_t pid;
  int ret;

  /* In case of fork is error. */
  /* 創建子進程 */
  pid = fork();
  if (pid < 0) {
    log_message(LOG_INFO, "xdaemon: fork error");
    return -1;
  }

  /* In case of this is parent process. */
  /* 退出父進程 */
  if (pid != 0) {
    if (!exitflag)
      exit(0);
    else
   return pid;
  }

  /* Become session leader and get pid. */
  /*  脫離原會話和進程組和控制終端,登陸新會話和進程組 */
  pid = setsid();
  if (pid < -1) {
    log_message(LOG_INFO, "xdaemon: setsid error");
    return -1; 
  }

  /* Change directory to root. */
  if (!nochdir) {
    /* 修改當前工作路徑 */
    ret = chdir("/");
    if (ret < 0) {
      log_message(LOG_INFO, "xdaemon: chdir error");
    }
  }

  /* File descriptor close. */
  /* 關閉文件描述符 */
  if (!noclose) {
    int fd;
    fd = open("/dev/null", O_RDWR, 0);
    if (fd != -1) {
      /*  賦值文件描述符,將標準輸入輸出重定向到空洞中    */
      dup2(fd, STDIN_FILENO);
      dup2(fd, STDOUT_FILENO);
      dup2(fd, STDERR_FILENO);
      if (fd > 2)
        close(fd);
    }
  }

  /* 重設文件權限掩碼 */
  umask(0);
  return 0;
}

  如上所示,爲keepalived中守護進程的創建方式,參照以上處理方式,守護進程創建可以分爲以下幾個步驟(在keepalived的處理中,省略了幾個)。

  首先,什麼是守護進程。守護進程就是獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。

  創建守護進程分爲以下幾個步驟:

  1、使進程在後臺運行

    創建子進程,退出父進程。

    在Linux中父進程先於子進程退出會造成子進程成爲孤兒進程,而每當系統發現一個孤兒進程是,就會自動由1號進程(init)收養它,這樣,原先的子進程就會變成init進程的子進程。


  2、脫離終端,登陸會話和進程組

    這個步驟是創建守護進程中最重要的一步。

    子進程在創建後,會繼承父進程的控制終端,登陸會話和進程組。我們需要使子進程完全的獨立出來,從而擺脫其他進程的控制(父進程等)。

    進程組:是一個或多個進程的集合。進程組有進程組ID來唯一識別。除了進程號(PID)外,進程組ID也是一個進程的必備屬性。

    登錄會話:會話期是一個或多個進程組的集合。通常,一個會話開始與用戶登錄,終止於用戶退出,在此期間該用戶的所有進程都屬於這個會話期。

     因此,我們需要擺脫他們,使之不受它們的影響,setsid函數可以使進程完全獨立出來,從而擺脫其他進程的控制。

    頭文件:unistd.h

    函數形式:extern __pid_t setsid (void) __THROW;

    功能:讓進程擺脫原回話控制;讓進程擺脫原進程組控制;讓進程擺脫原控制終端控制


  3、禁止進程重新打開控制終端 

    進行了上面兩步後,該進程已經成爲無終端的會話組長。但它可以重新申請打開一個控制終端。可以通過使進程不再成爲會話組長來禁止進程重新打開終端控制。

    實現方式:

      再次啓動子進程。


  4、修改當前工作目錄

    這一步也是必須的。使用fork創建的子進程繼承了父進程的當前工作目錄。由於在進程進行中,當前目錄所在的文件系統是不能卸載的,對以後的操作有可能造成一些麻煩。

    因此,通常的做法就是修改當前工作目錄,一般會改爲"/"爲守護進程的當前工作目錄。

    改變工作目錄的常見函數:chdir

    頭文件:unistd.h

    函數形式:int chdir(const char *path)

    功能:改變當前工作目錄


  5、重設文件權限掩碼

    使用fork創建的子進程繼承了父進程的文件創建掩碼。它能修改守護進程所創建的文件的存取位。

    因此,需要將文件創建掩碼清除。

    設置文件權限掩碼的函數:unmask。

    頭文件:sys/stat.h

    函數形式:mode_t  unmask(mode_t cmask);

    功能:進程設置文件模式創建屏蔽字,並返回以前的值。


  6、關閉文件描述符

    同文件權限掩碼一樣,子進程會從父進程那裏繼承打開的文件,繼承打開的文件描述符。

    這些文件可能永遠不會被守護進程讀寫,但是它們會消耗系統資源。也可能導致所在的文件系統無法卸下。因此需要關閉。


  7、處理SIGCHLD信號 

    處理SIGCHLD信號並不是必須的。但對於某些進程,特別是服務器進程往往在請求到來時生成子進程處理請求。

    如果父進程不等待子進程結束,子進程將成爲殭屍進程(zombie)從而佔用系統資源。

    如果父進程等待子進程結束,將增加父進程的負擔,影響服務器進程的併發性能。在Linux下可以簡單地將 SIGCHLD信號的操作設爲SIG_IGN。 

      signal(SIGCHLD,SIG_IGN); 

    這樣,內核在子進程結束時不會產生殭屍進程







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章