第五章:定时器及时钟服务
知识点归纳总结:
本章讨论了定时器和定时器服务;介绍了硬件定时器的原理和基于Intel x86的PC中的硬件定时器;
讲解了CPU操作和中断处理;描述了Linux中与定时器相关的系统调用、库函数和定时器服务命令;
探讨了进程间隔定时器、定时器生成的信号,并通过示例演示了进程间隔定时器。编程项目的目的是要在一个多任务处理系统中实现定时器、定时器中断和间隔定时器。
多任务处理系统作为一个Linux进程运行,该系统是Linux进程内并发任务的一个虚拟CPU, Linux进程的实时模式间隔定时器被设计为定期生成S1GALRM信号,充当虚拟CPU的定时器中断,虚拟CPU使用SIGALRM信号捕捉器作为定时器的中断处理程序。
其中让我最有收获的几个部分如下:
- CPU操作
- 中断处理
- 时钟服务函数
- 间隔计时器
- 编程项目样例代码
时钟服务函数:
`gettimeofday()`使用gettimeofday()来查看当前时间,这个函数会计算从1970年1月1号00:00(UTC)到当前的时间跨度。 > 说明:在使用gettimeofday()函数时,第二个参数一般都为空,因为我们一般都只是为了获得当前时间,而不用获得timezone的数值其函数原型如下:
#include <sys/time.h>
// 调用成功返回0,失败返回-1
int gettimeofday(struct timeval *tv, struct timezone *tz);
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
示例代码:
/************gettimeofday.c file**********/
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
struct timeval t;
int main()
{
gettimeofday(&t,NULL);
printf("sec=%ld usec=%d\n",t.tv_sec,t.tv_usec);
printf("%s",(char *)ctime(&t.tv_sec));
}
实践截图:
settimeofday
设置当前时间戳,settimeofday()会把目前时间设成由tv 所指的结构信息,当地时区信息则设成tz 所指的结构。
#include <time.h>
int settimeofday(const struct timeval *tv , const struct timezone *tz);
struct timeval {undefined
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
示例函数:
/***********settimeofday.c file*************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
struct timeval t;
int main()
{
int r;
t.tv_sec = 123456789;
t.tv_usec = 0;
/*if(!r)
{
printf("settimeofday() faild\n");
exit(1);
}*/
gettimeofday(&t,NULL);
printf("sec=%ld usec=%ld\n",t.tv_sec,t.tv_usec);
printf("%s", ctime(&t.tv_sec));
}
实践截图:
time系统调用
time_t time(time_t *t)
以秒为单位返回当前时间。如果参数t不是NULL,还会将时间存储在t指向的内存中。 time系统调用具有一定的局限性,只提供以秒为单位的分辨率,而不是以微秒为单位。
#include <stdio.h>
#include <time.h>
time_t start, end;
int main()
{
int i;
start = time(NULL);
printf("start = %ld\n", start);
for ( i = 0; i < 123456789; i++)
{
end = time(NULL);
}
printf("end = %ld time = %ld\n",end ,end-start);
}
time 和 date 命令
- date:打印或设置系统日期和时间。
- time:报告进程在用户模式和系统模式下的执行时间和总时间。
- hwclock:查询并设置硬件时钟(RTC),也可以通过BIOS来完成。
间隔定时器
定时器仅在进程以 用户模式执行时才减少计时。该定时器设置为完成最初100毫秒计时后开始计时:然后,它以1秒为周期运行。当定时器计时减少为0时,它会向进程发出一个SIGVTALRM ( 26 )信 号。如果进程未安装该信号的捕捉器,将会对该信号进行默认处理,即终止:在这种情况 下,进程将以信号数26终止。如果进程安装了信号捕捉器.Linux内核会让进程执行信号 捕捉器,以用户模式处理信号、在间隔时间开始之前,程序通过以下代码安装SIGVTALRM 信号的信号捕捉器;void timer_handler(int sig)
signal(SIGALRMZ timer_handler)
安装信号捕捉器后,程序启动定时器,然后在while(l)循环中执行-当在循环中执行时,每个硬件中断(例如来自硬件定时器的中断)都会导致CPU以及在CPU上执行的进程进入Linux内核来处理中断,当进程处于内核模式时,会检查待处理信号。如有待处理信号,它会试图先处理信号再返回用户模式。在这种情况下,SIGVTALRM信号将导致进程在用户模式下执行信号捕捉器,由于信号定时器程程序设计为每秒生成一个信号,进程将每秒执行一次timer_handler()
,使打印消息像脉冲星一样每秒显示一次。
信号捕捉函数timer_handler()
可计算定时器的时间结束次数当计数达到规定值(例如8)时,它用定时器值0来取消setitimer()
设置的间隔定时器。虽然定时器已经停止,但进程仍在无限while(l)循环中执行。 在这种情况下,从键盘按下"Ctrl+C”组合键,可以使进程以SIGINT(2)信号终止。
#include <signal.h>
#include <stdio.h>
#include <sys/time.h>
int count = 0;
struct itimerval t;
void timer_handler(int sig)
{
printf("timer_handler : signal = %d count = %d\n", sig, ++count);
if(count>=8)
{
printf("cancel timer\n");
t.it_value.tv_sec = 0;
t.it_value.tv_usec = 0;
setitimer(ITIMER_VIRTUAL, &t ,NULL);
}
}
int main()
{
struct itimerval timer;
signal(SIGVTALRM, timer_handler);
timer.it_value.tv_sec = 0;
timer.it_value.tv_usec = 10000;
timer.it_interval.tv_sec = 1;
timer.it_interval.tv_usec = 0;
setitimer(ITIMER_VIRTUAL, &timer,NULL);
printf("looping : enter Control-C to terminate\n");
while(1);
}
实践截图:
编程项目示例
ts.s file:#-----------ts. s file-----------+
.global tswitch, scheduler, running
tswitch:
SAVE: pushal
pushfl
movl running, %ebx
movl %esp , 4(%ebp)
FIND: call scheduler
RESUME: movl running, %ebx
movl 4(%ebx), %esp
popfl
popal
ret
t.c file:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/time.h>
#define NPROC 9
#define SSIZE 1024
// PROC status
#define FREE 0
#define READY 1
#define SLEEP 2
#define BLOCK 3
#define PAUSE 4
#define ZOMBIE 5
typedef struct proc {
struct proc *next;
int ksp; //
int pid; //
int priority; //
int status; //
int event; //
int ppid;
int exitStatus;
int joinPid;
int time; //
int pause; //
int stack[SSIZE]; //
}PROC;
PROC proc[NPROC];
PROC *freeList, *readyQueue, *running;
PROC *sleepList;
PROC *pauseList;
#include "queue.c"
//#include "wait.c"
int menu()
{
printf("**********menu**********\n");
printf("* creat switch exit ps *\n");
printf("************************\n");
}
int init()
{
int i, j;
PROC *p;
for (i=0; i<NPROC; i++)
{
p = &proc[i];
p->pid = i;
p->priority = 1;
p->status = FREE;
p->event = 0;
p->next = p+1;
}
proc[NPROC-1].next = 0;
freeList = &proc[0]; // all PROCs in freeList
readyQueue = 0;
sleepList = 0;
pauseList = 0;
// create P0 as initial running task
running = dequeue(&freeList);
running->status = READY;
running->priority = 0; // P0 has lowest priority 0
printList("freeList", freeList);
printf("init complete: P0 running\n");
}
int do_exit()
{
printf("task %d exit: ", running->pid);
running->status = FREE;
running->priority = 0;
enqueue(&freeList, running);
printList("freeList", freeList);
tswitch();
}
int do_ps()
{
printf("---------ps---------\n");
printList("readyQueue", readyQueue);
printList("sleepList ", sleepList);
printf ("-------------------\n");
}
int create(void (f)(), void *parm) // create a new task
{
int i;
PROC *p = dequeue(&freeList);
if (!p)
{
printf("create failed\n");
return -1;
}
p->ppid = running->pid;
p->status = READY;
p->priority = 1;
for (i=1; i<12; i++)
p->stack[SSIZE-i] = 0;
p->stack[SSIZE-1] = (int)parm;
p->stack[SSIZE-2] = (int)do_exit;
p->stack[SSIZE-3] = (int)f;
p->ksp = &p->stack[SSIZE-12];
enqueue(&readyQueue, p);
printf("%d created a new task %d\n", running->pid, p->pid);
return p->pid;
}
int func(void *parm)
{
char line[64], cmd[16];
printf("task %d start: parm = %d\n", running->pid, parm);
while(1)
{
printf("task %d running\n", running->pid);
menu();
printf("enter a command line:");
fgets(line, 64, stdin);
line[strlen(line)-1] = 0; // kill \n at end of line
sscanf(line, "%s", cmd);
if (strcmp(cmd, "create")==0)
create((void *)func, 0);
else if (strcmp(cmd, "switch")==0)
tswitch();
else if (strcmp(cmd, "exit")==0)
do_exit();
else if (strcmp(cmd, "ps")==0)
do_ps();
}
}
int main()
{
int i;
printf("Welcome to the MT multitasking system\n");
init();
for (i=1; i<5; i++) // create tasks
create((void *)func, 0);
printf("P0 switch to Pl\n");
while(1)
{
if (readyQueue)
tswitch();
}
}
int scheduler()
{
if (running->status == READY)
enqueue(&readyQueue, running);
running = dequeue(&readyQueue);
printf("next running = %d\n", running->pid);
}
项目运行截图: