Linux thread schedule priority

一,背景

?最近線上系統(tǒng)壓力比較大,loadaverage 八九十是經(jīng)常的烟具,然后導致某些比較核心的線程收到不到網(wǎng)絡(luò)消息席舍,或者長時間得不得調(diào)用的機會布轿,所以這個時候我們就需要把該線程的調(diào)用優(yōu)先級提高。
?不過Linux系統(tǒng)提供了一個比較牛的特性来颤,可以調(diào)整一個線程的調(diào)用策略和調(diào)用優(yōu)先級汰扭,它包含多種策略,默認的策略是SCHED_OTHER福铅,對于這種策略所有線程的優(yōu)先級都是0萝毛,采用round_robin的方式進行調(diào)度。除了它還有SCHED_BATCH滑黔,SCHED_IDLE笆包,SCHED_FIFO和SCHED_RR,而我們今天注重介紹后面兩種semi-realtime策略略荡。

  • SCHED_OTHER
    ?它是默認的線程分時調(diào)度策略庵佣,所有的線程的優(yōu)先級別都是0,線程的調(diào)度是通過分時來完成的汛兜。簡單地說巴粪,如果系統(tǒng)使用這種調(diào)度策略,程序?qū)o法設(shè)置線程的優(yōu)先級粥谬。請注意肛根,這種調(diào)度策略也是搶占式的,當高優(yōu)先級的線程準備運行的時候帝嗡,當前線程將被搶占并進入等待隊列晶通。這種調(diào)度策略僅僅決定線程在可運行線程隊列中的具有相同優(yōu)先級的線程的運行次序。

  • SCHED_FIFO
    ?它是一種實時的先進先出調(diào)用策略哟玷,且只能在超級用戶下運行狮辽。這種調(diào)用策略僅僅被使用于優(yōu)先級大于0的線程。它意味著巢寡,使用SCHED_FIFO的可運行線程將一直搶占使用SCHED_OTHER的運行線程喉脖。此外SCHED_FIFO是一個非分時的簡單調(diào)度策略,當一個線程變成可運行狀態(tài)抑月,它將被追加到對應(yīng)優(yōu)先級隊列的尾部((POSIX 1003.1)树叽。當所有高優(yōu)先級的線程終止或者阻塞時,它將被運行谦絮。對于相同優(yōu)先級別的線程题诵,按照簡單的先進先運行的規(guī)則運行洁仗。我們考慮一種很壞的情況,如果有若干相同優(yōu)先級的線程等待執(zhí)行性锭,然而最早執(zhí)行的線程無終止或者阻塞動作赠潦,那么其他線程是無法執(zhí)行的,除非當前線程調(diào)用如pthread_yield之類的函數(shù)草冈,所以在使用SCHED_FIFO的時候要小心處理相同級別線程的動作她奥。

  • SCHED_RR
    ?鑒于SCHED_FIFO調(diào)度策略的一些缺點,SCHED_RR對SCHED_FIFO做出了一些增強功能怎棱。從實質(zhì)上看哩俭,它還是SCHED_FIFO調(diào)用策略。它使用最大運行時間來限制當前進程的運行拳恋,當運行時間大于等于最大運行時間的時候凡资,當前線程將被切換并放置于相同優(yōu)先級隊列的最后。這樣做的好處是其他具有相同級別的線程能在“自私“線程下執(zhí)行谬运。

?綜上所述讳苦,我推薦使用SCHED_RR,該策略可以在可控的范圍內(nèi)改變我們想要提高的線程調(diào)度策略和優(yōu)先級吩谦。

二,pthread_setschedpolicy / pthread_setschedparam

int pthread_setschedparam(pthread_t thread, int policy,
                             const struct sched_param *param);
int pthread_getschedparam(pthread_t thread, int *policy,
                             struct sched_param *param);

Linux 系統(tǒng)為我們提供了上面的兩個api來設(shè)置和獲取一個已經(jīng)存在的線程的調(diào)度策略和調(diào)度優(yōu)先級膝藕,實驗1:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>

pthread_mutex_t mutex;

int set_realtime_priority()
{
    int ret,policy;
    struct sched_param params;
    pthread_t this_thread = pthread_self();
    printf("\nset realtime priority for thread policy: SCHED_FIFO prio: 1\n" );
    params.sched_priority = 10;
    ret = pthread_setschedparam(this_thread, SCHED_FIFO, &params);
    if (ret != 0) {
        printf( "Unsuccessful in setting thread realtime policy: SCHED_FIFO prio: 1\n");
        return 0;
    }
}

void *thr_fn2(void *arg)
{
        int ret,policy = 0;
        pthread_attr_t tattr;
        struct sched_param params;

        pthread_attr_init(&tattr);

        printf("+ start --------------------  child thread  -------------------------+\n");

        printf("| child thread getshed:                                              |\n");
        pthread_t this_thread = pthread_self();
        ret = pthread_getschedparam(this_thread, &policy, &params);
        if(ret == 0 && policy == SCHED_FIFO) {
            printf("| thread thr_fn2 policy is SCHED_FIFO prior is %d                     |\n", params.sched_priority);
        }
        printf("+ end ---------------------  child thread  --------------------------+\n\n");

        sleep(1);
        pthread_exit(0);
}

void  *run(void *arg)
{
        int err=0,i=0;
        pthread_t tid;
        void *tret;

        set_realtime_priority();

        printf("start create child thread .... \n\n");

        err=pthread_create(&tid, NULL, thr_fn2, NULL);
        if(err!=0)
        {
            perror("pthread_create");
            exit(-1);
        }

        err=pthread_join(tid,&tret);
        if(err!=0)
        {
            perror("pthread_join");
            exit(-1);
        }
        printf("thread exit code is %ld\n",(long)tret);
}

int main()
{
    int err;
    void *tret;
    pthread_t thread_id;

    err = pthread_create(&thread_id, NULL, run, NULL);
    if(err!=0)
    {
        perror("pthread_create");
        exit(-1);
    }

    err=pthread_join(thread_id,&tret);

    return 0;
}

gcc thread_setschedxxx.c -o thread_setschedxxx -lpthread

./thread_setschedxxx

set realtime priority for thread policy: SCHED_FIFO prio: 1
start create child thread ....

+ start --------------------  child thread  -------------------------+
| child thread getshed:                                              |
| thread thr_fn2 policy is SCHED_FIFO prior is 10                    |
+ end ---------------------  child thread  --------------------------+

thread exit code is 0

三式廷,pthread_attr_setschedpolicy / pthread_setschedparam

int pthread_attr_setschedparam(pthread_attr_t *attr,
                              const struct sched_param *param);
int pthread_attr_getschedparam(const pthread_attr_t *attr,
                              struct sched_param *param);

同樣系統(tǒng)也為我們提供了另外一套api,這兩個接口是先把相關(guān)的屬性設(shè)置到attr對象上面,在把這個對象作為參數(shù)傳給pthread_create,從而讓新建立的線程具備對應(yīng)的屬性芭挽。它與上面第二條里面提到的API區(qū)別就是:
?1. 需要把policy和priority分開設(shè)置(不重要)
?2.這套API只能設(shè)置還沒有創(chuàng)建的線程滑废,對于已經(jīng)創(chuàng)建的線程就只能用pthread_setschedparam來設(shè)置了。
實驗2:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>

pthread_mutex_t mutex;

void *thr_fn2(void *arg)
{
        int ret,policy = 0;
        pthread_attr_t tattr;
        struct sched_param params;

        pthread_attr_init(&tattr);

        printf("+ start --------------------  child thread  -------------------------+\n");

        printf("| child thread getshed:                                              |\n");
        pthread_t this_thread = pthread_self();
        ret = pthread_getschedparam(this_thread, &policy, &params);
        if(ret == 0 && policy == SCHED_FIFO) {
            printf("| thread thr_fn2 policy is SCHED_FIFO prior is %d                    |\n", params.sched_priority);
        }
        printf("+ end ---------------------  child thread  --------------------------+\n\n");

        sleep(1);
        pthread_exit(0);
}

void  *run(void *arg)
{
        int err=0,i=0;
        pthread_t tid;
        void *tret;
        pthread_attr_t tattr;
        int policy;
        struct sched_param param;

        pthread_t this_thread = pthread_self();
        pthread_getschedparam(this_thread, &policy, &param);
        printf("\nrun thread  policy is %d prior is %d\n\n",  policy, param.sched_priority);


        sleep(1);
        printf("start create child thread .... \n\n");

        //pthread_attr_init(&tattr);
        //err=pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED);
        //err=pthread_create(&tid[i], &tattr,thr_fn2, &rank[i]);
        err=pthread_create(&tid, NULL, thr_fn2, NULL);
        if(err!=0)
        {
            perror("pthread_create");
            exit(-1);
        }
        //pthread_attr_destroy(&tattr);

        err=pthread_join(tid,&tret);
        if(err!=0)
        {
            perror("pthread_join");
            exit(-1);
        }
        printf("thread exit code is %ld\n",(long)tret);
}

int main()
{
    int err, policy;
    pthread_attr_t tattr;
    struct sched_param param;
    void *tret;
    pthread_t thread_id;

    err = pthread_attr_init(&tattr);

    param.sched_priority=10;
    err = pthread_attr_setschedpolicy(&tattr, SCHED_FIFO);
    if(err != 0)
    {
        printf("failed to set schedpolicy\n");
    }

    err = pthread_attr_setschedparam(&tattr, &param);
    if(err != 0)
    {
        printf("failed to set schedparam\n");
    }

    err=pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED);
    if(err != 0)
    {
        printf("failed to set inheritsched\n");
    }

    err = pthread_create(&thread_id, &tattr, run, NULL);
    if(err!=0)
    {
        perror("pthread_create");
        exit(-1);
    }

    pthread_attr_destroy(&tattr);
    err=pthread_join(thread_id,&tret);

    return 0;
}

