English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Nel processo di sviluppo del programma, a volte dobbiamo utilizzare alcuni timer, di solito se la precisione del tempo non è alta, possiamo far dormire il processo per un periodo di tempo utilizzando le funzioni sleep, usleep per implementare la temporizzazione,
La prima ha unità in secondi (s), la seconda in microsecondi (us); ma a volte non vogliamo che il processo si blocchi insonne, dobbiamo farlo eseguire normalmente e poi eseguire l'operazione specificata al raggiungimento del tempo stabilito,
Sotto Linux, di solito utilizziamo la funzione alarm e la funzione setitimer per implementare la funzione di temporizzazione;
Di seguito, analizziamo dettagliatamente queste due funzioni:
(1) funzione alarm
Alarm, noto anche come funzione sveglia, può impostare un timer in un processo, che quando il timer raggiunge il tempo specificato, invia al processo il segnale SIGALRM;
Il prototipo della funzione alarm è come segue:
unsigned int alarm(unsigned int seconds); //seconds è il numero di secondi specificato
File di intestazione richiesto
#include<unistd.h>
Prototipo della funzione
unsigned int alarm(unsigned int seconds)
Parametro della funzione
seconds: specificare il numero di secondi
Valore di ritorno della funzione
Successo: Se il processo ha già impostato il tempo dell'allarme prima della chiamata a questo alarm(), viene restituito il tempo rimanente dell'ultimo allarme impostato, altrimenti viene restituito 0.
Errore: -1
Ecco un esempio semplice della funzione alarm():
void sigalrm_fn(int sig) { printf("alarm!\n"); alarm(2); return; } int main(void) { signal(SIGALRM, sigalrm_fn); // La funzione successiva deve avere un parametro int alarm(1); while(1) pause(); }
(2) Funzione setitimer()
Sotto Linux, se le esigenze di temporizzazione non sono particolarmente precise, è sufficiente utilizzare alarm() e signal(), ma se si desidera implementare una funzione di temporizzazione con alta precisione, è necessario utilizzare la funzione setitimer.
setitimer() è una API di Linux, non una libreria standard del linguaggio C, setitimer() ha due funzioni, una è specificare un intervallo di tempo dopo il quale viene eseguita una funzione, l'altra è eseguire una funzione a intervalli regolari;
Linux assegna a ogni compito 3 timer interni:
ITIMER_REAL: Timer in tempo reale, non importa in quale modalità esegue il processo (addirittura se il processo è sospeso), continua a contare. Dopo la scadenza, invia al processo il segnale SIGALRM.
ITIMER_VIRTUAL: Questo non è un timer in tempo reale, calcola il tempo di esecuzione del processo nel modalità utente (cioè durante l'esecuzione del programma). Dopo la scadenza, invia al processo il segnale SIGVTALRM.
ITIMER_PROF: il processo conta sia nel modalità utente (cioè durante l'esecuzione del programma) che nel modalità kernel (cioè durante la pianificazione del processo). La scadenza produce il segnale SIGPROF. Il tempo registrato da ITIMER_PROF è maggiore del tempo speso dalla pianificazione del processo rispetto a ITIMER_VIRTUAL.
Il timer viene assegnato un valore iniziale durante l'inizializzazione, che si riduce nel tempo, emette un segnale quando si riduce a 0 e ripristina il valore iniziale. Durante l'esecuzione del compito, possiamo utilizzare uno o tutti e tre i timer, ma non più di un timer dello stesso tipo contemporaneamente.
setitimer函数原型如下:
#include <sys/time.h> int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value); 定时器值由以下结构定义: struct itimerval { struct timeval it_interval; /* 下一个值 */ struct timeval it_value; /* 当前值 */ ; struct timeval { time_t tv_sec; /* 秒 */ suseconds_t tv_usec; /* 微秒 */ ;
it_interval用于指定每隔多长时间执行任务, it_value用于保存当前时间离执行任务还有多长时间。比如说, 你指定it_interval为2秒(微秒为0),开始的时候我们把it_value的时间也设定为2秒(微秒为0),当过了一秒, it_value就减少一个为1, 再过1秒,则it_value又减少1,变为0,这个时候发出信号(告诉用户时间到了,可以执行任务了),并且系统自动把it_value的时间重置为 it_interval的值,即2秒,再重新计数
下面是setitimer简单实例:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <signal.h> #include <sys/time.h> void test_func(); { static count = 0; printf("count is %d\n", count++); } void init_sigaction(); { struct sigaction act; act.sa_handler = test_func; // 设置处理信号的函数 act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGPROF, &act, NULL); // 时间到发送SIGROF信号 } void init_time()} { struct itimerval val; val.it_value.tv_sec = 1; // Abilita il timer dopo 1 secondo val.it_value.tv_usec = 0; val.it_interval = val.it_value; // L'intervallo del timer è di 1s setitimer(ITIMER_PROF, &val, NULL); } int main(int argc, char **argv) { init_sigaction(); init_time(); while(1); return 0; }
Si può notare che viene output un valore di count ogni secondo:
Ecco i risultati dell'esecuzione:
[root@localhost 5th]# ./test
count è 0
count è 1
count è 2
count è 3
count è 4
count è 5
count è 6
count è 7
count è 8
count è 9
Appendice:
signal
1. File di intestazione
#include <signal.h>
2. Funzione
Impostare l'azione corrispondente a un segnale
3. Prototipo della funzione
void (*signal(int signum,void(* handler)(int)))(int);
Analizziamo:
typedef void (*sig_t) (int);
sig_t signal(int sig, sig_t func);
Il primo parametro è il segnale di destinazione. Il parametro func è un puntatore che si riferisce a una funzione che gestisce il segnale. Questa funzione di gestione del segnale ha un parametro di tipo int e deve restituire void.
Il parametro func può anche essere impostato su alcuni dei seguenti valori:
SIG_IGN: Se il parametro func viene impostato su SIG_IGN, il segnale verrà ignorato.
SIG_DFL: Se il parametro func viene impostato su SIG_DFL, il segnale verrà gestito secondo il comportamento definito.
4. Tipi possibili di segnali sig
1) #define SIGHUP 1 /* hangup */
SIGHUP è un segnale molto utilizzato dagli amministratori di sistema Unix. Molti processi di servizio in background riletteranno i loro file di configurazione dopo aver ricevuto questo segnale. Tuttavia, la funzione effettiva di questo segnale è notificare al processo che il suo terminale di controllo è stato disconnesso. Il comportamento predefinito è terminare il processo.
2) #define SIGINT 2 /* interrupt */
对于Unix使用者来说,SIGINT是另外一个常用的信号。许多shell的CTRL-C组合使得这个信号被大家所熟知。该信号的正式名字是中断信号。默认行为是终止进程。
3) #define SIGQUIT 3 /* quit */
SIGQUIT信号被用于接收shell的CTRL-/组合。另外,它还用于告知进程退出。这是一个常用信号,用来通知应用程序从容的(译注:即在结束前执行一些退出动作)关闭。默认行为是终止进程,并且创建一个核心转储。
4) #define SIGILL 4 /* illegal instr. (not reset when caught) */
如果正在执行的进程中包含非法指令,操作系统将向该进程发送SIGILL信号。如果你的程序使用了线程,或者pointer functions,那么可能的话可以尝试捕获该信号来协助调试。([color=Red]注意:原文这句为:“If your program makes use of use of threads, or pointer functions, try to catch this signal if possible for aid in debugging.”。中间的两个use of use of,不知是原书排版的瑕疵还是我确实没有明白其意义;另外,我经常听说functions pointer,对于pointer functions,google了一下,应该是fortran里面的东西,不管怎样,还真不知道,确切含义还请知道的兄弟斧正。[/color])默认行为是终止进程,并且创建一个核心转储。
5) #define SIGTRAP 5 /* trace trap (not reset when caught) */
SIGTRAP这个信号是由POSIX标准定义的,用于调试目的。当被调试进程接收到该信号时,就意味着它到达了某一个调试断点。一旦这个信号被交付,被调试的进程就会停止,并且它的父进程将接到通知。默认行为是终止进程,并且创建一个核心转储。
6) #define SIGABRT 6 /* abort() */
SIGABRT fornisce un metodo per terminare un processo in modo anomalo (abort) e creare un dump di core. Tuttavia, se il segnale viene catturato e il gestore di segnale non restituisce, il processo non termina. Il comportamento predefinito è terminare il processo e creare un dump di core.
7) #define SIGFPE 8 /* eccezione a virgola mobile */
Quando un processo si verifica un errore a virgola mobile, viene inviato il segnale SIGFPE al processo. Per quei programmi che eseguono calcoli matematici complessi, si consiglia generalmente di catturare questo segnale. Il comportamento predefinito è terminare il processo e creare un dump di core.
8) #define SIGKILL 9 /* kill (non può essere catturato o ignorato) */
SIGKILL è uno dei segnali più difficili da affrontare. Come puoi vedere nel commento accanto, questo segnale non può essere catturato o ignorato. Una volta che il segnale è stato consegnato a un processo, il processo termina. Tuttavia, ci sono alcune rare situazioni in cui SIGKILL non termina il processo. Queste rare circostanze si verificano durante la gestione di un'operazione non interruptiva (ad esempio I/O su disco). Anche se queste situazioni sono rare, una volta che si verificano, possono causare un blocco del processo. L'unico modo per terminare il processo è riavviarlo. Il comportamento predefinito è terminare il processo.
9) #define SIGBUS 10 /* errore di bus */
Come suggerisce il nome, quando il CPU rileva un errore sul bus dati viene generato il segnale SIGBUS. Quando un programma tenta di accedere a un indirizzo di memoria non correttamente allineato viene generato questo segnale. Il comportamento predefinito è terminare il processo e creare un dump di core.
10) #define SIGSEGV 11 /* violazione di segmentazione */
SIGSEGV è un altro segnale conosciuto dai programmatori C/C++. Quando un programma non ha il permesso di accedere a un indirizzo di memoria protetta o accede a un indirizzo di memoria virtuale non valido (puntatori sporchi, dirty pointers, nota: a causa della mancanza di sincronizzazione con il contenuto del memories storage. Per quanto riguarda i puntatori selvaggi, si può consultare la spiegazione su http://en.wikipedia.org/wiki/Wild_pointer .) viene generato questo segnale. Il comportamento predefinito è terminare il processo e creare un dump di core.
11) #define SIGSYS 12 /* non-existent system call invoked */
Il segnale SIGSYS viene consegnato al processo quando esegue una chiamata di sistema inesistente. Il sistema operativo consegna il segnale e il processo viene terminato. Il comportamento predefinito è terminare il processo e creare un dump di core.
12) #define SIGPIPE 13 /* write on a pipe with no one to read it */
L'azione della pipe è simile a quella di un telefono, permettendo la comunicazione tra processi. Se un processo tenta di scrivere su una pipe e non c'è nessuno a leggere l'altro lato, il sistema operativo consegna il segnale SIGPIPE a questo processo fastidioso (qui è il processo che tenta di scrivere). Il comportamento predefinito è terminare il processo.
13) #define SIGALRM 14 /* alarm clock */
Quando il timer del processo scade, il segnale SIGALRM viene consegnato (delivered) al processo. Questi timer sono menzionati più avanti nel capitolo.
delle chiamate setitimer e alarm. Il comportamento predefinito è terminare il processo.
14) #define SIGTERM 15 /* software termination signal from kill */
Il segnale SIGTERM viene inviato al processo per notificare che è il momento di terminare e fare alcune operazioni di pulizia prima della terminazione. Il segnale SIGTERM è il segnale predefinito inviato dal comando kill di Unix e anche il segnale predefinito inviato dal sistema operativo al processo durante la chiusura. Il comportamento predefinito è terminare il processo.
15) #define SIGURG 16 /* urgent condition on IO channel */
Quando si verificano determinate condizioni sui socket aperti dal processo, SIGURG viene inviato al processo. Se il processo non cattura questo segnale, viene scartato. Il comportamento predefinito è scartare il segnale.
16) #define SIGSTOP 17 /* sendable stop signal not from tty */
Questo segnale non può essere catturato o ignorato. Una volta che il processo riceve il segnale SIGSTOP, si ferma immediatamente (stop) fino a quando non riceve un altro SIGCONT
Il segnale di stop. Il comportamento predefinito è fermare il processo fino a quando non riceve un segnale SIGCONT.
17) #define SIGTSTP 18 /* stop signal from tty */
SIGSTP与SIGSTOP类似,它们的区别在于SIGSTP信号可以被捕获或忽略。当shell从键盘接收到CTRL-Z的时候就会交付(deliver)这个信号给进程。默认行为是停止进程,直到接收到一个SIGCONT信号为止。
18) #define SIGCONT 19 /* continue a stopped process */
SIGCONT也是一个有趣的信号。如前所述,当进程停止的时候,这个信号用来告诉进程恢复运行。该信号的有趣之处在于:它不能被忽略或阻塞,但可以被捕获。这样做很有意义:因为进程大概不愿意忽略或阻塞SIGCONT信号,否则,如果进程接收到SIGSTOP或SIGSTP的时候该怎么办?默认行为是丢弃该信号。
19) #define SIGCHLD 20 /* to parent on child stop or exit */
SIGCHLD是由Berkeley Unix引入的,并且比SRV 4 Unix上的实现有更好的接口。(如果信号是一个没有追溯能力的过程(non un processo retroattivo),那么BSD的SIGCHID信号实现会比较好。在system V Unix的实现中,如果进程要求捕获该信号,操作系统会检查是否存在有任何未完成的子进程(这些子进程已经退出exit)的子进程,并且在等待调用wait的父进程收集它们的状态)。如果子进程退出的时候附带有一些终止信息(terminating information),那么信号处理句柄就会被调用。所以,仅仅要求捕获这个信号会导致信号处理句柄被调用(译注:即是上面说的“信号的追溯能力”),而这是却一种相当混乱的状况。)一旦一个进程的子进程状态发生改变,SIGCHLD信号就会被发送给该进程。就像我在前面章节提到的,父进程虽然可以fork出子进程,但没有必要等待子进程退出。一般来说这是不太好的,因为这样的话,一旦进程退出就可能会变成一个僵尸进程。可是如果父进程捕获SIGCHLD信号的话,它就可以使用wait系列调用中的某一个去收集子进程状态,或者判断发生了什么事情。当发送SIGSTOP、SIGSTP或SIGCONF信号给子进程时,SIGCHLD信号也会被发送给父进程。默认行为是丢弃该信号。
20) #define SIGTTIN 21 /* ai lettori del pgrp durante la lettura di tty in background */
Quando un processo in background tenta di eseguire una lettura, viene inviato un segnale SIGTTIN a tale processo. Il processo rimarrà bloccato fino a quando non riceve il segnale SIGCONT. Il comportamento predefinito è fermare il processo fino a quando non riceve il segnale SIGCONT.
21) #define SIGTTOU 22 /* come TTIN se (tp->t_local<OSTOP) */
Il segnale SIGTTOU è molto simile a SIGTTIN, la differenza sta nel fatto che SIGTTOU viene generato solo quando un processo in background tenta di eseguire un'operazione di scrittura su un tty che ha impostato l'attributo TOSTOP. Tuttavia, se il tty non ha impostato questa proprietà, SIGTTOU non viene inviato. Il comportamento predefinito è fermare il processo fino a quando non riceve il segnale SIGCONT.
22) #define SIGIO 23 /* segnale possibile di input/output */
Se un processo esegue operazioni di I/O su un descrittore di file, viene inviato un segnale SIGIO a tale processo. Il processo può impostare questo comportamento utilizzando la chiamata fcntl. Il comportamento predefinito è ignorare il segnale.
23) #define SIGXCPU 24 /* superato il limite di tempo di CPU */
Se il processo supera il limite di CPU che può utilizzare (limite di CPU), viene inviato un segnale SIGXCPU a tale processo. Questo limite può essere impostato utilizzando la funzione setrlimit discussa in seguito. Il comportamento predefinito è terminare il processo.
24) #define SIGXFSZ 25 /* superamento del limite di dimensione del file */
Se il processo supera il limite di dimensione del file che può utilizzare, viene inviato al processo il segnale SIGXFSZ. Ne discuteremo più avanti questo segnale. Il comportamento predefinito è terminare il processo.
25) #define SIGVTALRM 26 /* allarme di tempo virtuale */
Se il processo supera il contatore del timer virtuale impostato, viene inviato al processo il segnale SIGVTALRM. Il comportamento predefinito è terminare il processo.
26) #define SIGPROF 27 /* allarme di tempo di profilazione */
Quando viene impostato un timer, SIGPROF è un altro segnale che verrà inviato al processo. Il comportamento predefinito è terminare il processo.
27) #define SIGWINCH 28 /* cambiamenti di dimensione della finestra */
Quando il processo aggiusta la riga o la colonna del terminale (ad esempio, aumenta la dimensione del tuo xterm), viene inviato al processo il segnale SIGWINCH. Il comportamento predefinito è ignorare il segnale.
28) #define SIGUSR1 29 /* segnale definito dall'utente 1 */
29) #define SIGUSR2 30 /* segnale definito dall'utente 2 */
SIGUSR1 e SIGUSR2 sono segnali progettati per essere specificati dall'utente. Possono essere configurati per soddisfare qualsiasi tua esigenza. In altre parole, il sistema operativo non ha alcun comportamento associato a questi segnali. Il comportamento predefinito è terminare il processo. (Note: Secondo il testo originale, queste due frasi sembrano contraddittorie.)
5. Esempio
5.1. Implementazione di Ctrl+C in Windows sotto Linux1.
Il metodo comune sotto Linux:
signal(SIGINT, sigfunc); // Impostare il segnale void sigfunc(int signo) { ... // Operazioni relative ai segnali }
Di seguito è riportata l'implementazione di Ctrl+C in Windows sotto Linux
#include <stdio.h> #include <windows.h> static is_loop = 1; // Funzione per catturare l'evento Ctrl+C della console BOOL CtrlHandler( DWORD fdwCtrlType ) { switch (fdwCtrlType) { /* Gestire il segnale CTRL-C. */ case CTRL_C_EVENT: printf("EVENTO CTRL_C \n"); break; case CTRL_CLOSE_EVENT: printf("EVENTO DI CHIUSURA CTRL \n"); break; case CTRL_BREAK_EVENT: printf("EVENTO DI INTERRUZIONE CTRL \n"); break; case CTRL_LOGOFF_EVENT: printf("EVENTO DI DISCONNESSIONE CTRL \n"); break; case CTRL_SHUTDOWN_EVENT: printf("EVENTO DI SPEGNIMENTO CTRL \n"); break; default: return FALSE; } is_loop = 0; return (TRUE); } int main(int argc, char *argv[]) { printf("Impostare il gestore della console Ctrl\n"); SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); while (is_loop); return 0; }
5.2.Esecuzione di Ctrl+C sotto Linux in Windows
#include <stdio.h> #include <windows.h> #define CONTRL_C_HANDLE() signal(3, exit) int main(int argc, char *argv[]) { printf("Impostare il gestore della console Ctrl\n"); CONTRL_C_HANDLE(); while (1); system("PAUSE"); return 0; }
Questo è tutto il contenuto che l'editor vi ha portato su un breve discorso sull'uso di alcune funzioni di pianificazione temporizzata di Linux, spero che riceviate molto supporto e applausi per le lezioni di tutorial~