示范如何在命令行程序中处理用户中断信号(ctrl+c)

文章来源:http://blog.sina.com.cn/s/blog_48d4cf2d0100poiz.html

实现捕获、处理用户中断信号的命令行程序还是比较繁琐的。尤其是Linux,Windows有一定差异。下面我实现了一个跨平台的捕获、处理用户中断信号(ctrl+c)的命令行程序框架。在windows下用SetConsoleCtrlHandler API捕获处理中断,在Linux下用信号机制。

===================================================
      先看看interrupttable_console_program.h文件
===================================================
#pragma once

#include <boost/thread.hpp>

class InterruptServiceHandler
{
public:
      virtual ~InterruptServiceHandler(){}
      virtual void run() = 0;
      virtual void beforeInterruptQuit() = 0;
      virtual void beforeNormalQuit() = 0;
};

class InterruptService
{
public:
      InterruptService(InterruptServiceHandler* handler_);
      void join(bool grace_quit_flag_);     
private:     
      InterruptServiceHandler* handler;
      boost::thread_group grp;
      bool grace_quit_flag;
      boost::mutex single_close_mtx;
      bool single_close_flag;
      bool checkFirstClose();
#ifdef WIN32
      void signalHandler();
#else
      void detectSignalThread();
#endif
};
======================================================
      InterruptServiceHandler是纯虚接口类,可以看到需要实现三个函数:
      run,你想要做的事情;
      beforeInterruptQuit,发生中断时(用户输入ctrl+c)想要执行的代码;
      beforeNormalQuit,run正常结束时想要执行的代码,执行它的时候run一定已经结束。
      框架保证了beforeInterruptQuit和beforeNormalQuit只会有一个被调用。
      解释:接口里最让人疑惑之处应该是为什么需要beforeNormalQuit,这块代码貌似放在run函数的最后面即可。这样设计是为了避免执行两次退出清理代码。因为有可能中断处理的时候run函数也结束了。也有可能run函数结束后正在清理的时候发生中断。
      再解释:InterruptServiced的join的参数很重要。如果设置为true的话,程序会用优雅的方式退出,即必须在beforeInterruptQuit里通知run结束,等run结束后join函数返回,继续执行join之后的代码。这种方式的话如果beforeInterruptQuit没有合适地通知run结束,或者run无法结束(比如锁死在哪个疙瘩),则程序无法退出。如果join参数设置为false,则在调用beforeInterruptQuit之后直接调用exit(0)退出进程。后一种方式比较不优雅,但是其实很多时候都已经要退出进程了,做做清理工作也就ok了。不一定很care要run执行完毕。前者优雅,后者简单。根据实际情况各取所需吧。
======================================================
      来个示例吧。main.cpp
======================================================
#include <iostream>
#include "interrupttable_console_program.h"

using namespace std;
using namespace global;

class TestService
      : public InterruptServiceHandler
{
public:
      TestService(int max_)
            : running_flag(true)
            , max(max_)
            , cnt(0)
      {
      }
      virtual void run()
      {
            while (running_flag)
            {
                  boost::mutex::scoped_lock lock(mtx);
                  if (cnt < max)
                  {
                        ++ cnt;                       
                  }
                  else
                  {
                        break;
                  }
            }
      }
      virtual void beforeInterruptQuit()
      {
            boost::mutex::scoped_lock lock(mtx);
            running_flag = false;
            cout << "InterruptQuit after lock " << cnt << " times" << endl;
      }
      virtual void beforeNormalQuit()
      {
            boost::mutex::scoped_lock lock(mtx);
            cout << "NormailQuit after lock " << cnt << " times" << endl;
      }
public:
      boost::mutex mtx;
      int max;
      int cnt;
      bool running_flag;
};

int main()
{
      TestService test_service(1000000);
      InterruptService service(&test_service);
      service.join(true);
      cout << "Hello,World!" << endl;//如果join(true),一定会看到这行字,如果join(false),用户中断的时候就看不到这行字了
      return 0;

======================================================
      最后是interrupttable_console_program.cpp
======================================================
#include "interrupttable_console_program.h"
#include <boost/bind.hpp>
#include <stdexcept>
#ifdef WIN32
#include <windows.h>
#else
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#endif

#ifdef WIN32
boost::function0<void> before_interrupt_quit_func;
BOOL WINAPI console_ctrl_handler(DWORD ctrl_type)
{
      switch (ctrl_type)
      {
      case CTRL_C_EVENT:
      case CTRL_BREAK_EVENT:
      case CTRL_CLOSE_EVENT:
      case CTRL_SHUTDOWN_EVENT:
            before_interrupt_quit_func();
            return TRUE;
      default:
            return FALSE;
      }
}
void InterruptService::signalHandler()
{
      if (checkFirstClose())
      {
            handler->beforeInterruptQuit();
            if (!grace_quit_flag)
            {
                  exit(0);
                           
                           
}
#else
void InterruptService::detectSignalThread()
{
      // Wait for signal indicating time to shut down.
      sigset_t wait_mask;
      sigemptyset(&wait_mask);
      sigaddset(&wait_mask, SIGINT);
      sigaddset(&wait_mask, SIGQUIT);
      sigaddset(&wait_mask, SIGTERM);
      int sig = 0;
      sigwait(&wait_mask, &sig);
      if (checkFirstClose())
      {
            handler->beforeInterruptQuit();
            if (!grace_quit_flag)
            {
                  exit(0);
            }
      }
}
#endif

InterruptService::InterruptService(InterruptServiceHandler* handler_)
: handler(handler_)
, grace_quit_flag(true)
, single_close_flag(false)
{
      static int INTERRUPT_SERVICE_INSTANCE_TIMES = 0;
      if ((INTERRUPT_SERVICE_INSTANCE_TIMES++) != 0)
      {
            throw std::runtime_error("misuse: InterruptService should only be instance once!");
      }
#ifdef WIN32
      grp.create_thread(boost::bind(&InterruptServiceHandler::run, handler));
      before_interrupt_quit_func = boost::bind(&InterruptService::signalHandler, this);
      SetConsoleCtrlHandler(console_ctrl_handler, TRUE);
#else
      // Block all signals for background thread.
      sigset_t new_mask;
      sigfillset(&new_mask);     
      pthread_sigmask(SIG_BLOCK, &new_mask, NULL);
      grp.create_thread(boost::bind(&InterruptServiceHandler::run, handler));
#endif
}

void InterruptService::join(bool grace_quit_flag_)
{
      grace_quit_flag = grace_quit_flag_;
#ifndef WIN32
      sigset_t wait_mask;
      sigemptyset(&wait_mask);
      sigaddset(&wait_mask, SIGINT);
      sigaddset(&wait_mask, SIGQUIT);
      sigaddset(&wait_mask, SIGTERM);
      pthread_sigmask(SIG_BLOCK, &wait_mask, 0);
      boost::thread detect_signal_thread(boost::bind(&InterruptService::detectSignalThread, this));
      sigset_t new_mask;
      sigfillset(&new_mask);     
      pthread_sigmask(SIG_BLOCK, &new_mask, NULL);
#endif
      grp.join_all();
      if (checkFirstClose())
      {
            handler->beforeNormalQuit();                 
      }
}

bool InterruptService::checkFirstClose()
{
      boost::mutex::scoped_lock lock(single_close_mtx);
      if (!single_close_flag)
      {
            single_close_flag = true;
            return true;           
         
      else
      {
            return false;
      }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值