Как обрабатывать сигнал ctrl-break в интерфейсе командной строки - PullRequest
5 голосов
/ 08 октября 2008

Прежде чем начать, я хочу уточнить, что это не инструмент командной строки, а приложение, которое принимает команды через собственный интерфейс командной строки.

Редактировать: Я должен извиниться за свое объяснение, видимо, я не очень хорошо объяснил его. Еще раз ...

Я создаю приложение интерфейса командной строки, которое принимает команды от пользователя. У меня есть настройка обработчика сигналов для перехвата сигналов, которая затем устанавливает флаг, который мне нужен для завершения приложения. Проблема, с которой я сталкиваюсь, заключается в том, что все функции консоли, которые я могу найти, являются блокирующими, что означает, что я не могу определить, что мне нужно выйти из цикла обработки консоли, пока пользователь не нажмет клавишу (или не введет, в зависимости от функции ).

Есть ли какой-нибудь стандартный способ, которым я могу выполнять неблокируемое взаимодействие с консолью, или есть элегантный способ структурировать программу так, чтобы, если я просто завершу работу с сигнальным потоком, все было обработано и освобождено должным образом (пожалуйста, Не поймите это неправильно, я знаю, как это можно сделать, используя блокировку и освобождение ресурсов из потока сигнализации, но это может стать грязным, поэтому я бы предпочел этого избежать)

Надеюсь, это объяснение имеет больше смысла ...

Ответы [ 5 ]

8 голосов
/ 08 октября 2008

ОК - это работает для меня в Windows и переносимо - обратите внимание на #ifdef SIGBREAK - это не стандартный сигнал.

#include <csignal>
#include <iostream>
#include <ostream>
#include <string>
using namespace std;

namespace
{
    volatile sig_atomic_t quit;

    void signal_handler(int sig)
    {
        signal(sig, signal_handler);
        quit = 1;
    }
}

int main()
{
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
#ifdef SIGBREAK
    signal(SIGBREAK, signal_handler);
#endif
    /* etc */

    while (!quit)
    {
        string s;
        cin >> s;
        cout << s << endl;
    }
    cout << "quit = " << quit << endl;
}
6 голосов
/ 08 октября 2008

На * nix, вы можете использовать функцию signal для регистрации обработчика сигнала:


#include <signal.h>

void signal_handler(int sig)
{
  // Handle the signal
}

int main(void)
{
  // Register the signal handler for the SIGINT signal (Ctrl+C)
  signal(SIGINT, signal_handler);
  ...
}

Теперь, когда кто-нибудь нажимает Ctrl + C, вызывается ваш обработчик сигнала.

2 голосов
/ 08 октября 2008
1 голос
/ 08 октября 2008

В * nix-системах вам может не понадобиться обработчик сигналов, чтобы это работало. Вы можете указать, что хотите игнорировать вызов SIGINT

int main(void)
{
  // Register to ignore the SIGINT signal (Ctrl+C)
  signal(SIGINT, SIG_IGN);

  while(1)
  {
    retval = my_blocking_io_func();
    if(retval == -1 && errno == EINTR)
    {
      // do whatever you want to do in case of interrupt
    }
  }
}

Важный способ, которым это работает, состоит в том, чтобы признать, что неблокирующие функции действительно прерываются. Как правило, вы понимаете, что функция блокировки не работает (например, read ()) и попробуйте снова. Если бы это было какое-то другое значение, вы бы предприняли соответствующее действие, связанное с ошибкой.

0 голосов
/ 03 сентября 2013

Лучшим решением * nix, которое является поточно-ориентированным, является использование pthread_sigmask () вместо signal ().
Например, вот как вы подписываете SIGINT, SIGTERM и SIGPIPE в текущем и будущих порожденных потоках:

sigset_t waitset;
sigemptyset(&waitset);
sigaddset(&waitset, SIGINT);
sigaddset(&waitset, SIGTERM);
sigaddset(&waitset, SIGPIPE);
pthread_sigmask(SIG_BLOCK, &waitset, NULL);  
...