45092


RTOSのプログラムをLinuxのAPIでシミュレーションできないかと考えています。同じコードをPCでテストすることが目的で、リアルタイム性はあまり気にしてません。  
manページ等をみると、スケジューリングポリシーにSCHED_FIFOを指定すると優先度の高いスレッドがブロックされない限り、低優先度のスレッドはブロックされるといった記述がありました。そこで、下記のサンプルコードを試してみましたが期待通りの動作にはなりませんでした。

具体的には、高優先度のスレッドがwhile文で無限ループしているので低優先度のスレッドのprintfが永久に実行されない、というのが期待する動作です。なぜこのサンプルコードではスレッドの切替が行われてしまうのでしょうか？

**@user20098さんのコメントを受けて、カーネルバージョン等の追記**  
Windows10(64bit)上のVirtualBoxでUbuntu16.04(AMD64)をインストールしてテストしています。

[code]

    $ uname -a
    Linux vagrant 4.4.0-87-generic #110-Ubuntu SMP Tue Jul 18 12:55:35 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
    
[/code]

* * *
[code]

    #include <stdio.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <assert.h>
    
    
    static void* prio_high(void* arg)
    {
        volatile int i = 0;
        while (1)
            i++;
    
        return NULL;
    }
    
    static void* prio_low(void* arg)
    {
        unsigned int i = 0;
        while (1)
        {
            printf("i = %d\n", i++);
        }
        return NULL;
    }
    
    int main(int argc, char *argv[])
    {
        struct sched_param param1;
        struct sched_param param2;
        pthread_attr_t attr1;
        pthread_attr_t attr2;
        pthread_t th1;
        pthread_t th2;
        int ret;
    
        ret = pthread_attr_init(&attr1);
        assert(ret == 0);
        ret = pthread_attr_init(&attr2);
        assert(ret == 0);
    
        ret = pthread_attr_setschedpolicy(&attr1, SCHED_FIFO);
        assert(ret == 0);
        ret = pthread_attr_setschedpolicy(&attr2, SCHED_FIFO);
        assert(ret == 0);
    
        param1.sched_priority = 9;
        ret = pthread_attr_setschedparam(&attr1, &param1);
        assert(ret == 0);
        param2.sched_priority = 10;
        ret = pthread_attr_setschedparam(&attr2, &param2);
        assert(ret == 0);
    
        ret = pthread_create(&th1, &attr1, prio_low, NULL);
        assert(ret == 0);
        ret = pthread_create(&th2, &attr2, prio_high, NULL);
        assert(ret == 0);
    
        while (1)
            sleep(1);
    
        return 0;
    }
    
[/code]


CC BY-SA 4.0
2018-06-23T14:19:35.457
45021
2018-06-26T13:35:13.310
2018-06-24T06:03:07.277
17238
17238
question
linux c
pthreadとSCHED_FIFOによる優先度スケジューリングについて


`pthread_attr_setinheritsched()` をする必要があります。

[code]

    #include <stdio.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <assert.h>
    
    
    static void* prio_high(void* arg)
    {
        volatile int i = 0;
        while (1)
            i++;
    
        return NULL;
    }
    
    static void* prio_low(void* arg)
    {
        unsigned int i = 0;
        while (1)
        {
            printf("i = %d\n", i++);
        }
        return NULL;
    }
    
    int main(int argc, char *argv[])
    {
        struct sched_param param1;
        struct sched_param param2;
        pthread_attr_t attr1;
        pthread_attr_t attr2;
        pthread_t th1;
        pthread_t th2;
        int ret;
    
        ret = pthread_attr_init(&attr1);
        assert(ret == 0);
        ret = pthread_attr_init(&attr2);
        assert(ret == 0);
    
        ret = pthread_attr_setschedpolicy(&attr1, SCHED_FIFO);
        assert(ret == 0);
        ret = pthread_attr_setschedpolicy(&attr2, SCHED_FIFO);
        assert(ret == 0);
    
        param1.sched_priority = 9;
        ret = pthread_attr_setschedparam(&attr1, &param1);
        assert(ret == 0);
        param2.sched_priority = 10;
        ret = pthread_attr_setschedparam(&attr2, &param2);
        assert(ret == 0);
    
        ret = pthread_attr_setinheritsched(&attr1, PTHREAD_EXPLICIT_SCHED);
        assert(ret == 0);
        ret = pthread_attr_setinheritsched(&attr2, PTHREAD_EXPLICIT_SCHED);
        assert(ret == 0);
    
        ret = pthread_create(&th1, &attr1, prio_low, NULL);
        assert(ret == 0);
        ret = pthread_create(&th2, &attr2, prio_high, NULL);
        assert(ret == 0);
    
        while (1)
            sleep(1);
    
        return 0;
    }
    
