

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、<p><b> 目錄</b></p><p><b> 摘要1</b></p><p> 1設(shè)計(jì)題目與要求1</p><p><b> 1.1設(shè)計(jì)題目1</b></p><p><b> 1.2設(shè)計(jì)要求1</b></p
2、><p> 2 總的設(shè)計(jì)思想及系統(tǒng)平臺(tái)、語(yǔ)言、工具2</p><p> 2.1設(shè)計(jì)思想:2</p><p> 2.1.1Linux內(nèi)核對(duì)定時(shí)器的描述2</p><p> 2.1.2Linux 內(nèi)核定時(shí)器3</p><p> 2.1.3Linux 信號(hào)signal處理機(jī)制6</p><
3、;p> 2.1.4多線程編程6</p><p> 2.1.5內(nèi)核定時(shí)器機(jī)制的實(shí)現(xiàn)8</p><p> 3.?dāng)?shù)據(jù)結(jié)構(gòu)與模塊說(shuō)明(功能與流程圖)11</p><p> 3.1.定時(shí)器使用:11</p><p> 3.3程序流程圖:13</p><p> 4. 源程序:13</p>
4、<p> 5.運(yùn)行結(jié)果與運(yùn)行情況14</p><p> 6.調(diào)試記錄:15</p><p> 7.自我評(píng)析和總結(jié):16</p><p><b> 8.參考文獻(xiàn)17</b></p><p><b> 摘要</b></p><p> 每個(gè)正在系統(tǒng)上
5、運(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)過(guò)了多少個(gè)100ns的CPU時(shí)間,linux是一個(gè)具有保護(hù)模式的操作系統(tǒng)。它一直工作在i386 cpu的保護(hù)模式之下。內(nèi)存被分為兩個(gè)單元: 內(nèi)核區(qū)域和用戶區(qū)域。一般地,在使用虛擬內(nèi)存技術(shù)的多任務(wù)系統(tǒng)上,內(nèi)核
6、和應(yīng)用有不同的地址空間,因此,在內(nèi)核和應(yīng)用之間以及在應(yīng)用與應(yīng)用之間進(jìn)行數(shù)據(jù)交換需要專門的機(jī)制來(lái)實(shí)現(xiàn),本文站在用戶空間的角度,測(cè)試一個(gè)多線程程序的程序執(zhí)行時(shí)間。當(dāng)一個(gè)進(jìn)程希望獲得信號(hào)量時(shí), 如果信號(hào)量已經(jīng)被占有, 則該進(jìn)程將會(huì)被放到等待隊(duì)列上sleep直到cpu將其喚醒。相對(duì)于spinlock來(lái)說(shuō)開(kāi)銷太大,適用于長(zhǎng)時(shí)間占有的lock。不可用于中斷狀態(tài),因?yàn)樗鼡碛行盘?hào)量的進(jìn)程可以sleep, 可以被搶占,信號(hào)量可以設(shè)置為同時(shí)允許的進(jìn)程數(shù)。&
7、lt;/p><p><b> 1設(shè)計(jì)題目與要求</b></p><p> 1.1設(shè)計(jì)題目:內(nèi)核定時(shí)器</p><p> 1.2設(shè)計(jì)要求:通過(guò)研究?jī)?nèi)核的時(shí)間管理算法,學(xué)習(xí)內(nèi)核源代碼;然后應(yīng)用這些知識(shí)并且使用“信號(hào)”建立一種用戶空間機(jī)制來(lái)測(cè)量一個(gè)多線程程序的執(zhí)行時(shí)間。</p><p> 2 總的設(shè)計(jì)思想及系統(tǒng)平臺(tái)、語(yǔ)言
8、、工具</p><p><b> 2.1設(shè)計(jì)思想:</b></p><p> 2.1.1Linux內(nèi)核對(duì)定時(shí)器的描述 </p><p> Linux在include/linux/timer.h頭文件中定義了數(shù)據(jù)結(jié)構(gòu)timer_list來(lái)描述一個(gè)內(nèi)核定</p><p><b> 時(shí)器: </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> (1)雙向鏈表元素list:用來(lái)將多個(gè)定時(shí)器連接成一條雙向循環(huán)隊(duì)列。 </p><p> ?。?)expires:指定定時(shí)器到期的時(shí)間,這個(gè)時(shí)間被表示成自系統(tǒng)啟
11、動(dòng)以來(lái)的時(shí)鐘滴答計(jì)</p><p> 數(shù)(也即時(shí)鐘節(jié)拍數(shù))。當(dāng)一個(gè)定時(shí)器的expires值小于或等于jiffies變量時(shí),我們就說(shuō)這個(gè)</p><p> 定時(shí)器已經(jīng)超時(shí)或到期了。在初始化一個(gè)定時(shí)器后,通常把它的expires域設(shè)置成當(dāng)前expires</p><p> 變量的當(dāng)前值加上某個(gè)時(shí)間間隔值(以時(shí)鐘滴答次數(shù)計(jì))。 </p><p&
12、gt; ?。?)函數(shù)指針function:指向一個(gè)可執(zhí)行函數(shù)。當(dāng)定時(shí)器到期時(shí),內(nèi)核就執(zhí)行function</p><p> 所指定的函數(shù)。而data域則被內(nèi)核用作function函數(shù)的調(diào)用參數(shù)。 </p><p> 內(nèi)核函數(shù)init_timer()用來(lái)初始化一個(gè)定時(shí)器。實(shí)際上,這個(gè)初始化函數(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> 由于定時(shí)器通常被連接在一個(gè)雙向循環(huán)隊(duì)列中等待執(zhí)行(此時(shí)我們說(shuō)定時(shí)器處于pending</p><p> 狀態(tài))。因此函數(shù)time_pending()就可以用list成員是否為空來(lái)判斷一個(gè)定時(shí)器是否處于</p><p> pending狀態(tài)。如下
15、所示 </p><p> (include/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> 時(shí)間比較操作 </b></p><p> 在定時(shí)器應(yīng)用中經(jīng)常需要比較兩個(gè)時(shí)間值,以確定timer是否超時(shí),所
17、以Linux內(nèi)核在</p><p> timer.h頭文件中定義了4個(gè)時(shí)間關(guān)系比較操作宏。這里我們說(shuō)時(shí)刻a在時(shí)刻b之后,就意味著</p><p> 時(shí)間值a≥b。Linux強(qiáng)烈推薦用戶使用它所定義的下列4個(gè)時(shí)間比較操作宏</p><p> (include/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)核定時(shí)器</p><p> 定時(shí)器是管理內(nèi)核時(shí)間的基礎(chǔ),用來(lái)計(jì)算流逝的時(shí)間,它以某種頻率(節(jié)拍率)自行觸發(fā)時(shí)鐘中斷,當(dāng)時(shí)鐘中斷發(fā)生時(shí),內(nèi)核就通過(guò)一種特殊中斷處理程序?qū)ζ溥M(jìn)行處理。</p><p> 但是原來(lái)的實(shí)現(xiàn)只能是time_t my
20、time形式的,經(jīng)過(guò)簡(jiǎn)單的localtime(mytime)和ctime(&mytime)處理.精度是不夠的,為了返回高精度的時(shí)間,這里使用了gettimeofday函數(shù)。</p><p> 這個(gè)syscall用來(lái)供用戶獲取timeval格式的當(dāng)前時(shí)間信息(精確度為微秒級(jí)),以及系統(tǒng)的當(dāng)前時(shí)區(qū)信息(timezone)。結(jié)構(gòu)類型timeval的指針參數(shù)tv指向接受時(shí)間信息的用戶空間緩沖區(qū),參數(shù)tz是一個(gè)t
21、imezone結(jié)構(gòu)類型的指針,指向接收時(shí)區(qū)信息的用戶空間緩沖區(qū)。這兩個(gè)參數(shù)均為輸出參數(shù),返回值0表示成功,返回負(fù)值表示出錯(cuò)。函數(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ù)的實(shí)現(xiàn)主要分成兩個(gè)大的方面: </p><p> ?。?)如果tv指針有效,則說(shuō)明用戶要以timeva
25、l格式來(lái)檢索系統(tǒng)當(dāng)前時(shí)間。為此,先調(diào)用do_gettimeofday()函數(shù)來(lái)檢索系統(tǒng)當(dāng)前時(shí)間并保存到局部變量ktv中。然后再調(diào)用copy_to_user()宏將保存在內(nèi)核空間中的當(dāng)前時(shí)間信息拷貝到由參數(shù)指針tv所指向的用戶空間緩沖區(qū)中。 </p><p> ?。?)如果tz指針有效,則說(shuō)明用戶要檢索當(dāng)前時(shí)區(qū)信息,因此調(diào)用copy_to_user()宏將全局變量sys_tz中的時(shí)區(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ù)的完成實(shí)際的當(dāng)前時(shí)間檢索工作。由于gettimeofday()系統(tǒng)調(diào)用要求時(shí)間精度要達(dá)到微秒級(jí),因此do_gettimeofday()函數(shù)不能簡(jiǎn)單地返回xtime中的值即可,而必須
33、精確地確定自從時(shí)鐘驅(qū)動(dòng)的Bottom Half上一次更新xtime的那個(gè)時(shí)刻到do_gettimeofday()函數(shù)的當(dāng)前執(zhí)行時(shí)刻之間的具體時(shí)間間隔長(zhǎng)度,以便精確地修正xtime的值.</p><p> 假定被do_gettimeofday()用來(lái)修正xtime的時(shí)間間隔為fixed_usec,而從wall_jiffies到j(luò)iffies之間的時(shí)間間隔是lost_usec,而從jiffies到do_gettim
34、eofday()函數(shù)的執(zhí)行時(shí)刻的時(shí)間間隔是offset_usec。則下列三個(gè)等式成立: </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í)鐘中斷服務(wù)函數(shù)timer_interrupt()執(zhí)行時(shí)刻的CPU TSC寄存器的值,因此我們可以用X86 CPU的TSC寄存器來(lái)計(jì)算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ā)生時(shí)鐘中斷到timer_interrupt()服務(wù)函數(shù)真正執(zhí)行時(shí)刻之間的時(shí)間延遲間隔。每一次timer_interrupt()被執(zhí)行時(shí)都會(huì)計(jì)算這一間隔,并利用TSC的當(dāng)前值更新last_tsc_low變量(可以參見(jiàn)7.4節(jié))。假定current_tsc_low是do_gettimeofday()函數(shù)執(zhí)行時(shí)刻TSC的當(dāng)前值,全局變量fast_gettimeoffset_quotient則表示TS
37、C寄存器每增加1所代表的時(shí)間間隔值,它是由time_init()函數(shù)所計(jì)算的。 </p><p> 根據(jù)上述原理分析,do_gettimeofday()函數(shù)的執(zhí)行步驟如下: </p><p> (1)調(diào)用函數(shù)do_gettimeoffset()計(jì)算從上一次時(shí)鐘中斷發(fā)生到執(zhí)行do_gettimeofday()函數(shù)的當(dāng)前時(shí)刻之間的時(shí)間間隔offset_usec。 </p>&
38、lt;p> ?。?)通過(guò)wall_jiffies和jiffies計(jì)算lost_usec的值。 </p><p> ?。?)然后,令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í)上的值。 </p><p> ?。?)用一個(gè)while{}
39、循環(huán)來(lái)判斷usec是否已經(jīng)溢出而超過(guò)106us=1秒。如果溢出,則將usec減去106us并相應(yīng)地將sec增加1,直到usec不溢出為止。 </p><p> ?。?)最后,用sec和usec分別更新參數(shù)指針?biāo)赶虻膖imeval結(jié)構(gòu)變量。至此,整個(gè)查詢過(guò)程結(jié)束。 </p><p> 函數(shù)do_gettimeoffset()根據(jù)CPU是否配置有TSC寄存器這一條件分別有不同的實(shí)現(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平臺(tái)上,do_gettimeoffset()函數(shù)實(shí)際上就是do_fast_gettimeoffset()函數(shù)。它通過(guò)TSC寄存器來(lái)計(jì)算do_fas
43、t_gettimeoffset()函數(shù)被執(zhí)行的時(shí)刻到上一次時(shí)鐘中斷發(fā)生時(shí)的時(shí)間間隔值。其源碼如下(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> 對(duì)該函數(shù)的注釋如下: </p><p> (1)先調(diào)用rdtsc()函數(shù)讀取當(dāng)前時(shí)刻TSC寄存器的值,并將其高32位保存在edx局部變量中,低32位保存在局部變量eax中。 </p><p>
50、 ?。?)讓局部變量eax=Δtsc_low=eax-last_tsc_low;也即計(jì)算當(dāng)前時(shí)刻的TSC值與上一次時(shí)鐘中斷服務(wù)函數(shù)timer_interrupt()執(zhí)行時(shí)的TSC值之間的差值。 </p><p> ?。?)顯然,從上一次timer_interrupt()到當(dāng)前時(shí)刻的時(shí)間間隔就是(Δtsc_low*fast_gettimeoffset_quotient)。因此用一條mul指令來(lái)計(jì)算這個(gè)乘法表達(dá)式的值
51、。 </p><p> (4)返回值delay_at_last_interrupt+(Δtsc_low*fast_gettimeoffset_quotient)就是從上一次時(shí)鐘中斷發(fā)生時(shí)到當(dāng)前時(shí)刻之間的時(shí)間偏移間隔值。</p><p> 2.1.3Linux 信號(hào)signal處理機(jī)制</p><p> 信號(hào)signal機(jī)制是進(jìn)程之間相互傳遞消息的一種方法,全稱
52、為軟中斷信號(hào)。系統(tǒng)調(diào)用signal用來(lái)設(shè)定某個(gè)信號(hào)的處理方法,其調(diào)用聲明的格式如下: </p><p> void (*signal(int signum, void (*handler)(int)))(int); 成功則返回該信號(hào)以前的處理配置,出錯(cuò)則返回SIG_ERR。在使用該調(diào)用的進(jìn)程中加入以下頭文件:<signal.h></p><p><b> 幾個(gè)常見(jiàn)
53、信號(hào):</b></p><p> SIGINT: 當(dāng)用戶按某些終端鍵時(shí), 引發(fā)終端產(chǎn)生的信號(hào). 如Ctrl+C鍵, 這將產(chǎn)生中斷信號(hào)(SIGINT),它將停止一個(gè)已失去控制的程序。</p><p> SIGSEGV: 由硬件異常(除數(shù)為0, 無(wú)效的內(nèi)存引用等等)產(chǎn)生的信號(hào)。這些條件通常由硬件檢測(cè)到, 并將其通知內(nèi)核,然后內(nèi)核為該條件發(fā)生時(shí)正在運(yùn)行的進(jìn)程產(chǎn)生該信號(hào)。</
54、p><p> SIGURG: 在網(wǎng)絡(luò)連接上傳來(lái)帶外數(shù)據(jù)時(shí)產(chǎn)生。</p><p> SIGPIPE: 在管道的讀進(jìn)程已終止后, 一個(gè)進(jìn)程寫(xiě)此管道時(shí)產(chǎn)生,當(dāng)類型為SOCK_STREAM的socket已不再連接時(shí), 進(jìn)程寫(xiě)到該socket也產(chǎn)生此信號(hào)。</p><p> SIGALRM: 進(jìn)程所設(shè)置的鬧鐘時(shí)鐘超時(shí)的時(shí)候產(chǎn)生。</p><p>
55、SIGABRT: 進(jìn)程調(diào)用abort函數(shù)時(shí)產(chǎn)生此信號(hào), 進(jìn)程異常終止。</p><p> SIGCHLD: 在一個(gè)進(jìn)程終止或停止時(shí), 它將把該信號(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)程。</p><p> SIGIO: 此信號(hào)指示一個(gè)異步
56、I/O事件。</p><p> SIGSYS: 該信號(hào)指示一個(gè)無(wú)效的系統(tǒng)調(diào)用。</p><p> SIGTSTP: 交互式停止信號(hào). Ctrl+Z, 按下時(shí), 終端將產(chǎn)生此信號(hào), 進(jìn)程被掛起。</p><p> 2.1.4多線程編程</p><p> 多線程是計(jì)算機(jī)同時(shí)運(yùn)行多個(gè)執(zhí)行線程的能力(這些線程可以是同一程序的組成部分,或者也可
57、以是完全不同的程序)。Linux系統(tǒng)下的多線程遵循POSIX線程接口,稱為pthread。編寫(xiě)Linux下的多線程程序,需要使用頭文件pthread.h,連接時(shí)需要使用庫(kù)libpthread.a。而Linux下pthread的實(shí)現(xiàn)是通過(guò)系統(tǒng)調(diào)用clone()來(lái)實(shí)現(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> 運(yùn)行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> 再次運(yùn)行,我們可能得到如下結(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é)果不一樣,這是兩個(gè)線程爭(zhēng)奪CPU資源的結(jié)果。上面的示例中,我們使用到了兩個(gè)函數(shù),pthread_create和pthread_join,并聲明了一個(gè)pthread_t型的變量。<
67、/p><p> pthread_t在頭文件/usr/include/bits/pthreadtypes.h中定義:</p><p> typedef unsigned long int pthread_t;</p><p> 它是一個(gè)線程的標(biāo)識(shí)符。函數(shù)pthread_create用來(lái)創(chuàng)建一個(gè)線程,它的原型為:</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> 第一個(gè)參數(shù)為指向線程標(biāo)識(shí)符的指針,第二個(gè)參數(shù)用來(lái)設(shè)置線程屬性,第三個(gè)參數(shù)是線程運(yùn)行函數(shù)的起始地址,最后一個(gè)參數(shù)是運(yùn)行函數(shù)的參數(shù)。這里,我們
69、的函 數(shù)thread不需要參數(shù),所以最后一個(gè)參數(shù)設(shè)為空指針。第二個(gè)參數(shù)我們也設(shè)為空指針,這樣將生成默認(rèn)屬性的線程。對(duì)線程屬性的設(shè)定和修改我們將在下一節(jié) 闡述。當(dāng)創(chuàng)建線程成功時(shí),函數(shù)返回0,若不為0則說(shuō)明創(chuàng)建線程失敗,常見(jiàn)的錯(cuò)誤返回代碼為EAGAIN和EINVAL。前者表示系統(tǒng)限制創(chuàng)建新的線程,例如線程數(shù)目過(guò)多了;后者表示第二個(gè)參數(shù)代表的線程屬性值非法。創(chuàng)建線程成功后,新創(chuàng)建的線程則運(yùn)行參數(shù)三和參數(shù)四確定的函數(shù),原來(lái)的線程則繼續(xù)運(yùn)行下一行
70、代碼。</p><p> 函數(shù)pthread_join用來(lái)等待一個(gè)線程的結(jié)束。函數(shù)原型為:extern int pthread_join __P ((pthread_t __th, void **__thread_return));第一個(gè)參數(shù)為被等待的線程標(biāo)識(shí)符,第二個(gè)參數(shù)為一個(gè)用戶定義的指針,它可以用來(lái)存儲(chǔ)被等待線程的返回值。這個(gè)函數(shù)是一個(gè)線程阻塞的函數(shù),調(diào)用它的函數(shù)將 一直等待到被等待的線程結(jié)束為止,當(dāng)函數(shù)
71、返回時(shí),被等待線程的資源被收回。一個(gè)線程的結(jié)束有兩種途徑,一種是象我們上面的例子一樣,函數(shù)結(jié)束了,調(diào)用它的 線程也就結(jié)束了;另一種方式是通過(guò)函數(shù)pthread_exit來(lái)實(shí)現(xiàn)。它的函數(shù)原型為:</p><p> extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));</p><p>
72、 唯一的參數(shù)是函數(shù)的返回代碼,只要pthread_join中的第二個(gè)參數(shù)thread_return不是NULL,這個(gè)值將被傳遞給 thread_return。最后要說(shuō)明的是,一個(gè)線程不能被多個(gè)線程等待,否則第一個(gè)接收到信號(hào)的線程成功返回,其余調(diào)用pthread_join的線 程則返回錯(cuò)誤代碼ESRCH。</p><p> 2.1.5內(nèi)核定時(shí)器機(jī)制的實(shí)現(xiàn)</p><p> 2.1.5.1
73、動(dòng)態(tài)定時(shí)器機(jī)制的初始化 </p><p> 函數(shù)init_timervecs()實(shí)現(xiàn)對(duì)動(dòng)態(tài)定時(shí)器機(jī)制的初始化。該函數(shù)僅被sched_init()初始化例程所調(diào)用。動(dòng)態(tài)定時(shí)器機(jī)制初始化過(guò)程的主要任務(wù)就是將tv1、tv2、…、tv5這5個(gè)結(jié)構(gòu)變量中的定時(shí)器向量指針數(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í)器向量指針數(shù)組vec[]的大小,值為64。宏
77、TVR_SIZE是指timer_vec_root結(jié)構(gòu)類型中的定時(shí)器向量數(shù)組vec[]的大小,值為256。 </p><p> 2.1.5.2將一個(gè)定時(shí)器插入到鏈表中 </p><p> 函數(shù)add_timer()用來(lái)將參數(shù)timer指針?biāo)赶虻亩〞r(shí)器插入到一個(gè)合適的定時(shí)器鏈表中。它首先調(diào)用timer_pending()函數(shù)判斷所指定的定時(shí)器是否已經(jīng)位于在某個(gè)定時(shí)器向量中等待</
78、p><p> 執(zhí)行。如果是,則不進(jìn)行任何操作,只是打印一條內(nèi)核告警信息就返回了;如果不是,則調(diào)用</p><p> internal_add_timer()函數(shù)完成實(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()用于將一個(gè)不處于任何定時(shí)器向量中的定時(shí)器插入到它應(yīng)該所</p><p> 處的定時(shí)器向量中去(根據(jù)定時(shí)器的expires值來(lái)決定)。如下所示(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> 對(duì)該函數(shù)的注釋如下: </p><p> (1)首先,計(jì)算定時(shí)器的expires值與timer_jiffies的插值(注意!這里應(yīng)該使用動(dòng)態(tài)</p>&l
94、t;p> 定時(shí)器自己的時(shí)間基準(zhǔn)),這個(gè)差值就表示這個(gè)定時(shí)器相對(duì)于上一次運(yùn)行定時(shí)器機(jī)制的那個(gè)時(shí)刻</p><p> 還需要多長(zhǎng)時(shí)間間隔才到期。局部變量idx保存這個(gè)差值。 </p><p> (2)根據(jù)idx的值確定這個(gè)定時(shí)器應(yīng)被插入到哪一個(gè)定時(shí)器向量中。其具體的確定方法我</p><p> 們?cè)?.6.2節(jié)已經(jīng)說(shuō)過(guò)了,這里不再詳述。最后,定時(shí)器向量的
95、頭部指針vec表示這個(gè)定時(shí)器應(yīng)</p><p> 該所處的定時(shí)器向量鏈表頭部。 </p><p> ?。?)最后,調(diào)用list_add()函數(shù)將定時(shí)器插入到vec指針?biāo)赶虻亩〞r(shí)器隊(duì)列的尾部。 </p><p> 2.1.5.3修改一個(gè)定時(shí)器的expires值 </p><p> 當(dāng)一個(gè)定時(shí)器已經(jīng)被插入到內(nèi)核動(dòng)態(tài)定時(shí)器鏈表中后,我
96、們還可以修改該定時(shí)器的expires值。函數(shù)mod_timer()實(shí)現(xiàn)這一點(diǎ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值更新定時(shí)器的expires成員。然后調(diào)用detach_timer()函數(shù)將該定時(shí)器從它原來(lái)所屬的鏈表中刪除。最后調(diào)用internal_add_timer()函數(shù)將該定時(shí)器根</p><p> 據(jù)它新的expires值重新插入到相應(yīng)的鏈表中。 </p><p> 函數(shù)detach_timer()首先調(diào)用timer_pen
100、ding()來(lái)判斷指定的定時(shí)器是否已經(jīng)處于某個(gè)鏈</p><p> 表中,如果定時(shí)器原來(lái)就不處于任何鏈表中,則detach_timer()函數(shù)什么也不做,直接返回0</p><p> 值,表示失敗。否則,就調(diào)用list_del()函數(shù)將定時(shí)器從它原來(lái)所處的鏈表中摘除。如下所示</p><p> (kernel/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)平臺(tái):一臺(tái)Linux主機(jī)且有超級(jí)用戶權(quán)限</p><p> 2.3. 編程工具:VI編輯器,Gedit編輯器</p>
103、<p> 3.?dāng)?shù)據(jù)結(jié)構(gòu)與模塊說(shuō)明(功能與流程圖)</p><p> 3.1.定時(shí)器使用:</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> 這個(gè)syscall用來(lái)供用戶獲取timeval格式的當(dāng)前時(shí)間信息(精確度為微秒級(jí)),以及系統(tǒng)的當(dāng)前時(shí)區(qū)信息(timezone)。結(jié)構(gòu)類型timeval的指針參數(shù)tv指向接受時(shí)間信息的用戶空間緩沖區(qū),參
105、數(shù)tz是一個(gè)timezone結(jié)構(gòu)類型的指針,指向接收時(shí)區(qū)信息的用戶空間緩沖區(qū)。這兩個(gè)參數(shù)均為輸出參數(shù),返回值0表示成功,返回負(fù)值表示出錯(cuò)。實(shí)現(xiàn)過(guò)程如下:</p><p><b> ……</b></p><p><b> main(){ </b></p><p> struct timeval tpstart,tpen
106、d; </p><p> /*申請(qǐng)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、> 進(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用來(lái)創(chuàng)建一個(gè)線程,函數(shù)pthread_join用來(lái)等待一個(gè)線程的結(jié)束。實(shí)現(xiàn)過(guò)程如下:</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; /* 聲明了一個(gè)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; /* 聲明了一個(gè)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> /*申請(qǐng)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.運(yùn)行結(jié)果與運(yùn)行情況</p><p><b> 運(yùn)行截圖如下:</b></p><p><b> 運(yùn)行結(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. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 眾賞文庫(kù)僅提供信息存儲(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 操作系統(tǒng)課程設(shè)計(jì)1
- 操作系統(tǒng)課程設(shè)計(jì)1
- 操作系統(tǒng)課程設(shè)計(jì)-- 操作系統(tǒng)
- 操作系統(tǒng)課程設(shè)計(jì)
- 操作系統(tǒng)課程設(shè)計(jì)
- 操作系統(tǒng)課程設(shè)計(jì)
- 操作系統(tǒng)課程設(shè)計(jì)
- 操作系統(tǒng)課程設(shè)計(jì)
- 操作系統(tǒng)課程設(shè)計(jì)
- 內(nèi)存管理(操作系統(tǒng))操作系統(tǒng)課程設(shè)計(jì)
- 操作系統(tǒng)課程設(shè)計(jì)題目
- 操作系統(tǒng)課程設(shè)計(jì)報(bào)告
- 操作系統(tǒng)課程設(shè)計(jì)論文
- 操作系統(tǒng)課程設(shè)計(jì) (4)
- 課程設(shè)計(jì)報(bào)告--操作系統(tǒng)
- linux操作系統(tǒng)課程設(shè)計(jì)
- 操作系統(tǒng)課程設(shè)計(jì)報(bào)告
- 操作系統(tǒng)原理課程設(shè)計(jì)
- 操作系統(tǒng)課程設(shè)計(jì)--模擬操作系統(tǒng)的實(shí)現(xiàn)
- 操作系統(tǒng)課程設(shè)計(jì)報(bào)告
評(píng)論
0/150
提交評(píng)論