2016-02-05 141 views
1

我有一個Python v3.4應用程序,它使用check_output()來調用一個叫做fork()的C++應用程序,原始進程退出並繼續子進程。看起來check_output()也在等待子進程,而不是在主進程返回時返回,並表示守護進程已成功啓動。調用守護進程時爲什麼不返回Python check_output()?

我是否需要改變我如何在C++中調用fork()或者Python check_output()調用需要以某種方式被告知只等待父進程退出?我是否需要在C++中使用第二個fork()作爲described here


這裏被剝離下來的Python表現出的問題:

#! /usr/local/bin/python3 

import logging 
import argparse 
from subprocess import CalledProcessError, check_output, STDOUT 

ARGS = ["[email protected]:23021:"] 

if __name__ == "__main__": 
    parser = argparse.ArgumentParser(
     description="Try launching subprocess") 
    parser.add_argument("exec", type=str, help="the exec to run") 
    args = parser.parse_args() 

    logging.basicConfig(level=logging.INFO, 
         format='%(asctime)s - %(message)s', 
         datefmt='%Y-%m-%d %H:%M:%S') 

    cmd_list = [args.exec] + ARGS 
    logging.info(str(cmd_list)) 

try: 
    output = check_output(cmd_list, 
          stderr=STDOUT, 
          universal_newlines=True) 
    logging.info("Exec OK with output:") 
    logging.info(output) 
except CalledProcessError as e: 
    logging.info("Exec Not OK with output:") 
    logging.info(str(e)) 

這裏是C++代碼來守護進程的C++應用程序:

void 
daemonize() 
{ 
    // This routine backgrounds the process to run as a daemon. 
    // Returns to caller only if we are the child, otherwise exits normally. 
    if (getppid() == 1) { 
    return; // Leave if we're already a daemon 
    } 

    // Create the backgrounded child process. 
    const pid_t parent = getpid(); 
    const pid_t pid = fork(); 
    CSysParamAccess param; 
    const string progName(param.getProgramKindName()); 

    ::close(STDIN_FILENO); 
    ::close(STDOUT_FILENO); 
    ::close(STDERR_FILENO); 

    if (pid < 0) { 
    cerr << "Error: " << progName << " failed to fork server. Aborting." 
     << endl; // inform the client of the failure 

    exit(appExit::failForkChild); // Error. No child created. 
    } else if (pid > 0) { 
    // We're in the parent. Optionally print the child's pid, then exit. 
    if (param.getDebug()) { 
     clog << "Successful fork. The Application server's (" << progName 
      << ") pid is: " << pid << "(self) from parent " << parent << endl; 
    } 

    ::close(STDIN_FILENO); 
    ::close(STDOUT_FILENO); 
    ::close(STDERR_FILENO); 

    exit(appExit::normal); 
    } 

    ::close(STDIN_FILENO); 
    ::close(STDOUT_FILENO); 
    ::close(STDERR_FILENO); 

    // Here only in the child (daemon). 
    if (-1 == setsid()) { // Get a new process group 
    cerr << "Error: Failed to become session leader while daemonising - errno: " 
     << errno; 

    exit(appExit::failForkChild); // Error. Child failed. 
    } 

    signal(SIGHUP, SIG_IGN); // Per example. 

    // Fork again, allowing the parent process to terminate. 
    const pid_t midParent = getpid(); 
    const pid_t grandChildPid = fork(); 

    if (grandChildPid < 0) { 
    cerr << "Error: Failed to fork while daemonising - errno: " << errno; 

    exit(appExit::failForkChild); // Error. GrandChild failed. 
    } else if (grandChildPid > 0) { 
    // We're in the parent. Optionally print the grandchild's pid, then exit. 
    if (param.getDebug()) { 
     clog << "Successful second fork. The Application server's (" << progName 
      << ") pid is: " << grandChildPid << "(self) from parent " 
      << midParent << endl; 
    } 

    ::close(STDIN_FILENO); 
    ::close(STDOUT_FILENO); 
    ::close(STDERR_FILENO); 

    exit(appExit::normal); 
    } 

    // Here only in the grandchild (daemon). 
    appGlobalSetSignalHandlers(); 

    // Set the current working directory to the root directory. 
    if (chdir("/") == -1) { 
    cerr << 
     "Error: Failed to change working directory while daemonising - errno:" 
     << errno; 

    exit(appExit::failForkChild); // Error. GrandChild failed. 
    } 

    // Set the user file creation mask to zero. 
    umask(0); 

    //close(STDIN_FILENO); // Cannot close due to assertion in transfer.cpp 
    // Theoretically, we would reopen stderr and stdout using the log file. 
    ::close(STDIN_FILENO); 
    ::close(STDOUT_FILENO); 
    ::close(STDERR_FILENO); 

    // We only return here if we're the grandchild process, the Application 
    // server. The summoner exited in daemonize(). 
    clog << "Application " << param.getProgramKindName() 
     << " (" << appGlobalProgramName() << ") successfully started." << endl; 
} 

它與回聲時調用和失敗用我的C++應用程序:

> stuckfork.py echo 
2016-02-05 10:17:34 - ['echo', '[email protected]:23021:'] 
2016-02-05 10:17:34 - Exec OK with output: 
2016-02-05 10:17:34 - [email protected]:23021: 

> stuckfork.py flumep 
2016-02-05 10:17:53 - ['flumep', '[email protected]:23021:'] 
    C-c Traceback (most recent call last): 
    File "/home/user/Bin/Bin/stuckfork.py", line 26, in <module> 
    universal_newlines=True) 
    File "/usr/local/lib/python3.4/subprocess.py", line 609, in check_output 
    output, unused_err = process.communicate(inputdata, timeout=timeout) 
    File "/usr/local/lib/python3.4/subprocess.py", line 947, in communicate 
    stdout = _eintr_retry_call(self.stdout.read) 
    File "/usr/local/lib/python3.4/subprocess.py", line 491, in _eintr_retry_call 
    return func(*args) 
KeyboardInterrupt 
> 

我已經將問題縮小到我的一個C++靜態構造函數正在做的事情,導致啓動過程失效,這就是爲什麼Python仍在等待。現在劃分看哪一個。

回答

0

問題被打開的文件描述符,這是由於這個靜態代碼正在運行:

FILE *origStdErr = fdopen(dup(fileno(stderr)), "a"); 

一旦該行被刪除,守護進程的close(0)close(1)close(2)有適當的效果和Python代碼停止等待。

1

一個正確的解決方案是找到正確的文件描述符,將輸出從分叉的C++子節點輸出到python並關閉它。

現在,您可以嘗試在C++子進程或調用子進程(在fork()之後)調用close(1) SYSTEM CALL。這將表明蟒蛇停止試圖從孩子讀取。

我不確定這是否可行,因爲您發佈的代碼是不夠的。