setitimer unter Linux in C

Huemler

Mitglied
Hallo ich habe folgendes Problem.
Ich arbeite mit C unter Linux und möchte folgendes Problem lösen:

Ich lese Zeichen von der seriellen Schnittstelle ein, kommt ein "q" dann wird das Programm beendet.
Jetzt möchte ich aber auch, das sich das Programm beendet, wenn z.B 1 Sekunde lang kein Zeichen mehr kommt.
Wenn jedoch innerhalb der Sekunde ein neues Zeichen kommt, soll der TImer gestoppt oder zurückgesetzt werden und wieder neu gestartet werden.
Deshalb habe ich nun geschaut mit was für Befehlen man dies unter Linux machen könnte und bin auf "setitimer" gestoßen.
Jetzt hab ich schon ewig rumprobiert, aber es klappt nicht wirklich.

Hat jemand schon so etwas mit "setitimer" realisiert oder hat jemand einen Tipp wie ich das realisieren kann?

Mein Code zum einlesen der Zeichen(ohne den Timer) sieht folgendermaßen aus:

/* Daten von der seriellen Schnittstelle lesen */
freopen("text.txt", "a+", stdout);
while((c=getch()) != 'q')
{
count++;
putc(c, stdout);
printf(" Inhalt von buffer: %X\n",c);
}
freopen("/dev/console", "w", stdout);
printf("Empfangene Zeichen = %d\n", count);
printf("Port wurde geschlossen\n");
return(0);

Gruß Huemler
 
Hallo,

ich denke die Funktion poll tuts indem Fall auch ...

Schau mal da:

C:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define __USE_XOPEN
#include <sys/poll.h>
#undef __USE_XOPEN

#define STDIN_PATH "/dev/stdin"

int main()
{
    struct pollfd pfd;
    int status;
    char input[1024];
    /* timeout in ms for poll */
    int timeout = 10000;

    bzero(&pfd, sizeof(pfd));

    if ((pfd.fd = open(STDIN_PATH, O_RDONLY)) < 0) {
        perror(__FILE__ ": Error in open()");
        goto err;
    }
    pfd.events = POLLRDNORM;

    /* block until data is available to read or timeout is reached */
    while ((status = poll(&pfd, 1, timeout)) >= 0) {
        if (status == 0) {
            printf("Timeout reached\n");
        } else if (status == -1) {
            perror(__FILE__ ": Error in poll()");
            goto err;
        } else {
            if (read(pfd.fd, input, 1024) == -1) {
                perror(__FILE__ ": Error in read()");
                goto err;
            }
            input[strlen(input) - 1] = '\0'; /* just to prevent output of \n */
            printf(__FILE__": Got input %s\n", input);
        }
    }

    return EXIT_SUCCESS;
err:
    if (pfd.fd)
        close(pfd.fd);
    return EXIT_FAILURE;
}

Gruß,
RedWing
 
Vielen Dank RedWing,
habe es ausprobiert, so auf den ersten Blick scheint es zu funktionieren.
Habe die poll Funktion noch garnicht gekannt.
 
Hi,

kein Problem. Schau dir einfach mal die man page zu poll an, dann bekommst du genauere Infos.

Gruß,
RedWing
 
Mh jetzt hab ich doch noch ne Frage.
Hast du schon mit poll gearbeitet und Erfahrungen damit gemacht?
Weil ich hab folgendes Problem, wenn ich
"pfd.events = POLLRDNORM;"
benutze dann gibt er mir nur jedes zweite Zeichen aus, der timeout funktioniert jedoch.
Wenn ich aber "pfd.events = POLLOUT;"
benutze dann gibt er mir jedes Zeichen aus, der timeout geht jedoch nichtmehr, da bei diesem Mode nicht geblockt wird.

Ich brauch aber natürlich jedes Zeichen + timeout.
 
Also POLLOUT bzw POLLWRNORM ist eigtl. nur fürs schreiben auf die Datei gedacht. Er blockiert halt die meiste Zeit nicht weil du wahrsch. sofort was schreiben kannst. Du solltest wenn du von der seriellen Schnittstelle lesen willst schon POLLIN oder POLLRDNORM als Ereignisse verwenden.

Wie liest du denn die Zeichen aus bzw wie sieht dein poll Konstrukt jetzt aus? Hast du mal über pfd.revents abgefragt welches Ereigniss anliegt wenn die poll Funktion zurückkehrt?

Äquivalent könntest du auch versuchen das ganze mit select zu regeln. Siehe "man select"

Gruß,
RedWing
 
