在程序開發過程中,我們時不時要用到一些定時器,通常如果時間精度要求不高,可以使用sleep,uslepp函數讓進程睡眠一段時間來實現定時,
前者單位為秒(s),后者為微妙(us);但有時候我們又不想讓進程睡眠阻塞在哪兒,我們需要進程正常執行,當到達規定的時間時再去執行相應的操作,
在linux下面我們一般使用alarm函數跟setitimer函數來實現定時功能;
下面對這兩個函數進行詳細分析:
(1)alarm函數
alarm也稱為鬧鐘函數,它可以在進程中設置一個定時器,當定時器指定的時間到時,它向進程發送SIGALRM信號;
alarm函數原型如下:
unsigned int alarm(unsigned int seconds);//seconds 為指定的秒數
所需頭文件
#include<unistd.h>
函數原型
unsigned int alarm(unsigned int seconds)
函數參數
seconds:指定秒數
函數返回值
成功:如果調用此alarm()前,進程已經設置了鬧鐘時間,則返回上一個鬧鐘時間的剩余時間,否則返回0。
出錯:-1
下面是alarm()函數的簡單例子:
void sigalrm_fn(int sig) { printf("alarm!/n"); alarm(2); return; } int main(void) { signal(SIGALRM, sigalrm_fn); //后面的函數必須是帶int參數的 alarm(1); while(1) pause(); }
(2)setitimer()函數
在linux下如果對定時要求不太精確的話,使用alarm()和signal()就行了,但是如果想要實現精度較高的定時功能的話,就要使用setitimer函數。
setitimer()為Linux的API,并非C語言的Standard Library,setitimer()有兩個功能,一是指定一段時間后,才執行某個function,二是每間格一段時間就執行某個function;
Linux為每個任務安排了3個內部定時器:
ITIMER_REAL:實時定時器,不管進程在何種模式下運行(甚至在進程被掛起時),它總在計數。定時到達,向進程發送SIGALRM信號。
ITIMER_VIRTUAL:這個不是實時定時器,當進程在用戶模式(即程序執行時)計算進程執行的時間。定時到達后向該進程發送SIGVTALRM信號。
ITIMER_PROF:進程在用戶模式(即程序執行時)和核心模式(即進程調度用時)均計數。定時到達產生SIGPROF信號。ITIMER_PROF記錄的時間比ITIMER_VIRTUAL多了進程調度所花的時間。
定時器在初始化是,被賦予一個初始值,隨時間遞減,遞減至0后發出信號,同時恢復初始值。在任務中,我們可以一種或者全部三種定時器,但同一時刻同一類型的定時器只能使用一個。
setitimer函數原型如下:
#include <sys/time.h> int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value); Timer values are defined by the following structures: struct itimerval { struct timeval it_interval; /* next value */ struct timeval it_value; /* current value */ }; struct timeval { time_t tv_sec; /* seconds */ suseconds_t tv_usec; /* microseconds */ };
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; //1秒后啟用定時器 val.it_value.tv_usec = 0; val.it_interval = val.it_value; //定時器間隔為1s setitimer(ITIMER_PROF, &val, NULL);}int main(int argc, char **argv){ init_sigaction(); init_time(); while(1); return 0;}
可以看出每個一秒輸出一個count的值:
下面是運行結果:
[root@localhost 5th]# ./test
count is 0
count is 1
count is 2
count is 3
count is 4
count is 5
count is 6
count is 7
count is 8
count is 9
附錄:
signal
1. 頭文件
#include <signal.h>
2. 功能
設置某一信號的對應動作
3. 函數原型
void (*signal(int signum,void(* handler)(int)))(int);
分解來看:
typedef void (*sig_t) (int);
sig_t signal(int sig, sig_t func);
第一個參數是目標信號。func參數是一個指針,指向某個處理該信號的函數。這個處理信號函數帶有一個int型參數,并應返回void。
func參數也可以設定為下面的一些值:
SIG_IGN: 如果func參數被設置為SIG_IGN,該信號將被忽略。
SIG_DFL: 如果func參數被設置為SIG_DFL,該信號會按照確定行為處理。
4. sig信號的可能類型
1) #define SIGHUP 1 /* hangup */
SIGHUP是Unix系統管理員很常用的一個信號。許多后臺服務進程在接受到該信號后將會重新讀取它們的配置文件。然而,該信號的實際功能是通知進程它的控制終端被斷開。缺省行為是終止進程。
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提供了一種在異常終止(abort)一個進程的同時創建一個核心轉儲的方法。然而如果該信號被捕獲,并且信號處理句柄沒有返回,那么進程不會終止。缺省行為是終止進程,并且創建一個核心轉儲。
7) #define SIGFPE 8 /* floating point exception */
當進程發生一個浮點錯誤時,SIGFPE信號被發送給該進程。對于那些處理復雜數學運算的程序,一般會建議你捕獲該信號。缺省行為是終止進程,并且創建一個核心轉儲。
8) #define SIGKILL 9 /* kill (cannot be caught or ignored) */
SIGKILL是這些信號中最難對付的一個。正如你在它旁邊的注釋中看到的那樣,這個信號不能被捕獲或忽略。一旦該信號被交付給一個進程,那么這個進程就會終止。然而,會有一些極少數情況SIGKILL不會終止進程。這些罕見的情形在處理一個“非中斷操作”(比如磁盤I/O)的時候發生。雖然這樣的情形極少發生,然而一旦發生的話,會造成進程死鎖。唯一結束進程的辦法就只有重新啟動了。缺省行為是終止進程。
9) #define SIGBUS 10 /* bus error */
如同它的名字暗示的那樣,CPU檢測到數據總線上的錯誤時將產生SIGBUS信號。當程序嘗試去訪問一個沒有正確對齊的內存地址時就會產生該信號。缺省行為是終止進程,并且創建一個核心轉儲。
10) #define SIGSEGV 11 /* segmentation violation */
SIGSEGV是另一個C/C++程序員很熟悉的信號。當程序沒有權利訪問一個受保護的內存地址時,或者訪問無效的虛擬內存地址(臟指針,dirty pointers,譯注:由于沒有和后備存儲器中內容進行同步而造成。關于野指針,可以參見http://en.wikipedia.org/wiki/Wild_pointer 的解釋。)時,會產生這個信號。缺省行為是終止進程,并且創建一個核心轉儲。
11) #define SIGSYS 12 /* non-existent system call invoked */
SIGSYS信號會在進程執行一個不存在的系統調用時被交付。操作系統會交付該信號,并且進程會被終止。缺省行為是終止進程,并且創建一個核心轉儲。
12) #define SIGPIPE 13 /* write on a pipe with no one to read it */
管道的作用就像電話一樣,允許進程之間的通信。如果進程嘗試對管道執行寫操作,然而管道的另一邊卻沒有回應者時,操作系統會將SIGPIPE信號交付給這個討厭的進程(這里就是那個打算寫入的進程)。缺省行為是終止進程。
13) #define SIGALRM 14 /* alarm clock */
在進程的計時器到期的時候,SIGALRM信號會被交付(delivered)給進程。這些計時器由本章后面將會提及
的setitimer和alarm調用設置。缺省行為是終止進程。
14) #define SIGTERM 15 /* software termination signal from kill */
SIGTERM信號被發送給進程,通知該進程是時候終止了,并且在終止之前做一些清理活動。SIGTERM信號是Unix的kill命令發送的缺省信號,同時也是操作系統關閉時向進程發送的缺省信號。缺省行為是終止進程。
15) #define SIGURG 16 /* urgent condition on IO channel */
在進程已打開的套接字上發生某些情況時,SIGURG將被發送給該進程。如果進程不捕獲這個信號的話,那么將被丟棄。缺省行為是丟棄這個信號。
16) #define SIGSTOP 17 /* sendable stop signal not from tty */
本信號不能被捕獲或忽略。一旦進程接收到SIGSTOP信號,它會立即停止(stop),直到接收到另一個SIGCONT
信號為止。缺省行為是停止進程,直到接收到一個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上的實現有更好的接口。(如果信號是一個沒有追溯能力的過程(not a retroactive process),那么BSD的SIGCHID信號實現會比較好。在system V Unix的實現中,如果進程要求捕獲該信號,操作系統會檢查是否存在有任何未完成的子進程(這些子進程是已經退出exit)的子進程,并且在等待調用wait的父進程收集它們的狀態)。如果子進程退出的時候附帶有一些終止信息(terminating information),那么信號處理句柄就會被調用。所以,僅僅要求捕獲這個信號會導致信號處理句柄被調用(譯注:即是上面說的“信號的追溯能力”),而這是卻一種相當混亂的狀況。)一旦一個進程的子進程狀態發生改變,SIGCHLD信號就會被發送給該進程。就像我在前面章節提到的,父進程雖然可以fork出子進程,但沒有必要等待子進程退出。一般來說這是不太好的,因為這樣的話,一旦進程退出就可能會變成一個僵尸進程。可是如果父進程捕獲SIGCHLD信號的話,它就可以使用wait系列調用中的某一個去收集子進程狀態,或者判斷發生了什么事情。當發送SIGSTOP,SIGSTP或SIGCONF信號給子進程時,SIGCHLD信號也會被發送給父進程。缺省行為是丟棄該信號。
20) #define SIGTTIN 21 /* to readers pgrp upon background tty read */
當一個后臺進程嘗試進行一個讀操作時,SIGTTIN信號被發送給該進程。進程將會阻塞直到接收到SIGCONT信號為止。缺省行為是停止進程,直到接收到SIGCONT信號。
21) #define SIGTTOU 22 /* like TTIN if (tp->t_local<OSTOP) */
SIGTTOU信號與SIGTTIN很相似,不同之處在于SIGTTOU信號是由于后臺進程嘗試對一個設置了TOSTOP屬性的tty執行寫操作時才會產生。然而,如果tty沒有設置這個屬性,SIGTTOU就不會被發送。缺省行為是停止進程,直到接收到SIGCONT信號。
22) #define SIGIO 23 /* input/output possible signal */
如果進程在一個文件描述符上有I/O操作的話,SIGIO信號將被發送給這個進程。進程可以通過fcntl調用來設置。缺省行為是丟棄該信號。
23) #define SIGXCPU 24 /* exceeded CPU time limit */
如果一旦進程超出了它可以使用的CPU限制(CPU limit),SIGXCPU信號就被發送給它。這個限制可以使用隨后討論的setrlimit設置。缺省行為是終止進程。
24) #define SIGXFSZ 25 /* exceeded file size limit */
如果一旦進程超出了它可以使用的文件大小限制,SIGXFSZ信號就被發送給它。稍后我們會繼續討論這個信號。缺省行為是終止進程。
25) #define SIGVTALRM 26 /* virtual time alarm */
如果一旦進程超過了它設定的虛擬計時器計數時,SIGVTALRM信號就被發送給它。缺省行為是終止進程。
26) #define SIGPROF 27 /* profiling time alarm */
當設置了計時器時,SIGPROF是另一個將會發送給進程的信號。缺省行為是終止進程。
27) #define SIGWINCH 28 /* window size changes */
當進程調整了終端的行或列時(比如增大你的xterm的尺寸),SIGWINCH信號被發送給該進程。缺省行為是丟棄該信號。
28) #define SIGUSR1 29 /* user defined signal 1 */
29) #define SIGUSR2 30 /* user defined signal 2 */
SIGUSR1和SIGUSR2這兩個信號被設計為用戶指定。它們可以被設定來完成你的任何需要。換句話說,操作系統沒有任何行為與這兩個信號關聯。缺省行為是終止進程。(譯注:按原文的意思翻譯出來似乎這兩句話有點矛盾。)
5. 例子
5.1. Linux下的Ctrl+C在Windows下的實現一
Linux下通常的做法:
signal(SIGINT, sigfunc); // 設置信號void sigfunc(int signo) { ... //處理信號相關的操作 }
以下是Linux下的Ctrl+C在Windows下的實現
#include <stdio.h> #include <windows.h> static is_loop = 1; // 捕獲控制臺 Ctrl+C 事件的函數 BOOL CtrlHandler( DWORD fdwCtrlType ) { switch (fdwCtrlType) { /* Handle the CTRL-C signal. */ case CTRL_C_EVENT: printf("CTRL_C_EVENT /n"); break; case CTRL_CLOSE_EVENT: printf("CTRL_CLOSE_EVENT /n"); break; case CTRL_BREAK_EVENT: printf("CTRL_BREAK_EVENT /n"); break; case CTRL_LOGOFF_EVENT: printf("CTRL_LOGOFF_EVENT /n"); break; case CTRL_SHUTDOWN_EVENT: printf("CTRL_SHUTDOWN_EVENT /n"); break; default: return FALSE; } is_loop = 0; return (TRUE); } int main(int argc, char *argv[]) { printf("Set Console Ctrl Handler/n"); SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); while (is_loop); return 0; }
5.2.Linux下的Ctrl+C在Windows下的實現二
#include <stdio.h> #include <windows.h> #define CONTRL_C_HANDLE() signal(3, exit) int main(int argc, char *argv[]) { printf("Set Console Ctrl Handler/n"); CONTRL_C_HANDLE(); while (1); system("PAUSE"); return 0; }
以上就是小編為大家帶來的淺談linux幾種定時函數的使用全部內容了,希望大家多多支持VEVB武林網~
新聞熱點
疑難解答