gcc thread_attr_inherit.c -o thread_attr_inherit -lpthread

./thread_attr_inherit

run thread  policy is 1 prior is 10

start create child thread ....

+ start --------------------  child thread  -------------------------+
| child thread getshed:                                              |
| thread thr_fn2 policy is 1 prior is 10                             |
+ end ---------------------  child thread  --------------------------+

thread exit code is 0

注意:

  • 我們在使用pthread_attr_setschedxxx接口的時候必須同時把attr對象的inheritsched屬性設(shè)置為“PTHREAD_EXPLICIT_SCHED”袜爪,否則子線程將繼承來自父親線程的所有屬性蠕趁。
  • 一般要用pthread_getschedparam接口來獲取調(diào)度策略policy和調(diào)度優(yōu)先級param.sched_priority。若使用pthread_attr_getschedxxx獲取調(diào)度屬性的話必須使用創(chuàng)建線程時使用的attr對象辛馆,否則獲取不到俺陋。

四,權(quán)限問題

?很多人估計跟我一樣昙篙,看到這么好用的功能腊状,趕緊寫個demo程序驗證一下,但是悲劇的是如果使用pthread_setschedpolicy會報錯:

pthread_setschedparam: Operation not permitted

使用pthread_attr_setschedxxx則會報錯:

