pthreadとSCHED_FIFOによる優先度スケジューリングについて


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]




質問で挙げられているサンプルコードに`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`が正しいです)




質問で挙げられているサンプルコードに`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`が正しいです)

