获取时钟
UNIX 的时间系统存在一个基点 , 就是格林威治时间 1970 年 1 月 1 日凌晨 0 点 0 分 0 秒 , 也是传说中 UNIX 的生日 .
UNIX 中存在三种格式的时间 :
(1) 系统时间 . UNIX 从出生到现在的秒数 , 表现为一个 time_t 类型的变量
(2) 高分辨率时间 . 精确到微秒的时间 , 表现为一个 timeval 结构的变量
(3) 日历时间 . 以 ' 年 、 月 、 日 、 时 、 分 、 秒 ' 结构表示的时间 , 表现为 tm 结构 .
系统时间 . 它是 UNIX 中最基本的时间形式 . 用于系统时间的函数如下 :
#include <time.h> time_t time(time_t *tloc); double difftime(time_t time2, time_t time1); |
函数 difftime 获取两次 time 调用返回的系统时间差 .
秒数往往很难读懂 , UNIX 中更改系统时间为日历时间的函数如下 :
#include <time.h> struct tm *localtime(const time_t *clock); time_t mktime(struct tm*timeptr); |
函数 localtime 转换系统时间 , clock 为当地时间 , 并以 tm 结构返回 .
函数 mktime 实现函数 localtime 的反功能 .
下面给出一个打印本地时间的例子
[bill@billstone Unix_study]$ cat time1.c #include <time.h> #include <stdio.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; } [bill@billstone Unix_study]$ make time1 cc time1.c -o time1 [bill@billstone Unix_study]$ ./time1 now=[1239927129] [2009 04 17 08:12:09] [bill@billstone Unix_study]$ |
信号的概念
信号是传送给进程的事件通知 , 它可以完成进程间异步事件的通信 .
导致信号产生的原因很多 , 但总体说来有三种可能 :
(1) 程序错误 . 当硬件出现异常 , 除数为 0 或者软件非法访问等情况时发生 .
(2) 外部事件 . 当定时器到达 , 用户按健中断或者进程调用 abort 等信号发送函数时方生 .
(3) 显式请求 . 当进程调用 kill, raise 等信号发送函数或者用户执行 shell 命令 kill 传递信号时发生 .
同样的 , 当进程收到信号时有三种处理方式 :
(1) 系统默认 . 系统针对不同的信号有不同的默认处理方式 .
(2) 忽略信号 . 信号收到后 , 立即丢弃 . 注意信号 SIGSTOP 和 SIGKILL 不能忽略 .
(3) 捕获信号 . 进程接收信号 , 并调用自定义的代码响应之 .
信号操作
函数 signal 设置对信号的操作动作 , 原型如下 :
#include <signal.h> void (*signal (int sig, void (*f) (int()) (int); |
这是个复杂的函数原型 , 不果可以分开看 :
typedef void (*func)(int); func signal(int sig, func f); |
其中 , func 参数有三种选择 :SIG_DFL( 恢复信号默认处理机制 ), SIG_IGN( 忽略信号处理 ) 和函数地址 ( 调用信号捕获函数执行处理 ).
首先看一个忽略终止信号 SIGINT 的例子 .
[bill@billstone Unix_study]$ cat sig1.c #include <signal.h> #include <stdio.h>
int main() { signal(SIGINT, SIG_IGN); sleep(10); // 睡眠 10 秒
return 0; } [bill@billstone Unix_study]$ make sig1 cc sig1.c -o sig1 [bill@billstone Unix_study]$ ./sig1 [bill@billstone Unix_study]$ |
在程序运行的 10 秒内 , 即使你键入 Ctrl+C 中断命令 , 进程也不退出 .
再看一个捕获自定义信号的例子 .
[bill@billstone Unix_study]$ cat sig2.c #include <signal.h> #include <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); } |
在后台运行 , 结果如下 :
[bill@billstone Unix_study]$ make sig2 cc sig2.c -o sig2 [bill@billstone Unix_study]$ ./sig2& // 后台运行 [2] 13822 [bill@billstone Unix_study]$ kill -USR1 13822 // 发送信号 SIGUSR1 SIGUSR1[1], SIGUSR2[0] [bill@billstone Unix_study]$ kill -USR2 13822 // 发送信号 SIGUSR2 SIGUSR1[1], SIGUSR2[1] [bill@billstone Unix_study]$ kill -USR2 13822 // 发送信号 SIGUSR2 SIGUSR1[1], SIGUSR2[2] [bill@billstone Unix_study]$ kill -9 13822 // 发送信号 SIGSTOP, 杀死进程 [bill@billstone Unix_study]$ [2]+ 已杀死 ./sig2 [bill@billstone Unix_study]$ |
UNIX 应用程序可以向进程显式发送任意信号 , 原型如下 :
#include <sys/types.h> #include <signal.h> int kill(pid_t pid, int signo); int raise(int signo); |
看一个发送和捕获 SIGTERM 终止信号的例子
[bill@billstone Unix_study]$ cat sig3.c #include <signal.h> #include <stdio.h> #include <assert.h> #include <unistd.h> #include <sys/types.h>
void childfunc(int sig){ fprintf(stderr, "Get Sig/n"); }
int main() { pid_t pid; int status;
assert((pid = fork()) >= 0); 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; } [bill@billstone Unix_study]$ make sig3 cc sig3.c -o sig3 [bill@billstone Unix_study]$ ./sig3 Parent [13898] Fork child pid=[13899] Get Sig Kill child pid=[13899], exit status[0] [bill@billstone Unix_study]$ |
定时器
UNIX 下定时器可以分为普通的定时器和精确的定时器 .
普通定时器通过 alarm 函数实现 , 它的精度是秒 , 而且每调用一次 alarm 函数只能产生一次定时操作 , 如果需要反复定时 , 就要多次调用 alarm. 调用 fork 后 , 子进程中的定时器将被取消 , 但调用 exec 后 , 定时器仍然有效 .
在 UNIX 中使用普通定时器需要三个步骤 :
(1) 调用 signal 函数设置捕获定时信号
(2) 调用函数 alarm 定时 .
(3) 编写响应定时信号函数 .
下面是一个定时器的例子 , 每隔 1 秒向进程发送定时信号 , 用户可键入 Ctrl+C 或 Delete 结束程序 .
[bill@billstone Unix_study]$ cat time2.c #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() { int status;
signal(SIGALRM, timefunc); alarm(1); while(1);
return 0; } [bill@billstone Unix_study]$ make time2 cc time2.c -o time2 [bill@billstone Unix_study]$ ./time2 Alarm 0 Alarm 1 Alarm 2 // 按 Ctrl+C 结束 [bill@billstone Unix_study]$ |
函数 alarm 设置的定时器只能精确到秒 , 而下面函数理论上可以精确到毫秒 :
#include <sys/select.h> #include <sys/time.h> int getitimer(int which, struct itimerval *value); int setitimer(int which, const struct itimerval value, struct itimerval *ovalue); |
函数 setitimer 可以提供三种定时器 , 它们相互独立 , 任意一个定时完成都将发送定时信号到进程 , 并且重新计时 . 参数 which 确定了定时器的类型 :
(1) ITIMER_REAL. 定时真实时间 , 与 alarm 类型相同 . 对应信号为 SIGALRM.
(2) ITIMER_VIRT. 定时进程在用户态下的实际执行时间 . 对应信号为 SIGVTALRM.
(3) ITIMER_PROF. 定时进程在用户态和核心态下的实际执行时间 . 对应信号为 SIGPROF.
在一个 UNIX 进程中 , 不能同时使用 alarm 和 ITIMER_REAL 类定时器 .
结构 itimerval 描述了定时器的组成 :
struct itimerval{ struct timeval it_interval; struct timeval it_value; } |
结构成员 it_value 指定首次定时的时间 , 结构成员 it_interval 指定下次定时的时间 . 定时器工作时 , 先将 it_value 的时间值减到 0, 发送一个信号 , 再将 it_vale 赋值为 it_interval 的值 , 重新开始定时 , 如此反复 . 如果 it_value 值被设置为 0, 则定时器停止定时 .
结构 timeval 秒数了一个精确到微秒的时间 :
struct timeval{ long tv_sec; long tv_usec; } |
下面设计了一个精确定时器的例子 , 进程每 1.5 秒发送定时信号 SIGPROF, 用户可键入 Ctrl+C 或 Delete 结束程序 .
[bill@billstone Unix_study]$ cat time3.c #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; 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);
return 0; } [bill@billstone Unix_study]$ make time3 cc time3.c -o time3 [bill@billstone Unix_study]$ ./time3 ITIMER_PROF[0] ITIMER_PROF[1] ITIMER_PROF[2]
[bill@billstone Unix_study]$ |