间隔定时器问题

时间:2011-04-09 01:26:23

标签: c unix timer

我们当前的项目基于通过包含滚动来扩展more。为此,必须在一定时间内设置定时器间隔。我不确定的部分是报警信号的循环应该在哪里。我看到的所有示例都在main中有定时器值,然后在无限循环中通过pause()显式调用信号处理程序。

我的代码有点不同,因为功能要求就像

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秒,并根据用户的f / s输入按正/负20%进行更改。

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 个答案:

答案 0 :(得分:1)

如果不了解您的要求的详细信息,您无法更轻松地使用此功能 select()

将初始选择超时设置为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();
}

答案 1 :(得分:1)

使用pause()是危险的,因为它不是原子操作......操作系统可能会中断您的程序,导致您“丢失”信号的到来。此外,当pause()由于信号到达而返回时,它只会再次调用pause()。这意味着你将不得不在信号处理程序中完成所有工作,这可能不是最好的事情,即如果你在下一个信号关闭时进入信号处理程序,你可以最终得到如果您没有计划进行此类活动,那么会出现一些不可预测的行为。

更好的方法是做以下事情:

1)设置一个信号掩码,在程序开始时阻止SIGPROF 2)使用sigwait()而不是使用信号处理程序来完成繁重的工作,而是使用包含SIGPROF掩码的sigset_t进行设置。 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循环中处理某些东西时到达。

希望这有帮助,

杰森