操作系統(tǒng)課程設(shè)計(jì)內(nèi)核定時(shí)器_第1頁
操作系統(tǒng)課程設(shè)計(jì)內(nèi)核定時(shí)器_第2頁
操作系統(tǒng)課程設(shè)計(jì)內(nèi)核定時(shí)器_第3頁
操作系統(tǒng)課程設(shè)計(jì)內(nèi)核定時(shí)器_第4頁
操作系統(tǒng)課程設(shè)計(jì)內(nèi)核定時(shí)器_第5頁
已閱讀5頁,還剩13頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、摘要摘要.11設(shè)計(jì)題目與要求設(shè)計(jì)題目與要求.21 11 1 設(shè)計(jì)題目設(shè)計(jì)題目:內(nèi)核定時(shí)器.21 12 2 設(shè)計(jì)要求設(shè)計(jì)要求:通過研究內(nèi)核的時(shí)間管理算法,學(xué)習(xí)內(nèi)核源代碼;然后應(yīng)用這些知識(shí)并且使用“信號(hào)”建立一種用戶空間機(jī)制來測量一個(gè)多線程程序的執(zhí)行時(shí)間。.22 2 總的設(shè)計(jì)思想及系統(tǒng)平臺(tái)、語言、工具總的設(shè)計(jì)思想及系統(tǒng)平臺(tái)、語言、工具.2設(shè)計(jì)思想:.2內(nèi)核對(duì)定時(shí)器的描述.22.1.2Linux 內(nèi)核定時(shí)器.32.1.3Linux 信號(hào)signal處理機(jī)制.6多線程編程.7內(nèi)核定時(shí)器機(jī)制的實(shí)現(xiàn).92.2.系統(tǒng)平臺(tái):.122.3. 編程工具:.123數(shù)據(jù)結(jié)構(gòu)與模塊說明(功能與流程圖)數(shù)據(jù)結(jié)構(gòu)與模塊說明

2、(功能與流程圖).12定時(shí)器使用:.123.2 多線程程序:.13程序流程圖:.144.4. 源程序:源程序:.145 5運(yùn)行結(jié)果與運(yùn)行情況運(yùn)行結(jié)果與運(yùn)行情況.156 6調(diào)試記錄:調(diào)試記錄:.167 7自我評(píng)析和總結(jié):自我評(píng)析和總結(jié):.178.8.參考文獻(xiàn)參考文獻(xiàn).17內(nèi)核定時(shí)器內(nèi)核定時(shí)器摘要摘要每個(gè)正在系統(tǒng)上運(yùn)行的程序都是一個(gè)進(jìn)程。每個(gè)進(jìn)程包含一到多個(gè)線程。進(jìn)程也可能是整個(gè)程序或者是部分程序的動(dòng)態(tài)執(zhí)行。線程是一組指令的集合,或者是程序的特殊段,它可以在程序里獨(dú)立執(zhí)行。也可以把它理解為代碼運(yùn)行的上下文。內(nèi)核時(shí)間指明線程執(zhí)行操作系統(tǒng)代碼已經(jīng)經(jīng)過了多少個(gè) 100ns 的 CPU 時(shí)間,linux

3、是一個(gè)具有保護(hù)模式的操作系統(tǒng)。它一直工作在 i386 cpu 的保護(hù)模式之下。內(nèi)存被分為兩個(gè)單元: 內(nèi)核區(qū)域和用戶區(qū)域。一般地,在使用虛擬內(nèi)存技術(shù)的多任務(wù)系統(tǒng)上,內(nèi)核和應(yīng)用有不同的地址空間,因此,在內(nèi)核和應(yīng)用之間以及在應(yīng)用與應(yīng)用之間進(jìn)行數(shù)據(jù)交換需要專門的機(jī)制來實(shí)現(xiàn),本文站在用戶空間的角度,測試一個(gè)多線程程序的程序執(zhí)行時(shí)間。當(dāng)一個(gè)進(jìn)程希望獲得信號(hào)量時(shí), 如果信號(hào)量已經(jīng)被占有, 則該進(jìn)程將會(huì)被放到等待隊(duì)列上 sleep 直到 cpu 將其喚醒。相對(duì)于 spinlock 來說開銷太大,適用于長時(shí)間占有的 lock。不可用于中斷狀態(tài),因?yàn)樗鼡碛行盘?hào)量的進(jìn)程可以 sleep, 可以被搶占,信號(hào)量可以設(shè)置