Zuletzt bearbeitet:
Meine Routine zum lesen von der Schnittstelle sieht folgendermaßen aus:
while ((status = poll(&pfd, 1, timeout)) >= 0)
{
if (status == 0)
{
printf("Timeout erreicht\n");
return(0);
}
else if ((c=getch()) != 'q')
{
count++;
putc(c, stdout);
printf(" Inhalt von buffer: %X\n",c);
}
else
{
printf("Empfangene Zeichen = %d\n", count);
printf("Port wurde geschlossen\n");
return(0); /* nötig oder nicht? */
}
}

Und hier wird also nur jedes zweite Zeichen beachtet.
Ich glaube es liegt am getch, dieses getch ist nämlich ein für Linux geschriebenes getch.
Denn wenn ich den Code ausführe denn du gepostet hats, dann wird jedes Zeichen ausgegeben.
Aber ich brauche das getch () weil ich Zeichenweise einlesen muss, damit ich sofort wenn z.B ein q kommt das Programm beenden kann.
Und das geht meines Wissens ja bei read nicht, da read auf ein Enter wartet.
 
Hallo,

also i.A. ist es bei read eigtl. nicht so das es auch \n wartet. Das liegt viel mehr an dem Beschreiben des Eingabestreams stdin, denn da liegt erst was drin wenn ein \n von der Tastatur kommt. Ich denke das read auch bei deinem Fall funktioniert, da du ja nicht von stdin sondern von einem anderen Device liest. Hast dus denn schonmal mit read probiert?

Gruß,
RedWing
 
Hallo,

falls das nicht funktionieren sollte hätt ich noch ein Beispiel für dich wie man setitimer verwenden kann.
C:
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>

void timer_alarm(int signr)
{
    struct itimerval t_val;
    int res;

    printf(__FILE__ ": Timer event occured\n");

    /* reprogram timer to 5 seconds */
    bzero(&t_val, sizeof(t_val));
    t_val.it_value.tv_sec = 1;
    if ((res = setitimer(ITIMER_REAL, &t_val, NULL)) < 0) {
        perror(__FILE__ ": Error in setitimer()");
    }
}

int main()
{
    struct itimerval t_val;
    int res;

    if (signal(SIGALRM, timer_alarm) == SIG_ERR) {
        fprintf(stderr, __FILE__ ": Cannot register signal handler\n");
        goto err;
    }

    /* program timer to 5 seconds */
    bzero(&t_val, sizeof(t_val));
    t_val.it_value.tv_sec = 1;
    if ((res = setitimer(ITIMER_REAL, &t_val, NULL)) < 0) {
        perror(__FILE__ ": Error in setitimer()");
        goto err;
    }

    /* only for demo purpose */
    while(1)
        pause();

    return EXIT_SUCCESS;
err:
    return EXIT_FAILURE;
}

siehe man setitimer

Anpassungen deinerseits währen dann halt das du nach jeder Eingabe den Timer reprogrammieren musst...
Allerdings würd ich das mit dem Timer erst versuchen wenn das mit dem read nicht klappt, da read in Verbindung mit poll die saubere Variante sein sollte.
Wenn du nur eine Auflösung mit Sekundenbereich benötigst tuts auch die Funktion alarm...

Gruß,
RedWing
 
Juhu es funktioniert.
Vielen Vielen Dank für deine Hilfe.
Hab es mit setitimer hinbekommen.
Hier kann ich jetzt den Timer im µSekundenbereich einstellen.

Falls es jemand hilft hier meine Routine:
Code:
void timer_handler (int i)
{
 signal (SIGALRM, timer_handler);
 for (j=0; j<4000; j++)
 {
  putc(buffer[j], stdout);
 }	
 exit(0);	
}

int main(void)
{
	struct itimerval timer;
	timer.it_value.tv_sec = SEC_INTERVAL;
	timer.it_value.tv_usec = USEC_INTERVAL;
	timer.it_interval.tv_sec = SEC_INTERVAL;
	timer.it_interval.tv_usec = USEC_INTERVAL;
	setitimer (ITIMER_REAL, &timer, 0);
	signal(SIGALRM, timer_handler);

while((c=getch()) != 'q') 
		{
		setitimer (ITIMER_REAL, &timer, 0);
		signal(SIGALRM, timer_handler);
		count++;
		buffer[j] = c;
		j++;
		}
		for (j=0; j<4000; j++)
		{
		putc(buffer[j], stdout);
		}
		return(0);	
}

Gruß Huemler
 
Zuletzt bearbeitet:
Zurück