[技術] Linux下的Timer

Written on 10:36 上午 by Yu Lai

其實之前就有用到Timer,只是最近又再一次用到,所以把相關的部份整理一下。
在Linux下實做定時器有兩種方法,分別是alarm()和setitimer(),以下分別介紹:

1. alarm()
alarm()可以用來設定信號SIGALRM在指定的秒數後傳到目前的process。
使用起來比起setitimer較為簡單,但由於使用的單位是秒數,
所以如果不要求很精確的話,用alarm()配合signal()就夠了。

unsigned int alarm(unsigned int seconds)

alarm()用來設置信號SIGALRM在經過參數seconds指定的秒數後傳送給目前的process。
如果參數seconds為0,則之前設置的鬧鐘會被取消,並將剩下的時間返回,
如果之前未設鬧鐘則返回0。

void sigalrm_fn(int sig) {

printf("alarm!\n");
alarm(2);

return;

}

int main(void) {

signal(SIGALRM, sigalrm_fn);
alarm(1);
while(1) pause();

}


2. setitimer()
setitimer()比alarm功能強大,支持3種類型的Timer:
ITIMER_REAL : 以系統真實的時間來計算,它送出SIGALRM信號。
ITIMER_VIRTUAL : 以Process在User-Mode下花費的時間來計算,它送出SIGVTALRM信號。
ITIMER_PROF : 以Process在User-Mode下和Kernel-Mode下所費的時間來計算,它送出SIGPROF信號。

int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));

setitimer()第一個參數which指定定時器類型(上面三種之一);
第二個參數是struct itimerval的一個instance;第三個參數可不做處理。
setitimer()調用成功返回0,否則返回-1。

struct itimerval {
struct timeval it_interval; /* timer interval */
struct timeval it_value; /* current value */
};

itimerval結構中的it_value是減少的時間,當這個值為0的時候就發出相應的信號。
然後再將it_value設置為it_interval值。也就是說如果itimerval.it_interval不為0,
則該定時器將持續有效(每隔一段時間就會發送一個信號)。

下面是關於setitimer的一個簡單示範,在該例子中,
每隔一秒發出一個SIGALRM,每隔0.5秒發出一個SIGVTALRM信號:
int sec;

void sigroutine(int signo) {

switch (signo){
case SIGALRM:
printf("Catch a signal -- SIGALRM \n");
signal(SIGALRM, sigroutine);
break;
case SIGVTALRM:
printf("Catch a signal -- SIGVTALRM \n");
signal(SIGVTALRM, sigroutine);
break;
}
return;

}

int main() {

struct itimerval value, ovalue, value2;
sec = 5;

printf("process id is %d\n", getpid());
signal(SIGALRM, sigroutine);
signal(SIGVTALRM, sigroutine);

value.it_value.tv_sec = 1;
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = 1;
value.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &value, &ovalue);

value2.it_value.tv_sec = 0;
value2.it_value.tv_usec = 500000;
value2.it_interval.tv_sec = 0;
value2.it_interval.tv_usec = 500000;
setitimer(ITIMER_VIRTUAL, &value2, &ovalue);

for(;;) ;

}


PS:
Linux信號機制基本上是從Unix系統中繼承過來的。
早期Unix系統中的信號機制比較簡單和原始,後來在實踐中暴露出一些問題,
因此,把那些建立在早期機制上的信號叫做"不可靠信號",
信號值小於SIGRTMIN(SIGRTMIN=32,SIGRTMAX=63)的信號都是不可靠信號。
這就是"不可靠信號"的來源。它的主要問題是:Process每次處理信號後,
就將對信號的響應設置為Default動作。在某些情況下,將導致對信號的錯誤處理;
因此,User如果不希望這樣的操作,那麼就要在信號處理函數結尾再一次使用signal(),重新安裝該信號。

[技術] 在C中列出目錄與檔案

Written on 8:50 上午 by Yu Lai

一般在C中,我們可以使用opendir()與readdir()來列出目錄下所有的檔案。
以下是範例程式:

#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>

main() {
DIR * dir;
struct dirent * ptr;
int i;
dir =opendir(“/etc/rc.d”);
while((ptr = readdir(dir))!=NULL) {
printf(“d_name: %s\n”,ptr->d_name);
}
closedir(dir);
}

配合struct dirent,定義如下:
struct dirent {
ino_t d_ino;
off_t d_off;
unsigned short int d_reclen;
unsigned char d_type;
char d_name[256];
};

d_ino 此目錄進入點的inode
d_off 目錄文件開頭至此目錄進入點的位移
d_reclen d_name的長度,不包含NULL字符
d_type d_name所指的檔案類型
d_name 檔名
其中unsigned cahr d_type;可以來判斷是子目錄或是一般的檔案。
但不知是我使用的uClibC的問題,沒實作到還怎樣,使用上一直有問題。

後來還是找到解決方法,方法有2種,分別如下:
1. 再使用一次readdir()來判斷回傳值,若回傳為NULL,即為檔案。
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>

main() {
DIR * dir;
DIR * dir2;
struct dirent * ptr;
int i;
dir =opendir(“/etc/rc.d”);
while((ptr = readdir(dir))!=NULL) {
char pathname[100];
sprintf(pathname,"/etc/rc.d/%s", ptr->d_name);
if((dir2 = opendir(pathname))==NULL) {
printf("%s: file\n", ptr->d_name);
} else {
printf("%s: directory\n", ptr->d_name);
closedir(dir2);
}
}
closedir(dir);
}

2. 使用int stat(const char *file_name, struct stat *buf),
在struct stat其中的 st_mode 可以用來判斷是哪種檔案(也就是上面 d_type的 功用)。
為了方便,POSIX另外有定義幾個MACRO:
S_ISLNK(st_mode) : 是symbolic link
S_ISREG(st_mode) 一般檔案(regular file)
S_ISDIR(st_mode) 目錄(directory)
S_ISCHR(st_mode) 字元設備檔(char device)
S_ISBLK(st_mode) 區塊設備檔(block device)
S_ISSOCK(st_mode) local-domain socket
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>

main() {
DIR * dir;
struct dirent * ptr;
int i;
dir =opendir(“/etc/rc.d”);
while((ptr = readdir(dir))!=NULL) {
char pathname[100];
struct stat buf;
sprintf(pathname,"/etc/rc.d/%s", ptr->d_name);
stat(pathname, &buf);
if(S_ISREG(buf.st_mode))
printf("%s: file\n", ptr->d_name);
else
printf("%s: directory\n", ptr->d_name);
}
closedir(dir);
}