20191317王鵬宇第五章學習筆記
第五章:定時器及時鐘服務
知識點歸納總結:
本章討論了定時器和定時器服務;介紹了硬體定時器的原理和基於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);
}
專案執行截圖: