Проблема с интервальным таймером - PullRequest
0 голосов
/ 09 апреля 2011

Наш текущий проект основан на расширении more включением прокрутки. Для этого необходимо установить интервал таймера на определенный период. Часть, в которой я не уверен, - где должна быть петля для сигнала тревоги. Все примеры, которые я видел, имеют значения таймера в основном, а затем явно вызывают обработчик сигнала через pause() в бесконечном цикле while.

Мой код немного отличается, так как требования к функциональности похожи на

print first screen of text after getting terminal dimensions
print prompt
   if prompt = space, print another screen of text //WORKS
   if prompe = q, restore original terminal settings & quit program //WORKS
   if prompt = ENTER, initialize scroll at 1 line every 2 seconds //DOESN'T WORK
   if prompt == f/s, increase/decrease scroll speed by 20%  //DOESN'T WORK

Чтение в буфере, указатель файла и структура itimerval являются глобальными переменными, чтобы избежать передачи в качестве аргументов через цепочку функций.

Основная функция программы

void processInput(FILE *fp){
  void printLine(int); //prints a single line of text
  signal(SIGPROF, printLine);
  int c;

  //print first screen of text, check for more text to display

  info(); //print prompt at bottom of screen
  FILE *fterm= fopen("/dev/tty", "r");

  while ((c=getc(fterm)) != EOF){
    if (c== '\n'){
      setTimer(2);

     //four more conditionals like this in basic similarity

  }
}

Моя функция setTimer имеет базовый интервал, равный 2 секундам, и меняет его на плюс / минус 20% в зависимости от значения f / s от пользователя.

void setTimer(int direction){
  int speed=2000000; //2 seconds
  int change= 400000; //400 milliseconds, 20% of 2 seconds
  if (direction == 1) //slow down by 20%
    speed+= change;
  if (direction == 0)
    speed -= change;

  timer.it_value.tv_sec=2;
  timer.it_value.tv_usec=0;
  timer.it_interval.tv_sec=0;
  timer.it_interval.tv_usec= speed;

  setitimer(ITIMER_PROF, &timer, NULL);

}

Первый вопрос: я должен использовать SIGALRM против SIGPROF и соответственно изменить переменную ITIMER_XXXX?

Во-вторых, где я должен поставить петлю для запуска сигнала? Я пытался

while(1)
  pause();

в некоторых условных выражениях, но это привело к остановке выполнения и игнорированию любого ввода.

Ответы [ 2 ]

1 голос
/ 09 апреля 2011

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

Лучше было бы сделать следующее:

1) Установите маску сигнала, которая блокирует SIGPROF при запуске вашей программы.
2) Вместо того, чтобы использовать обработчик сигнала для выполнения тяжелой работы, используйте sigwait() и установите его с sigset_t, который содержит маску для SIGPROF.
3) Настройте основной поток вашей программы следующим образом:

sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGPROF);
sigprocmask(SIG_BLOCK, &sigset, NULL); //block the SIGPROF signal

//process your input

//if you need to, initialize your timer and set it to go off

while(SOME_FLAG_IS_TRUE) //maybe this loops forever ... depends on what you want?
{
    sigwait(&sigset, &signal_num);

    if (signal_num != SIGPROF)
        continue;

    //process input ... 

    //... setup new interval timer with correct timeout ...

    //... repeat loop and/or exit loop or set flag to exit loop
}

Это должно всегда перехватывать сигнал от интервального таймера, так как sigwait () будет правильно возвращаться после ожидания поступления сигнала в ваш процесс, а сигнал SIGPROF всегда блокируется, то есть вы не можете «потерять» сигналы .. вместо этого хотя бы один из них будет поставлен в очередь и ожидает обнаружения следующего вызова на sigwait() на тот случай, если он поступит, пока вы обрабатываете что-то в цикле while.

Надеюсь, это поможет,

Jason

1 голос
/ 09 апреля 2011

Не зная деталей ваших требований, вы не могли бы сделать это проще, используя выберите ()

Установите начальный тайм-аут выбора на 2 секунды и отрегулируйте его в соответствии с вводом f / s, при этом если есть какой-либо стандартный ввод до истечения времени ожидания, которое вы обрабатываете.

Более или менее действительный общий план:

   int retval;
fd_set rfds;

int input = fileno(fterm);

struct timeval tv, delay;

delay.tv_sec  = 2;
delay.tv_usec = 0;

while (true)
{
    FD_ZERO(&rfds);
    FD_SET(input, &rfds);

    tv.tv_sec  = delay.tv_sec;
    tv.tv_usec = delay.tv_usec;

    retval = select(input + 1, &rfds, NULL, NULL, &tv);

    if (retval == -1)
        perror("select()");
    else
        if (retval)
        {
            if (FD_ISSET(input, &rfds))
            {
                command = readInput(...);

                switch(command)
                {
                    case 'q' ... cleanup & quit
                    case 's' ... calc 20% off delay values
                    case etc ...
                    case default...error handling
                }
            }
        }
        else //timeout
            printLine();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...