自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一次腦殘的記錄:Linux 中實(shí)時(shí)任務(wù)調(diào)度與優(yōu)先級(jí)

系統(tǒng) Linux
在本文中,可以把線程約等于進(jìn)程,有的地方也可能稱為任務(wù),在不同的語境下有一些不同的慣用說法。

[[405787]]

失敗是成功之母,這篇文章就是一次真實(shí)的失敗調(diào)試記錄。

通過這篇文章,您能深刻體驗(yàn)到 Linux 系統(tǒng)中下面幾個(gè)概念:

  1. 實(shí)時(shí)進(jìn)程和普通進(jìn)程的調(diào)度策略;
  2. Linux 中混亂的進(jìn)程優(yōu)先級(jí)是如何計(jì)算的;
  3. CPU親和性的測(cè)試;
  4. 多處理器(SMP)遇到實(shí)時(shí)進(jìn)程和普通進(jìn)程的程序設(shè)計(jì);
  5. 道哥的腦袋被門夾了一下的短路經(jīng)歷;

背景知識(shí):Linux 調(diào)度策略

關(guān)于進(jìn)程的調(diào)度策略,不同的操作系統(tǒng)有不同的整體目標(biāo),因此調(diào)度算法也就各不相同。

這需要根據(jù)進(jìn)程的類型(計(jì)算密集型?IO密集型?)、優(yōu)先級(jí)等因素來進(jìn)行選擇。

對(duì)于 Linux x86 平臺(tái)來說,一般采用的是 CFS:完全公平調(diào)度算法。

之所以叫做完全公平,是因?yàn)椴僮飨到y(tǒng)以每個(gè)線程占用 CPU 的比率來進(jìn)行動(dòng)態(tài)的計(jì)算,操作系統(tǒng)希望每一個(gè)進(jìn)程都能夠平均的使用 CPU 這個(gè)資源,雨露均沾。

我們?cè)趧?chuàng)建一個(gè)線程的時(shí)候,默認(rèn)就是這個(gè)調(diào)度算法 SCHED_OTHER,默認(rèn)的優(yōu)先級(jí)為 0。

  • PS: 在 Linux 操作系統(tǒng)中,線程的內(nèi)核對(duì)象與進(jìn)程的內(nèi)核對(duì)象(其實(shí)就是一些結(jié)構(gòu)體變量)是很類似的,所以線程可以說是輕量級(jí)的進(jìn)程。
  • 在本文中,可以把線程約等于進(jìn)程,有的地方也可能稱為任務(wù),在不同的語境下有一些不同的慣用說法。

可以這么理解:如果系統(tǒng)中一共有 N 個(gè)進(jìn)程,那么每個(gè)進(jìn)程會(huì)得到 1/N 的執(zhí)行機(jī)會(huì)。每個(gè)進(jìn)程執(zhí)行一段時(shí)間之后,就被調(diào)出,換下一個(gè)進(jìn)程執(zhí)行。

如果這個(gè) N 的數(shù)量太大了,導(dǎo)致每個(gè)進(jìn)程剛開始執(zhí)行時(shí),分給它的時(shí)間就到了。如果這個(gè)時(shí)候就進(jìn)行任務(wù)調(diào)度,那么系統(tǒng)的資源就耗費(fèi)在進(jìn)程上下文切換上去了。

因此,操作系統(tǒng)引入了最小粒度,也就是每個(gè)進(jìn)程都有一個(gè)最小的執(zhí)行時(shí)間保證,稱作時(shí)間片。

除了 SCHED_OTHER 調(diào)度算法,Linux 系統(tǒng)還支持兩種實(shí)時(shí)調(diào)度策略:

  • 1. SCHED_FIFO:根據(jù)進(jìn)程的優(yōu)先級(jí)進(jìn)行調(diào)度,一旦搶占到 CPU 則一直運(yùn)行,直達(dá)自己主動(dòng)放棄或被被更高優(yōu)先級(jí)的進(jìn)程搶占;
  • 2. SCHED_RR:在 SCHED_FIFO 的基礎(chǔ)上,加上了時(shí)間片的概念。當(dāng)一個(gè)進(jìn)程搶占到 CPU 之后,運(yùn)行到一定的時(shí)間后,調(diào)度器會(huì)把這個(gè)進(jìn)程放在 CPU 中,當(dāng)前優(yōu)先級(jí)進(jìn)程隊(duì)列的末尾,然后選擇另一個(gè)相同優(yōu)先級(jí)的進(jìn)程來執(zhí)行;

