版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領
文檔簡介
1、<p><b> 目錄</b></p><p><b> 摘要1</b></p><p> 1設計題目與要求1</p><p><b> 1.1設計題目1</b></p><p><b> 1.2設計要求1</b></p
2、><p> 2 總的設計思想及系統(tǒng)平臺、語言、工具2</p><p> 2.1設計思想:2</p><p> 2.1.1Linux內(nèi)核對定時器的描述2</p><p> 2.1.2Linux 內(nèi)核定時器3</p><p> 2.1.3Linux 信號signal處理機制6</p><
3、;p> 2.1.4多線程編程6</p><p> 2.1.5內(nèi)核定時器機制的實現(xiàn)8</p><p> 3.數(shù)據(jù)結(jié)構(gòu)與模塊說明(功能與流程圖)11</p><p> 3.1.定時器使用:11</p><p> 3.3程序流程圖:13</p><p> 4. 源程序:13</p>
4、<p> 5.運行結(jié)果與運行情況14</p><p> 6.調(diào)試記錄:15</p><p> 7.自我評析和總結(jié):16</p><p><b> 8.參考文獻17</b></p><p><b> 摘要</b></p><p> 每個正在系統(tǒng)上
5、運行的程序都是一個進程。每個進程包含一到多個線程。進程也可能是整個程序或者是部分程序的動態(tài)執(zhí)行。線程是一組指令的集合,或者是程序的特殊段,它可以在程序里獨立執(zhí)行。也可以把它理解為代碼運行的上下文。內(nèi)核時間指明線程執(zhí)行操作系統(tǒng)代碼已經(jīng)經(jīng)過了多少個100ns的CPU時間,linux是一個具有保護模式的操作系統(tǒng)。它一直工作在i386 cpu的保護模式之下。內(nèi)存被分為兩個單元: 內(nèi)核區(qū)域和用戶區(qū)域。一般地,在使用虛擬內(nèi)存技術(shù)的多任務系統(tǒng)上,內(nèi)核
6、和應用有不同的地址空間,因此,在內(nèi)核和應用之間以及在應用與應用之間進行數(shù)據(jù)交換需要專門的機制來實現(xiàn),本文站在用戶空間的角度,測試一個多線程程序的程序執(zhí)行時間。當一個進程希望獲得信號量時, 如果信號量已經(jīng)被占有, 則該進程將會被放到等待隊列上sleep直到cpu將其喚醒。相對于spinlock來說開銷太大,適用于長時間占有的lock。不可用于中斷狀態(tài),因為它擁有信號量的進程可以sleep, 可以被搶占,信號量可以設置為同時允許的進程數(shù)。&
7、lt;/p><p><b> 1設計題目與要求</b></p><p> 1.1設計題目:內(nèi)核定時器</p><p> 1.2設計要求:通過研究內(nèi)核的時間管理算法,學習內(nèi)核源代碼;然后應用這些知識并且使用“信號”建立一種用戶空間機制來測量一個多線程程序的執(zhí)行時間。</p><p> 2 總的設計思想及系統(tǒng)平臺、語言
8、、工具</p><p><b> 2.1設計思想:</b></p><p> 2.1.1Linux內(nèi)核對定時器的描述 </p><p> Linux在include/linux/timer.h頭文件中定義了數(shù)據(jù)結(jié)構(gòu)timer_list來描述一個內(nèi)核定</p><p><b> 時器: </b
9、></p><p> struct timer_list { </p><p> struct list_head list; </p><p> unsigned long expires; </p><p> unsigned long data; </p><p> void (*func
10、tion)(unsigned long); </p><p><b> }; </b></p><p> 各數(shù)據(jù)成員的含義如下: </p><p> ?。?)雙向鏈表元素list:用來將多個定時器連接成一條雙向循環(huán)隊列。 </p><p> ?。?)expires:指定定時器到期的時間,這個時間被表示成自系統(tǒng)啟
11、動以來的時鐘滴答計</p><p> 數(shù)(也即時鐘節(jié)拍數(shù))。當一個定時器的expires值小于或等于jiffies變量時,我們就說這個</p><p> 定時器已經(jīng)超時或到期了。在初始化一個定時器后,通常把它的expires域設置成當前expires</p><p> 變量的當前值加上某個時間間隔值(以時鐘滴答次數(shù)計)。 </p><p&
12、gt; ?。?)函數(shù)指針function:指向一個可執(zhí)行函數(shù)。當定時器到期時,內(nèi)核就執(zhí)行function</p><p> 所指定的函數(shù)。而data域則被內(nèi)核用作function函數(shù)的調(diào)用參數(shù)。 </p><p> 內(nèi)核函數(shù)init_timer()用來初始化一個定時器。實際上,這個初始化函數(shù)僅僅將結(jié)構(gòu)中的</p><p> list成員初始化為空。如下所示(
13、include/linux/timer.h): </p><p> static inline void init_timer(struct timer_list * timer) </p><p><b> { </b></p><p> timer->list.next = timer->list.prev = NU
14、LL; </p><p><b> } </b></p><p> 由于定時器通常被連接在一個雙向循環(huán)隊列中等待執(zhí)行(此時我們說定時器處于pending</p><p> 狀態(tài))。因此函數(shù)time_pending()就可以用list成員是否為空來判斷一個定時器是否處于</p><p> pending狀態(tài)。如下
15、所示 </p><p> ?。╥nclude/linux/timer.h): </p><p> static inline int timer_pending (const struct timer_list * </p><p><b> timer) </b></p><p><b> {
16、</b></p><p> return timer->list.next != NULL; </p><p><b> } </b></p><p><b> 時間比較操作 </b></p><p> 在定時器應用中經(jīng)常需要比較兩個時間值,以確定timer是否超時,所
17、以Linux內(nèi)核在</p><p> timer.h頭文件中定義了4個時間關(guān)系比較操作宏。這里我們說時刻a在時刻b之后,就意味著</p><p> 時間值a≥b。Linux強烈推薦用戶使用它所定義的下列4個時間比較操作宏</p><p> ?。╥nclude/linux/timer.h): </p><p> #define time
18、_after(a,b) ((long)(b) - (long)(a) < 0) </p><p> #define time_before(a,b) time_after(b,a) </p><p> #define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0) </p><p> #defi
19、ne time_before_eq(a,b) time_after_eq(b,a)</p><p> 2.1.2Linux 內(nèi)核定時器</p><p> 定時器是管理內(nèi)核時間的基礎,用來計算流逝的時間,它以某種頻率(節(jié)拍率)自行觸發(fā)時鐘中斷,當時鐘中斷發(fā)生時,內(nèi)核就通過一種特殊中斷處理程序?qū)ζ溥M行處理。</p><p> 但是原來的實現(xiàn)只能是time_t my
20、time形式的,經(jīng)過簡單的localtime(mytime)和ctime(&mytime)處理.精度是不夠的,為了返回高精度的時間,這里使用了gettimeofday函數(shù)。</p><p> 這個syscall用來供用戶獲取timeval格式的當前時間信息(精確度為微秒級),以及系統(tǒng)的當前時區(qū)信息(timezone)。結(jié)構(gòu)類型timeval的指針參數(shù)tv指向接受時間信息的用戶空間緩沖區(qū),參數(shù)tz是一個t
21、imezone結(jié)構(gòu)類型的指針,指向接收時區(qū)信息的用戶空間緩沖區(qū)。這兩個參數(shù)均為輸出參數(shù),返回值0表示成功,返回負值表示出錯。函數(shù)sys_gettimeofday()的源碼如下(kernel/time.c): </p><p> asmlinkage long sys_gettimeofday(struct timeval *tv, struct timezone *tz) </p><p&g
22、t;<b> { </b></p><p> if (tv) { </p><p> struct timeval ktv; </p><p> do_gettimeofday(&ktv); </p><p> if (copy_to_user(tv, &ktv, sizeof(ktv))) &
23、lt;/p><p> return -EFAULT; </p><p><b> } </b></p><p> if (tz) { </p><p> if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) </p><p> return -
24、EFAULT; </p><p><b> } </b></p><p> return 0; </p><p><b> } </b></p><p> 顯然,函數(shù)的實現(xiàn)主要分成兩個大的方面: </p><p> ?。?)如果tv指針有效,則說明用戶要以timeva
25、l格式來檢索系統(tǒng)當前時間。為此,先調(diào)用do_gettimeofday()函數(shù)來檢索系統(tǒng)當前時間并保存到局部變量ktv中。然后再調(diào)用copy_to_user()宏將保存在內(nèi)核空間中的當前時間信息拷貝到由參數(shù)指針tv所指向的用戶空間緩沖區(qū)中。 </p><p> ?。?)如果tz指針有效,則說明用戶要檢索當前時區(qū)信息,因此調(diào)用copy_to_user()宏將全局變量sys_tz中的時區(qū)信息拷貝到參數(shù)指針tz所指向的用
26、戶空間緩沖區(qū)中。 </p><p> ?。?)最后,返回0表示成功。 </p><p> 函數(shù)do_gettimeofday()的源碼如下(arch/i386/kernel/time.c): </p><p><b> /* </b></p><p> * This version of gettimeofday
27、has microsecond resolution </p><p> * and better than microsecond precision on fast x86 machines with TSC. </p><p><b> */ </b></p><p> void do_gettimeofday(struct tim
28、eval *tv) </p><p><b> { </b></p><p> unsigned long flags; </p><p> unsigned long usec, sec; </p><p> read_lock_irqsave(&xtime_lock, flags); </p&g
29、t;<p> usec = do_gettimeoffset(); </p><p><b> { </b></p><p> unsigned long lost = jiffies - wall_jiffies; </p><p> if (lost) </p><p> usec += lo
30、st * (1000000 / HZ); </p><p><b> } </b></p><p> sec = xtime.tv_sec; </p><p> usec += xtime.tv_usec; </p><p> read_unlock_irqrestore(&xtime_lock, fla
31、gs); </p><p> while (usec >= 1000000) { </p><p> usec -= 1000000; </p><p><b> sec++; </b></p><p><b> } </b></p><p> tv->
32、tv_sec = sec; </p><p> tv->tv_usec = usec; </p><p><b> } </b></p><p> 該函數(shù)的完成實際的當前時間檢索工作。由于gettimeofday()系統(tǒng)調(diào)用要求時間精度要達到微秒級,因此do_gettimeofday()函數(shù)不能簡單地返回xtime中的值即可,而必須
33、精確地確定自從時鐘驅(qū)動的Bottom Half上一次更新xtime的那個時刻到do_gettimeofday()函數(shù)的當前執(zhí)行時刻之間的具體時間間隔長度,以便精確地修正xtime的值.</p><p> 假定被do_gettimeofday()用來修正xtime的時間間隔為fixed_usec,而從wall_jiffies到jiffies之間的時間間隔是lost_usec,而從jiffies到do_gettim
34、eofday()函數(shù)的執(zhí)行時刻的時間間隔是offset_usec。則下列三個等式成立: </p><p> fixed_usec=(lost_usec+offset_usec) </p><p> lost_usec=(jiffies-wall_jiffies)*TICK_SIZE=(jiffies-wall_jiffies)*(1000000/HZ) 由于全局變量last_tsc_l
35、ow表示上一次時鐘中斷服務函數(shù)timer_interrupt()執(zhí)行時刻的CPU TSC寄存器的值,因此我們可以用X86 CPU的TSC寄存器來計算offset_usec的值。也即: </p><p> offset_usec=delay_at_last_interrupt+(current_tsc_low-last_tsc_low)*fast_gettimeoffset_quotient 其中,delay_a
36、t_last_interrupt是從上一次發(fā)生時鐘中斷到timer_interrupt()服務函數(shù)真正執(zhí)行時刻之間的時間延遲間隔。每一次timer_interrupt()被執(zhí)行時都會計算這一間隔,并利用TSC的當前值更新last_tsc_low變量(可以參見7.4節(jié))。假定current_tsc_low是do_gettimeofday()函數(shù)執(zhí)行時刻TSC的當前值,全局變量fast_gettimeoffset_quotient則表示TS
37、C寄存器每增加1所代表的時間間隔值,它是由time_init()函數(shù)所計算的。 </p><p> 根據(jù)上述原理分析,do_gettimeofday()函數(shù)的執(zhí)行步驟如下: </p><p> ?。?)調(diào)用函數(shù)do_gettimeoffset()計算從上一次時鐘中斷發(fā)生到執(zhí)行do_gettimeofday()函數(shù)的當前時刻之間的時間間隔offset_usec。 </p>&
38、lt;p> ?。?)通過wall_jiffies和jiffies計算lost_usec的值。 </p><p> ?。?)然后,令sec=xtime.tv_sec,usec=xtime.tv_usec+lost_usec+offset_usec。顯然,sec表示系統(tǒng)當前時間在秒數(shù)量級上的值,而usec表示系統(tǒng)當前時間在微秒量級上的值。 </p><p> ?。?)用一個while{}
39、循環(huán)來判斷usec是否已經(jīng)溢出而超過106us=1秒。如果溢出,則將usec減去106us并相應地將sec增加1,直到usec不溢出為止。 </p><p> ?。?)最后,用sec和usec分別更新參數(shù)指針所指向的timeval結(jié)構(gòu)變量。至此,整個查詢過程結(jié)束。 </p><p> 函數(shù)do_gettimeoffset()根據(jù)CPU是否配置有TSC寄存器這一條件分別有不同的實現(xiàn)。其定義
40、如下(arch/i386/kernel/time.c): </p><p> #ifndef CONFIG_X86_TSC </p><p> static unsigned long do_slow_gettimeoffset(void) </p><p><b> { </b></p><p><b>
41、; …… </b></p><p><b> } </b></p><p> static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; </p><p><b> #else </b></p><p
42、> #define do_gettimeoffset() do_fast_gettimeoffset() </p><p><b> #endif </b></p><p> 顯然,在配置有TSC寄存器的i386平臺上,do_gettimeoffset()函數(shù)實際上就是do_fast_gettimeoffset()函數(shù)。它通過TSC寄存器來計算do_fas
43、t_gettimeoffset()函數(shù)被執(zhí)行的時刻到上一次時鐘中斷發(fā)生時的時間間隔值。其源碼如下(arch/i386/kernel/time.c): </p><p> static inline unsigned long do_fast_gettimeoffset(void) </p><p><b> { </b></p><p>
44、 register unsigned long eax, edx; </p><p> /* Read the Time Stamp Counter */ </p><p> rdtsc(eax,edx); </p><p> /* .. relative to previous jiffy (32 bits is enough) */ </p>
45、<p> eax -= last_tsc_low; /* tsc_low delta */ </p><p><b> /* </b></p><p> * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient </p><p> * = (tsc_low d
46、elta) * (usecs_per_clock) </p><p> * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy) </p><p><b> * </b></p><p> * Using a mull instead of a divl saves up to
47、31 clock cycles </p><p> * in the critical path. </p><p><b> */ </b></p><p> __asm__("mull %2" </p><p> :"=a" (eax), "=d"
48、(edx) </p><p> :"rm" (fast_gettimeoffset_quotient), </p><p> "0" (eax)); </p><p> /* our adjusted time offset in microseconds */ </p><p> return
49、 delay_at_last_interrupt + edx; </p><p><b> } </b></p><p> 對該函數(shù)的注釋如下: </p><p> (1)先調(diào)用rdtsc()函數(shù)讀取當前時刻TSC寄存器的值,并將其高32位保存在edx局部變量中,低32位保存在局部變量eax中。 </p><p>
50、 ?。?)讓局部變量eax=Δtsc_low=eax-last_tsc_low;也即計算當前時刻的TSC值與上一次時鐘中斷服務函數(shù)timer_interrupt()執(zhí)行時的TSC值之間的差值。 </p><p> ?。?)顯然,從上一次timer_interrupt()到當前時刻的時間間隔就是(Δtsc_low*fast_gettimeoffset_quotient)。因此用一條mul指令來計算這個乘法表達式的值
51、。 </p><p> ?。?)返回值delay_at_last_interrupt+(Δtsc_low*fast_gettimeoffset_quotient)就是從上一次時鐘中斷發(fā)生時到當前時刻之間的時間偏移間隔值。</p><p> 2.1.3Linux 信號signal處理機制</p><p> 信號signal機制是進程之間相互傳遞消息的一種方法,全稱
52、為軟中斷信號。系統(tǒng)調(diào)用signal用來設定某個信號的處理方法,其調(diào)用聲明的格式如下: </p><p> void (*signal(int signum, void (*handler)(int)))(int); 成功則返回該信號以前的處理配置,出錯則返回SIG_ERR。在使用該調(diào)用的進程中加入以下頭文件:<signal.h></p><p><b> 幾個常見
53、信號:</b></p><p> SIGINT: 當用戶按某些終端鍵時, 引發(fā)終端產(chǎn)生的信號. 如Ctrl+C鍵, 這將產(chǎn)生中斷信號(SIGINT),它將停止一個已失去控制的程序。</p><p> SIGSEGV: 由硬件異常(除數(shù)為0, 無效的內(nèi)存引用等等)產(chǎn)生的信號。這些條件通常由硬件檢測到, 并將其通知內(nèi)核,然后內(nèi)核為該條件發(fā)生時正在運行的進程產(chǎn)生該信號。</
54、p><p> SIGURG: 在網(wǎng)絡連接上傳來帶外數(shù)據(jù)時產(chǎn)生。</p><p> SIGPIPE: 在管道的讀進程已終止后, 一個進程寫此管道時產(chǎn)生,當類型為SOCK_STREAM的socket已不再連接時, 進程寫到該socket也產(chǎn)生此信號。</p><p> SIGALRM: 進程所設置的鬧鐘時鐘超時的時候產(chǎn)生。</p><p>
55、SIGABRT: 進程調(diào)用abort函數(shù)時產(chǎn)生此信號, 進程異常終止。</p><p> SIGCHLD: 在一個進程終止或停止時, 它將把該信號發(fā)送給其父進程。 按系統(tǒng)默認, 將忽略此信號,如果父進程希望被告知其子進程的這種狀態(tài)改變, 則應該捕捉此信號。通常是用wait系列函數(shù)捕捉, 如果不wait的話, 子進程將成為一個僵尸進程。</p><p> SIGIO: 此信號指示一個異步
56、I/O事件。</p><p> SIGSYS: 該信號指示一個無效的系統(tǒng)調(diào)用。</p><p> SIGTSTP: 交互式停止信號. Ctrl+Z, 按下時, 終端將產(chǎn)生此信號, 進程被掛起。</p><p> 2.1.4多線程編程</p><p> 多線程是計算機同時運行多個執(zhí)行線程的能力(這些線程可以是同一程序的組成部分,或者也可
57、以是完全不同的程序)。Linux系統(tǒng)下的多線程遵循POSIX線程接口,稱為pthread。編寫Linux下的多線程程序,需要使用頭文件pthread.h,連接時需要使用庫libpthread.a。而Linux下pthread的實現(xiàn)是通過系統(tǒng)調(diào)用clone()來實現(xiàn)的。clone()是Linux所特有的系統(tǒng)調(diào)用,它的使用方式類似fork。下面展示多線程程序部分050119.c。</p><p> /* 05011
58、9.c */</p><p><b> ……</b></p><p> #include <pthread.h></p><p> #include <stdio.h></p><p> void thread(void)</p><p><b> {&
59、lt;/b></p><p><b> int i;</b></p><p> for(i=0;i<3;i++)</p><p> printf("This is a pthread.\n");</p><p><b> } </b></p>&
60、lt;p> int pthread (void)</p><p><b> {</b></p><p> pthread_t id;</p><p> int i,ret;</p><p> ret=pthread_create(&id,NULL,(void *) thread,NULL);<
61、;/p><p> if(ret!=0){</p><p> printf ("Create pthread error!\n");</p><p><b> exit (1);</b></p><p><b> }</b></p><p> for(
62、i=0;i<3;i++)</p><p> printf("This is the main process.\n");</p><p> pthread_join(id,NULL);</p><p> return (0);</p><p><b> }</b></p>
63、<p><b> ……</b></p><p><b> 我們編譯此程序:</b></p><p> gcc 050119.c -lpthread -o 050119.out</p><p> 運行050119.out,我們得到如下結(jié)果:</p><p> This is th
64、e main process.</p><p> This is a pthread.</p><p> This is the main process.</p><p> This is the main process.</p><p> This is a pthread.</p><p> This
65、 is a pthread.</p><p> 再次運行,我們可能得到如下結(jié)果:</p><p> This is a pthread.</p><p> This is the main process.</p><p> This is a pthread.</p><p> This is the ma
66、in process.</p><p> This is a pthread.</p><p> This is the main process.</p><p> 前后兩次結(jié)果不一樣,這是兩個線程爭奪CPU資源的結(jié)果。上面的示例中,我們使用到了兩個函數(shù),pthread_create和pthread_join,并聲明了一個pthread_t型的變量。<
67、/p><p> pthread_t在頭文件/usr/include/bits/pthreadtypes.h中定義:</p><p> typedef unsigned long int pthread_t;</p><p> 它是一個線程的標識符。函數(shù)pthread_create用來創(chuàng)建一個線程,它的原型為:</p><p> exter
68、n int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,void *(*__start_routine) (void *), void *__arg));</p><p> 第一個參數(shù)為指向線程標識符的指針,第二個參數(shù)用來設置線程屬性,第三個參數(shù)是線程運行函數(shù)的起始地址,最后一個參數(shù)是運行函數(shù)的參數(shù)。這里,我們
69、的函 數(shù)thread不需要參數(shù),所以最后一個參數(shù)設為空指針。第二個參數(shù)我們也設為空指針,這樣將生成默認屬性的線程。對線程屬性的設定和修改我們將在下一節(jié) 闡述。當創(chuàng)建線程成功時,函數(shù)返回0,若不為0則說明創(chuàng)建線程失敗,常見的錯誤返回代碼為EAGAIN和EINVAL。前者表示系統(tǒng)限制創(chuàng)建新的線程,例如線程數(shù)目過多了;后者表示第二個參數(shù)代表的線程屬性值非法。創(chuàng)建線程成功后,新創(chuàng)建的線程則運行參數(shù)三和參數(shù)四確定的函數(shù),原來的線程則繼續(xù)運行下一行
70、代碼。</p><p> 函數(shù)pthread_join用來等待一個線程的結(jié)束。函數(shù)原型為:extern int pthread_join __P ((pthread_t __th, void **__thread_return));第一個參數(shù)為被等待的線程標識符,第二個參數(shù)為一個用戶定義的指針,它可以用來存儲被等待線程的返回值。這個函數(shù)是一個線程阻塞的函數(shù),調(diào)用它的函數(shù)將 一直等待到被等待的線程結(jié)束為止,當函數(shù)
71、返回時,被等待線程的資源被收回。一個線程的結(jié)束有兩種途徑,一種是象我們上面的例子一樣,函數(shù)結(jié)束了,調(diào)用它的 線程也就結(jié)束了;另一種方式是通過函數(shù)pthread_exit來實現(xiàn)。它的函數(shù)原型為:</p><p> extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));</p><p>
72、 唯一的參數(shù)是函數(shù)的返回代碼,只要pthread_join中的第二個參數(shù)thread_return不是NULL,這個值將被傳遞給 thread_return。最后要說明的是,一個線程不能被多個線程等待,否則第一個接收到信號的線程成功返回,其余調(diào)用pthread_join的線 程則返回錯誤代碼ESRCH。</p><p> 2.1.5內(nèi)核定時器機制的實現(xiàn)</p><p> 2.1.5.1
73、動態(tài)定時器機制的初始化 </p><p> 函數(shù)init_timervecs()實現(xiàn)對動態(tài)定時器機制的初始化。該函數(shù)僅被sched_init()初始化例程所調(diào)用。動態(tài)定時器機制初始化過程的主要任務就是將tv1、tv2、…、tv5這5個結(jié)構(gòu)變量中的定時器向量指針數(shù)組vec[]初始化為NULL。如下所示(kernel/timer.c): </p><p> void init_time
74、rvecs (void) </p><p><b> { </b></p><p><b> int i; </b></p><p> for (i = 0; i < TVN_SIZE; i++) { </p><p> INIT_LIST_HEAD(tv5.vec + i);
75、 </p><p> INIT_LIST_HEAD(tv4.vec + i); </p><p> INIT_LIST_HEAD(tv3.vec + i); </p><p> INIT_LIST_HEAD(tv2.vec + i); </p><p><b> } </b></p>&l
76、t;p> for (i = 0; i < TVR_SIZE; i++) </p><p> INIT_LIST_HEAD(tv1.vec + i); </p><p><b> } </b></p><p> 上述函數(shù)中的宏TVN_SIZE是指timer_vec結(jié)構(gòu)類型中的定時器向量指針數(shù)組vec[]的大小,值為64。宏
77、TVR_SIZE是指timer_vec_root結(jié)構(gòu)類型中的定時器向量數(shù)組vec[]的大小,值為256。 </p><p> 2.1.5.2將一個定時器插入到鏈表中 </p><p> 函數(shù)add_timer()用來將參數(shù)timer指針所指向的定時器插入到一個合適的定時器鏈表中。它首先調(diào)用timer_pending()函數(shù)判斷所指定的定時器是否已經(jīng)位于在某個定時器向量中等待</
78、p><p> 執(zhí)行。如果是,則不進行任何操作,只是打印一條內(nèi)核告警信息就返回了;如果不是,則調(diào)用</p><p> internal_add_timer()函數(shù)完成實際的插入操作。其源碼如下(kernel/timer.c): </p><p> void add_timer(struct timer_list *timer) </p><p
79、><b> { </b></p><p> unsigned long flags; </p><p> spin_lock_irqsave(&timerlist_lock, flags); </p><p> if (timer_pending(timer)) </p><p> got
80、o bug; </p><p> internal_add_timer(timer); </p><p> spin_unlock_irqrestore(&timerlist_lock, flags); </p><p><b> return; </b></p><p><b> bu
81、g: </b></p><p> spin_unlock_irqrestore(&timerlist_lock, flags); </p><p> printk("bug: kernel timer added twice at %p.\n", </p><p> __builtin_return_address
82、(0)); </p><p><b> } </b></p><p> 函數(shù)internal_add_timer()用于將一個不處于任何定時器向量中的定時器插入到它應該所</p><p> 處的定時器向量中去(根據(jù)定時器的expires值來決定)。如下所示(kernel/timer.c): </p><p>
83、 static inline void internal_add_timer(struct timer_list </p><p><b> *timer) </b></p><p><b> { </b></p><p><b> /* </b></p><p>
84、 * must be cli-ed when calling this </p><p><b> */ </b></p><p> unsigned long expires = timer->expires; </p><p> unsigned long idx = expires - timer_jiffies;
85、</p><p> struct list_head * vec; </p><p> if (idx < TVR_SIZE) { </p><p> int i = expires & TVR_MASK; </p><p> vec = tv1.vec + i; </p><p> }
86、 else if (idx < 1 << (TVR_BITS + TVN_BITS)) { </p><p> int i = (expires >> TVR_BITS) & TVN_MASK; </p><p> vec = tv2.vec + i; </p><p> } else if (idx < 1
87、<< (TVR_BITS + 2 * TVN_BITS)) { </p><p> int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK; </p><p> vec = tv3.vec + i; </p><p> } else if (idx < 1 <
88、< (TVR_BITS + 3 * TVN_BITS)) { </p><p> int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK; </p><p> vec = tv4.vec + i; </p><p> } else if ((signed long) i
89、dx < 0) { </p><p> /* can happen if you add a timer with expires == jiffies, </p><p> * or you set a timer to go off in the past </p><p><b> */ </b></p>
90、<p> vec = tv1.vec + tv1.index; </p><p> } else if (idx <= 0xffffffffUL) { </p><p> int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK; </p><p> vec
91、 = tv5.vec + i; </p><p> } else { </p><p> /* Can only get here on architectures with 64-bit jiffies */ </p><p> INIT_LIST_HEAD(&timer->list); </p><p><
92、b> return; </b></p><p><b> } </b></p><p><b> /* </b></p><p> * Timers are FIFO! </p><p><b> */ </b></p>&l
93、t;p> list_add(&timer->list, vec->prev); </p><p><b> } </b></p><p> 對該函數(shù)的注釋如下: </p><p> ?。?)首先,計算定時器的expires值與timer_jiffies的插值(注意!這里應該使用動態(tài)</p>&l
94、t;p> 定時器自己的時間基準),這個差值就表示這個定時器相對于上一次運行定時器機制的那個時刻</p><p> 還需要多長時間間隔才到期。局部變量idx保存這個差值。 </p><p> ?。?)根據(jù)idx的值確定這個定時器應被插入到哪一個定時器向量中。其具體的確定方法我</p><p> 們在7.6.2節(jié)已經(jīng)說過了,這里不再詳述。最后,定時器向量的
95、頭部指針vec表示這個定時器應</p><p> 該所處的定時器向量鏈表頭部。 </p><p> ?。?)最后,調(diào)用list_add()函數(shù)將定時器插入到vec指針所指向的定時器隊列的尾部。 </p><p> 2.1.5.3修改一個定時器的expires值 </p><p> 當一個定時器已經(jīng)被插入到內(nèi)核動態(tài)定時器鏈表中后,我
96、們還可以修改該定時器的expires值。函數(shù)mod_timer()實現(xiàn)這一點。如下所示(kernel/timer.c): </p><p> int mod_timer(struct timer_list *timer, unsigned long expires) </p><p><b> { </b></p><p> int
97、ret; </p><p> unsigned long flags; </p><p> spin_lock_irqsave(&timerlist_lock, flags); </p><p> timer->expires = expires; </p><p> ret = detach_timer(tim
98、er); </p><p> internal_add_timer(timer); </p><p> spin_unlock_irqrestore(&timerlist_lock, flags); </p><p> return ret; </p><p><b> } </b></p&
99、gt;<p> 該函數(shù)首先根據(jù)參數(shù)expires值更新定時器的expires成員。然后調(diào)用detach_timer()函數(shù)將該定時器從它原來所屬的鏈表中刪除。最后調(diào)用internal_add_timer()函數(shù)將該定時器根</p><p> 據(jù)它新的expires值重新插入到相應的鏈表中。 </p><p> 函數(shù)detach_timer()首先調(diào)用timer_pen
100、ding()來判斷指定的定時器是否已經(jīng)處于某個鏈</p><p> 表中,如果定時器原來就不處于任何鏈表中,則detach_timer()函數(shù)什么也不做,直接返回0</p><p> 值,表示失敗。否則,就調(diào)用list_del()函數(shù)將定時器從它原來所處的鏈表中摘除。如下所示</p><p> ?。╧ernel/timer.c): </p>&l
101、t;p> static inline int detach_timer (struct timer_list *timer) </p><p><b> { </b></p><p> if (!timer_pending(timer)) </p><p> return 0; </p><p>
102、 list_del(&timer->list); </p><p> return 1; </p><p><b> } </b></p><p> 2.2.系統(tǒng)平臺:一臺Linux主機且有超級用戶權(quán)限</p><p> 2.3. 編程工具:VI編輯器,Gedit編輯器</p>
103、<p> 3.數(shù)據(jù)結(jié)構(gòu)與模塊說明(功能與流程圖)</p><p> 3.1.定時器使用:</p><p> int gettimeofday(struct timeval *tv,struct timezone *tz); </p><p> strut timeval{</p><p> long tv_sec;
104、/*秒數(shù)*/ </p><p> long tv_usec; /*微秒數(shù)*/ </p><p><b> };</b></p><p> 這個syscall用來供用戶獲取timeval格式的當前時間信息(精確度為微秒級),以及系統(tǒng)的當前時區(qū)信息(timezone)。結(jié)構(gòu)類型timeval的指針參數(shù)tv指向接受時間信息的用戶空間緩沖區(qū),參
105、數(shù)tz是一個timezone結(jié)構(gòu)類型的指針,指向接收時區(qū)信息的用戶空間緩沖區(qū)。這兩個參數(shù)均為輸出參數(shù),返回值0表示成功,返回負值表示出錯。實現(xiàn)過程如下:</p><p><b> ……</b></p><p><b> main(){ </b></p><p> struct timeval tpstart,tpen
106、d; </p><p> /*申請struct timeval的變量,tv_sec返回的是秒數(shù),tv_usec返回的是微秒數(shù)*/</p><p> float timeuse; </p><p> gettimeofday(&tpstart,NULL); </p><p> pthread(); </p><
107、;p> gettimeofday(&tpend,NULL); </p><p> timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+ tpend.tv_usec-tpstart.tv_usec; </p><p> timeuse/=1000000; </p><p> printf("Use
108、d Time:%f sec\n",timeuse); </p><p><b> exit(0); </b></p><p><b> }</b></p><p><b> ……</b></p><p> 3.2 多線程程序:</p><p
109、> 進行多線程程序設計時,我們使用到了兩個函數(shù),pthread_create和pthread_join,聲明了一個pthread_t型的變量。pthread_t在頭文件/usr/include/bits/pthreadtypes.h中定義,它是一個線程的標識符。函數(shù)pthread_create用來創(chuàng)建一個線程,函數(shù)pthread_join用來等待一個線程的結(jié)束。實現(xiàn)過程如下:</p><p><b&
110、gt; ……</b></p><p> int pthread_create(&id,NULL,(void *) thread,NULL);</p><p> pthread_join(id,NULL);</p><p> void thread(void){</p><p><b> int i;&l
111、t;/b></p><p> for(i=0;i<3;i++)</p><p> printf("This is a pthread.\n");</p><p><b> }</b></p><p> int pthread(void){</p><p>
112、 pthread_t id; /* 聲明了一個pthread_t型的變量*/</p><p> int i,ret;</p><p> ret=pthread_create(&id,NULL,(void *) thread,NULL);</p><p> if(ret!=0){</p><p> printf("
113、Create pthread error!\n");</p><p><b> exit(1);</b></p><p><b> }</b></p><p> for(i=0;i<3;i++)</p><p> printf("This is the main p
114、rocess.\n");</p><p> pthread_join(id,NULL);</p><p> return(0);</p><p><b> }</b></p><p><b> ……</b></p><p><b> 3.3程序流
115、程圖:</b></p><p><b> 4. 源程序: </b></p><p> #include <sys/time.h>#include <pthread.h>#include <stdio.h></p><p> int gettimeofday(struct timeval
116、 *tv,struct timezone *tz); </p><p> int pthread_create(&id,NULL,(void *) thread,NULL);</p><p> //pthread_join(id,NULL);</p><p> strut timeval{</p><p> long tv_s
117、ec; /*秒數(shù)*/ </p><p> long tv_usec; /*微秒數(shù)*/ </p><p><b> };</b></p><p> void thread(void){int i;for(i=0;i<3;i++)printf("This is a pthread.\n");}int
118、 pthread(void){pthread_t id; /* 聲明了一個pthread_t型的變量*/int i,ret;ret=pthread_create(&id,NULL,(void *) thread,NULL);if(ret!=0){printf("Create pthread error!\n");exit(1);}for(i=0;i<3;i++)printf(&qu
119、ot;This is the main process.\n");pthread_join(id,NULL);return(0);}main(){ struct timeval tpstart,tpend; </p><p> /*申請struct timeval的變量,tv_sec返回的是秒數(shù),tv_usec返回的是微秒數(shù)*/float timeuse; gettimeofday(&
120、amp;tpstart,NULL); pthread(); gettimeofday(&tpend,NULL); timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+ tpend.tv_usec-tpstart.tv_usec; timeuse/=1000000; printf("Used Time:%f sec\n",timeuse); exit(0);
121、}</p><p> 5.運行結(jié)果與運行情況</p><p><b> 運行截圖如下:</b></p><p><b> 運行結(jié)果記錄如下:</b></p><p> [root@localhost ~]# gcc 050119.c -lpthread -o 050119.out</
122、p><p> 050119.c: In function ‘pthread’:</p><p> 050119.c:17: 警告:隱式聲明與內(nèi)建函數(shù) ‘exit’ 不兼容</p><p> [root@localhost ~]# ./050119.out</p><p> This is the main process.</p&g
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 眾賞文庫僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 操作系統(tǒng)課程設計1
- 操作系統(tǒng)課程設計1
- 操作系統(tǒng)課程設計-- 操作系統(tǒng)
- 操作系統(tǒng)課程設計
- 操作系統(tǒng)課程設計
- 操作系統(tǒng)課程設計
- 操作系統(tǒng)課程設計
- 操作系統(tǒng)課程設計
- 操作系統(tǒng)課程設計
- 內(nèi)存管理(操作系統(tǒng))操作系統(tǒng)課程設計
- 操作系統(tǒng)課程設計題目
- 操作系統(tǒng)課程設計報告
- 操作系統(tǒng)課程設計論文
- 操作系統(tǒng)課程設計 (4)
- 課程設計報告--操作系統(tǒng)
- linux操作系統(tǒng)課程設計
- 操作系統(tǒng)課程設計報告
- 操作系統(tǒng)原理課程設計
- 操作系統(tǒng)課程設計--模擬操作系統(tǒng)的實現(xiàn)
- 操作系統(tǒng)課程設計報告
評論
0/150
提交評論