2010-08-20 104 views
18

我的工作,那將在Linux和Mac OS X上運行的服務器應用程序它是這樣的:linux上的文件描述符3有什麼特別之處?

  • 啓動主要應用
  • 控制器進程的叉
  • 調用lock_down()在控制過程中
  • 再次叉終止主要應用
  • 控制器進程,然後,創建工作進程
  • 最終控制器保持分叉更多的工作呃進程

我可以使用多種方法(例如,系統日誌或文件),但現在我正在思考syslog。 「有趣的是」在控制器進程中沒有系統日誌輸出,除非我在下面包含#ifdef部分。

工作人員可以在Mac OS X和Linux中完美處理日誌,無論是否具有下面的ifdef'ed部分。該控制器還可以在沒有#ifdef'ed部分的情況下在Mac OS X中完美記錄,但是在Linux上,如果我想要從控制器進程中查看任何輸出到syslog(或該日誌文件),則需要ifdef。

那麼,爲什麼呢?

static int 
lock_down(void) 
{ 
    struct rlimit rl; 
    unsigned int n; 
    int fd0; 
    int fd1; 
    int fd2; 

    // Reset file mode mask 
    umask(0); 

    // change the working directory 
    if ((chdir("/")) < 0) 
     return EXIT_FAILURE; 

    // close any and all open file descriptors 
    if (getrlimit(RLIMIT_NOFILE, &rl)) 
     return EXIT_FAILURE; 
    if (RLIM_INFINITY == rl.rlim_max) 
     rl.rlim_max = 1024; 

    for (n = 0; n < rl.rlim_max; n++) { 
#ifdef __linux__   
     if (3 == n) // deep magic... 
      continue; 
#endif 
     if (close(n) && (EBADF != errno)) 
      return EXIT_FAILURE; 
    } 

    // attach file descriptors 0, 1 and 2 to /dev/null 
    fd0 = open("/dev/null", O_RDWR); 
    fd1 = dup2(fd0, 1); 
    fd2 = dup2(fd0, 2); 
    if (0 != fd0) 
     return EXIT_FAILURE; 

    return EXIT_SUCCESS; 
} 

camh很接近,但是使用closelog()是這樣做的想法,所以榮譽歸功於jilles。除了在syslogs腳下關閉文件描述符之外,還有其他的東西必須繼續。爲了使代碼工作,我添加了一個調用closelog()只是在循環之前:

closelog(); 
for (n = 0; n < rl.rlim_max; n++) { 
    if (close(n) && (EBADF != errno)) 
     return EXIT_FAILURE; 
} 

我依靠手冊頁的逐字理解,他說:

使用openlog的( )是可選的;它會自動被syslog()調用,如果有必要...

我解釋這是說syslog會檢測文件描述符是否在它下面關閉。顯然它沒有。需要在linux上顯式地使用closelog()來告訴syslog描述符已關閉。

還有一件事仍令我感到困惑的是,沒有使用closelog()阻止第一個分支進程(控制器)打開並使用日誌文件。以下分叉進程可以使用syslog或日誌文件而不會造成問題。也許文件系統中存在一些緩存效應,使第一個分支進程具有哪個文件描述符可用的不可靠「想法」,而下一組分支進程是否被充分延遲以不受此影響?

+3

+1 //'deep magic ...':-)當你擁有Linux內核時,誰需要Harry Potter? – 2010-08-20 22:33:23

+0

那麼在那一點上fd 3上的內容是什麼? – Gilles 2010-08-20 22:42:14

+0

@David。與內核無關。內核中甚至沒有fd 0,1和2。 – camh 2010-08-20 23:59:07

回答

6

syslog(3)可能會將文件描述符保留爲syslogd的套接字打開;將其關閉在腳下可能會導致問題。 closelog(3)電話可能會有所幫助。

13

文件描述符3的特殊之處在於,它通常是從分配新文件描述符的系統調用返回的第一個文件描述符,因爲通常爲stdin,stdout和stderr設置0,1和2 。

這意味着,如果你調用任何庫函數分配其自己的內部目的的文件描述符,以執行其功能,它會得到FD 3.

的openlog(3)庫調用需要打開/dev/log與syslog守護進程進行通信。如果隨後關閉所有文件描述符,則可能會破壞系統日誌庫函數,如果它們沒有以某種方式處理該函數。

1

Syslog在啓動時綁定給定的描述符。大部分時間描述符3.如果關閉它沒有日誌。

syslog-ng -d -v 

給你更多關於它在幕後做什麼的信息。

輸出應該像這樣的事情:

binding fd 3, inetaddr: 0.0.0.0, port: 514 
io.c: Preparing fd 3 for reading 
io.c: Preparing fd 4 for reading 
binding fd 5, unixaddr: /dev/log 
io.c: listening on fd 5 
+0

這對調試syslog-ng很有用,但對於其他程序沒有任何作用。 syslog-ng中的fd3可以很容易地在某些其他進程中連接到fd9。 – 2010-08-20 23:05:55

+1

這是真的,但解釋了爲什麼在運行他的代碼後,他的日誌中什麼都沒有了,什麼似乎是他不清楚的(syslog使用給定的文件描述符進行通信) – jdehaan 2010-08-21 10:25:49

8

調試這在Linux上使用strace跟蹤正在作出實際的系統調用的方式;使用syslog的文件描述符就變得很明顯了:

$ cat syslog_test.c 
#include <stdio.h> 
#include <syslog.h> 

int main(void) 
{ 
    openlog("test", LOG_PID, LOG_LOCAL0); 
    syslog(LOG_ERR, "waaaaaah"); 
    closelog(); 
    return 0; 
} 
$ gcc -W -Wall -o syslog_test syslog_test.c 
$ strace ./syslog_test 
... 
socket(PF_FILE, SOCK_DGRAM, 0)   = 3 
fcntl64(3, F_SETFD, FD_CLOEXEC)   = 0 
connect(3, {sa_family=AF_FILE, path="/dev/log"}, 16) = 0 
send(3, "<131>Aug 21 00:47:52 test[24264]"..., 42, MSG_NOSIGNAL) = 42 
close(3)        = 0 
exit_group(0)       = ? 
Process 24264 detached