4、為同時(shí)允許的進(jìn)程數(shù)。1設(shè)計(jì)題目與要求設(shè)計(jì)題目與要求11 設(shè)計(jì)題目:內(nèi)核定時(shí)器12 設(shè)計(jì)要求:通過研究內(nèi)核的時(shí)間管理算法,學(xué)習(xí)內(nèi)核源代碼;然后應(yīng)用這些知識(shí)并且使用“信號(hào)”建立一種用戶空間機(jī)制來測量一個(gè)多線程程序的執(zhí)行時(shí)間。2 2 總的設(shè)計(jì)思想及系統(tǒng)平臺(tái)、語言、工具2.12.1設(shè)計(jì)思想:Linux 內(nèi)核對(duì)定時(shí)器的描述 Linux 在頭文件中定義了數(shù)據(jù)結(jié)構(gòu) timer_list 來描述一個(gè)內(nèi)核定時(shí)器: struct timer_list struct list_head list; unsigned long expires; unsigned long data; void (*function)

5、(unsigned long); ; 各數(shù)據(jù)成員的含義如下: (1)雙向鏈表元素 list:用來將多個(gè)定時(shí)器連接成一條雙向循環(huán)隊(duì)列。 (2)expires:指定定時(shí)器到期的時(shí)間,這個(gè)時(shí)間被表示成自系統(tǒng)啟動(dòng)以來的時(shí)鐘滴答計(jì)數(shù)(也即時(shí)鐘節(jié)拍數(shù))。當(dāng)一個(gè)定時(shí)器的 expires 值小于或等于 jiffies 變量時(shí),我們就說這個(gè)定時(shí)器已經(jīng)超時(shí)或到期了。在初始化一個(gè)定時(shí)器后,通常把它的 expires 域設(shè)置成當(dāng)前expires變量的當(dāng)前值加上某個(gè)時(shí)間間隔值(以時(shí)鐘滴答次數(shù)計(jì))。 (3)函數(shù)指針 function:指向一個(gè)可執(zhí)行函數(shù)。當(dāng)定時(shí)器到期時(shí),內(nèi)核就執(zhí)行 function所指定的函數(shù)。而 dat

6、a 域則被內(nèi)核用作 function 函數(shù)的調(diào)用參數(shù)。 內(nèi)核函數(shù) init_timer()用來初始化一個(gè)定時(shí)器。實(shí)際上,這個(gè)初始化函數(shù)僅僅將結(jié)構(gòu)中的list 成員初始化為空。如下所示(): static inline void init_timer(struct timer_list * timer) timer-list.next = timer-list.prev = NULL; 由于定時(shí)器通常被連接在一個(gè)雙向循環(huán)隊(duì)列中等待執(zhí)行(此時(shí)我們說定時(shí)器處于 pending狀態(tài))。因此函數(shù) time_pending()就可以用 list 成員是否為空來判斷一個(gè)定時(shí)器是否處于pending 狀態(tài)。如

7、下所示 (): static inline int timer_pending (const struct timer_list * timer) return timer-list.next != NULL; 時(shí)間比較操作 在定時(shí)器應(yīng)用中經(jīng)常需要比較兩個(gè)時(shí)間值,以確定 timer 是否超時(shí),所以 Linux 內(nèi)核在頭文件中定義了 4 個(gè)時(shí)間關(guān)系比較操作宏。這里我們說時(shí)刻 a 在時(shí)刻 b 之后,就意味著時(shí)間值 ab。Linux 強(qiáng)烈推薦用戶使用它所定義的下列 4 個(gè)時(shí)間比較操作宏(): #define time_after(a,b) (long)(b) - (long)(a) = 0) #de

8、fine time_before_eq(a,b) time_after_eq(b,a)2.1.2Linux 內(nèi)核定時(shí)器定時(shí)器是管理內(nèi)核時(shí)間的基礎(chǔ),用來計(jì)算流逝的時(shí)間,它以某種頻率(節(jié)拍率)自行觸發(fā)時(shí)鐘中斷,當(dāng)時(shí)鐘中斷發(fā)生時(shí),內(nèi)核就通過一種特殊中斷處理程序?qū)ζ溥M(jìn)行處理。但是原來的實(shí)現(xiàn)只能是 time_t mytime 形式的,經(jīng)過簡單的 localtime(mytime)和ctime(&mytime)處理.精度是不夠的,為了返回高精度的時(shí)間,這里使用了 gettimeofday 函數(shù)。這個(gè) syscall 用來供用戶獲取 timeval 格式的當(dāng)前時(shí)間信息(精確度為微秒級(jí)) ,以及系統(tǒng)的

