2013-07-02 56 views
3

我正在爲我正在編寫的C項目編寫一個服務腳本,它在啓動時會執行一些實用程序。我想使用日誌工具捕獲所有的輸出。我在/ etc/rc5/myscript中有類似如下內容執行守護進程時的Linux/Bash文件描述符

#!/bin/bash  
#save fd 1 in fd 3 for use later 
exec 3<&1 
$SERVICESCRIPT | logger 

記錄器僅從stdin中讀取數據,直到它到達EOF。第二個腳本是檢查一堆公用程序是否正在運行並觸發它自己的一些功能的地方。在這些實用程序中,有一個可以分叉併成爲守護進程。現在,我從腳本運行它,它繼承了所有的腳本文件。這會導致腳本在被調用後永遠不會返回到命令行。

我曾嘗試幾種方法來解決這個:

首先,在我的腳本踢的守護進程我也做了以下內容:

(
exec 4<&- 
exec 3<&- 
$daemon_process 
) 

這應該推出一個下標,靠近3和4(分別用於存儲標準輸出和管道輸出)並運行程序。但是當我試圖回到命令行時,我仍然覺得這個掛鉤讓我相信管道沒有關閉。經過進一步調查,如果我在關閉後發出回聲,並將它們重定向到傳送給記錄器的fd,我會在日誌中看到它們,告訴我fd確實仍處於完好狀態。如果我在c程序中關閉fds 2-4,我會看到它返回到命令行,但這是一個非常混亂和不愉快的修復。

其次,我試過如下:

$daemon_process 4<&- 3<&- 

調用程序時,應關閉FDS,但很可惜我看到的再也不會回來的命令行腳本的相同的結果。

當腳本動手時,我可以「CTRL-C」將它恢復到命令行,但這絕不是一個解決方案。

任何想法?

謝謝!!!!

回答

0

您的/etc/rc5/myscript不會因爲$SERVICESCRIPT中的任何內容而被阻止。它是阻塞的,因爲它正在等待logger終止,直到寫入其STDIN的所有內容都終止(在這種情況下,這是你的守護進程)。

你可以用這個簡化的例子看到這個行爲。考慮這個簡單的C程序,孤兒本身,然後什麼也不做永遠:

#include <stdlib.h> 

int main(int argc, char *argv[]){ 
     if(fork()){ 
       exit(0); 
     } 
     while(1){ 
       sleep(1); 
     } 
     return EXIT_SUCCESS; 
} 

,這簡單的「記錄儀」,它只是從標準輸入讀取直到EOF:

#include <stdio.h> 

int main(int argc, char *argv[]){ 
     char c; 
     while(1){ 
       c = getc(stdin); 
       if(c == EOF){ 
         break; 
       } 
     } 
     return 0; 
} 

如果我運行這些結合在一起,我贏了不要讓我的命令提示符回來。

$ ./forktest | ./logger 
<hangs> 

這是因爲我的shell正在等待整個管道完成。 forktest「完成」(它殺死自己),但logger沒有完成,這就是我們正在等待的。 forktest的孤兒過程保持打開logger的STDIN。你看到從STDOUT(FD 1)孤立的管(注意它的父處理1)要STDIN(FD 0)的logger由另一終端檢查/proc/$pid/fd上述運行時:

$ ps -ef | grep forktest 
cneylan 25451  1 0 16:27 pts/7 00:00:00 ./forktest 
$ ps -ef | grep logger 
cneylan 25450 24379 0 16:27 pts/7 00:00:00 ./logger 
$ ls -l /proc/25451/fd 
total 0 
lrwx------ 1 cneylan cneylan 64 Jul 2 16:28 0 -> /dev/pts/7 
l-wx------ 1 cneylan cneylan 64 Jul 2 16:28 1 -> pipe:[944400] 
lrwx------ 1 cneylan cneylan 64 Jul 2 16:28 2 -> /dev/pts/7 
lrwx------ 1 cneylan cneylan 64 Jul 2 16:28 3 -> /dev/pts/7 
$ ls -l /proc/25450/fd 
total 0 
lr-x------ 1 cneylan cneylan 64 Jul 2 16:28 0 -> pipe:[944400] 
lrwx------ 1 cneylan cneylan 64 Jul 2 16:28 1 -> /dev/pts/7 
lrwx------ 1 cneylan cneylan 64 Jul 2 16:28 2 -> /dev/pts/7 
lrwx------ 1 cneylan cneylan 64 Jul 2 16:28 3 -> /dev/pts/7 

作爲一個側面說明,在發生這種情況時執行^ C只會發出logger進程的信號,因爲守護程序[應該有]調用setsid(2),這是daemonizing itself中必需的步驟之一。因此,無論^ C被殺死你的後臺程序,你需要有你的代碼調用setsid(2)或你的代碼已經調用setsid(2)和你有一大堆的背景:)

+0

我想在調用守護程序之前關閉管道的fds,以便程序不會永遠打開它。問題是,我期望這樣做的bash中的命令實際上並沒有這樣做。 實質上,我希望在調用「forktest」之前關閉任何管道fd。 我使用Linux提供的守護進程()函數。我已經嘗試了(1,1)參數以及(1,0)。在(1,1)的情況下,守護進程函數應該將0-2 fds重定向到/ dev/null。但是,我想保留它們以便在屏幕上寫入錯誤,因此在腳本中關閉管道密碼是更可取的。具有隨機打開的文件描述符的 – blrg891

+0

不會導致您的問題。你的問題是你的'/ etc/rc5/myscript'正在等待'logger'完成。 –

+0

沒錯,但是記錄器正在等待EOF,它永遠不會到達,因爲守護進程持有一個打開管道的文件描述符。一旦記錄器獲得EOF,它就完成了。 – blrg891

0

運行流氓守護進程正如你正確識別的logger阻塞是寫入結束時在讀取結束處和(最終)$daemon_process之間的管道問題。既然你想把後者的輸出寫到屏幕上,

$daemon_process >/dev/tty 

會解決問題。