訂閱
糾錯(cuò)
加入自媒體

Linux :多處理器遇到實(shí)時(shí)進(jìn)程和普通進(jìn)程的程序設(shè)計(jì)

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

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

實(shí)時(shí)進(jìn)程和普通進(jìn)程的調(diào)度策略;

Linux 中混亂的進(jìn)程優(yōu)先級(jí)是如何計(jì)算的;

CPU親和性的測(cè)試;

多處理器(SMP)遇到實(shí)時(shí)進(jìn)程和普通進(jìn)程的程序設(shè)計(jì);

道哥的腦袋被門夾了一下的短路經(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í)等因素來(lái)進(jìn)行選擇。

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

之所以叫做完全公平,是因?yàn)椴僮飨到y(tǒng)以每個(gè)線程占用 CPU 的比率來(lái)進(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)體變量)是很類似的,所以線程可以說(shuō)是輕量級(jí)的進(jìn)程。

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

可以這么理解:如果系統(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)程來(lái)執(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í)越高。

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

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

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

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

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

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

struct sched_param param;
param.__sched_priority = xxx;

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

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í),是通過(guò) nice 值來(lái)實(shí)現(xiàn)的,內(nèi)核中也有一個(gè)公式來(lái)把應(yīng)用層傳入的 nice 值,轉(zhuǎn)成內(nèi)核角度的優(yōu)先級(jí)數(shù)值:

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è)試了!

測(cè)試代碼說(shuō)明

注意點(diǎn):

#define _GNU_SOURCE 必須在 #include <sched.h> 之前定義;

#include <sched.h> 必須在 #include <pthread.h> 之前包含進(jìn)來(lái);

這個(gè)順序能夠保證在后面設(shè)置繼承的 CPU 親和性時(shí),CPU_SET, CEPU_ZERO這兩個(gè)函數(shù)能被順利定位到。

// filename: test.c
#define _GNU_SOURCE
#include <unistd.h>  
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <pthread.h>
// 用來(lái)打印當(dāng)前的線程信息:調(diào)度策略是什么??jī)?yōu)先級(jí)是多少?
void get_thread_info(const int thread_index)

   int policy;
   struct sched_param param;
   printf("====> thread_index = %d ", thread_index);
   pthread_getschedparam(pthread_self(), &policy, &param);
   if (SCHED_OTHER == policy)
       printf("thread_index %d: SCHED_OTHER ", thread_index);
   else if (SCHED_FIFO == policy)
       printf("thread_index %d: SCHED_FIFO ", thread_index);
   else if (SCHED_RR == policy)
       printf("thread_index %d: SCHED_RR ", thread_index);
   printf("thread_index %d: priority = %d ", thread_index, param.sched_priority);

// 線程函數(shù),
void *thread_routine(void *args)

   // 參數(shù)是:線程索引號(hào)。四個(gè)線程,索引號(hào)從 1 到 4,打印信息中使用。
   int thread_index = *(int *)args;
   // 為了確保所有的線程都創(chuàng)建完畢,讓線程睡眠1秒。
   sleep(1);
   // 打印一下線程相關(guān)信息:調(diào)度策略、優(yōu)先級(jí)。

1  2  3  下一頁(yè)>  
聲明: 本文由入駐維科號(hào)的作者撰寫,觀點(diǎn)僅代表作者本人,不代表OFweek立場(chǎng)。如有侵權(quán)或其他問(wèn)題,請(qǐng)聯(lián)系舉報(bào)。

發(fā)表評(píng)論

0條評(píng)論,0人參與

請(qǐng)輸入評(píng)論內(nèi)容...

請(qǐng)輸入評(píng)論/評(píng)論長(zhǎng)度6~500個(gè)字

您提交的評(píng)論過(guò)于頻繁,請(qǐng)輸入驗(yàn)證碼繼續(xù)

  • 看不清,點(diǎn)擊換一張  刷新

暫無(wú)評(píng)論

暫無(wú)評(píng)論

    掃碼關(guān)注公眾號(hào)
    OFweek人工智能網(wǎng)
    獲取更多精彩內(nèi)容
    文章糾錯(cuò)
    x
    *文字標(biāo)題:
    *糾錯(cuò)內(nèi)容:
    聯(lián)系郵箱:
    *驗(yàn) 證 碼:

    粵公網(wǎng)安備 44030502002758號(hào)