如何确保popen()ed进程在退出时运行析构函数?

时间:2011-08-07 15:02:15

标签: c++ popen sigpipe pclose

如果我有一个管道来运行某个命令,则管道命令需要进行一些清理,但是,如果启动管道的进程有错误,则管道命令不会清除。在这种情况下,管道命令是否获得SIGPIPE?如何确保cleanupPipe析构函数始终运行?当抛出errorOccurred异常时,我发现cleanupPipe析构函数没有运行。我有SIGPIPE处理程序设置为抛出异常,所以如果SIGPIPE是结果,我希望我的析构函数在SIGPIPE导致抛出异常展开堆栈时运行。

void
testCase() {
  class cleanup {
  public:
    cleanup(FILE *pipe)
      : _pipe(pipe) {
    }
    ~cleanup() {
      ::pclose(_pipe);
    }

  private:
    FILE *_pipe;

  };

  string cmd("runMyCommandImplementationHere argsHere");
  FILE *pipePtr = ::popen(cmd, "w");
  cleanup cleanUpPipe(pipePtr);

  // Normally, write data to pipe until process in pipe gets all the data it
  // needs and exits gracefully.
  for (;;) {
    if (someErrorOccured()) {
      // When this error occurs, we want to ensure cleanupPipe is run in piped
      // process.
      throw errorOccurred(status);
    }
    if (finishedWritingData()) {
      break;
    }
    writeSomeDataToPipe(pipePtr);
  }
}

void
myCommandImplementationHere() {
  class cleaupPipe {
  public:
    cleanupPipe(const string &filename)
      : _filename(filename) {
    }
    ~cleanupPipe() {
      ::unlink(_filename.c_str());
    }

  private:
    string _filename;

  };

  string file("/tmp/fileToCleanUp");
  cleanupPipe cleanup(file);

  doSomeWorkOnFileWhileReadingPipeTillDone(file);
}

1 个答案:

答案 0 :(得分:3)

在信号处理程序中抛出异常是一个非常糟糕的主意。信号处理程序必须是异步安全的。更糟糕的是,信号处理程序运行的主要是与主线代码不同的执行线程。最好保持信号处理程序小而且非常原始。例如,使SIGPIPE处理程序设置一些易失性全局变量,指示发生了SIGPIPE,并在主线代码中将其作为错误条件进行测试。

其他几条评论:

  • 在处理popenpclosewrite等C函数时,您应该检查返回状态。在您致电popenpclose时,您没有这样做,至少在示例代码中没有。
  • 为什么class Cleanup中的不对称?构造函数接收已经构造的FILE指针,但析构函数通过pclose销毁它。如果构造函数调用popen,将命令字符串作为构造函数的参数,IMO会更好。

<强>附录
也许比为SIGPIPE创建设置一些全局变量的处理程序更好的是设置SIGPIPE的处理程序忽略,然后检查写入管道的EPIPE错误。