20191205 2021-2022-1-diocs-定時器及時鍾服務(第八周學習筆記)
一、任務詳情
自學教材第5章,提交學習筆記(10分)
知識點歸納以及自己最有收獲的內容 (3分)
問題與解決思路(2分)
實踐內容與截圖,代碼鏈接(3分)
...(知識的結構化,知識的完整性等,提交markdown文檔,使用openeuler系統等)(2分)
二、教材內容歸納整理
本章討論了定時器和定時器服務;介紹了硬件定時器的原理和基於Intel x86的 PC 中的硬件定時器;講解了 CPU操作和中斷處理;描述了Linux中與定時器相關的系統調用、庫函數和定時器服務命令;探討了進程間隔定時器、定時器生成的信號,並通過示例演示了進程間隔定時器。編程項目的目的是要在一個多任務處理系統中實現定時器、定時器中斷和間隔定時器。多任務處理系統作為—個Linux進程運行,該系統是 Linux進程內並發任務的一一個虛擬 CPU。Linux 進程的實時模式間隔定時器被設計為定期生成SIGALRM信號,充當虛擬CPU 的定時器中斷,虛擬CPU使用SIGALRM信號捕捉器作為定時器的中斷處理程序。該項目可讓讀進程通過定時器隊列實現任務間隔定時器,還可讓讀進程使用Linux 信號掩碼來實現臨界區,以防止各項任務和中斷處理程序之間出現競態條件。
思維導圖
一、知識點總結
1.個人計算機定時器
(1)實時時鍾(RTC):RTC由一個小型備用電池供電。即使在個人計算機關機時,它也能連續運行。它用於實時提供時間和日期信息。當Linux啟動時,它使用RTC更新系統時間變量,以與當前時間保持一致。在所有類Unix 系統中,時間變量是一個長整數,包含從1970年1月1日起經過的秒數。
(2)可編程間隔定時器(PIT)(Wang 2015)∶PIT是與CPU分離的一個硬件定時器。可對它進行編程,以提供以毫秒為單位的定時器刻度。在所有I/O設備中,PIT 可以最高優先級 IRQ0中斷。PIT定時器中斷由Linux 內核的定時器中斷處理程序來處理,為系統操作提供基本的定時單元,例如進程調度、進程間隔定時器和其他許多定時事件。
(3)多核CPU 中的本地定時器(Intel 1997;Wang 2015))∶在多核CPU中,每個核都是一個獨立的處理器,它有自己的本地定時器,由 CPU時鍾驅動。
(4)高分辨率定時器∶大多數電腦都有一個時間戳定時器(TSC)由系統時鍾驅動。它的內容可通過64 位 TSC寄存器讀取。由於不同系統主板的時鍾頻率可能不同,TSC不適合作為實時設備,但它可提供納秒級的定時器分辨率。—些高端個人計算機可能還配備有專用高速定時器,以提供納秒級定時器分辨率。
2.CPU操作
每個CPU都有一個程序計數器(PC),也稱為指令指針(IP),以及一個標志或狀態寄存器(SR)、一個堆棧指針(SP)和幾個通用寄存器,當 PC指向內存中要執行的下一條指令時,SR包含 CPU 的當前狀態,如操作模式、中斷掩碼和條件碼,SP指向當前堆棧棧頂。
while (power-on){ (1). fetch instruction:load*PC as instruction,increment PC to point to the next instruction in memory; (2). decode instruction: interpret the instruction's operation code and generate operandis; (3). execute instruction: perform operation on operands,write results to memory if needed; execution may use the stack,implicitly change PC, etC. (4) . check for pending interrupts; may handle interrupts; }
3.中斷處理
外部設備(如定時器)的中斷被饋送到中斷控制器的預定義輸入行(Intel 1990;Wang 2015),按優先級對中斷輸入排序,並將具有最高優先級的中斷作為中斷請求(IRQ)路由到 CPU。
對於每個中斷,可以編程中斷控制器以生成一個唯一編號,叫作中斷向量,標識中斷源。在獲取中斷向量號后,CPU用它作為內存中中斷向量表(AMD64 20I1)中的條目索引,條目包含一個指向中斷處理程序入口地址的指針來實際處理中斷。當中斷處理結束時,CPU恢復指令的正常執行。
4.間隔定時器
間隔定時器由 setitimer()系統調用創建。getitimer()系統調用返回間隔定時器的狀態。
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which,const struct itimerval *new_value,
struct itimerva1 *old_value);
各間隔定時器在參數 which指定的不同時間域中工作。當間隔定時器定時到期時,會向進程發送一個信號,並將定時器重置為指定的間隔值(如果是非零數)。一個信號就是發送給某個進程進行處理的一個數字(1到31)。有3類間隔定時器,分別是:
(1)ITIMER_REAL:實時減少,在到期時生成一個SIGALRM(14)信號。
(2)ITIMER_VIRTUAL:僅當進程在用戶模式下執行時減少,在到期時生成一個SIGVTALRM(26)信號。
(3)ITIMER PROF:當進程正在用戶模式和系統(內核)模式下執行時減少。這類間隔
5.間隔定時器
定時器計時,並向進程生成一個信號。操作系統內核不必使用額外的數據結構來處理進程的VIRTUAL 和 PROF定時器。但是,REAL模式間隔定時器各不相同,因為無論進程是否正在執行,它們都必須由定時器中斷處理程序來更新。因此,操作系統內核必須使用額外的數據結構來處理進程的 REAL 模式定時器,並在定時器到期或被取消時采取措施。在大多數操作系統內核中,使用的數據結構都是定時器隊列。我們將在本章末尾解釋編程項目中的定時器隊列。
二、最有收獲的內容
Gettimeofday-settimeofday系統調用
#include <sys/time.h> int gettimeofday(struct timeval*tv,struct timezone *tz); int settimeofday(const struct timeval *tv,const struct timezone *tz);
這些是對Linux 內核的系統調用。第一個參數 tv指向一個timeval結構體。
struct timeval { time_t tv_BeC; /* secondg */tV_ugec; suseConds_t tv_usec /* microseconds * /);
第二個參數 timezone已過期,應設置為NULL。gettimeofday()函數用於返回當前時間(當前秒的秒和微秒)。settimeofday(函數用於設置當前時間。在 Unix/Linux中,時間表示自1970年1月1日00∶00∶00起經過的秒數。它可以通過庫函數 ctime(&time)轉換為日歷形式。
time系統調用
time_t time(time_t *t)
以秒為單位返回當前時間。如果參數t不是NULL,還會將時間存儲在t指向的內存中。time 系統調用具有一定的局限性,只提供以秒為單位的分辨率,而不是以微秒為單位。
times系統調用
clock_t times(struct tms *buf);
可用於獲取某進程的具體執行時間。它將進程時間存儲在 struct tms buf 中,即∶
struct tms{ clock t tms utime; // user mode time clock_t tms_stime; // system mode time clock__t tms_cutime; // user time of children clock_t tms_cstime; // system time of children );
以時鍾計時單元報告所有時間。這可以為分析某個正在執行的進程提供信息,包括其子進程的時間。
time和data命令
date:打印或設置系統日期和時間。
time:報告進程在用戶模式和系統模式下的執行時間和總時間。
hwclock:查詢並設置硬件時鍾(RTC),也可以通過 BIOS來完成。
三、問題與解決思路(解決思路是基於OpenEuler系統下解決的)
問題:Linux里如何用C編程實現gettimeofday系統調用
解決思路:
編寫C代碼實現gettimeofday()系統調用來獲取系統時間
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> struct timeval t; int main() { gettimeofday(&t,NULL); printf("sec=%ld usec=%d\n", t.tv_sec, t.tv_usec); printf((char *)ctime(&t.tv_sec)); }
運行截圖:
問題:Linux里如何用C編程實現settimeofday系統調用
解決思路:
編寫C代碼實現time系統調用
#include<stdio.h> #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); }
運行截圖:
四、實踐內容(截圖、代碼鏈接)
Linux多任務編程——多線程實現多任務
(1)線程號的比較
所需頭文件:
#include
int pthread_equal(pthread_t t1, pthread_t t2);
功能:
判斷線程號 t1 和 t2 是否相等。為了方便移植,盡量使用函數來比較線程 ID。
參數:
t1,t2:待判斷的線程號。
返回值:
相等: 非 0
不相等:0
代碼鏈接:
https://gitee.com/two_thousand_and_thirteen/codes/mgadw1n0ft5k4lqr7oscj41
運行截圖:
(2)線程的創建
所需頭文件:
#include
int pthread_create( pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg );
功能:
創建一個線程。
參數:
thread:線程標識符地址。
attr:線程屬性結構體地址,通常設置為 NULL。
start_routine:線程函數的入口地址。
arg:傳給線程函數的參數。
返回值:
成功:0
失敗:非 0
pthread_create() 創建的線程從指定的回調函數開始運行,該函數運行完后,該線程也就退出了。線程依賴進程存在的,共享進程的資源,如果創建線程的進程結束了,線程也就結束了。
代碼鏈接:
https://gitee.com/two_thousand_and_thirteen/codes/7p0wx4gn6hjorezd53avm52
運行截圖:
(3)回收線程資源
所需頭文件:
#include
int pthread_join(pthread_t thread, void **retval);
功能:
等待線程結束(此函數會阻塞),並回收線程資源,類似進程的 wait() 函數。如果線程已經結束,那么該函數會立即返回。
參數:
thread:被等待的線程號。
retval:用來存儲線程退出狀態的指針的地址。
返回值:
成功:0
失敗:非 0
代碼鏈接:
https://gitee.com/two_thousand_and_thirteen/codes/91rse72fjcdvki8qzlp3g73
運行結果:
(4)線程退出
在進程中我們可以調用 exit() 函數或 _exit() 函數來結束進程,在一個線程中我們可以通過 pthread_exit() 在不終止整個進程的情況下停止它的控制流。
所需頭文件:
#include
void pthread_exit(void *retval);
功能:
退出調用線程。一個進程中的多個線程是共享該進程的數據段,因此,通常線程退出后所占用的資源並不會釋放。
參數:
retval:存儲線程退出狀態的指針。
返回值:
無
代碼鏈接:
https://gitee.com/two_thousand_and_thirteen/codes/tcjuyro2i45mbhkzfgs0716
運行結果: