C ++:在SIGUSR信号上序列化对象

时间:2015-03-23 18:36:13

标签: c++ serialization boost signals

让我们假设一个程序在运行到期之前必须终止。终止信号可以由用户指定(程序在使用MOAB调度程序的集群上运行)。 此外,我们假设可以添加一个冷却时间段,即在终止前SIGUSR1秒/分钟发送警告信号(X),

如何为检查点目的保存内存状态?

我玩弄了感兴趣的对象的boost::serialization +程序状态变量的想法,例如

SIGKILL

我不知道如何将class Foo { private: friend class boost::serialization::access; template<class Archive> void serialize(Archive & ar, const unsigned int version) { ar & x; ar & y; } int x; int y; public: Foo(){}; Foo(int x_, int y_) : x(x_), y(y_) {} }; void handler (const Foo & f) { //serialize and dump FOO std::ofstream ofs("dump"); { boost::archive::text_oarchive oa(ofs); oa << f; } // exit exit(0); } int main () { Foo bar(1, 2); void (*sighandler)( /* ?*/ ); sighandler = signal (SIGUSR, handler); //do stuff } 传递给信号处理程序。 根据我收集的信息处理,必须在全局范围内执行信号处理,即必须保存的对象无法正确确定范围。 因此,我的解决方案无效。

为了记录,我查看了更高级别的选项,例如BLCR,但问题是我使用gnu-parallel处理高级别的并行化,这样BLCR保存了并行状态,而不是孩子的过程。

1 个答案:

答案 0 :(得分:1)

正确处理信号可能涉及很多错综复杂的细节。例如,当异步处理信号时,只能使用async-signal-safe functions。调用任何不可重入的安全函数可能会导致未定义的行为。

对于这种特殊情况,可能值得使用Boost.Asio对signal handling的支持。它处理所有低级细节,允许用户控制何时处理信号,而不限于异步信号安全功能。

void handle_signal(const boost::system::error_code& error,
                   int signal_number)
{
  if (error) return;
  // signal occurred, can use non-reentrant functions to handle it.
}

...

// Register to handle SIGUSR1.
boost::asio::io_service io_service;
boost::asio::signal_set signals(io_service, SIGUSR1);

// Asynchronously wait for a signal to occur.
signals.async_wait(&handle_signal);

while (running)
{
  ...
  io_service.poll(); // Check it signal was received.
}

处理信号时,有两类信号:

  • 线程导向信号是绑定到特定线程的信号。这些包括在给定线程执行特定机器语言指令时生成的某些信号,例如SIGSEGVSIGFPE。此外,信号可以通过pthread_kill()发送到特定线程。一些文献将这些信号称为同步信号。
  • 过程导向信号是指向特定进程的信号。内核会将信号传递给任何没有阻塞信号的线程。一些文献将这些称为异步信号。

可以执行处理信号:

  • 异步:信号在任意执行点处理。当信号传递给尚未阻止它的线程时,线程将被中断并执行已注册sigaction()的相关信号处理程序。由于线程在任意点被中断,当前上下文可能是未知的,因此只能安全地调用可重入函数。例如,考虑线程已获取互斥锁,被信号中断的情况,然后尝试在信号处理程序中获取相同的互斥锁,从而导致死锁。甚至信号处理程序本身也可能被信号中断。
  • 同步:信号被阻止并在已知点处明确处理。线程可以选择阻止信号。当线程定向信号被发送到已阻塞信号的线程时,或者当一个进程接收到过程导向信号时,其中所有线程都阻塞了信号,该信号将被置于待定国家。可以调用诸如sigwait()之类的函数来获取有关未决信号的信息。由于应用程序显式调用获取信号信息而不是被中断,因此当前上下文是已知的,因此可以调用非重入函数。

当同步处理过程导向信号时,例如从kill命令发送的信号,确保所有线程都阻塞信号非常重要。否则,信号可能被另一个线程异步处理。这通常通过使用信号掩码在主线程中设置被阻塞和被忽略的信号来实现,因为子线程将在创建时继承其父线程的线程掩码。但是,这种方法可能无法保证工作,因为可能无法控制第三方库中的内部线程。有一种技术可以消除管理信号掩模的需要,同时仍然允许以self-pipe trick的同步方式处理信号。

自管技巧将创建一个内部管道来传递信号信息。一个相当基本的信号处理程序注册了sigaction(),它将通过将接收到的信号编号写入管道来异步处理信号。然后可以从管道的另一端读取以接收信号信息。 Boost.Asio使用此技术。此外,由于Boost.Asio使用反应堆并且内部管道是非阻塞的,因此可以异步处理信号信息而不会出现中断。

这是一个完整的示例demonstrating,一个处理SIGUSR1然后正常退出的小应用程序:

#include <chrono>
#include <iostream>
#include <thread>
#include <boost/asio.hpp>

// Mockup model.
class Foo {};

// Mockup serializer.
void store_state(Foo& foo)
{
  // ... serialize state with Boost.Serialization ...
  // ... persists state, perhaps to the filesystem or a
  //     message queue
  std::cout << "Storing state for object: " << &foo << std::endl;
}

int main()
{
  bool running = true;
  Foo foo;
  std::cout << "Object is: " << &foo << std::endl;

  // Setup signal handling.
  boost::asio::io_service io_service;
  boost::asio::signal_set signals(io_service);
  signals.add(SIGUSR1);
  signals.async_wait([&running, &foo](
      const boost::system::error_code& error,
      int signal_number) 
    {
      // On error, return early.
      if (error) return;
      std::cout << "Handling SIGUSR1: " 
                << std::boolalpha << (SIGUSR1 == signal_number)
                << std::noboolalpha << std::endl;

      // Set flag to allow graceful exit.
      running = false;

      // Store the state.
      store_state(foo);
    });

  // Main work loop.
  while (running)
  {
    std::cout << "Busy work" << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    io_service.poll(); // Handle signals.
  }
  std::cout << "Finish" << std::endl;
}

执行和输出:

./a.out &
PID=$!
sleep 2
kill -s SIGUSR1 $PID
Object is: 0x7fff8a1e620f
Busy work
Busy work
Busy work
Busy work
Handling SIGUSR1: true
Storing state for object: 0x7fff8a1e620f
Finish