pthread_create: Operation not permitted

經(jīng)過一番搜索和實驗苔可,發(fā)現(xiàn)有以下解決方案:

解決方案:

網(wǎng)絡(luò)解決方案:在宿主機上執(zhí)行“sysctl -w kernel.sched_rt_runtime_us=-1”
卻是可以解決問題缴挖,但是這個命令改變了全局的sched_rt_runtime_us這樣會有風險,可能導致別的線程拿不到時間片焚辅,所以不推薦映屋!

非容器環(huán)境

?對于宿主機上面的線程苟鸯,我們可以通過把父親線程的pid添加到/sys/fs/cgroup/cpu/tasks來解決

sprintf(cmd, "echo %d >> /sys/fs/cgroup/cpu/tasks", getpid());
system(cmd);

s = pthread_create(&thread, attrp, &thread_start, NULL);
容器環(huán)境

在宿主機上面設(shè)置:

echo 950000 > /sys/fs/cgroup/cpu/system.slice/cpu.rt_runtime_us
echo 200000 > /sys/fs/cgroup/cpu/system.slice/docker-287a7b24d1e0fd73cbb04f19bea13e6e17d7aead59d6e84ff0025f752ea8a01d.scope/cpu.rt_runtime_us 
上面這一步可以替換為在容器里面執(zhí)行
echo 200000 > /sys/fs/cgroup/cpu/cpu.rt_runtime_us //分配200000給容器的cpu.rt_runtime_us

