Problème avec la timer d’intervalle

Notre projet actuel est basé sur une extension more en incluant scroll. Pour ce faire, un intervalle de temps doit être défini pour une certaine période. La partie dont je ne suis pas sûr est où la boucle pour le signal d’alarme devrait être. Tous les exemples que j’ai vus ont les valeurs du temporisateur dans le principal, puis appellent explicitement le gestionnaire de signaux via pause() dans une boucle while infinie.

Mon code est un peu différent, car les exigences de fonctionnalité vont comme

 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 

Le tampon en lecture, le pointeur de fichier et la structure itimerval sont tous des variables globales permettant d’éviter de passer en argument via une chaîne de fonctions.

La fonction principale du programme est

 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 } } 

Ma fonction setTimer a un intervalle de base de 2 secondes et la modifie de plus ou moins 20% en fonction de l’entrée f / s de l’utilisateur.

 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); } 

Première question: devrais-je utiliser SIGALRM vs SIGPROF et modifier la variable ITIMER_XXXX en conséquence?

Deuxièmement, où dois-je mettre en boucle pour déclencher le signal? j’ai essayé

 while(1) pause(); 

dans plusieurs conditions, mais cela a eu pour effet d’arrêter l’exécution et d’ignorer toute entrée.

Sans connaître les détails de vos besoins, ne pourriez-vous pas faire cela plus facilement en utilisant select () ?

Définissez votre délai de sélection initial à 2 secondes et ajustez-le en fonction de l’entrée de la f / s, en attendant s’il y a une entrée standard avant le délai d’attente, vous le traitez.

Description générale plus ou moins valable:

  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(); } 

Travailler avec pause() est dangereux car ce n’est pas une opération atomique … votre programme pourrait être interrompu par le système d’exploitation vous faisant “perdre” l’arrivée d’un signal. De plus, lorsque pause() retourne lui-même à cause de l’arrivée d’un signal, il appelle simplement pause() nouveau. Cela signifie que vous allez devoir faire tout votre travail à l’intérieur d’un gestionnaire de signal, ce qui n’est peut-être pas la meilleure chose, c.-à-d. Si vous êtes dans le gestionnaire de signal lorsque le prochain signal disparaît, vous pouvez vous retrouver avec un comportement imprévisible si vous n’avez pas prévu ce type d’événement.

Une meilleure approche serait de faire ce qui suit:

1) Configurez un masque de signal qui bloque SIGPROF au début de votre programme.
2) Plutôt que d’utiliser un gestionnaire de signal pour faire votre travail, utilisez sigwait() et configurez-le avec un sigset_t contenant un masque pour SIGPROF.
3) Configurez le stream principal de votre programme de la manière suivante:

 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 } 

Cela devrait toujours attraper le signal de l’intervalle de temps car sigwait () retournera correctement après avoir attendu un signal pour arriver à votre processus, et le signal SIGPROF est toujours bloqué, ce qui signifie que vous ne pouvez pas “perdre” les signaux … au moins l’un d’entre eux sera mis en queue et attendra que le prochain appel à sigwait() soit détecté au cas où l’un d’entre eux arriverait pendant que vous traitez quelque chose dans votre boucle while.

J’espère que cela t’aides,

Jason