本文想測(cè)試的就是 SCHED_FIFO 與普通的 SCHED_OTHER 這兩種調(diào)度策略混合的情況。

背景知識(shí):Linux 線程優(yōu)先級(jí)

在 Linux 系統(tǒng)中,優(yōu)先級(jí)的管理顯得比較混亂,先看下面這張圖:

這張圖表示的是內(nèi)核中的優(yōu)先級(jí),分為兩段。

前面的數(shù)值 0-99 是實(shí)時(shí)任務(wù),后面的數(shù)值 100-139 是普通任務(wù)。

數(shù)值越低,代表這個(gè)任務(wù)的優(yōu)先級(jí)越高。

數(shù)值越低,代表這個(gè)任務(wù)的優(yōu)先級(jí)越高。

數(shù)值越低,代表這個(gè)任務(wù)的優(yōu)先級(jí)越高。

再強(qiáng)調(diào)一下,以上是從內(nèi)核角度來看的優(yōu)先級(jí)。

好了,重點(diǎn)來了:

我們?cè)趹?yīng)用層創(chuàng)建線程的時(shí)候,設(shè)置了一個(gè)優(yōu)先級(jí)數(shù)值,這是從應(yīng)用層角度來看的優(yōu)先級(jí)數(shù)值。

但是內(nèi)核并不會(huì)直接使用應(yīng)用層設(shè)置的這個(gè)數(shù)值,而是經(jīng)過了一定的運(yùn)算,才得到內(nèi)核中所使用的優(yōu)先級(jí)數(shù)值(0 ~ 139)。

1. 對(duì)于實(shí)時(shí)任務(wù)

我們?cè)趧?chuàng)建線程的時(shí)候,可以通過下面這樣的方式設(shè)置優(yōu)先級(jí)數(shù)值(0 ~ 99):

  1. struct sched_param param; 
  2. param.__sched_priority = xxx; 

當(dāng)創(chuàng)建線程函數(shù)進(jìn)入內(nèi)核層面的時(shí)候,內(nèi)核通過下面這個(gè)公式來計(jì)算真正的優(yōu)先級(jí)數(shù)值:

  1. kernel priority = 100 - 1 - param.__sched_priority 

如果應(yīng)用層傳入數(shù)值 0,那么在內(nèi)核中優(yōu)先級(jí)數(shù)值就是 99(100 - 1 - 0 = 99),在所有實(shí)時(shí)任務(wù)中,它的優(yōu)先級(jí)是最低的。

如果應(yīng)用層傳輸數(shù)值 99,那么在內(nèi)核中優(yōu)先級(jí)數(shù)值就是 0(100 - 1 - 99 = 0),在所有實(shí)時(shí)任務(wù)中,它的優(yōu)先級(jí)是最高的。

因此,從應(yīng)用層的角度看,傳輸人優(yōu)先級(jí)數(shù)值越大,線程的優(yōu)先級(jí)就越高;數(shù)值越小,優(yōu)先級(jí)就越低。

與內(nèi)核角度是完全相反的!

2. 對(duì)于普通任務(wù)

調(diào)整普通任務(wù)的優(yōu)先級(jí),是通過 nice 值來實(shí)現(xiàn)的,內(nèi)核中也有一個(gè)公式來把應(yīng)用層傳入的 nice 值,轉(zhuǎn)成內(nèi)核角度的優(yōu)先級(jí)數(shù)值:

  1. kernel prifoity = 100 + 20 + nice 

nice 的合法數(shù)值是:-20 ~ 19。

如果應(yīng)用層設(shè)置線程 nice 數(shù)值為 -20,那么在內(nèi)核中優(yōu)先級(jí)數(shù)值就是 100(100 + 20 + (-20) = 100),在所有的普通任務(wù)中,它的優(yōu)先級(jí)是最高的。

如果應(yīng)用層設(shè)置線程 nice 數(shù)值為 19,那么在內(nèi)核中優(yōu)先級(jí)數(shù)值就是 139(100 +20 +19 = 139),在所有的普通任務(wù)中,它的優(yōu)先級(jí)是最低的。

