linux下的daemon進程

轉自:http://www.cnblogs.com/xuxm2007/archive/2011/07/29/2121280.htmlhtml

 

#include <unistd.h>
int daemon(int nochdir,int noclose)
在建立精靈進程的時候,每每須要將精靈進程的工做目錄修改成"/"根目錄
而且將標準輸入,輸出和錯誤輸出重定向到/dev/null
daemon的做用就是當參數nochdir爲0時,將根目錄修改成工做目錄
noclose爲0時,作輸入,輸出以及錯誤輸出重定向到/dev/null
執行成功返回0
錯誤返回-1linux

<-----------------------------------------> 編程

淺析daemon精靈守護進程建立背後的故事
#include <unistd.h>
int main(int argc, char *argv[])
{
...
if (daemon(0, 0)) {//調用glibc庫函數daemon,建立daemon守護進程
perror("daemon");
return -1;
}
    好了執行到這裏的就是daemon的子進程了[luther.gliethttp].
...
}
=========================================================
int daemon( int nochdir, int noclose )
{
pid_t pid;
if ( !nochdir && chdir("/") != 0 )//若是nochdir=0,那麼改變到"/"根目錄
return -1;
if ( !noclose )//若是沒有close
{
int fd = open("/dev/null", O_RDWR);//打開空洞文件.
if ( fd < 0 )
return -1;
//對於每一個進程,它的fds文件描述符表中:0,1和2文件句柄位置對應的fops文件操做函數集,
//fdt->fd[0],fdt->fd[1],fdt->fd[2],
//規定將分別與標準輸入,標準輸出,標準錯誤輸出相關聯[luther.gliethttp].
//因此用戶應用程序調用open函數打開文件時,默認都是以3索引爲開始句柄,故當前open返回的文件句柄最小值爲3[luther.gliethttp].
//dup2(unsigned int oldfd, unsigned int newfd)系統調用就是用oldfd的fops操做文件集file,複製到newfd所在處
//即:fdt->fd[newfd] = fdt->fd[oldfd];
if ( dup2( fd, 0 ) < 0 || //使用字符設備/dev/null的fops函數操做集,替換0句柄對應的文件操做集.
     dup2( fd, 1 ) < 0 || //使用字符設備/dev/null的fops函數操做集,替換1句柄對應的文件操做集.
          dup2( fd, 2 ) < 0 ) //使用字符設備/dev/null的fops函數操做集,替換2句柄對應的文件操做集.
{
close(fd);
return -1;
}
//若是上面替換成功,那麼鍵盤的任何操做將不會對該進程產生任何影響,由於0,1,2句柄所在處的fops文件操做集已經都變成了,
安全

//被重定向爲"/dev/null"空洞設備的fops.因此對0,1,2句柄的讀寫操做,也就是在對/dev/null設備做讀寫操做.
close(fd);//關閉打開的/dev/null
}
   pid = fork();//建立子進程.
if (pid < 0)
return -1;
if (pid > 0)
    _exit(0);//返回執行的是父進程,那麼父進程退出,讓子進程變成真正的孤兒進程.
//ok,咱們指望的daemon子進程執行到這裏了.
if ( setsid() < 0 )//設置session id.
return -1;
return 0;//成功建立daemon子進程[luther.gliethttp].
}
session

http://docs.linuxtone.org/ebooks/C&CPP/c/ch34s03.html函數

http://blog.csdn.net/yyyzlf/article/details/5267954操作系統

因爲守護進程的特色,編寫守護進程程序必須遵照必定的規則。本節將闡述這些規則的要點,並給出相關代碼。.net

8.2.1  實現守護進程的步驟

在Linux系統中,要編程實現一個守護進程必須遵照以下的步驟。nuxt

1.讓init進程成爲新產生進程的父進程。

調用fork函數建立子進程後,使父進程當即退出。這樣,產生的子進程將變成孤兒進程,並被init進程接管,同時,所產生的新進程將變爲在後臺運行。code

2.調用setsid函數

經過調用setsid函數,使得新建立的進程脫離控制終端,同時建立新的進程組,併成爲該進程組的首進程。爲了使讀者更好地理解這一步驟,下面介紹進程組、會話(session)的基本概念。

在Linux系統中,全部的進程都屬於各自的進程組。進程組是一個或多個進程的集合。打個比方,能夠認爲某個班級是一個進程組,而其中成員就是進程。一個班級至少有一個成員。當一個班級的最後一個成員不存在的時候,這個班級也就不存在了,也就是進程組消亡了。

每一個進程組都有相似於進程號的標識,稱爲進程組ID。進程組ID是由領頭進程的進程號決定的,每一個進程組都存在一個領頭進程。進程組的存在與否與領頭進程是否存在沒有關係。

會話是一個或多個進程組的集合。與進程組相似,每一個會話都存在一個領頭進程。Linux是一個多用戶的操做系統,在同一時刻系統中會存在屬於不一樣用戶的多個進程。若是用戶在某個終端上發送了某個信號,例如,按下「Ctrl+C」發送SIGINT信號,如何確保信號被正確地發送到對應的進程,同時不會影響使用其餘終端的用戶的進程?

會話和進程組是Linux內核用於管理多用戶狀況下用戶進程的方法。每一個進程都屬於一個進程組,而進程組又屬於某個會話。當用戶從終端登陸系統(不論是終端仍是僞終端),系統會建立一個新的會話。在該終端上啓動的進程都會被系統劃歸到會話的進程組中。

會話中的進程經過該會話中的領頭進程(常稱其爲控制進程)與一個終端相連。該終端是會話的控制終端。一個會話只能有一個控制終端,反之同樣。若是會話存在一個控制終端,則它必然擁有一個前臺進程組。屬於該組的進程能夠從控制終端得到輸入。這時,其餘的進程組都爲後臺進程組。圖8.3所示爲會話、進程組、進程與控制終端之間的關係。

圖8.3  會話、進程組、進程與控制終端的關係

因爲守護進程沒有控制終端,而使用fork函數建立的子進程繼承了父進程的控制終端、會話和進程組,所以,必須建立新的會話,以脫離父進程的影響。Linux系統提供了setsid函數用於建立新的會話。setsid函數的信息如表8.1所示。

表8.1                                                              setsid函數

頭文件

<unistd.h>

函數形式

pid_t setsid(void);

返回值

成功

失敗

是否設置errno

調用進程的會話ID

−1

setsid函數將建立新的會話,並使得調用setsid函數的進程成爲新會話的領頭進程。調用setsid函數的進程是新建立會話中的唯一的進程組,進程組ID爲調用進程的進程號。setsid函數產生這一結果還有個條件,即調用進程不爲一個進程的領頭進程。因爲在第一步中調用fork的父進程退出,使得子進程不多是進程組的領頭進程。該會話的領頭進程沒有控制終端與其相連。至此,知足了守護進程沒有控制終端的要求。

3.更改當前工做目錄

使用fork函數產生的子進程將繼承父進程的當前工做目錄。當進程沒有結束時,其工做目錄是不能被卸載的。爲了防止這種問題發生,守護進程通常會將其工做目錄更改到根目錄下(/目錄)。更改工做目錄使用的函數是chdir。

4.關閉文件描述符,並重定向標準輸入、輸出和錯誤輸出

新產生的進程從父進程繼承了某些打開的文件描述符,若是不使用這些文件描述符,則須要關閉它們。守護進程是運行在系統後臺的,不該該在終端有任何的輸出信息。可使用dup函數將標準輸入、輸出和錯誤輸出重定向到/dev/null設備上(/dev/null是一個空設備,向其寫入數據不會有任何輸出)。下面給出具體的代碼:

      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);

    }

5.設置守護進程的文件權限建立掩碼

不少狀況下,守護進程會建立一些臨時文件。出於安全性的考慮,每每不但願這些文件被別的用戶查看。這時,可使用umask函數修改文件權限,建立掩碼的取值,以知足守護進程的要求。

8.2.2  守護進程具體實現

本節給出一個守護進程建立的實例。程序p8.1.c中定義了daemon函數,用於實現對守護進程的建立。其建立思想在8.2.1中有詳細的介紹,程序的具體代碼以下:

//p8.1.c 守護進程的實現

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

/* daemon函數用於將調用函數的進程轉化爲守護進程 */

int

daemon (int nochdir, int noclose)

{

  pid_t pid;

  pid = fork ();

  /* 若是建立進程失敗 */

  if (pid < 0)

    {

      perror ("fork");

      return -1;

    }

  /* 父進程退出運行 */

  if (pid != 0)

    exit (0);

  /* 成爲會話領頭進程 */

  pid = setsid();

  if (pid < -1)

    {

      perror ("setsid");

      return -1;

    }

  /* 將工做目錄修改爲根目錄 */

  if (! nochdir)

    chdir ("/");

  /* 將標準輸入輸出重定向到空設備 */

  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 (0027);

  return 0;

}

int main(void)

{

      daemon(0,0);

      sleep(1000);

      return 0;

使用gcc編譯p8.1.c,獲得名爲p8.1的可執行文件。執行該程序,程序將以守護進程的狀態運行,如圖8.4所示。

相關文章
相關標籤/搜索