permette di bloccare una sezione di un file usando la semantica POSIX, o un
intero file usando la semantica BSD; in fig.~\ref{fig:file_flock_code} è
riportata il corpo principale del codice del programma, (il testo completo è
-allegato nella directory dei sorgenti).
+allegato nella directory dei sorgenti, nel file \texttt{Flock.c}).
La sezione relativa alla gestione delle opzioni al solito si è omessa, come la
funzione che stampa le istruzioni per l'uso del programma, essa si cura di
rispettive funzioni consente di fare contemporaneamente entrambe le cose.
Per risolvere questo problema nello sviluppo del kernel si è pensato di
-introdurre un meccanismo alternativo alla notifica dei segnali (esteso anche
+introdurre un meccanismo alternativo per la notifica dei segnali (esteso anche
ad altri eventi generici) che, ispirandosi di nuovo alla filosofia di Unix per
cui tutto è un file, consentisse di eseguire la notifica con l'uso di
opportuni file descriptor.\footnote{ovviamente si tratta di una funzionalità
che per un bug i kernel fino al 2.6.25 non avvalorano correttamente i campi
\var{ssi\_ptr} e \var{ssi\_int} per segnali inviati con \func{sigqueue}.}
+Come esempio di questa nuova interfaccia ed anche come esempio di applicazione
+della interfaccia di \itindex{epoll} \textit{epoll}, si è scritto un programma
+elementare che stampi sullo standard output sia quanto viene scritto da terzi
+su una \textit{named fifo}, che l'avvenuta ricezione di alcuni segnali. Il
+codice completo si trova al solito nei sorgenti allegati alla guida (nel file
+\texttt{FifoReporter.c}).
+
+In fig.~\ref{fig:fiforeporter_code_init} si è riportata la parte iniziale del
+programma in cui vengono effettuate le varie inizializzazioni necessarie per
+l'uso di \itindex{epoll} \textit{epoll} e \func{signalfd}, a partire
+(\texttt{\small 12--16}) dalla definizione delle varie variabili e strutture
+necessarie. Al solito si è tralasciata la parte dedicata alla decodifica delle
+opzioni che consentono ad esempio di cambiare il nome del file associato alla
+fifo.
+
+\begin{figure}[!htb]
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \includecodesample{listati/FifoReporter-init.c}
+ \end{minipage}
+ \normalsize
+ \caption{Sezione di inizializzazione del codice del programma
+ \file{FifoReporter.c}.}
+ \label{fig:fiforeporter_code_init}
+\end{figure}
+
+Il primo passo (\texttt{\small 19--20}) è la crezione di un file descriptor
+\texttt{epfd} di \itindex{epoll} \textit{epoll} con \func{epoll\_create} che è
+quello che useremo per il controllo degli altri. É poi necessario
+disabilitare la ricezione dei segnali (nel caso \const{SIGINT},
+\const{SIGQUIT} e \const{SIGTERM}) per i quali si vuole la notifica tramite
+file descriptor. Per questo prima li si inseriscono (\texttt{\small 22--25}) in
+una maschera di segnali \texttt{sigmask} che useremo con (\texttt{\small 26})
+\func{sigprocmask} per disabilitarli. Con la stessa maschera si potrà per
+passare all'uso (\texttt{\small 28--29}) di \func{signalfd} per abilitare la
+notifica sul file descriptor \var{sigfd}. Questo poi (\texttt{\small 30--33})
+dovrà essere aggiunto con \func{epoll\_ctl} all'elenco di file descriptor
+controllati con \texttt{epfd}.
+
+Occorrerà infine (\texttt{\small 35--38}) creare la \textit{named fifo} se
+questa non esiste ed aprirla per la lettura (\texttt{\small 39--40}); una
+volta fatto questo sarà necessario aggiungere il relativo file descriptor
+(\var{fifofd}) a quelli osservati da \itindex{epoll} \textit{epoll} in maniera
+del tutto analoga a quanto fatto con quello relativo alla notifica dei
+segnali.
+
+\begin{figure}[!htb]
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \includecodesample{listati/FifoReporter-main.c}
+ \end{minipage}
+ \normalsize
+ \caption{Ciclo principale del codice del programma \file{FifoReporter.c}.}
+ \label{fig:fiforeporter_code_body}
+\end{figure}
+
+Una volta completata l'inizializzazione verrà eseguito indefinitamente il
+ciclo principale del programma (\texttt{\small 2--45}) che si è riportato in
+fig.~\ref{fig:fiforeporter_code_body}, fintanto che questo non riceva un
+segnale di \texttt{SIGINT} (ad esempio con la pressione di \texttt{C-c}). Il
+ciclo prevede che si attenda (\texttt{\small 2--3}) la presenza di un file
+descriptor pronto in lettura con \func{epoll\_wait},\footnote{si ricordi che
+ entrambi i file descriptor \var{fifofd} e \var{sigfd} sono stati posti in
+ osservazioni per eventi di tipo \const{EPOLLIN}.} che si bloccherà fintanto
+che non siano stati scritti dati sulla fifo o che non sia arrivato un
+segnale.\footnote{per semplificare il codice non si è trattato il caso in cui
+ \func{epoll\_wait} viene interrotta da un segnale, assumendo che tutti
+ quelli che possano interessare siano stati predisposti per la notifica
+ tramite file descriptor, per gli altri si otterrà semplicemente l'uscita dal
+ programma.}
+
+Anche se in questo caso i file descriptor pronti possono essere al più due, si
+è comunque adottato un approccio generico in cui questi verranno letti
+all'interno di un opportuno ciclo (\texttt{\small 5--44}) sul numero
+restituito da \func{epoll\_wait}, esaminando i risultati presenti nel vettore
+\var{events} all'interno di una catena di condizionali alternativi sul valore
+del file descriptor riconosciuto come pronto.\footnote{controllando cioè a
+ quale dei due file descriptor possibili corrisponde il campo relativo,
+ \var{events[i].data.fd}.}
+
+Il primo condizionale (\texttt{\small 6--24}) è relativo al caso che si sia
+ricevuto un segnale e che il file descriptor pronto corrisponda
+(\texttt{\small 6}) a \var{sigfd}. Dato che in generale si possono ricevere
+anche notifiche relativi a più di un singolo segnale, si è scelto di leggere
+una struttura \const{signalfd\_siginfo} alla volta, eseguendo la lettura
+all'interno di un ciclo (\texttt{\small 8--24}) che prosegue fintanto che vi
+siano dati da leggere.
+
+Per questo ad ogni lettura si esamina (\texttt{\small 9--14}) se il valore di
+ritorno della funzione \func{read} è negativo, uscendo dal programma
+(\texttt{\small 11}) in caso di errore reale, o terminando il ciclo
+(\texttt{\small 13}) con un \texttt{break} qualora si ottenga un errore di
+\errcode{EAGAIN} per via dell'esaurimento dei dati.\footnote{si ricordi come
+ sia la fifo che il file descriptor per i segnali siano stati aperti in
+ modalità non-bloccante, come previsto per l’\textit{I/O multiplexing},
+ pertanto ci si aspetta di ricevere un errore di \errcode{EAGAIN} quando non
+ vi saranno più dati da leggere.}
+
+In presenza di dati invece il programma proseguirà l'esecuzione stampando
+(\texttt{\small 19--20}) il nome del segnale ottenuto all'interno della
+struttura \const{signalfd\_siginfo} letta in \var{siginf}\footnote{per la
+ stampa si è usato il vettore \var{sig\_names} a ciascun elemento del quale
+ corrisponde il nome del segnale avente il numero corrispondente, la cui
+ definizione si è omessa dal codice di fig.~\ref{fig:fiforeporter_code_init}
+ per brevità.} ed il \textit{pid} del processo da cui lo ha ricevuto; inoltre
+(\texttt{\small 21--24}) si controllerà anche se il segnale ricevuto è
+\var{SIGINT}, che si è preso come segnale da utilizzare per la terminazione
+del programma, che verrà eseguita dopo aver rimosso il file della \textit{name
+ fifo}.
+
+Il secondo condizionale (\texttt{\small 26--39}) è invece relativo al caso in
+cui ci siano dati pronti in lettura sulla fifo e che il file descriptor pronto
+corrisponda (\texttt{\small 26}) a \var{fifofd}. Di nuovo si effettueranno le
+letture in un ciclo (\texttt{\small 28--39}) ripetendole fin tanto che la
+funzione \func{read} non resituisce un errore di \errcode{EAGAIN}
+(\texttt{\small 29--35}).\footnote{il procedimento è lo stesso adottato per il
+ file descriptor associato al segnale, in cui si esce dal programma in caso
+ di errore reale, in questo caso però alla fine dei dati prima di uscire si
+ stampa anche (\texttt{\small 32}) un messaggio di chiusura.} Se invece vi
+sono dati validi letti dalla fifo si inserirà (\texttt{\small 36}) una
+terminazione di stringa sul buffer e si stamperà il tutto (\texttt{\small
+ 37--38}) sullo \textit{standard output}. L'ultimo condizionale
+(\texttt{\small 40--44}) è semplicemente una condizione di cattura per una
+eventualità che comunque non dovrebbe mai verificarsi, e che porta alla uscita
+dal programma con una opportuna segnalazione di errore.
+
+A questo punto si potrà eseguire il comando lanciandolo su un terminale, ed
+osservarne le reazioni agli eventi generati da un altro terminale; lanciando
+il programma otterremo qualcosa del tipo:
+\begin{Verbatim}
+piccardi@hain:~/gapil/sources$ ./a.out
+FifoReporter starting, pid 4568
+\end{Verbatim}
+%$
+e scrivendo qualcosa sull'altro terminale con:
+\begin{Verbatim}
+root@hain:~# echo prova > /tmp/reporter.fifo
+\end{Verbatim}
+si otterrà:
+\begin{Verbatim}
+Message from fifo:
+prova
+end message
+\end{Verbatim}
+mentre inviando un segnale:
+\begin{Verbatim}
+root@hain:~# kill 4568
+\end{Verbatim}
+si avrà:
+\begin{Verbatim}
+Signal received:
+Got SIGTERM
+From pid 3361
+\end{Verbatim}
+ed infine premendo \texttt{C-\bslash} sul terminale in cui è in esecuzione si
+vedrà:
+\begin{Verbatim}
+^\Signal received:
+Got SIGQUIT
+From pid 0
+\end{Verbatim}
+e si potrà far uscire il programma con \texttt{C-c} ottenendo:
+\begin{Verbatim}
+^CSignal received:
+Got SIGINT
+From pid 0
+SIGINT means exit
+\end{Verbatim}
+
+
Lo stesso paradigma di notifica tramite file descriptor usato per i segnali è
-stato adottato anche per i timer; in questo caso, rispetto a quanto visto in
+stato adottato anche per i timer. In questo caso, rispetto a quanto visto in
sez.~\ref{sec:sig_timer_adv}, la scadenza di un timer potrà essere letta da un
-file descriptor, senza dover ricorrere ad altri meccanismi di notifica come un
+file descriptor senza dover ricorrere ad altri meccanismi di notifica come un
segnale o un \textit{thread}. Di nuovo questo ha il vantaggio di poter
utilizzare le funzioni dell'\textit{I/O multiplexing} per attendere allo
-stesso tempo la disponibilità di dati o la ricezione scadenza di un
+stesso tempo la disponibilità di dati o la ricezione della scadenza di un
timer.\footnote{in realtà per questo sarebbe già sufficiente \func{signalfd}
per ricevere i segnali associati ai timer, ma la nuova interfaccia
semplifica notevolmente la gestione e consente di fare tutto con una sola
\textit{system call}.}
-Le funzioni di questa interfaccia ricalcano da vicino la struttura delle
+Le funzioni di questa nuova interfaccia ricalcano da vicino la struttura delle
analoghe versioni ordinarie introdotte con lo standard POSIX.1-2001, che
abbiamo già illustrato in sez.~\ref{sec:sig_timer_adv}.\footnote{questa
interfaccia è stata introdotta in forma considerata difettosa con il kernel
2.6.22, per cui è stata immediatamente tolta nel successivo 2.6.23 e
reintrodotta in una forma considerata adeguata nel kernel 2.6.25, il
supporto nelle \acr{glibc} è stato introdotto a partire dalla versione
- 2.8.6, la versione del kernel 2.6.22 non è supportata e non deve essere
- usata.} La prima funzione prevista, quella che consente di creare un
-\textit{timer}, è \funcd{timerfd\_create}, il cui prototipo è:
+ 2.8.6, la versione del kernel 2.6.22, presente solo su questo kernel, non è
+ supportata e non deve essere usata.} La prima funzione prevista, quella che
+consente di creare un timer, è \funcd{timerfd\_create}, il cui prototipo è:
\begin{prototype}{sys/timerfd.h}
{int timerfd\_create(int clockid, int flags)}
verranno notificate le scadenze dei timer. Come per quelli restituiti da
\func{signalfd} anche questo file descriptor segue la semantica dei sistemi
unix-like, in particolare resta aperto attraverso una \func{exec},\footnote{a
- meno che non si sia impostato il flag di \textit{close-on exex} con
+ meno che non si sia impostato il flag di \textit{close-on exec} con
\const{TFD\_CLOEXEC}.} e viene duplicato attraverso una \func{fork}; questa
ultima caratteristica comporta però che anche il figlio può utilizzare i dati
di un timer creato nel padre, a differenza di quanto avviene invece con i
come illustrato in sez.~\ref{sec:proc_fork}, allarmi, timer e segnali
pendenti nel padre vengono cancellati per il figlio dopo una \func{fork}.}
-Una volta creato il timer con \funcd{timerfd\_create} per poterlo utilizzare
+Una volta creato il timer con \func{timerfd\_create} per poterlo utilizzare
occorre \textsl{armarlo} impostandone un tempo di scadenza ed una eventuale
-periodicità di ripetizione, per farlo su usa la funzione omologa di
+periodicità di ripetizione, per farlo si usa la funzione omologa di
\func{timer\_settime} per la nuova interfaccia; questa è
-\func{timerfd\_settime}; il suo prototipo è:
+\funcd{timerfd\_settime} ed il suo prototipo è:
\begin{prototype}{sys/timerfd.h}
{int timerfd\_settime(int fd, int flags,
const struct itimerspec *new\_value,
successo o $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno
dei valori:
\begin{errlist}
- \item[\errcode{EINVAL}] l'argomento \param{clockid} non è
- \const{CLOCK\_MONOTONIC} o \const{CLOCK\_REALTIME}, o
- l'argomento \param{flag} non è valido, o è diverso da zero per kernel
- precedenti il 2.6.27.
- \item[\errcode{ENOMEM}] non c'è memoria sufficiente per creare un nuovo file
- descriptor di \func{signalfd}.
- \item[\errcode{ENODEV}] il kernel non può montare internamente il
- dispositivo per la gestione anonima degli inode associati al file
- descriptor.
+ \item[\errcode{EBADF}] l'argomento \param{fd} non corrisponde ad un file
+ descriptor.
+ \item[\errcode{EINVAL}] il file descriptor \param{fd} non è stato ottenuto
+ con \func{timerfd\_create}, o i valori di \param{flag} o dei campi
+ \var{tv\_nsec} in \param{new\_value} non sono validi.
+ \item[\errcode{EFAULT}] o \param{new\_value} o \param{old\_value} non sono
+ puntatori validi.
\end{errlist}
- ed inoltre \errval{EMFILE} e \errval{ENFILE}.
}
\end{prototype}
+In questo caso occorre indicare su quale timer si intende operare specificando
+come primo argomento il file descriptor ad esso associato, che deve essere
+stato ottenuto da una precedente chiamata a \func{timerfd\_create}. I restanti
+argomenti sono del tutto analoghi a quelli della omologa funzione
+\func{timer\_settime}, e prevedono l'uso di strutture \struct{itimerspec}
+(vedi fig.~\ref{fig:struct_itimerspec}) per le indicazioni di temporizzazione.
+
+I valori ed il significato di questi argomenti sono gli stessi che sono già
+stati illustrati in dettaglio in sez.~\ref{sec:sig_timer_adv} e non staremo a
+ripetere quanto detto in quell'occasione;\footnote{per brevità si ricordi che
+ con \param{new\_value.it\_value} si indica la prima scadenza del timer e
+ con \param{new\_value.it\_interval} la sua periodicità.} l'unica differenza
+riguarda l'argomento \param{flags} che serve sempre ad indicare se il tempo di
+scadenza del timer è da considerarsi relativo o assoluto rispetto al valore
+corrente dell'orologio associato al timer, ma che in questo caso ha come
+valori possibili rispettivamente soltanto $0$ e
+\const{TFD\_TIMER\_ABSTIME}.\footnote{anche questo valore, che è l'analogo di
+ \const{TIMER\_ABSTIME} è l'unico attualmente possibile per \param{flags}.}
+
+L'ultima funzione prevista dalla nuova interfaccia è \funcd{timerfd\_gettime},
+che è l'analoga di \func{timer\_gettime}, il suo prototipo è:
+\begin{prototype}{sys/timerfd.h}
+ {int timerfd\_gettime(int fd, struct itimerspec *curr\_value)}
+ Crea un timer associato ad un file descriptor per la notifica.
-
-.\footnote{si otterranno in entrambi i casi gli stessi
- risultati, il file descriptor risulterà pronto in lettura e in entrambi i
- processi si potranno leggere il numero di scadenze.}
+ \bodydesc{La funzione restituisce un numero di file descriptor in caso di
+ successo o $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno
+ dei valori:
+ \begin{errlist}
+ \item[\errcode{EBADF}] l'argomento \param{fd} non corrisponde ad un file
+ descriptor.
+ \item[\errcode{EINVAL}] il file descriptor \param{fd} non è stato ottenuto
+ con \func{timerfd\_create}.
+ \item[\errcode{EFAULT}] o \param{curr\_value} non è un puntatore valido.
+ \end{errlist}
+}
+\end{prototype}
-Questo infatti diverrà pronto in
-lettura per tutte le varie funzioni dell'I/O multiplexing in presenza di una o
-più scadenze del timer ad esso associato.
+Questo infatti diverrà pronto in lettura per tutte le varie funzioni dell'I/O
+multiplexing in presenza di una o più scadenze del timer ad esso associato.
-Inoltre sarà possibile ottenere il
-numero di volte che il timer è scaduto dalla ultima impostazione
+Inoltre sarà possibile ottenere il numero di volte che il timer è scaduto
+dalla ultima impostazione
che può essere
usato per leggere le notifiche delle scadenze dei timer. Queste possono essere
% http://lwn.net/Articles/267331/
-\begin{figure}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{15cm}
- \includecodesample{listati/FifoReporter-init.c}
- \end{minipage}
- \normalsize
- \caption{Sezione di inizializzazione del codice del programma
- \file{FifoReporter.c}.}
- \label{fig:fiforeporter_code}
-\end{figure}
-
-
-
-\begin{figure}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{15cm}
- \includecodesample{listati/FifoReporter-main.c}
- \end{minipage}
- \normalsize
- \caption{Ciclo principale del codice del programma \file{FifoReporter.c}.}
- \label{fig:fiforeporter_code}
-\end{figure}
-
-
-
\section{L'accesso \textsl{asincrono} ai file}
\label{sec:file_asyncronous_access}
% LocalWords: Jens Anxboe vmsplice seek ESPIPE GIFT TCP CORK MSG splicecp nr
% LocalWords: nwrite segs patch readahead posix fadvise TC advice FADV NORMAL
% LocalWords: SEQUENTIAL NOREUSE WILLNEED DONTNEED streaming fallocate EFBIG
-% LocalWords: POLLRDHUP half close pwait Gb madvise MADV ahead REMOVE tmpfs
+% LocalWords: POLLRDHUP half close pwait Gb madvise MADV ahead REMOVE tmpfs it
% LocalWords: DONTFORK DOFORK shmfs preadv pwritev syscall linux loff head XFS
% LocalWords: MERGEABLE EOVERFLOW prealloca hole FALLOC KEEP stat fstat union
% LocalWords: conditions sigwait CLOEXEC signalfd sizemask SIGKILL SIGSTOP ssi
-% LocalWords: sigwaitinfo FifoReporter Windows ptr sigqueue
+% LocalWords: sigwaitinfo FifoReporter Windows ptr sigqueue named timerfd TFD
+% LocalWords: clockid CLOCK MONOTONIC REALTIME itimerspec interval
+% LocalWords: ABSTIME gettime
%%% Local Variables:
#include <string.h> /* C strings library */
#include <sys/timerfd.h> /* timerfd */
#include <sys/epoll.h> /* Linux epoll interface */
-// #include <time.h>
+#include <time.h>
#include "macros.h"
#include "Gapil.h"
* Variables definition
*/
int i, n, nread, fd, epfd;
+ int wait=5, interval=1, nswait=0, nsinter=0; // timer default
pid_t pid;
struct epoll_event epev, events[MAX_EPOLL_EV];
struct itimerspec expiring;
- uint64_t expirated;
+ uint64_t expired;
/*
* Input section: decode command line parameters
* Use getopt function
*/
opterr = 0; /* don't want writing to stderr */
- while ( (i = getopt(argc, argv, "h")) != -1) {
+ while ( (i = getopt(argc, argv, "ht:i:w:n:")) != -1) {
switch (i) {
/*
* Handling options
usage();
return -1;
break;
+ case 'i': /* timer interval */
+ interval = strtol(optarg, NULL, 10); /* convert input */
+ break;
+ case 't': /* timer expiring */
+ wait = strtol(optarg, NULL, 10); /* convert input */
+ break;
+ case 'n': /* timer interval in ns */
+ nsinter = strtol(optarg, NULL, 10); /* convert input */
+ break;
+ case 'w': /* timer expiring in ns */
+ nswait = strtol(optarg, NULL, 10); /* convert input */
+ break;
case '?': /* unrecognized options */
printf("Unrecognized options -%c\n",optopt);
usage();
}
/* timerfd setup */
fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
- expiring.it_interval.tv_sec=1;
- expiring.it_interval.tv_nsec=0;
- expiring.it_value.tv_sec=5;
- expiring.it_value.tv_nsec=0;
+ expiring.it_interval.tv_sec=interval;
+ expiring.it_interval.tv_nsec=nsinter;
+ expiring.it_value.tv_sec=wait;
+ expiring.it_value.tv_nsec=nswait;
if (timerfd_settime(fd, 0, &expiring, NULL)) {
die("Cannot set timer");
}
+ printf("Timer interval %i sec, timer time %i sec\n", wait, interval);
pid = fork();
/* epoll setup */
if ((epfd=epoll_create(5)) < 0)
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &epev))
die("Failing in epoll_ctl");
/* main loop */
- //while (1) {
- if ((n=epoll_wait(epfd, events, MAX_EPOLL_EV, -1)) < 0)
- die("error on epoll_wait");
- debug("Got %i events\n", n);
- /* loop on epoll events */
- for (i=0; i<n; i++) {
- if (events[i].data.fd == fd) { // if timer expired
- printf("Timer expired:\n");
- while(nread=read(fd, &expirated, sizeof(expirated))) {
- if (nread < 0) {
- if (errno != EAGAIN)
- die("signalfd read error");
- else
- break;
- }
- if (nread != sizeof(expirated)) {
- printf("Error on timer data read, '\n");
- continue;
- }
- printf("Expired %llu times\n", expirated);
- if (pid == 0) {
- printf("in child\n");
- } else {
- printf("in father\n");
+ while (1) {
+ while (n=epoll_wait(epfd, events, MAX_EPOLL_EV, -1)) {
+ if (n < 0) {
+ if (errno != EAGAIN)
+ die("error on epoll_wait");
+ else
+ continue;
+ } else {
+ printf("Got %i events, pid %i, time %i\n", n, pid, time(NULL));
+ /* loop on epoll events */
+ for (i=0; i<n; i++) {
+ if (events[i].data.fd == fd) { // if timer expired
+ printf("Timer expired in pid %i:\n", pid);
+ while(nread=read(fd, &expired, sizeof(expired))) {
+ if (nread < 0) {
+ if (errno != EAGAIN)
+ die("signalfd read error");
+ else
+ break;
+ }
+ if (nread != sizeof(expired)) {
+ printf("Error on timer data read, '\n");
+ continue;
+ }
+ printf("Expired %llu times in pid %i\n",
+ expired, pid);
+ }
}
}
}
}
- // }
+ }
return 0;
}
/*
printf("Program testtimerfdfork : test timerfd across fork \n");
printf("Usage:\n");
printf(" testtimerfdfork [-h] \n");
+ printf(" -i sec interval for repetition\n");
+ printf(" -t sec time to wait to expire\n");
printf(" -h print this help\n");
exit(1);