因此,從應(yīng)用層的角度看,傳輸人優(yōu)先級(jí)數(shù)值越小,線程的優(yōu)先級(jí)就越高;數(shù)值越大,優(yōu)先級(jí)就越低。

與內(nèi)核角度是完全相同的!

背景知識(shí)交代清楚了,終于可以進(jìn)行代碼測(cè)試了!

[[405789]]

測(cè)試代碼說明

注意點(diǎn):

  1. #define _GNU_SOURCE 必須在 #include 之前定義;
  2. #include 必須在 #include 之前包含進(jìn)來;
  3. 這個(gè)順序能夠保證在后面設(shè)置繼承的 CPU 親和性時(shí),CPU_SET, CEPU_ZERO這兩個(gè)函數(shù)能被順利定位到。

  1. // filename: test.c 
  2. #define _GNU_SOURCE 
  3. #include <unistd.h>   
  4. #include <stdio.h> 
  5. #include <stdlib.h> 
  6. #include <sched.h> 
  7. #include <pthread.h> 
  8.  
  9. // 用來打印當(dāng)前的線程信息:調(diào)度策略是什么?優(yōu)先級(jí)是多少? 
  10. void get_thread_info(const int thread_index) 
  11.     int policy; 
  12.     struct sched_param param; 
  13.  
  14.     printf("\n====> thread_index = %d \n", thread_index); 
  15.  
  16.     pthread_getschedparam(pthread_self(), &policy, &param); 
  17.     if (SCHED_OTHER == policy) 
  18.         printf("thread_index %d: SCHED_OTHER \n", thread_index); 
  19.     else if (SCHED_FIFO == policy) 
  20.         printf("thread_index %d: SCHED_FIFO \n", thread_index); 
  21.     else if (SCHED_RR == policy) 
  22.         printf("thread_index %d: SCHED_RR \n", thread_index); 
  23.  
  24.     printf("thread_index %d: priority = %d \n", thread_index, param.sched_priority); 
  25.  
  26. // 線程函數(shù), 
  27. void *thread_routine(void *args) 
  28.     // 參數(shù)是:線程索引號(hào)。四個(gè)線程,索引號(hào)從 1 到 4,打印信息中使用。 
  29.     int thread_index = *(int *)args; 
  30.      
  31.     // 為了確保所有的線程都創(chuàng)建完畢,讓線程睡眠1秒。 
  32.     sleep(1); 
  33.  
  34.     // 打印一下線程相關(guān)信息:調(diào)度策略、優(yōu)先級(jí)。 
  35.     get_thread_info(thread_index); 
  36.  
  37.     long num = 0; 
  38.     for (int i = 0; i < 10; i++) 
  39.     { 
  40.         for (int j = 0; j < 5000000; j++) 
  41.         { 
  42.             // 沒什么意義,純粹是模擬 CPU 密集計(jì)算。 
  43.             float f1 = ((i+1) * 345.45) * 12.3 * 45.6 / 78.9 / ((j+1) * 4567.89); 
  44.             float f2 = (i+1) * 12.3 * 45.6 / 78.9 * (j+1); 
  45.             float f3 = f1 / f2; 
  46.         } 
  47.          
  48.         // 打印計(jì)數(shù)信息,為了能看到某個(gè)線程正在執(zhí)行 
  49.         printf("thread_index %d: num = %ld \n", thread_index, num++); 
  50.     } 
  51.      
  52.     // 線程執(zhí)行結(jié)束 
  53.     printf("thread_index %d: exit \n", thread_index); 
  54.     return 0; 
  55.  
  56. void main(void) 
  57.     // 一共創(chuàng)建四個(gè)線程:0和1-實(shí)時(shí)線程,2和3-普通線程(非實(shí)時(shí)) 
  58.     int thread_num = 4; 
  59.      
  60.     // 分配的線程索引號(hào),會(huì)傳遞給線程參數(shù) 
  61.     int index[4] = {1, 2, 3, 4}; 
  62.  
  63.     // 用來保存 4 個(gè)線程的 id 號(hào) 
  64.     pthread_t ppid[4]; 
  65.      
  66.     // 用來設(shè)置 2 個(gè)實(shí)時(shí)線程的屬性:調(diào)度策略和優(yōu)先級(jí) 
  67.     pthread_attr_t attr[2]; 
  68.     struct sched_param param[2]; 
  69.  
  70.     // 實(shí)時(shí)線程,必須由 root 用戶才能創(chuàng)建 
  71.     if (0 != getuid()) 
  72.     { 
  73.         printf("Please run as root \n"); 
  74.         exit(0); 
  75.     } 
  76.  
  77.     // 創(chuàng)建 4 個(gè)線程 
  78.     for (int i = 0; i < thread_num; i++) 
  79.     { 
  80.         if (i <= 1)    // 前2個(gè)創(chuàng)建實(shí)時(shí)線程 
  81.         { 
  82.             // 初始化線程屬性 
  83.             pthread_attr_init(&attr[i]); 
  84.              
  85.             // 設(shè)置調(diào)度策略為:SCHED_FIFO 
  86.             pthread_attr_setschedpolicy(&attr[i], SCHED_FIFO); 
  87.              
  88.             // 設(shè)置優(yōu)先級(jí)為 51,52。 
  89.             param[i].__sched_priority = 51 + i; 
  90.             pthread_attr_setschedparam(&attr[i], &param[i]); 
  91.              
  92.             // 設(shè)置線程屬性:不要繼承 main 線程的調(diào)度策略和優(yōu)先級(jí)。 
  93.             pthread_attr_setinheritsched(&attr[i], PTHREAD_EXPLICIT_SCHED); 
  94.              
  95.             // 創(chuàng)建線程 
  96.             pthread_create(&ppid[i], &attr[i],(void *)thread_routine, (void *)&index[i]); 
  97.         } 
  98.         else        // 后兩個(gè)創(chuàng)建普通線程 
  99.         { 
  100.             pthread_create(&ppid[i], 0, (void *)thread_routine, (void *)&index[i]); 
  101.         } 
  102.          
  103.     } 
  104.  
  105.     // 等待 4 個(gè)線程執(zhí)行結(jié)束 
  106.     for (int i = 0; i < 4; i++) 
  107.         pthread_join(ppid[i], 0); 
  108.  
  109.     for (int i = 0; i < 2; i++) 
  110.         pthread_attr_destroy(&attr[i]); 

編譯成可執(zhí)行程序的指令:

  1. gcc -o test test.c -lpthread 

腦殘測(cè)試開始

首先說一下預(yù)期結(jié)果,如果沒有預(yù)期結(jié)果,那其他任何問題都?jí)焊挥谜劻恕?/p>

一共有 4 個(gè)線程:

  1. 線程索引號(hào) 1和2:是實(shí)時(shí)線程(調(diào)度策略是 SCHED_FIFO,優(yōu)先級(jí)是 51,52);
  2. 線程索引號(hào) 3和4:是普通線程(調(diào)度策略是 SCHED_OTHER, 優(yōu)先級(jí)是 0);

我的測(cè)試環(huán)境是:Ubuntu16.04,是一臺(tái)安裝在 Windows10 上面的虛擬機(jī)。

我期望的結(jié)果是:

  1. 首先打印 1 號(hào)和 2 號(hào)這兩個(gè)線程的信息,因?yàn)樗鼈z是實(shí)時(shí)任務(wù),需要優(yōu)先被調(diào)度;
  2. 1 號(hào)線程的優(yōu)先級(jí)是 51,小于 2 號(hào)線程的優(yōu)先級(jí) 52,因此應(yīng)該是 2 號(hào)線程結(jié)束之后,才輪到 1 號(hào)線程執(zhí)行;
  3. 3 號(hào)和 4 號(hào)線程是普通進(jìn)程,它倆需要等到 1 號(hào)和 2 號(hào)線程全部執(zhí)行結(jié)束之后才開始執(zhí)行,并且 3 號(hào)和 4 號(hào)線程應(yīng)該是交替執(zhí)行,因?yàn)樗鼈z的調(diào)度策略和優(yōu)先級(jí)都是一樣的。

我滿懷希望的在工作電腦中測(cè)試,打印結(jié)果如下:

  1. ====> thread_index = 4  
  2. thread_index 4: SCHED_OTHER  
  3. thread_index 4: priority = 0  
  4.  
  5. ====> thread_index = 1  
  6. thread_index 1: SCHED_FIFO  
  7. thread_index 1: priority = 51  
  8.  
  9. ====> thread_index = 2  
  10. thread_index 2: SCHED_FIFO  
  11. thread_index 2: priority = 52  
  12. thread_index 2: num = 0  
  13. thread_index 4: num = 0  
  14.  
  15. ====> thread_index = 3  
  16. thread_index 3: SCHED_OTHER  
  17. thread_index 3: priority = 0  
  18. thread_index 1: num = 0  
  19. thread_index 2: num = 1  
  20. thread_index 4: num = 1  
  21. thread_index 3: num = 0  
  22. thread_index 1: num = 1  
  23. thread_index 2: num = 2  
  24. thread_index 4: num = 2  
  25. thread_index 3: num = 1 
  26.  
  27. 后面打印內(nèi)容不用輸出了,因?yàn)榍懊嬉呀?jīng)出現(xiàn)了問題。 

 問題很明顯:為什么 4 個(gè)線程為什么被同時(shí)執(zhí)行了?

1 號(hào)和 2 號(hào)這兩個(gè)線程應(yīng)該被優(yōu)先執(zhí)行啊,因?yàn)樗鼈z是實(shí)時(shí)任務(wù)!

怎么結(jié)果是這個(gè)樣子?徹底凌亂了,一點(diǎn)都不符合預(yù)期!

想不出個(gè)所以然,只能求助網(wǎng)絡(luò)!但是沒有找到有價(jià)值的線索。

其中有一個(gè)信息涉及到 Linux 系統(tǒng)的調(diào)度策略,這里記錄一下。

Linux 系統(tǒng)中,為了不讓實(shí)時(shí)任務(wù)徹底占據(jù) CPU 資源,會(huì)讓普通任務(wù)有很小的一段時(shí)間縫隙來執(zhí)行。

在目錄 /proc/sys/kernel 下面,有 2 個(gè)文件,用來限制實(shí)時(shí)任務(wù)占用 CPU 的時(shí)間:

  • sched_rt_runtime_us: 默認(rèn)值 950000 sched_rt_period_us: 默認(rèn)值 1000000

意思是:在 1000000 微秒(1秒)的周期內(nèi),實(shí)時(shí)任務(wù)占用 950000 微秒(0.95秒),剩下的 0.05 秒留給普通任務(wù)。

如果沒有這個(gè)限制的話,假如某個(gè) SCHED_FIFO 任務(wù)的優(yōu)先級(jí)特別高,恰巧出了 bug:一直占據(jù) CPU 資源不放棄,那么我們壓根就沒有機(jī)會(huì)來 kill 掉這個(gè)實(shí)時(shí)任務(wù),因?yàn)榇藭r(shí)系統(tǒng)無法調(diào)度其他的任何進(jìn)程來執(zhí)行。

而有了這個(gè)限制呢,我們就可以利用這 0.05 秒的執(zhí)行時(shí)間,來 kill 掉有 bug 的那個(gè)實(shí)時(shí)任務(wù)。

回到正題:資料上說,如果實(shí)時(shí)任務(wù)沒有被優(yōu)先調(diào)度,可以把這個(gè)時(shí)間限制刪掉就可以了。方法是:

  1. sysctl -w kernel.sched_rt_runtime_us=-1 

我照做之后,依舊無效!

換一臺(tái)虛擬機(jī),繼續(xù)測(cè)試

難道是電腦環(huán)境的問題嗎?于是,把測(cè)試代碼放到另一臺(tái)筆記本里的虛擬機(jī) Ubuntu14.04 里測(cè)試。

編譯的時(shí)候,有一個(gè)小問題,提示錯(cuò)誤:

  1. error: ‘for’ loop initial declarations are only allowed in C99 mode 

只要把編譯指令中添加 C99 標(biāo)準(zhǔn)就可以了:

  1. gcc -o test test.c -lpthread -std=c99 