如果設(shè)置的時候權(quán)限不允許的話,可以把當前的shell添加到tasks里面

echo $$ >> /sys/fs/cgroup/cpu/tasks

五棚点,屬性繼承的困擾

?從上面兩個例子種可以看到早处,子線程會繼承父親線程的線程屬性,如果我們不想讓其繼承父線程的屬性乙濒,我們可以通過下面的接口來實現(xiàn):

#include <pthread.h>
int pthread_attr_setinheritsched(pthread_attr_t *attr,
                             int inheritsched);

inherit 缺省值是 PTHREAD_INHERIT_SCHED 表示新建的線程將繼承創(chuàng)建者線程中定義的調(diào)度策略陕赃。將忽略在 pthread_create() 調(diào)用中定義的所有調(diào)度屬性。如果使用值PTHREAD_EXPLICIT_SCHED颁股,則將使用 pthread_create() 調(diào)用中的屬性么库。
實驗3:

我們發(fā)現(xiàn)雖然調(diào)用了pthread_attr_setinheritsched,但是仍然繼承了父線程的屬性甘有,仔細看man page,發(fā)現(xiàn)了一個已知bug:

As at glibc 2.8, if a thread attributes object is initialized using pthread_attr_init(3), then the 
scheduling policy of the attributes object is set to SCHED_OTHER and the scheduling 
priority is set to 0. However, if the inherit-scheduler attribute is then set to
PTHREAD_EXPLICIT_SCHED, then a thread created using the attribute object wrongly
inherits its scheduling attributes from the creating thread.  This bug does not occur 
if either the scheduling policy or scheduling priority attribute is explicitly set in 
the thread attributes object before calling pthread_create(3)

所以我們必須要顯示指定attr的屬性:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>

pthread_mutex_t mutex;

int set_realtime_priority()
{
    int ret,policy;
    struct sched_param params;
    pthread_t this_thread = pthread_self();
    printf("\nset realtime priority for thread policy: SCHED_FIFO prio: 1\n" );
    params.sched_priority = 10;
    ret = pthread_setschedparam(this_thread, SCHED_FIFO, &params);
    if (ret != 0) {
        printf( "Unsuccessful in setting thread realtime policy: SCHED_FIFO prio: 1\n");
        return 0;
    }
}

void *thr_fn2(void *arg)
{
        int ret,policy = 0;
        struct sched_param params;
        pthread_t this_thread = pthread_self();

        printf("+ start --------------------  child thread  -------------------------+\n");
        printf("| child thread getshed:                                              |\n");

        ret = pthread_getschedparam(this_thread, &policy, &params);
        if(ret == 0 && policy == SCHED_FIFO) {
            printf("| thread thr_fn2 policy is %d prior is %d                             |\n", policy, params.sched_priority);
        }
        printf("+ end ---------------------  child thread  --------------------------+\n\n");

        sleep(1);
        pthread_exit(0);
}

void  *run(void *arg)
{
        int err=0,i=0;
        pthread_t tid;
        void *tret;
        pthread_attr_t tattr;
        int policy;
        struct sched_param param;

        pthread_t this_thread = pthread_self();
        pthread_getschedparam(this_thread, &policy, &param);
        printf("\nrun thread  policy is %d prior is %d\n\n",  policy, param.sched_priority);

        sleep(1);
        printf("start create child thread .... \n\n");

        pthread_attr_init(&tattr);
        err = pthread_attr_setschedpolicy(&tattr, SCHED_OTHER);
        if(err!=0)
        {
            printf("failed to set policy\n");
        }

        param.sched_priority = 0;
        err=pthread_attr_setschedparam(&tattr,&param);
        if(err!=0)
        {
            printf("failed to set priority\n");
        }

        err=pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED);
        if(err!=0)
        {
            printf("failed to setinheritsched\n");
        }
        err=pthread_create(&tid, &tattr,thr_fn2, NULL);
        if(err!=0)
        {
            perror("pthread_create");
            exit(-1);
        }
        pthread_attr_destroy(&tattr);

        err=pthread_join(tid,&tret);
        if(err!=0)
        {
            perror("pthread_join");
            exit(-1);
        }
        printf("thread exit code is %ld\n",(long)tret);
}