9、當(dāng)前時(shí)區(qū)信息(timezone) 。結(jié)構(gòu)類型 timeval 的指針參數(shù) tv 指向接受時(shí)間信息的用戶空間緩沖區(qū),參數(shù) tz 是一個(gè) timezone 結(jié)構(gòu)類型的指針,指向接收時(shí)區(qū)信息的用戶空間緩沖區(qū)。這兩個(gè)參數(shù)均為輸出參數(shù),返回值 0 表示成功,返回負(fù)值表示出錯(cuò)。函數(shù) sys_gettimeofday()的源碼如下(kernel/time.c): asmlinkage long sys_gettimeofday(struct timeval *tv, struct timezone *tz) if (tv) struct timeval ktv; do_gettimeofday(&k

10、tv); if (copy_to_user(tv, &ktv, sizeof(ktv) return -EFAULT; if (tz) if (copy_to_user(tz, &sys_tz, sizeof(sys_tz) return -EFAULT; return 0; 顯然,函數(shù)的實(shí)現(xiàn)主要分成兩個(gè)大的方面: (1)如果 tv 指針有效,則說明用戶要以 timeval 格式來檢索系統(tǒng)當(dāng)前時(shí)間。為此,先調(diào)用do_gettimeofday()函數(shù)來檢索系統(tǒng)當(dāng)前時(shí)間并保存到局部變量 ktv 中。然后再調(diào)用copy_to_user()宏將保存在內(nèi)核空間中的當(dāng)前時(shí)間信息拷貝到由參數(shù)

11、指針 tv 所指向的用戶空間緩沖區(qū)中。 (2)如果 tz 指針有效,則說明用戶要檢索當(dāng)前時(shí)區(qū)信息,因此調(diào)用 copy_to_user()宏將全局變量 sys_tz 中的時(shí)區(qū)信息拷貝到參數(shù)指針 tz 所指向的用戶空間緩沖區(qū)中。 (3)最后,返回 0 表示成功。 函數(shù) do_gettimeofday()的源碼如下(arch/i386/kernel/time.c): /* * This version of gettimeofday has microsecond resolution * and better than microsecond precision on fast x86 machi

12、nes with TSC. */ void do_gettimeofday(struct timeval *tv) unsigned long flags; unsigned long usec, sec; read_lock_irqsave(&xtime_lock, flags); usec = do_gettimeoffset(); unsigned long lost = jiffies - wall_jiffies; if (lost) usec += lost * (1000000 / HZ); sec = xtime.tv_sec; usec += xtime.tv_use

13、c; read_unlock_irqrestore(&xtime_lock, flags); while (usec = 1000000) usec -= 1000000; sec+; tv-tv_sec = sec; tv-tv_usec = usec; 該函數(shù)的完成實(shí)際的當(dāng)前時(shí)間檢索工作。由于 gettimeofday()系統(tǒng)調(diào)用要求時(shí)間精度要達(dá)到微秒級(jí),因此 do_gettimeofday()函數(shù)不能簡單地返回 xtime 中的值即可,而必須精確地確定自從時(shí)鐘驅(qū)動(dòng)的 Bottom Half 上一次更新 xtime 的那個(gè)時(shí)刻到 do_gettimeofday()函數(shù)的當(dāng)前執(zhí)行時(shí)刻

14、之間的具體時(shí)間間隔長度,以便精確地修正 xtime 的值.假定被 do_gettimeofday()用來修正 xtime 的時(shí)間間隔為 fixed_usec,而從 wall_jiffies 到 jiffies之間的時(shí)間間隔是 lost_usec,而從 jiffies 到 do_gettimeofday()函數(shù)的執(zhí)行時(shí)刻的時(shí)間間隔是offset_usec。則下列三個(gè)等式成立: fixed_usec(lost_usecoffset_usec) lost_usec(jiffieswall_jiffies)TICK_SIZE(jiffieswall_jiffies)(1000000HZ) 由于全局變量

15、 last_tsc_low 表示上一次時(shí)鐘中斷服務(wù)函數(shù) timer_interrupt()執(zhí)行時(shí)刻的 CPU TSC 寄存器的值,因此我們可以用 X86 CPU 的 TSC 寄存器來計(jì)算 offset_usec 的值。也即: offset_usec=delay_at_last_interrupt(current_tsc_lowlast_tsc_low)fast_gettimeoffset_quotient 其中,delay_at_last_interrupt 是從上一次發(fā)生時(shí)鐘中斷到 timer_interrupt()服務(wù)函數(shù)真正執(zhí)行時(shí)刻之間的時(shí)間延遲間隔。每一次 timer_interrup

16、t()被執(zhí)行時(shí)都會(huì)計(jì)算這一間隔,并利用 TSC的當(dāng)前值更新 last_tsc_low 變量(可以參見 7.4 節(jié)) 。假定 current_tsc_low 是 do_gettimeofday()函數(shù)執(zhí)行時(shí)刻 TSC 的當(dāng)前值,全局變量 fast_gettimeoffset_quotient 則表示 TSC 寄存器每增加1 所代表的時(shí)間間隔值,它是由 time_init()函數(shù)所計(jì)算的。 根據(jù)上述原理分析,do_gettimeofday()函數(shù)的執(zhí)行步驟如下: (1)調(diào)用函數(shù) do_gettimeoffset()計(jì)算從上一次時(shí)鐘中斷發(fā)生到執(zhí)行 do_gettimeofday()函數(shù)的當(dāng)前時(shí)刻之間

17、的時(shí)間間隔 offset_usec。 (2)通過 wall_jiffies 和 jiffies 計(jì)算 lost_usec 的值。 (3)然后,令 sec=xtime.tv_sec,usec=xtime.tv_usec+lost_usec+offset_usec。顯然,sec 表示系統(tǒng)當(dāng)前時(shí)間在秒數(shù)量級(jí)上的值,而 usec 表示系統(tǒng)當(dāng)前時(shí)間在微秒量級(jí)上的值。 (4)用一個(gè) while循環(huán)來判斷 usec 是否已經(jīng)溢出而超過 106us1 秒。如果溢出,則將usec 減去 106us 并相應(yīng)地將 sec 增加 1,直到 usec 不溢出為止。 (5)最后,用 sec 和 usec 分別更新參數(shù)指針

18、所指向的 timeval 結(jié)構(gòu)變量。至此,整個(gè)查詢過程結(jié)束。 函數(shù) do_gettimeoffset()根據(jù) CPU 是否配置有 TSC 寄存器這一條件分別有不同的實(shí)現(xiàn)。其定義如下(arch/i386/kernel/time.c): #ifndef CONFIG_X86_TSC static unsigned long do_slow_gettimeoffset(void) static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; #else #define do_gettimeoffset() do_fas

19、t_gettimeoffset() #endif 顯然,在配置有 TSC 寄存器的 i386 平臺(tái)上,do_gettimeoffset()函數(shù)實(shí)際上就是do_fast_gettimeoffset()函數(shù)。它通過 TSC 寄存器來計(jì)算 do_fast_gettimeoffset()函數(shù)被執(zhí)行的時(shí)刻到上一次時(shí)鐘中斷發(fā)生時(shí)的時(shí)間間隔值。其源碼如下(arch/i386/kernel/time.c): static inline unsigned long do_fast_gettimeoffset(void) register unsigned long eax, edx; /* Read the T

20、ime Stamp Counter */ rdtsc(eax,edx); /* . relative to previous jiffy (32 bits is enough) */ eax -= last_tsc_low; /* tsc_low delta */ /* * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient * = (tsc_low delta) * (usecs_per_clock) * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy) * *

21、 Using a mull instead of a divl saves up to 31 clock cycles * in the critical path. */ _asm_(mull %2 :=a (eax), =d (edx) :rm (fast_gettimeoffset_quotient), 0 (eax); /* our adjusted time offset in microseconds */ return delay_at_last_interrupt + edx; 對(duì)該函數(shù)的注釋如下: (1)先調(diào)用 rdtsc()函數(shù)讀取當(dāng)前時(shí)刻 TSC 寄存器的值,并將其高 3

22、2 位保存在 edx 局部變量中,低 32 位保存在局部變量 eax 中。 (2)讓局部變量 eaxtsc_loweaxlast_tsc_low;也即計(jì)算當(dāng)前時(shí)刻的 TSC 值與上一次時(shí)鐘中斷服務(wù)函數(shù) timer_interrupt()執(zhí)行時(shí)的 TSC 值之間的差值。 (3)顯然,從上一次 timer_interrupt()到當(dāng)前時(shí)刻的時(shí)間間隔就是(tsc_lowfast_gettimeoffset_quotient) 。因此用一條 mul 指令來計(jì)算這個(gè)乘法表達(dá)式的值。 (4)返回值 delay_at_last_interrupt(tsc_lowfast_gettimeoffset_quot

23、ient)就是從上一次時(shí)鐘中斷發(fā)生時(shí)到當(dāng)前時(shí)刻之間的時(shí)間偏移間隔值。2.1.3Linux 信號(hào) signal 處理機(jī)制信號(hào) signal 機(jī)制是進(jìn)程之間相互傳遞消息的一種方法,全稱為軟中斷信號(hào)。系統(tǒng)調(diào)用signal 用來設(shè)定某個(gè)信號(hào)的處理方法,其調(diào)用聲明的格式如下: void (*signal(int signum, void (*handler)(int)(int); 成功則返回該信號(hào)以前的處理配置,出錯(cuò)則返回 SIG_ERR。在使用該調(diào)用的進(jìn)程中加入以下頭文件:幾個(gè)常見信號(hào):SIGINT: 當(dāng)用戶按某些終端鍵時(shí), 引發(fā)終端產(chǎn)生的信號(hào). 如 Ctrl+C 鍵, 這將產(chǎn)生中斷信號(hào)(SIGINT

24、),它將停止一個(gè)已失去控制的程序。SIGSEGV: 由硬件異常(除數(shù)為 0, 無效的內(nèi)存引用等等)產(chǎn)生的信號(hào)。這些條件通常由硬件檢測到, 并將其通知內(nèi)核,然后內(nèi)核為該條件發(fā)生時(shí)正在運(yùn)行的進(jìn)程產(chǎn)生該信號(hào)。SIGURG: 在網(wǎng)絡(luò)連接上傳來帶外數(shù)據(jù)時(shí)產(chǎn)生。SIGPIPE: 在管道的讀進(jìn)程已終止后, 一個(gè)進(jìn)程寫此管道時(shí)產(chǎn)生,當(dāng)類型為SOCK_STREAM 的 socket 已不再連接時(shí), 進(jìn)程寫到該 socket 也產(chǎn)生此信號(hào)。SIGALRM: 進(jìn)程所設(shè)置的鬧鐘時(shí)鐘超時(shí)的時(shí)候產(chǎn)生。SIGABRT: 進(jìn)程調(diào)用 abort 函數(shù)時(shí)產(chǎn)生此信號(hào), 進(jìn)程異常終止。SIGCHLD: 在一個(gè)進(jìn)程終止或停止時(shí), 它將

25、把該信號(hào)發(fā)送給其父進(jìn)程。 按系統(tǒng)默認(rèn), 將忽略此信號(hào),如果父進(jìn)程希望被告知其子進(jìn)程的這種狀態(tài)改變, 則應(yīng)該捕捉此信號(hào)。通常是用 wait 系列函數(shù)捕捉, 如果不 wait 的話, 子進(jìn)程將成為一個(gè)僵尸進(jìn)程。SIGIO: 此信號(hào)指示一個(gè)異步 I/O 事件。SIGSYS: 該信號(hào)指示一個(gè)無效的系統(tǒng)調(diào)用。SIGTSTP: 交互式停止信號(hào). Ctrl+Z, 按下時(shí), 終端將產(chǎn)生此信號(hào), 進(jìn)程被掛起。2.1.4 多線程編程多線程是計(jì)算機(jī)同時(shí)運(yùn)行多個(gè)執(zhí)行線程的能力(這些線程可以是同一程序的組成部分,或者也可以是完全不同的程序) 。Linux 系統(tǒng)下的多線程遵循 POSIX 線程接口,稱為pthread。編

26、寫 Linux 下的多線程程序,需要使用頭文件 pthread.h,連接時(shí)需要使用庫libpthread.a。而 Linux 下 pthread 的實(shí)現(xiàn)是通過系統(tǒng)調(diào)用 clone()來實(shí)現(xiàn)的。clone()是Linux 所特有的系統(tǒng)調(diào)用,它的使用方式類似 fork。下面展示多線程程序部分 050119.c。/* 050119.c */#include #include void thread(void)int i;for(i=0;i3;i+)printf(This is a pthread.n);int pthread (void)pthread_t id;int i,ret;ret=pthr

27、ead_create(&id,NULL,(void *) thread,NULL);if(ret!=0)printf (Create pthread error!n);exit (1);for(i=0;i3;i+)printf(This is the main process.n);pthread_join(id,NULL);return (0);我們編譯此程序:運(yùn)行 050119.out,我們得到如下結(jié)果:This is the main process.This is a pthread.This is the main process.This is the main proce

28、ss.This is a pthread.This is a pthread.再次運(yùn)行,我們可能得到如下結(jié)果:This is a pthread.This is the main process.This is a pthread.This is the main process.This is a pthread.This is the main process.前后兩次結(jié)果不一樣,這是兩個(gè)線程爭奪 CPU 資源的結(jié)果。上面的示例中,我們使用到了兩個(gè)函數(shù),pthread_create 和 pthread_join,并聲明了一個(gè) pthread_t 型的變量。pthread_t 在頭文件/us

29、r/include/bits/pthreadtypes.h 中定義:typedef unsigned long int pthread_t;它是一個(gè)線程的標(biāo)識(shí)符。函數(shù) pthread_create 用來創(chuàng)建一個(gè)線程,它的原型為:extern int pthread_create _P (pthread_t *_thread, _const pthread_attr_t *_attr,void *(*_start_routine) (void *), void *_arg);第一個(gè)參數(shù)為指向線程標(biāo)識(shí)符的指針,第二個(gè)參數(shù)用來設(shè)置線程屬性,第三個(gè)參數(shù)是線程運(yùn)行函數(shù)的起始地址,最后一個(gè)參數(shù)是運(yùn)行函數(shù)的

30、參數(shù)。這里,我們的函 數(shù) thread 不需要參數(shù),所以最后一個(gè)參數(shù)設(shè)為空指針。第二個(gè)參數(shù)我們也設(shè)為空指針,這樣將生成默認(rèn)屬性的線程。對(duì)線程屬性的設(shè)定和修改我們將在下一節(jié) 闡述。當(dāng)創(chuàng)建線程成功時(shí),函數(shù)返回 0,若不為 0 則說明創(chuàng)建線程失敗,常見的錯(cuò)誤返回代碼為 EAGAIN 和 EINVAL。前者表示系統(tǒng)限制創(chuàng)建新的線程,例如線程數(shù)目過多了;后者表示第二個(gè)參數(shù)代表的線程屬性值非法。創(chuàng)建線程成功后,新創(chuàng)建的線程則運(yùn)行參數(shù)三和參數(shù)四確定的函數(shù),原來的線程則繼續(xù)運(yùn)行下一行代碼。函數(shù) pthread_join 用來等待一個(gè)線程的結(jié)束。函數(shù)原型為:extern int pthread_join _P

31、(pthread_t _th, void *_thread_return);第一個(gè)參數(shù)為被等待的線程標(biāo)識(shí)符,第二個(gè)參數(shù)為一個(gè)用戶定義的指針,它可以用來存儲(chǔ)被等待線程的返回值。這個(gè)函數(shù)是一個(gè)線程阻塞的函數(shù),調(diào)用它的函數(shù)將 一直等待到被等待的線程結(jié)束為止,當(dāng)函數(shù)返回時(shí),被等待線程的資源被收回。一個(gè)線程的結(jié)束有兩種途徑,一種是象我們上面的例子一樣,函數(shù)結(jié)束了,調(diào)用它的 線程也就結(jié)束了;另一種方式是通過函數(shù) pthread_exit 來實(shí)現(xiàn)。它的函數(shù)原型為:extern void pthread_exit _P (void *_retval) _attribute_ (_noreturn_);唯一的參

32、數(shù)是函數(shù)的返回代碼,只要 pthread_join 中的第二個(gè)參數(shù) thread_return 不是NULL,這個(gè)值將被傳遞給 thread_return。最后要說明的是,一個(gè)線程不能被多個(gè)線程等待,否則第一個(gè)接收到信號(hào)的線程成功返回,其余調(diào)用 pthread_join 的線 程則返回錯(cuò)誤代碼ESRCH。2.1.5 內(nèi)核定時(shí)器機(jī)制的實(shí)現(xiàn).1 動(dòng)態(tài)定時(shí)器機(jī)制的初始化 函數(shù) init_timervecs()實(shí)現(xiàn)對(duì)動(dòng)態(tài)定時(shí)器機(jī)制的初始化。該函數(shù)僅被 sched_init()初始化例程所調(diào)用。動(dòng)態(tài)定時(shí)器機(jī)制初始化過程的主要任務(wù)就是將 tv1、tv2、tv5 這 5 個(gè)結(jié)構(gòu)變量中的定時(shí)器向量指針數(shù)組 v

33、ec初始化為 NULL。如下所示(kernel/timer.c): void init_timervecs (void) int i; for (i = 0; i TVN_SIZE; i+) INIT_LIST_HEAD(tv5.vec + i); INIT_LIST_HEAD(tv4.vec + i); INIT_LIST_HEAD(tv3.vec + i); INIT_LIST_HEAD(tv2.vec + i); for (i = 0; i expires; unsigned long idx = expires - timer_jiffies; struct list_head * v

34、ec; if (idx TVR_SIZE) int i = expires & TVR_MASK; vec = tv1.vec + i; else if (idx 1 TVR_BITS) & TVN_MASK; vec = tv2.vec + i; else if (idx 1 (TVR_BITS + TVN_BITS) & TVN_MASK; vec = tv3.vec + i; else if (idx 1 (TVR_BITS + 2 * TVN_BITS) & TVN_MASK; vec = tv4.vec + i; else if (signed lon

35、g) idx 0) /* can happen if you add a timer with expires = jiffies, * or you set a timer to go off in the past */ vec = tv1.vec + tv1.index; else if (idx (TVR_BITS + 3 * TVN_BITS) & TVN_MASK; vec = tv5.vec + i; else /* Can only get here on architectures with 64-bit jiffies */ INIT_LIST_HEAD(&

36、timer-list); return; /* * Timers are FIFO! */ list_add(&timer-list, vec-prev); 對(duì)該函數(shù)的注釋如下: (1)首先,計(jì)算定時(shí)器的 expires 值與 timer_jiffies 的插值(注意!這里應(yīng)該使用動(dòng)態(tài)定時(shí)器自己的時(shí)間基準(zhǔn)) ,這個(gè)差值就表示這個(gè)定時(shí)器相對(duì)于上一次運(yùn)行定時(shí)器機(jī)制的那個(gè)時(shí)刻還需要多長時(shí)間間隔才到期。局部變量 idx 保存這個(gè)差值。 (2)根據(jù) idx 的值確定這個(gè)定時(shí)器應(yīng)被插入到哪一個(gè)定時(shí)器向量中。其具體的確定方法我們?cè)诠?jié)已經(jīng)說過了,這里不再詳述。最后,定時(shí)器向量的頭部指針 vec 表示這

37、個(gè)定時(shí)器應(yīng)該所處的定時(shí)器向量鏈表頭部。 (3)最后,調(diào)用 list_add()函數(shù)將定時(shí)器插入到 vec 指針?biāo)赶虻亩〞r(shí)器隊(duì)列的尾部。 .3 修改一個(gè)定時(shí)器的 expires 值 當(dāng)一個(gè)定時(shí)器已經(jīng)被插入到內(nèi)核動(dòng)態(tài)定時(shí)器鏈表中后,我們還可以修改該定時(shí)器的 expires值。函數(shù) mod_timer()實(shí)現(xiàn)這一點(diǎn)。如下所示(kernel/timer.c): int mod_timer(struct timer_list *timer, unsigned long expires) int ret; unsigned long flags; spin_lock_irqsave(&timer

38、list_lock, flags); timer-expires = expires; ret = detach_timer(timer); internal_add_timer(timer); spin_unlock_irqrestore(&timerlist_lock, flags); return ret; 該函數(shù)首先根據(jù)參數(shù) expires 值更新定時(shí)器的 expires 成員。然后調(diào)用 detach_timer()函數(shù)將該定時(shí)器從它原來所屬的鏈表中刪除。最后調(diào)用 internal_add_timer()函數(shù)將該定時(shí)器根據(jù)它新的 expires 值重新插入到相應(yīng)的鏈表中。 函數(shù)

39、 detach_timer()首先調(diào)用 timer_pending()來判斷指定的定時(shí)器是否已經(jīng)處于某個(gè)鏈表中,如果定時(shí)器原來就不處于任何鏈表中,則 detach_timer()函數(shù)什么也不做,直接返回 0值,表示失敗。否則,就調(diào)用 list_del()函數(shù)將定時(shí)器從它原來所處的鏈表中摘除。如下所示(kernel/timer.c): static inline int detach_timer (struct timer_list *timer) if (!timer_pending(timer) return 0; list_del(&timer-list); return 1; 2

40、.2.系統(tǒng)平臺(tái):系統(tǒng)平臺(tái):一臺(tái) Linux 主機(jī)且有超級(jí)用戶權(quán)限2.3. 編程工具:編程工具:VI 編輯器,Gedit 編輯器3數(shù)據(jù)結(jié)構(gòu)與模塊說明(功能與流程圖)數(shù)據(jù)結(jié)構(gòu)與模塊說明(功能與流程圖)3.1定時(shí)器使用:int gettimeofday(struct timeval *tv,struct timezone *tz); strut timevallong tv_sec; /*秒數(shù)*/ long tv_usec; /*微秒數(shù)*/ ;這個(gè) syscall 用來供用戶獲取 timeval 格式的當(dāng)前時(shí)間信息(精確度為微秒級(jí)) ,以及系統(tǒng)的當(dāng)前時(shí)區(qū)信息(timezone) 。結(jié)構(gòu)類型 time

41、val 的指針參數(shù) tv 指向接受時(shí)間信息的用戶空間緩沖區(qū),參數(shù) tz 是一個(gè) timezone 結(jié)構(gòu)類型的指針,指向接收時(shí)區(qū)信息的用戶空間緩沖區(qū)。這兩個(gè)參數(shù)均為輸出參數(shù),返回值 0 表示成功,返回負(fù)值表示出錯(cuò)。實(shí)現(xiàn)過程如下:main() struct timeval tpstart,tpend; /*申請(qǐng) struct timeval 的變量,tv_sec 返回的是秒數(shù),tv_usec 返回的是微秒數(shù)*/float timeuse; gettimeofday(&tpstart,NULL); pthread(); gettimeofday(&tpend,NULL); timeu

42、se=1000000*(tpend.tv_sec-tpstart.tv_sec)+ tpend.tv_usec-tpstart.tv_usec; timeuse/=1000000; printf(Used Time:%f secn,timeuse); exit(0); 3.2 多線程程序:進(jìn)行多線程程序設(shè)計(jì)時(shí),我們使用到了兩個(gè)函數(shù),pthread_create 和 pthread_join,并聲明了一個(gè) pthread_t 型的變量。pthread_t 在頭文件/usr/include/bits/pthreadtypes.h 中定義,它是一個(gè)線程的標(biāo)識(shí)符。函數(shù) pthread_create 用

43、來創(chuàng)建一個(gè)線程,函數(shù) pthread_join 用來等待一個(gè)線程的結(jié)束。實(shí)現(xiàn)過程如下:int pthread_create(&id,NULL,(void *) thread,NULL);pthread_join(id,NULL);void thread(void)int i;for(i=0;i3;i+)printf(This is a pthread.n);int pthread(void)pthread_t id; /* 聲明了一個(gè) pthread_t 型的變量*/int i,ret;ret=pthread_create(&id,NULL,(void *) thread,NU

44、LL);if(ret!=0)printf(Create pthread error!n);exit(1);for(i=0;i3;i+)printf(This is the main process.n);pthread_join(id,NULL);return(0);3.3 程序流程圖:獲取進(jìn)程開始時(shí)間獲取進(jìn)程結(jié)束時(shí)間開始計(jì)算使用時(shí)間功能函數(shù)結(jié)束4.4. 源程序:源程序: #include #include #include int gettimeofday(struct timeval *tv,struct timezone *tz); int pthread_create(&id,

45、NULL,(void *) thread,NULL);/pthread_join(id,NULL);strut timevallong tv_sec; /*秒數(shù)*/ long tv_usec; /*微秒數(shù)*/ ;void thread(void)int i;for(i=0;i3;i+)printf(This is a pthread.n);int pthread(void)pthread_t id; /* 聲明了一個(gè)pthread_t型的變量*/int i,ret;ret=pthread_create(&id,NULL,(void *) thread,NULL);if(ret!=0)p

46、rintf(Create pthread error!n);exit(1);for(i=0;i3;i+)printf(This is the main process.n);pthread_join(id,NULL);return(0);main() struct timeval tpstart,tpend; /*申請(qǐng)struct timeval的變量,tv_sec返回的是秒數(shù),tv_usec返回的是微秒數(shù)*/float timeuse; gettimeofday(&tpstart,NULL); pthread(); gettimeofday(&tpend,NULL); timeuse=1000000*(tpend.tv_s

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論