執(zhí)行程序,打印信息如下:

  1. ====> thread_index = 2  
  2.  
  3. ====> thread_index = 1  
  4. thread_index 1: SCHED_FIFO  
  5. thread_index 1: priority = 51  
  6. thread_index 2: SCHED_FIFO  
  7. thread_index 2: priority = 52  
  8. thread_index 1: num = 0  
  9. thread_index 2: num = 0  
  10. thread_index 2: num = 1  
  11. thread_index 1: num = 1  
  12. thread_index 2: num = 2  
  13. thread_index 1: num = 2  
  14. thread_index 2: num = 3  
  15. thread_index 1: num = 3  
  16. thread_index 2: num = 4  
  17. thread_index 1: num = 4  
  18. thread_index 2: num = 5  
  19. thread_index 1: num = 5  
  20. thread_index 2: num = 6  
  21. thread_index 1: num = 6  
  22. thread_index 2: num = 7  
  23. thread_index 1: num = 7  
  24. thread_index 2: num = 8  
  25. thread_index 1: num = 8  
  26. thread_index 2: num = 9  
  27. thread_index 2: exit  
  28.  
  29. ====> thread_index = 4  
  30. thread_index 4: SCHED_OTHER  
  31. thread_index 4: priority = 0  
  32. thread_index 1: num = 9  
  33. thread_index 1: exit  
  34.  
  35. ====> thread_index = 3  
  36. thread_index 3: SCHED_OTHER  
  37. thread_index 3: priority = 0  
  38. thread_index 3: num = 0  
  39. thread_index 4: num = 0  
  40. thread_index 3: num = 1  
  41. thread_index 4: num = 1  
  42. thread_index 3: num = 2  
  43. thread_index 4: num = 2  
  44. thread_index 3: num = 3  
  45. thread_index 4: num = 3  
  46. thread_index 3: num = 4  
  47. thread_index 4: num = 4  
  48. thread_index 3: num = 5  
  49. thread_index 4: num = 5  
  50. thread_index 3: num = 6  
  51. thread_index 4: num = 6  
  52. thread_index 3: num = 7  
  53. thread_index 4: num = 7  
  54. thread_index 3: num = 8  
  55. thread_index 4: num = 8  
  56. thread_index 3: num = 9  
  57. thread_index 3: exit  
  58. thread_index 4: num = 9  
  59. thread_index 4: exit 

1 號(hào)和 2 號(hào)線程同時(shí)執(zhí)行,完畢之后,再 3 號(hào)和 4 號(hào)線程同時(shí)執(zhí)行。

但是這同樣也不符合預(yù)期:2 號(hào)線程的優(yōu)先級(jí)比 1 號(hào)線程高,應(yīng)該優(yōu)先執(zhí)行才對(duì)!

不知道應(yīng)該怎么查這個(gè)問題了,想不出思路,只好請(qǐng)教 Linux 內(nèi)核的大神,建議檢查一下內(nèi)核版本。

這時(shí),我才想起來在 Ubuntu16.04 這臺(tái)虛擬機(jī)上因?yàn)槟撤N原因,降過內(nèi)核版本。

往這個(gè)方向去排查了一下,最后確認(rèn)也不是內(nèi)核版本的差異導(dǎo)致的問題。

比較結(jié)果,尋找差異

只好再回過頭來看一下這兩次次打印信息的差異:

  1. 工作電腦里的 Ubuntu16.04 中:4 個(gè)線程同時(shí)調(diào)度執(zhí)行,調(diào)度策略和優(yōu)先級(jí)都沒有起作用;
  2. 筆記本里的 Ubuntu14.04 中:1 號(hào)和 2 號(hào)實(shí)時(shí)任務(wù)被優(yōu)先執(zhí)行了,說明調(diào)度策略起作用了,但是優(yōu)先級(jí)沒有起作用;

突然, CPU 的親和性從腦袋里蹦了出來!

緊接著立馬感覺到問題出在哪里了:這TMD大概率就是多核引起的問題!

[[405790]]

于是我把這 4 個(gè)線程都綁定到 CPU0 上去,也就是設(shè)置 CPU 親和性。

在線程入口函數(shù) thread_routine 的開頭,增加下面的代碼:

  1. cpu_set_t mask; 
  2. int cpus = sysconf(_SC_NPROCESSORS_CONF); 
  3. CPU_ZERO(&mask); 
  4. CPU_SET(0, &mask); 
  5. if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) 
  6.     printf("set thread affinity failed! \n"); 