[/code]

これを root 権限で実行すると、`printf()` の出力は出ません(最初に少しだけ出ますが)。

ただし、prio_high が高い優先度で実行されるため、sshd も処理をすることができず、  
ctrl+c すら届かなくなります。

* * *

上記に加え、下記 sysctl の設定ではどうでしょうか?

<https://stackoverflow.com/questions/10287561/sched-fifo-thread-is-preempted-
by-sched-other-thread-in-linux>

[code]

    # sysctl -a | grep sched_rt
    kernel.sched_rt_period_us = 1000000
    kernel.sched_rt_runtime_us = 950000
    # sysctl kernel.sched_rt_runtime_us=1000000
    kernel.sched_rt_runtime_us = 1000000
    # sysctl -a | grep sched_rt
    kernel.sched_rt_period_us = 1000000
    kernel.sched_rt_runtime_us = 1000000
    # 
    
[/code]

(tanalab2 さんのコメントと異なり、1000000 にしてます)

ただし、sched_rt_period_us の時間中の sched_rt_runtime_us を realtime thread に使い、残りの時間を
SCHED_OTHER thread に使う、というものですので、変化ないかもしれませんが。


CC BY-SA 4.0
2018-06-24T11:39:21.917
45030
2018-06-24T13:46:43.623
2018-06-24T13:46:43.623
5288
5288
45021
answer

[code]

    ret = pthread_create(&th1, &attr1, prio_low, NULL);
    assert(ret == 0);
    
[/code]

で最初のpthread、th1が作られます。他にpthreadは無いので、最高優先度を持つpthreadとなりますから、prio_lowのルーチンが実行されます。

その後で

[code]

    ret = pthread_create(&th2, &attr2, prio_high, NULL);
    assert(ret == 0);
    
[/code]

とth1よりも高い優先度を持つpthread、th2が作られますが、それに先立って th1が実行されたという事象が取り消される訳ではありません。

＝＝  
th2のpthreadを作るほうが先、th1のpthreadを作るほうが後にすると、動作が変わるかと思われます。


CC BY-SA 4.0
2018-06-24T12:18:39.400
45032
2018-06-24T12:18:39.400
217
45021
answer


質問で挙げられているサンプルコードに`pthread_attr_setinheritsched()`は必要です。( @masm と同回答)  
これがないと、親スレッドのスケジュールポリシーを子スレッドが引き継ぎます。

[code]

    /* 記述例 */
    pthread_attr_setinheritsched(&attr1, PTHREAD_EXPLICIT_SCHED);
    pthread_attr_setinheritsched(&attr2, PTHREAD_EXPLICIT_SCHED);
    
[/code]

また、Linuxのpthread実装では、SHCED_FIFOを指定するにはroot権限が必要ですので、  
rootユーザでプログラムを実行する必要があります。

ただし、この状況でもVirtualBoxの仮想マシンのCPU構成がマルチCPU環境だと、  
優先度の低いスレッドにもCPUが割り当たるので、サンプルコードの2スレッドは両方動作します。

＃このことは仮想マシンだけでなく、物理マシンにも当てはまります。

マルチCPU構成環境で実行しているのであれば、CPU数を「1」にするか、`taskset`コマンドで  
使用するCPU数を「1」に制限した状態でサンプルプログラムを実行すれば、期待通りの  
動作(`printf()`の出力がされなくなる)になると予想します。

* * *

なお、サンプルプログラムのスケジュールポリシーがFIFOになっているかどうかは、

[code]

    /proc/<PID>/task/<TID>/sched
    
[/code]

疑似ファイルを確認するとわかると思います。(`<TID>`はLinuxのスレッドID)  
(`policy`行の値が「1」であればFIFOのはずです。(※コメントの記述(`priority`)は誤りで、`policy`が正しいです)


CC BY-SA 4.0
2018-06-26T13:35:13.310
45092
2018-06-26T13:35:13.310
20098
45021
answer