int main()
{
    int err, policy;
    void *tret;
    pthread_t thread_id;

    // set realtime policy and priority
    set_realtime_priority();

    err = pthread_create(&thread_id, NULL, run, NULL);
    if(err!=0)
    {
        perror("pthread_create");
        exit(-1);
    }

    err=pthread_join(thread_id,&tret);

    return 0;
}

編譯執(zhí)行結(jié)果:
set realtime priority for thread policy: SCHED_FIFO prio: 1

run thread  policy is 1 prior is 10

start create child thread ....

+ start --------------------  child thread  -------------------------+
| child thread getshed:                                              |
+ end ---------------------  child thread  --------------------------+

thread exit code is 0

chrt小工具

我們可以使用這個小工具來動態(tài)設(shè)置現(xiàn)成的調(diào)度策略和優(yōu)先級诉儒,比如:

chrt -p 1  14790     //設(shè)置一個線程的調(diào)度策略為SCHED_RR且優(yōu)先級為-2
14790 root      -2   0  648936 194452   8424 S  0.0  2.5   0:00.05 ivan_prio

chrt -p 99 14790  //設(shè)置一個線程的調(diào)度策略為SCHED_RR且優(yōu)先級為RT
14790 root      rt   0  648936 194452   8424 S  0.0  2.5   0:00.05 ivan_prio

chrt -f -p 1 14790 //設(shè)置一個線程的調(diào)度策略為SCHED_FIFO且優(yōu)先級為-2
14790 root      -2   0  648936 194452   8424 S  0.0  2.5   0:00.05 ivan_prio

chrt -o -p 0 14790 //設(shè)置一個線程的調(diào)度策略為SCHED_OTHER且優(yōu)先級為20,如果設(shè)置SCHED_OTHER策略時,優(yōu)先級只能指定‘0’亏掀,在top種PR顯示為20
14790 root      20   0  648936 194900   8424 S  0.0  2.5   0:00.05 ivan_prio

https://blog.csdn.net/u010317005/article/details/80531985
https://www.cnblogs.com/tmpt/p/3603561.html

參考文獻

<1> https://blog.csdn.net/xiaoyeyopulei/article/details/7965840
<2> http://man7.org/linux/man-pages/man3/pthread_setschedparam.3.html
<3> http://man7.org/linux/man-pages/man3/pthread_attr_setschedparam.3.html
<4> http://man7.org/linux/man-pages/man3/pthread_attr_setinheritsched.3.html
<5> https://github.com/coreos/bugs/issues/410

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末忱反,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子滤愕,更是在濱河造成了極大的恐慌温算,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件间影,死亡現(xiàn)場離奇詭異注竿,居然都是意外死亡,警方通過查閱死者的電腦和手機魂贬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門巩割,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人付燥,你說我怎么就攤上這事宣谈。” “怎么了键科?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵闻丑,是天一觀的道長。 經(jīng)常有香客問我勋颖,道長梆掸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任牙言,我火速辦了婚禮酸钦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己卑硫,他們只是感情好徒恋,可當我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著欢伏,像睡著了一般入挣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上硝拧,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天径筏,我揣著相機與錄音,去河邊找鬼障陶。 笑死滋恬,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的抱究。 我是一名探鬼主播恢氯,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鼓寺!你這毒婦竟也來了勋拟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤妈候,失蹤者是張志新(化名)和其女友劉穎敢靡,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體苦银,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡醋安,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了墓毒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡亲怠,死狀恐怖所计,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情团秽,我是刑警寧澤主胧,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站习勤,受9級特大地震影響踪栋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜图毕,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一夷都、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧予颤,春花似錦囤官、人聲如沸冬阳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肝陪。三九已至,卻和暖如春刑顺,著一層夾襖步出監(jiān)牢的瞬間氯窍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工蹲堂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留狼讨,地道東北人。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓贯城,卻偏偏與公主長得像熊楼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子能犯,可洞房花燭夜當晚...
    茶點故事閱讀 45,455評論 2 359

推薦閱讀更多精彩內(nèi)容