然后繼續(xù)在 Ubuntu16.04 虛擬機(jī)中驗(yàn)證,打印信息很完美,完全符合預(yù)期:

  1. ====> thread_index = 1  
  2.  
  3. ====> thread_index = 2  
  4. thread_index 2: SCHED_FIFO  
  5. thread_index 2: priority = 52  
  6. thread_index 2: num = 0  
  7. 。。。 
  8. thread_index 2: num = 9  
  9. thread_index 2: exit  
  10. thread_index 1: SCHED_FIFO  
  11. thread_index 1: priority = 51  
  12. thread_index 1: num = 0  
  13. 。。。 
  14. thread_index 1: num = 9  
  15. thread_index 1: exit  
  16.  
  17. ====> thread_index = 3  
  18. thread_index 3: SCHED_OTHER  
  19. thread_index 3: priority = 0  
  20.  
  21. ====> thread_index = 4  
  22. thread_index 4: SCHED_OTHER  
  23. thread_index 4: priority = 0  
  24. thread_index 3: num = 0  
  25. thread_index 4: num = 0  
  26. 。。。 
  27. thread_index 4: num = 8  
  28. thread_index 3: num = 8  
  29. thread_index 4: num = 9  
  30. thread_index 4: exit  
  31. thread_index 3: num = 9  
  32. thread_index 3: exit 

至此,問題真相大白:就是多核處理器導(dǎo)致的問題!

而且這兩臺(tái)測(cè)試的虛擬機(jī),安裝的時(shí)候分配的 CPU 核心是不同的,所以才導(dǎo)致打印結(jié)果的不同。

真相大白

最后,再確認(rèn)一下這 2 個(gè)虛擬機(jī)中的 CPU 信息:

Ubuntu 16.04 中 cpuinfo 信息:

  1. $ cat /proc/cpuinfo  
  2. processor : 0 
  3. vendor_id : GenuineIntel 
  4. cpu family : 6 
  5. model  : 158 
  6. model name : Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz 
  7. stepping : 10 
  8. cpu MHz  : 2807.996 
  9. cache size : 9216 KB 
  10. physical id : 0 
  11. siblings : 4 
  12. core id  : 0 
  13. cpu cores : 4 
  14. 。。。其他信息 
  15.  
  16. processor : 1 
  17. vendor_id : GenuineIntel 
  18. cpu family : 6 
  19. model  : 158 
  20. model name : Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz 
  21. stepping : 10 
  22. cpu MHz  : 2807.996 
  23. cache size : 9216 KB 
  24. physical id : 0 
  25. siblings : 4 
  26. core id  : 1 
  27. cpu cores : 4 
  28. 。。。其他信息 
  29.  
  30. processor : 2 
  31. vendor_id : GenuineIntel 
  32. cpu family : 6 
  33. model  : 158 
  34. model name : Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz 
  35. stepping : 10 
  36. cpu MHz  : 2807.996 
  37. cache size : 9216 KB 
  38. physical id : 0 
  39. siblings : 4 
  40. core id  : 2 
  41. cpu cores : 4 
  42. 。。。其他信息 
  43.  
  44. processor : 3 
  45. vendor_id : GenuineIntel 
  46. cpu family : 6 
  47. model  : 158 
  48. model name : Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz 
  49. stepping : 10 
  50. cpu MHz  : 2807.996 
  51. cache size : 9216 KB 
  52. physical id : 0 
  53. siblings : 4 
  54. core id  : 3 
  55. cpu cores : 4 
  56. 。。。其他信息 

在這臺(tái)虛擬機(jī)中,正好有 4 個(gè)核心,而我的測(cè)試代碼正好也創(chuàng)建了 4 個(gè)線程,于是每個(gè)核心被分配一個(gè)線程,一個(gè)都不閑著,同時(shí)執(zhí)行。

因此打印信息中顯示 4 個(gè)線程是并行執(zhí)行的。

這個(gè)時(shí)候,什么調(diào)度策略、什么優(yōu)先級(jí),都不起作用了!(準(zhǔn)確的說:調(diào)度策略和優(yōu)先級(jí),在線程所在的那個(gè) CPU 中是起作用的)

如果我在測(cè)試代碼中,一開始就創(chuàng)建 10 個(gè)線程,很可能會(huì)更快發(fā)現(xiàn)問題!

再來看看筆記本電腦里虛擬機(jī) Ubuntu14.04 的 CPU 信息:

  1. $ cat /proc/cpuinfo  
  2. processor   : 0 
  3. vendor_id   : GenuineIntel 
  4. cpu family  : 6 
  5. model       : 142 
  6. model name  : Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz 
  7. stepping    : 9 
  8. microcode   : 0x9a 
  9. cpu MHz     : 2304.000 
  10. cache size  : 4096 KB 
  11. physical id : 0 
  12. siblings    : 2 
  13. core id     : 0 
  14. cpu cores   : 2 
  15. 。。。其他信息 
  16.  
  17.  
  18. processor   : 1 
  19. vendor_id   : GenuineIntel 
  20. cpu family  : 6 
  21. model       : 142 
  22. model name  : Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz 
  23. stepping    : 9 
  24. microcode   : 0x9a 
  25. cpu MHz     : 2304.000 
  26. cache size  : 4096 KB 
  27. physical id : 0 
  28. siblings    : 2 
  29. core id     : 1 
  30. cpu cores   : 2 
  31. 。。。其他信息 

在這臺(tái)虛擬機(jī)中,有 2 個(gè)核心,于是 2 個(gè)實(shí)時(shí)任務(wù) 1 號(hào)和 2 號(hào)被優(yōu)先執(zhí)行(因?yàn)槭?2 個(gè)核心同時(shí)執(zhí)行,所以這 2 個(gè)任務(wù)的優(yōu)先級(jí)也就沒什么意義了),結(jié)束之后,再執(zhí)行 3 號(hào)和 4 號(hào)線程。

再思考一下

這一圈測(cè)試下來,真的想用鍵盤敲自己的腦袋,怎么就沒有早點(diǎn)考慮到多核的因素呢?!

深層的原因:

  1. 之前的很多項(xiàng)目,都是 ARM、mips、STM32等單核情況,思維定式讓我沒有早點(diǎn)意識(shí)到多核這個(gè)屏體因素;
  2. 做過的一些 x86 平臺(tái)項(xiàng)目,并沒有涉及到實(shí)時(shí)任務(wù)這樣的要求。一般都是使用系統(tǒng)默認(rèn)的調(diào)度策略,這也是 Linux x86 作為通用電腦,在調(diào)度策略上所關(guān)注的重要指標(biāo):讓每一個(gè)任務(wù)都公平的使用 CPU 資源。

隨著 x86 平臺(tái)在工控領(lǐng)域的逐漸應(yīng)用,實(shí)時(shí)性問題就顯得更突出、更重要了。

所以才有了 Windows 系統(tǒng)中的 intime,Linux 系統(tǒng)中的 preempt、xenomai 等實(shí)時(shí)補(bǔ)丁。

本文轉(zhuǎn)載自微信公眾號(hào)「 IOT物聯(lián)網(wǎng)小鎮(zhèn)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 IOT物聯(lián)網(wǎng)小鎮(zhèn)公眾號(hào)。

 

責(zé)任編輯:姜華 來源: IOT物聯(lián)網(wǎng)小鎮(zhèn)
相關(guān)推薦

2020-06-04 08:36:55

Linux內(nèi)核線程

2021-04-23 21:40:33

Python優(yōu)先級(jí)調(diào)度器

2021-04-06 10:45:18

React前端優(yōu)先級(jí)

2023-01-05 08:48:57

技術(shù)管理排優(yōu)先級(jí)

2012-08-14 09:38:29

WAN優(yōu)化

2023-11-03 08:22:09

Android系統(tǒng)算法

2021-02-02 14:55:48

React前端高優(yōu)先

2019-07-31 15:14:33

2010-09-01 14:10:36

CSS優(yōu)先級(jí)

2024-10-10 10:32:04

2013-12-24 13:59:03

2022-12-23 09:41:14

優(yōu)先級(jí)反轉(zhuǎn)

2023-11-16 09:30:27

系統(tǒng)任務(wù)

2020-03-10 07:51:35

面試諷刺標(biāo)準(zhǔn)

2019-11-01 12:36:14

人工智能機(jī)器學(xué)習(xí)技術(shù)

2018-01-15 14:50:49

APP轉(zhuǎn)讓App賬號(hào)

2023-03-29 09:36:32

2010-03-18 14:09:20

Java線程同步

2020-09-30 09:07:37

DevOps

2024-03-11 07:46:40

React優(yōu)先級(jí)隊(duì)列二叉堆
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)