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

发布于 2024-11-28 02:37:26 字数 1321 浏览 0 评论 0原文

如果我有一个管道来运行某些命令,则管道命令需要进行一些清理,但是,如果启动管道的进程出现错误,则管道命令不会进行清理。在这种情况下,管道命令是否收到 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);
}

If I have a pipe to run some command, the piped command needs to do some cleanup, however, if the processes that started the pipe has an error, the piped command is not cleaning up. Is the piped command getting SIGPIPE in this case? How can I ensure cleanupPipe destructor is always run? When the errorOccurred exception is thrown, I am seeing that cleanupPipe destructor is not run. I have SIGPIPE handler set up to throw an exception, so if SIGPIPE is the result, I would expect my destructor to be run when the SIGPIPE results in thrown exception unwinding the stack.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

橘虞初梦 2024-12-05 02:37:26

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

其他一些注释:

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

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

Throwing an exception in a signal handler is a very bad idea. Signal handlers must be asynchronous-safe. To make matters worse, signal handlers run in which is essentially a different thread of execution than your mainline code. It is best to keep your signal handlers small and very primitive. For example, make the SIGPIPE handler set some volatile global variable that indicates that SIGPIPE occurred and test for that as an error condition in your mainline code.

A couple of other comments:

  • You should check the return status when dealing with C functions such as popen, pclose, and write. You aren't doing so on your call to popen or pclose, at least not in the sample code.
  • Why the asymmetry in class Cleanup? The constructor receives an already-constructed FILE pointer, but the destructor destroys it via pclose. IMO it would be better if the constructor calls popen, taking the command string as an argument to the constructor.

Addendum
Perhaps even better than creating a handler for SIGPIPE that sets some global variable is to set the handler for SIGPIPE to ignore, and then check for an EPIPE error from your writes to the pipe.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文