1. 程式人生 > 其它 >20191317王鵬宇第五章學習筆記

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);

}

專案執行截圖: