系统时间是UNIX中最基本的时间形式,其他任何时钟形式都是从系统时间中转化而来的。UNIX中用于系统时间的函数如下:
#include <time.h>
time_t time(time_t *tloc);
double difftime(time_t time2, time_t time1);//获取系统时间差
/************读取系统时间****************/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
time_t start, finish;
time(&start);
sleep(6);
time(&finish);
printf("start=[%d], finish=[%d], difftime=[%f]\n", start, finish, difftime(finish, start));
return 0;
}
struct tm *localtime(const time_t *clock);
time_t mktime(struct tm *timeptr);
PS:函数mktime实现函数localtime的反功能,它返回tm结构对应的系统时间。
/************读取本地时间****************/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
struct tm *when;
time_t now;
time(&now);
when = localtime(&now);
printf("now = [%d] [%04d年%02d月%02d日 %02d:%02d:%02d]\n", now, when->tm_year+1900,
when->tm_mon+1, when->tm_mday, when->tm_hour, when->tm_min, when->tm_sec);
return 0;
}
函数signal更改信号的默认处理方式,函数kill或raise可以向进程发送信号。在实践应用中常见的信号处理有:1.忽略SIGINT等进程终止类信号,屏幕用户终止进程;2. 忽略或捕获子进程结束信号SIG(H)LD,释放进程表项,预防僵死进程;3. 捕获定时器信号,完成进程定时或并发操作; 4. 捕获自定义信号,完成进程特定操作。
/***********忽略信号*************/
#include <signal.h>
#include <stdio.h>
int main()
{
signal(SIGINT, SIG_IGN);/*屏蔽信号SIGINT*/
return 0;
}
/*************信号捕获****************/
#include <signal.h>
#inclue <stdio.h>
int usr1=0, usr2=0;
void func(int);
int main()
{
signal(SIGUSR1, func);
signal(SIGUSR2, func);
for(;;) sleep(1);
return 0;
}
void func(int sig)
{
if(sig == SIGUSR1) usr1++;
if(sig == SIGUSR2) usr2++;
fprintf(stderr, “SIGUSR1[%d]; SIGUSR2[%d]\n”, usr1, usr2);
signal(SIGUSR1, func);
signal(SIGUSR2, func);
}
函数原型:
int kill(pid_t pid, int signo);
int raise(int signo);
函数kill发送信号signo到参数pid决定的特定进程中,其中参数pid的取值含义如下所示:
取值 |
含义 |
>0 |
发送信号signo到进程pid中 |
0 |
发送信号signo到与调用进程同组进程中 |
-1 |
发送信号signo到实际用户id等于调用进程的有效用户id的进程中 |
<-1 |
发送信号signo到进程id等于pid绝对值的进程中 |
kill函数调用成功返回0,否则返回-1.
raise函数向调用进程自身发送信号signo,成功时返回0,否则返回-1.
/***************发送信号******************/
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void childfunc(int sig)
{
fprintf(stderr, "Get Sig\n");
exit(5);
}
int main()
{
pid_t pid;
int status;
if((pid=fork()) < 0)
exit(1);
else if(pid == 0)
{
signal(SIGTERM, childfunc);
sleep(30);
exit(0);
}
fprintf(stderr, "Parent [%d] fork child pid=[%d]\n", getpid(), pid);
sleep(1);
kill(pid, SIGTERM);
wait(&status);
fprintf(stderr, "Kill child pid=[%d], exit status[%d]\n", pid, status>>8);
return 0;
}
函数原型:
unsigned int alarm(unsigned int seconds);
函数alarm总能调用成功,它返回上次定时器剩余的定时时间,如果是第一次设置定时器,返回0.
在UNIX中使用普通定时器的三个步骤如下:
步骤1:设置捕获定时信号。
更改信号SIGALRM的默认处理机制,并设置捕获响应函数。
步骤2:定时。
在需要定时的代码处调用函数alarm即可。
步骤3:编写响应定时信号函数。
捕获定时信号函数模型如下代码所示:
void timefunc(int sig)
{
………………….
signal(SIGALRM, timefunc);/*再次设置捕获定时信号*/
alarm(1);/*再次定时*/
}
PS:由于在部分Linux版本中,信号的捕获只响应一次,故函数中需要增加信号捕获设置,函数alarm设置的定时器也只能定时一次,故函数中需要重新定时。
/***************函数alarm定时器实例**************/
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int n=0;
void timefunc(int sig)
{
fprintf(stderr, "Alarm %d\n", n++);
signal(SIGALRM, timefunc);/*捕获定时信号*/
alarm(1); /*定时开始*/
}
int main()
{
signal(SIGALRM, timefunc);/*捕获定时信号*/
alarm(1); /*定时开始*/
while(1);
return 0;
}
函数原型:
int gettimer(int which, struct itimerval *value);
int settimer(int which, const struct itimerval *value, struct itimerval *ovalue);
参数which与定时器类型:
取值 |
含义 |
信号发送 |
TIMER_REAL |
定时真实时间,与alarm类型相同 |
SIGALRM |
TIMER_VIRT |
定时进程在用户态下的实际执行时间 |
SIGVTALRM |
TIMER_PROF |
定时进程在用户态和核心态下的实际执行时间 |
SIGPROF |
settimer函数设置的定时器不但可以计时到微秒,还能自动循环定时。结构itimerval描述了定时器的组成:
struct itimerval
{
struct timerval it_interval; /*下次定时取值*/
struct timerval it_value; /*本次定时设置值*/
};
/****************精确定时器实例*********************/
#include <sys/select.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int n = 0;
void timefunc(int sig)
{
fprintf(stderr, "ITIMER_PROF[%d]\n", n++);
signal(SIGPROF, timefunc);
}
int main()
{
struct itimerval value;
value.it_value.tv_sec=1; /*定时1.5秒*/
value.it_value.tv_usec=500000;
value.it_interval.tv_sec=1;
value.it_interval.tv_usec=500000;
signal(SIGPROF, timefunc); /*捕获定时信号*/
setitimer(ITIMER_PROF, &value, NULL);/*定时开始*/
while(1);
}
函数原型:
int setjmp(jmp_buf env);//保存当前的堆栈环境
void longjmp(jmp_buf env, int val);//恢复保存在env中的堆栈信息,并使程序跳转到env中保存的位置处重新执行。
在UNIX中使用全局跳转的步骤如下:
#include <setjmp.h>
jmp_buf env;
i = setjmp(env);
/***************跳转语句实例********************/
#include <unistd.h>
#include <stdio.h>
#include <setjmp.h>
int j = 0;
jmp_buf env;
int main()
{
int i, k=0;
i = setjmp(env);
printf("setjmp=[%d];j=[%d];k=[%d]\n", i, j++, k++);
if(j > 2)
exit(0);
sleep(1);
longjmp(env, 0);
return 0;
}
ps:程序运行的过程中没有出现想要的结果(k应该一直为0),可能Linux对这个函数的支持不同?。
处理UNIX中I/O超时的方式有终端方式、信号跳转方式和多路复用方式等多种。
5.1 终端I/O超时方式
利用ioctl函数,设置文件描述符对应的标准输入文件属性为“接收输入后立刻返回,如果无输入则10秒钟后定时返回”模式。
/**************终端越野模式*****************/
#include <unistd.h>
#include <stdio.h>
#include <termio.h>
#include <fcntl.h>
int main()
{
struct termio old, new;
char c = 0;
ioctl(0, TCGETA, &old);
new = old;
new.c_lflag &= ~ICANON;
new.c_cc[VMIN] = 0;
new.c_cc[VTIME] = 30;
ioctl(0, TCSETA, &new);
if(read(0, &c, 1) != 1)
printf("time out\n");
else
printf("\n%d\n", c);
ioctl(0, TCSETA, &old);
return 0;
}
在read函数前调用setjmp保存堆栈数据并使用alarm设定3秒定时器。倘若输入超时则必然接收定时信号,此时只需让longjmp函数从setjmp处返回,并跳过read语句即可。
/*********************信号跳转方式超时处理**********************/
#include <setjmp.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int timeout = 0;
jmp_buf env;
void timefunc(int sig)
{
timeout = 1;
longjmp(env, 1);
}
int main()
{
char c;
signal(SIGALRM, timefunc);
setjmp(env);
if(timeout == 0) /*正常处理*/
{
alarm(3);
read(0, &c, 1);
alarm(0);
printf("\n%d\n", c);
}
else /*打印超时*/
printf("timeout\n");
return 0;
}
一个进程可能同时打开多个文件,UNIX中函数select可以同时监控多个文件描述符的输入输出,进程将一直阻塞,直到越野或者产生I/O为止,通知进程读取或发送数据。函数select的原型如下:
int select(int nfds, fd_set *readfs, fd_set *writeds, fd_set *exceptionds, struct timeval *timeout);
FD_CLR(int fd, fd_set *fdset);
FD_ISSET(int fd, fd_set *fdset);
FD_SET(int fd, fd_set *fdset);
FD_ZERO(fd_set *fdset);
PS: nfds为最大描述符编号+1, 当不知道监控的最大描述符编号时,可以使用常数FD_SETSIZE。
timout的含义:为0时,函数不等待, 立即返回。为NULL时,函数永远等待,直到有文件描述符就绪。
/************多路利用方式超时处理******************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/times.h>
#include <sys/select.h>
int main()
{
struct timeval timeout;
fd_set readfds;
int i;
char c;
timeout.tv_sec = 3;
timeout.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(0, &readfds); /*监控文件0(标准输入)*/
i = select(1, &readfds, NULL, NULL, &timeout);
if(i > 0) /*有输入*/
{
read(0, &c, 1);
printf("\n%d\n", c);
}
else if(i == 0) /*超时*/
printf("\ntimeout\n");
else
printf("\nerror\n");
return 0;
}
PS:当参数readfds, writefds和exceptfds都为NULL,函数select退化成可精确到微秒级的sleep调用,如下:
select(0, NULL, NULL, NULL, &timeout);