Linuxのschedのpriorityとリアルタイムシステムについて

# はじめに
Linuxでいわゆるリアルタイムシステムを指向するときは[**sched(7)**](https://linuxjm.osdn.jp/html/LDP_man-pages/man7/sched.7.html)の**priority**値を意識することになるが、個人的にいつもpriorityの値を相対的にしか見られず、絶対的にかつどっちが上・下かについてわからなくなってしまうので、そんな自分のためにメモ書きしておくことにした・・・つもりが、なにか途中から趣旨を間違えた記事になってしまった。本当は「priority一覧表」がほしかっただけだったのに...

なお、Linux-4.10くらい、procps-ng-3.3.12くらい、util-linux-2.29.2くらいを見ています。

# schedのpolicyとpriority値
Linuxでは、ユーザプログラム・kernelスレッド問わず、タスクにsched policyを設定できる。下記の上3つがいわゆるリアルタイムスケジューリング、下3つがいわゆるタイムシェアスケジューリングになる。

- **SCHED_DEADLINE**, **SCHED_FIFO**, **SCHED_RR**
- **SCHED_OTHER**, **SCHED_BATCH**, **SCHED_IDLE**

**SCHED_DEADLINE**は自動的に最高優先度になるので置いておき、残りのリアルタイムスケジューリングの**SCHED_FIFO**, **SCHED_RR**はpriority値を設定できる。これがtask_structのrt_priorityとなり、必ずpriority値の順にスケジューリングされるようになる。

これに対し、タイムシェアスケジューリングはpriorityを設定できない。代わりにnice値を設定できる(**SCHED_IDLE**を除く)。

~~なおkernel内で**SCHED_OTHER**と**SCHED_NORMAL**を混同したコードが見られるため注意が必要。どちらも同じ意味。(というかkernel内は**SCHED_NORMAL**を積極的に使いたい？)~~ **※コメント補足より、SCHED_OTHERがユーザ空間(POSIX)、SCHED_NORMALがkernel、それぞれでの統一された表記で、実装としては両者は同じものを指す。**

# nice値
**SCHED_OTHER**, **SCHED_BATCH**では、nice値によりタイムシェアの割り当て時間がスケールする。寝ていた期間などによりボーナスやpreemptは働くものの、時間はおおむねnice値が**1違うと1.25倍**変わるようになる。参考サイト、
 - [CFSのnice値について : 革命の日々 その２](http://blog.gachapin-sensei.com/archives/618698.html)
 - [帰ってきたCon Kolivas、大論争を呼ぶの巻（3/3） － ＠IT](http://www.atmarkit.co.jp/flinux/rensai/watch2009/watch09c.html)

指数的に効くため、大きくnice値が異なると相対的にリアルタイムスケジューリングっぽくなるものの、preempt保証があるわけではないので、リアルタイムスケジューリングと異なりlatency問題は残る。**sched_latency_ns**使いチューニングできるものの、短くしすぎると今度はcontext swtichのオーバヘッドが問題になる。

### nice値のコード上の効き方
**1.25**倍の根拠は、[kernel/kernel/sched/core.c](http://lxr.free-electrons.com/source/kernel/sched/core.c?v=4.10#L8825)より、

```c:core.c
8825 /*
8826  * Nice levels are multiplicative, with a gentle 10% change for every
8827  * nice level changed. I.e. when a CPU-bound task goes from nice 0 to
8828  * nice 1, it will get ~10% less CPU time than another CPU-bound task
8829  * that remained on nice 0.
8830  *
8831  * The "10% effect" is relative and cumulative: from _any_ nice level,
8832  * if you go up 1 level, it's -10% CPU usage, if you go down 1 level
8833  * it's +10% CPU usage. (to achieve that we use a multiplier of 1.25.
8834  * If a task goes up by ~10% and another task goes down by ~10% then
8835  * the relative distance between them is ~25%.)
8836  */
8837 const int sched_prio_to_weight[40] = {
8838  /* -20 */     88761,     71755,     56483,     46273,     36291,
8839  /* -15 */     29154,     23254,     18705,     14949,     11916,
8840  /* -10 */      9548,      7620,      6100,      4904,      3906,
8841  /*  -5 */      3121,      2501,      1991,      1586,      1277,
8842  /*   0 */      1024,       820,       655,       526,       423,
8843  /*   5 */       335,       272,       215,       172,       137,
8844  /*  10 */       110,        87,        70,        56,        45,
8845  /*  15 */        36,        29,        23,        18,        15,
8846 };
8847 
8848 /*
8849  * Inverse (2^32/x) values of the sched_prio_to_weight[] array, precalculated.
8850  *
8851  * In cases where the weight does not change often, we can use the
8852  * precalculated inverse to speed up arithmetics by turning divisions
8853  * into multiplications:
8854  */
8855 const u32 sched_prio_to_wmult[40] = {
8856  /* -20 */     48388,     59856,     76040,     92818,    118348,
8857  /* -15 */    147320,    184698,    229616,    287308,    360437,
8858  /* -10 */    449829,    563644,    704093,    875809,   1099582,
8859  /*  -5 */   1376151,   1717300,   2157191,   2708050,   3363326,
8860  /*   0 */   4194304,   5237765,   6557202,   8165337,  10153587,
8861  /*   5 */  12820798,  15790321,  19976592,  24970740,  31350126,
8862  /*  10 */  39045157,  49367440,  61356676,  76695844,  95443717,
8863  /*  15 */ 119304647, 148102320, 186737708, 238609294, 286331153,
8864 };
```

計算してもらうとわかるとおり1.25倍になって...いないぞ...結構ぶれていた。下記は小数点3桁までで四捨五入している。

|nice値|sched_prio<br>to_weight|1.25倍?|sched_prio<br>to_wmult|1.25倍?|
|--:|--:|--:|--:|--:|
|-19|88761|1.237|48388|1.237|
|-18|71755|1.270|59856|1.270|
|-17|56483|1.221|76040|1.221|
|-16|46273|1.275|92818|1.275|
|-15|36291|1.245|118348|1.245|
|-14|29154|1.254|147320|1.254|
|-13|23254|1.243|184698|1.243|
|-12|18705|1.251|229616|1.251|
|-11|14949|1.255|287308|1.255|
|-10|11916|1.248|360437|1.248|
|-9|9548|1.253|449829|1.253|
|-8|7620|1.249|563644|1.249|
|-7|6100|1.244|704093|1.244|
|-6|4904|1.256|875809|1.256|
|-5|3906|1.252|1099582|1.252|
|-4|3121|1.248|1376151|1.248|
|-3|2501|1.256|1717300|1.256|
|-2|1991|1.255|2157191|1.255|
|-1|1586|1.242|2708050|1.242|
|0|1277|1.247|3363326|1.247|
|1|1024|1.249|4194304|1.249|
|2|820|1.252|5237765|1.252|
|3|655|1.245|6557202|1.245|
|4|526|1.243|8165337|1.243|
|5|423|1.263|10153587|1.263|
|6|335|1.232|12820798|1.232|
|7|272|1.265|15790321|1.265|
|8|215|1.250|19976592|1.250|
|9|172|1.255|24970740|1.255|
|10|137|1.245|31350126|1.245|
|11|110|1.264|39045157|1.264|
|12|87|1.243|49367440|1.243|
|13|70|1.250|61356676|1.250|
|14|56|1.244|76695844|1.244|
|15|45|1.250|95443717|1.250|
|16|36|1.241|119304647|1.241|
|17|29|1.261|148102320|1.261|
|18|23|1.278|186737708|1.278|
|19|18|1.200|238609294|1.200|
|20|15|hoge|286331153|hoge|

### nice値のその他細かい話
**SCHED_IDLE**は実装上はタイムシェアであるものの、割り当て時間を極端に短くして、**SCHED_BATCH**, **SCHED_OTHER**に負け続けるという実装のように読める。

autogroup(**/proc/sys/kernel/sched_autogroup_enabled**)やcgroupの機能により時間のスライスがタスクごとではなくてグループごとに行われるため、たくさんのタスクがわさわさ動くと実際の動きは予想しづらくなる。

nice値のことをドキュメントやsyscallの関数名で「priority」と表現していることがあるが、紛らわしいので、私はnice値に統一して呼ぶようにしている。

古い記事によっては「nice値がpriority値のベースとなりそこから動的に-5から+5される」という趣旨の記述が見られるが、コードを追う限り裏付けできなかった。Linux-2.6.23(9th October, 2007)からCFS(Completely Fair Scheduler)に置き換わったが、変わる前の**O(1)スケジューラ**のころの記述なのではないかと考えている。(**※コメント補足より、まさにこの通りとのこと。**) nice値はあくまでPOSIX仕様でどう動くかは実装次第であり、またLinuxでも古くからあるため適当に書かれた記事が多い。英語記事ではあるが[Linux Scheduler – CFS and Nice | oakbytes](https://oakbytes.wordpress.com/2012/06/06/linux-scheduler-cfs-and-nice/)が比較的まとまっているように見える。

# ioprio値との関係
schedのpriority値とioprio値は直接の関係はない。**IOPRIO_CLASS_NONEだとsched設定がioprioに派生**する。このパターンについては[先のCFQとクラスの記事](http://qiita.com/rarul/items/3d80436a9fea90445cdb#%E7%89%B9%E6%AE%8A%E3%81%AAioprio_class_none)にまとめている。

# schedのpolicyとclass
タスクに設定されたschedのpolicyは、コード上は**sched class**に対応づけられる。[kernel/kernel/sched/sched.h](http://lxr.free-electrons.com/source/kernel/sched/sched.h?v=4.10#L1306)より、

```c:sched.h
1306 extern const struct sched_class stop_sched_class;
1307 extern const struct sched_class dl_sched_class;
1308 extern const struct sched_class rt_sched_class;
1309 extern const struct sched_class fair_sched_class;
1310 extern const struct sched_class idle_sched_class;
```

### stop_sched_class
stop_sched_classは、**migration/%u**のkernelスレッドにのみ使われる(**sched_set_stop_task()**あたり)。policyで何を指定しても使うことはできない。ユーザランドから観測するとSCHED_FIFOのprilrity値99のように見えるが、classが違い、これが最優先に動く。このスレッドはcpu migrationをしていて、タスクをこれまでとは異なるCPUで動かす時の処理を行う。

### dl_sched_class
dl_sched_classは、**SCHED_DEADLINE**を指定したときに使われる。**stop_sched_class**の次の優先度で動く。

### rt_sched_class
rt_sched_classは、**SCHED_FIFO**, **SCHED_RR**を指定したときに使われる。**dl_sched_class**の次の優先度で動く。rt_sched_class同士の場合は設定したpriority値の順で動く。

### fair_sched_class
fair_sched_classは、**SCHED_OTHER**, **SCHED_BATCH**, **SCHED_IDLE**を指定したときに使われる。**SCHED_IDLE**だけは特殊で、先の通り常に不遇を受けて、fair_sched_classの中で最低の優先度で動く。それ以外はnice値の箇所で説明したとおり。

### idle_sched_class
idle_sched_classは、**swapper/%d**(idle_task)の時にのみ選ばれる。これは、何もすることがないときに割り当てられるタスク。このあたりに詳しい [〆(.. )ｶﾘｶﾘｯ!! Linuxのidleスレッドめも - φ(・・*)ゞ ｳｰﾝ　カーネルとか弄ったりのメモ](http://kernhack.hatenablog.com/entry/2013/11/14/172702)

なお、[linux/kernel/sched/idle_task.c](http://lxr.free-electrons.com/source/kernel/sched/idle_task.c?v=4.10)の先頭に注意書きがあるとおり、**SCHED_IDLE**のidleと**idle-task**のidleは別物である。

```
  3 /*
  4  * idle-task scheduling class.
  5  *
  6  * (NOTE: these are not related to SCHED_IDLE tasks which are
  7  *  handled in sched/fair.c)
  8  */
```

# priority一覧表
priority値はツールや使用する場面によって表現が大きく変わってしまう。基本はkernel内の**struct task_struct**のメンバ**prio**, **static_prio**, **normal_prio**(それぞれの意味要確認)に入る値を正と考えるべきだが、確認時は各種ツールを解することになるので、より注意が必要なところ。~~パクリもと~~この記事を書こうと思うきっかけになった[プロセスの優先度＠CetnOS 5.5 | Mazn.net](http://www.mazn.net/blog/2010/09/18/311.html)に感謝したい。

|kernel<br>prio|chrt|top<br>PRI|ps<br>rtprio|ps<br>pri|ps<br>priority|補足|
|:----:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
|-1| 0| rt|  0|140|-101|**SCHED_DEADLINE**と一部のタスク|
| 0|99| rt| 99|139|-100|**SCHED_FIFO**,**SCHED_RR**の最高|
| 1|98|-99| 98|138|-99|**↑**|
| 2|97|-98| 97|137|-98|**↑**|
|...|...|...|...|...|...|リアルタイムスケジューリング|
|97| 2| -3| 2 | 42| -3|**↓**|
|98| 1| -2| 1 | 41| -2|**SCHED_FIFO**,**SCHED_RR**の最低|
|100| 0|  0| - | 39|  0|**SCHED_OTHER**,**SCHED_BATCH**の最高|
|101| 0|  1| - | 38|  1|**↑**|
|102| 0|  2| - | 37|  2|**↑**|
|...|...|...|...|...|...|タイムシェアスケジューリング|
|119| 0| 19| - | 20| 19|**↑**|
|120| 0| 20| - | 19| 20|**SCHED_OTHER**デフォルト値|
|121| 0| 21| - | 18| 21|**↓**|
|...|...|...|...|...|...|タイムシェアスケジューリング|
|138| 0| 38| - |  1| 38|**↓**|
|139| 0| 39| - |  0| 39|**SCHED_OTHER**,**SCHED_BATCH**の最低|

なお、priorityとsched policyによりどのようにスケジューリングされるか(**CFS**)を追った、~~ムチャしやがった~~意欲的な記事がこちら。[Linux スケジューラーのコア実装とシステムコール - Qiita](http://qiita.com/nhiroki/items/2fa7bb048118145b00cd)

# sched programming interface
schedに絡むシステムコールはたくさんあるが、歴史的経緯からか、interfaceの一貫性が怪しい。下記に早見表をまとめる。実際に使うときはman等参照されたい。

|function ABI|nice|policy|prio|補足事項|
|:------------|:--:|:----:|:--:|:--|
|[int nice(int inc);](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/nice.2.html)|Y| | |現在からの相対値|
|[int getpriority(int which, int who);](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/getpriority.2.html)|Y| | |PRIO_PROCESS, PRIO_PGRP, PRIO_USER|
|[int setpriority(int which, int who, int prio);](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/getpriority.2.html)|Y| | |↑|
|[int sched_getparam(pid_t pid, struct sched_param *param);](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/sched_getparam.2.html)| | |Y|param.sched_priority|
|[int sched_setparam(pid_t pid, const struct sched_param *param);](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/sched_getparam.2.html)| | |Y|↑|
|[int sched_getscheduler(pid_t pid);](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/sched_getscheduler.2.html)| |Y| |priority値取れない|
|[int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param);](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/sched_getscheduler.2.html)| |Y|Y|param.sched_priority|
|[int sched_getattr(pid_t pid, const struct sched_attr *attr, unsigned int size, unsigned int flags);](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/sched_getattr.2.html)|Y|Y|Y|attr.sched_policy, attr.sched_nice, attr.sched_priority|
|[int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags);](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/sched_getattr.2.html)|Y|Y|Y|sizeof(attr), flagsは0固定,これでしかSCHED_DEADLINE設定できない|
|[int pthread_setschedprio(pthread_t thread, int prio);](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/pthread_setschedprio.3.html)| | |Y|pthread_getschedprioはない|
|[int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param);](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/pthread_getschedparam.3.html)| |Y|Y|param.sched_priority|
|[int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param);](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/pthread_getschedparam.3.html)| |Y|Y|↑|
|[int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/pthread_attr_getschedpolicy.3.html)| |Y| |attr.schedpolicyへ代入される|
|[int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/pthread_attr_getschedpolicy.3.html)| |Y| |↑|
|[int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/pthread_attr_getschedparam.3.html)| | |Y|param.sched_priorityへ代入される|
|[int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/pthread_attr_getschedparam.3.html)| | |Y|↑|

sched_getattr(2), sched_setattr(2)はglibcヘルパー関数がないので自分で[syscall(2)](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/syscall.2.html)しないといけない。

...テーブルの縦の結合ってどう書くの？...

# priority値の定義
### 値の定義
[kernel/include/linux/sched/prio.h](http://lxr.free-electrons.com/source/include/linux/sched/prio.h?v=4.10)にだいたいが定義されている。・・・というかもう全部引用する。

```c:prio.h
  4 #define MAX_NICE        19
  5 #define MIN_NICE        -20
  6 #define NICE_WIDTH      (MAX_NICE - MIN_NICE + 1)
  7 
  8 /*
  9  * Priority of a process goes from 0..MAX_PRIO-1, valid RT
 10  * priority is 0..MAX_RT_PRIO-1, and SCHED_NORMAL/SCHED_BATCH
 11  * tasks are in the range MAX_RT_PRIO..MAX_PRIO-1. Priority
 12  * values are inverted: lower p->prio value means higher priority.
 13  *
 14  * The MAX_USER_RT_PRIO value allows the actual maximum
 15  * RT priority to be separate from the value exported to
 16  * user-space.  This allows kernel threads to set their
 17  * priority to a value higher than any user task. Note:
 18  * MAX_RT_PRIO must not be smaller than MAX_USER_RT_PRIO.
 19  */
 20 
 21 #define MAX_USER_RT_PRIO        100
 22 #define MAX_RT_PRIO             MAX_USER_RT_PRIO
 23 
 24 #define MAX_PRIO                (MAX_RT_PRIO + NICE_WIDTH)
 25 #define DEFAULT_PRIO            (MAX_RT_PRIO + NICE_WIDTH / 2)
 26 
 27 /*
 28  * Convert user-nice values [ -20 ... 0 ... 19 ]
 29  * to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ],
 30  * and back.
 31  */
 32 #define NICE_TO_PRIO(nice)      ((nice) + DEFAULT_PRIO)
 33 #define PRIO_TO_NICE(prio)      ((prio) - DEFAULT_PRIO)
 34 
 35 /*
 36  * 'User priority' is the nice value converted to something we
 37  * can work with better when scaling various scheduler parameters,
 38  * it's a [ 0 ... 39 ] range.
 39  */
 40 #define USER_PRIO(p)            ((p)-MAX_RT_PRIO)
 41 #define TASK_USER_PRIO(p)       USER_PRIO((p)->static_prio)
 42 #define MAX_USER_PRIO           (USER_PRIO(MAX_PRIO))
 43 
 44 /*
 45  * Convert nice value [19,-20] to rlimit style value [1,40].
 46  */
 47 static inline long nice_to_rlimit(long nice)
 48 {
 49         return (MAX_NICE - nice + 1);
 50 }
 51 
 52 /*
 53  * Convert rlimit style value [1,40] to nice value [-20, 19].
 54  */
 55 static inline long rlimit_to_nice(long prio)
 56 {
 57         return (MAX_NICE - prio + 1);
 58 }
```

よって、**NICE_WIDTH**は40、**MAX_PRIO**は140、**DEFAULT_PRIO**は120 になる。

### 最大値の定義
[linux/kernel/sched/core.c:sched_get_priority_max()](http://lxr.free-electrons.com/source/kernel/sched/core.c?v=4.10#L5085)が最大値を与える。

```c:core.c
5085 /**
5086  * sys_sched_get_priority_max - return maximum RT priority.
5087  * @policy: scheduling class.
5088  *
5089  * Return: On success, this syscall returns the maximum
5090  * rt_priority that can be used by a given scheduling class.
5091  * On failure, a negative error code is returned.
5092  */
5093 SYSCALL_DEFINE1(sched_get_priority_max, int, policy)
5094 {
5095         int ret = -EINVAL;
5096 
5097         switch (policy) {
5098         case SCHED_FIFO:
5099         case SCHED_RR:
5100                 ret = MAX_USER_RT_PRIO-1;
5101                 break;
5102         case SCHED_DEADLINE:
5103         case SCHED_NORMAL:
5104         case SCHED_BATCH:
5105         case SCHED_IDLE:
5106                 ret = 0;
5107                 break;
5108         }
5109         return ret;
5110 }
```

**MAX_USER_RT_PRIO**は先に見たとおり100なので、最大値はrt_priorityでいうところの99となる。

### 最小値の定義
[linux/kernel/sched/core.c:sched_get_priority_min()](http://lxr.free-electrons.com/source/kernel/sched/core.c?v=4.10#L5112)が最小値を与える。

```c:core
5112 /**
5113  * sys_sched_get_priority_min - return minimum RT priority.
5114  * @policy: scheduling class.
5115  *
5116  * Return: On success, this syscall returns the minimum
5117  * rt_priority that can be used by a given scheduling class.
5118  * On failure, a negative error code is returned.
5119  */
5120 SYSCALL_DEFINE1(sched_get_priority_min, int, policy)
5121 {
5122         int ret = -EINVAL;
5123 
5124         switch (policy) {
5125         case SCHED_FIFO:
5126         case SCHED_RR:
5127                 ret = 1;
5128                 break;
5129         case SCHED_DEADLINE:
5130         case SCHED_NORMAL:
5131         case SCHED_BATCH:
5132         case SCHED_IDLE:
5133                 ret = 0;
5134         }
5135         return ret;
5136 }
```

より、最小値はrt_priorityでいうところの1となる。

### chrt
[util-linux/schedutils/chrt.c:245 show_sched_pid_info()](https://github.com/karelzak/util-linux/blob/v2.29.2/schedutils/chrt.c#L256)より、

```c:chrt.c
		if (sched_getparam(pid, &sp) != 0)
			err(EXIT_FAILURE, _("failed to get pid %d's attributes"), pid);
		else
			prio = sp.sched_priority;
```

なのでsched_getparam(2)を使っている。これは[linux/kernel/core.c:4584のsys_sched_getparam()](http://lxr.free-electrons.com/source/kernel/sched/core.c?v=4.10#L4584)より、

```c:core.c
4584         if (task_has_rt_policy(p))
4585                 lp.sched_priority = p->rt_priority;
```

で、**task_has_rt_policy(p)**(==SCHED_FIFO,SCHED_RR)の時は**rt_priority**が、そうでない場合は0が返る。

### top(PRI)
[procps-ng/top/top.c:5430のtask_show()のEU_PRI](https://gitlab.com/procps-ng/procps/blob/v3.3.12/top/top.c#L5430)より、

```c:top.c
         case EU_PRI:
            if (-99 > p->priority || 999 < p->priority) {
               cp = make_str("rt", W, Jn, AUTOX_NO);
            } else
               cp = make_num(p->priority, W, Jn, AUTOX_NO, 0);
            break;
```

**p->priority**は**libprocps**で取得される値で、[**/proc/[PID]/stat**](https://linuxjm.osdn.jp/html/LDP_man-pages/man5/proc.5.html)の「**(18) priority  %ld**」のこと。**-99**より小さいまたは999より大きい場合は**"rt"**を表示し、それ以外はそのまま表示している。

### ps(rtprio)
[procps-ng/ps/output.c:702のpr_rtprio()](https://gitlab.com/procps-ng/procps/blob/v3.3.12/ps/output.c#L702)より、

```c:output.c
// Based on "type", FreeBSD would do:
//    REALTIME  "real:%u", prio
//    NORMAL    "normal"
//    IDLE      "idle:%u", prio
//    default   "%u:%u", type, prio
// We just print the priority, and have other keywords for type.
static int pr_rtprio(char *restrict const outbuf, const proc_t *restrict const pp){
  if(pp->sched==0 || pp->sched==(unsigned long)-1) return snprintf(outbuf, COLWID, "-");
  return snprintf(outbuf, COLWID, "%ld", pp->rtprio);
}
```

**pp->rtprio**は**libprocps**で取得される値で、[**/proc/[PID]/stat**](https://linuxjm.osdn.jp/html/LDP_man-pages/man5/proc.5.html)の「**(40) rt_priority  %u**」のこと。同様に、**pp->sched**は「**(41) policy  %u**」のこと。

**pp->sched**が0とは**SCHED_NORMAL**(==**SCHED_OTHER**)のことなので、リアルタイム系policyじゃない時は**"-"**を、そうでないときはrt_priorityをそのまま表示している

### ps(pri)
[procps-ng/ps/output.c:655のpr_pri()](https://gitlab.com/procps-ng/procps/blob/v3.3.12/ps/output.c#L655)より、

```c:output.c
// not legal as UNIX "PRI"
// "pri"               (was 20..60, now    0..139)
static int pr_pri(char *restrict const outbuf, const proc_t *restrict const pp){         /* 20..60 */
    return snprintf(outbuf, COLWID, "%ld", 39 - pp->priority);
}
```

**p->priority**は**libprocps**で取得される値で、[**/proc/[PID]/stat**](https://linuxjm.osdn.jp/html/LDP_man-pages/man5/proc.5.html)の「**(18) priority  %ld**」のこと。39オフセットから反転させた値にしている。

### ps(priority)
[procps-ng/ps/output.c:624のpr_pri()](https://gitlab.com/procps-ng/procps/blob/v3.3.12/ps/output.c#L624)より、

```c:output.c
// legal as UNIX "PRI"
// "priority"         (was -20..20, now -100..39)
static int pr_priority(char *restrict const outbuf, const proc_t *restrict const pp){    /* -20..20 */
    return snprintf(outbuf, COLWID, "%ld", pp->priority);
}
```

**p->priority**は**libprocps**で取得される値で、[**/proc/[PID]/stat**](https://linuxjm.osdn.jp/html/LDP_man-pages/man5/proc.5.html)の「**(18) priority  %ld**」のこと。ここはそのまま表示している。

### /proc/[PID]/stat
[linux/fs/proc/array.c:389 do_task_stat()](http://lxr.free-electrons.com/source/fs/proc/array.c?v=4.10#L389)より、

```c:array.c
481         /* scale priority and nice values from timeslices to -20..20 */
482         /* to make it look like a "normal" Unix priority/nice value  */
483         priority = task_prio(task);
(----------snip----------)
504         seq_put_decimal_ll(m, " ", priority);
(----------snip----------)
542         seq_put_decimal_ull(m, " ", task->rt_priority);
```

task_prio()は[linux/kernel/sched/core.c:3838](http://lxr.free-electrons.com/source/kernel/sched/core.c?v=4.10#L3838)で、

```c:core.c
3838 /**
3839  * task_prio - return the priority value of a given task.
3840  * @p: the task in question.
3841  *
3842  * Return: The priority value as seen by users in /proc.
3843  * RT tasks are offset by -200. Normal tasks are centered
3844  * around 0, value goes from -16 to +15.
3845  */
3846 int task_prio(const struct task_struct *p)
3847 {
3848         return p->prio - MAX_RT_PRIO;
3849 }
```

となっている。なのでまとめると、

 - (18) priority %ld は、**p->prio**から**MAX_RT_PRIO**(==100)を引いたもの
 - (40) rt_priority %u は、**task->rt_priority**そのまま

**p->prio**はおおむね、[linux/kernel/sched/core.c:871のnormal_prio()](http://lxr.free-electrons.com/source/kernel/sched/core.c?v=4.10#L871)ということでよい。

```c:core.c
871 /*
872  * __normal_prio - return the priority that is based on the static prio
873  */
874 static inline int __normal_prio(struct task_struct *p)
875 {
876         return p->static_prio;
877 }
878 
879 /*
880  * Calculate the expected normal priority: i.e. priority
881  * without taking RT-inheritance into account. Might be
882  * boosted by interactivity modifiers. Changes upon fork,
883  * setprio syscalls, and whenever the interactivity
884  * estimator recalculates.
885  */
886 static inline int normal_prio(struct task_struct *p)
887 {
888         int prio;
889 
890         if (task_has_dl_policy(p))
891                 prio = MAX_DL_PRIO-1;
892         else if (task_has_rt_policy(p))
893                 prio = MAX_RT_PRIO-1 - p->rt_priority;
894         else
895                 prio = __normal_prio(p);
896         return prio;
897 }
```

 - **task_has_dl_policy()**(==SCHED_DEADLINE)の時は**-1**(**MAX_DL_PRIO**は0(include/linux/sched/deadline.h))
 - **task_has_rt_policy()**(==SCHED_FIFO,SCHED_RR)の時は**0から98**(**MAX_RT_PRIO**は100)
 - それ以外は**p->static_prio**

# Real-Time Linux patch
LinuxをよりリアルタイムOSっぽく扱えるようにするためのパッチ群が[Real-Time Linux Wiki](https://rt.wiki.kernel.org/index.php/Main_Page)にて公開されている。きちんと中身を確認はできていないが、おおむね下記の点に変更が入っている。
 - 割り込みコンテキストを割り込みスレッド(irq/xxx)に変える。SCHED_FIFOのpriority==-50のスレッド
 - softirqをすべてksoftirqdで処理する
 - spin_lock_irqsave()などで割り込み禁止にしなくなる
 - spinlockをmutexにして優先度の高いタスクが割り込める機会を増やす (**コメント補足より追記**)

などのことをして、レイテンシが小さくなるようにしている。

# ポエム: Linuxにおけるリアルタイムシステムとは
Linuxで完全なリアルタイム性を保証しようと思うとものすごく難しい。保証しようと思ったとたん抜けがあるといけないわけで、行儀の悪いユーザプログラムが**SCHED_FIFO**になっているのはもってのほか、行儀の悪いドライバ1つ混ざるだけで保証なんてできなくなってしまう。SMPやVCPUで以前に比べればネックになりにくくはなったものの、L1/L2キャッシュ状態・分岐予測・CPUの動的周波数変更・HyperVisor介入、などのOSに見えにくいところの予測不能点も増えた。まとまりないもののずらずらと書き下してみる。

### sched priority
**SCHED_FIFO**のpriorityは使いこなしの基本。**CFS(Completely Fair Scheduler)**で多くが改善されたとはいえ、**SCHED_OTHER**(タイムシェア)のままではリアルタイムは厳しい。

なお[SCHED_FIFOにするだけではCPUを100%使うことができない](http://stackoverflow.com/questions/20722615/sched-fifo-process-with-priority-of-99-gets-preempted)問題があるので、**sched_rt_runtime_us**の確認も忘れないこと。

### mlock
せっかく**SCHED_FIFO**にしても、初回アクセスでページフォルト起こしたりスワップアウトしたりしていては話にならない。仮想アドレス割り当てだけでなく物理メモリもきちんと割り当てるために、[mlock(2)](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/mlock.2.html), [mlockall(2)](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/mlock.2.html)を使いリアルタイム処理中にアクセスするアドレスを固定しておく必要がある。なおL1,L2キャッシュミスヒットはそれでも起こるが、ページフォルト起こしてディスクから読むのに比べればオーダが異なるので、まだ許容範囲(？)となる。

### CPU binding
context switchや割り込みやL1キャッシュ乱れの影響を減らすため、リアルタイム性がほしいタスクとそうでないタスクとで割り当てるCPUを分ける方法がある。HyperVisorでさらにVCPUを分けておく方法もあるが、いずれにしても複数の(論理)CPUを使える前提でないと使えない方法である。幸いにもCPU bindingする方法はそこそこQiitaに書かれているので詳細はそちらを。例えば[プロセス・スレッドを特定のCPUコアでのみ動作させる方法](http://qiita.com/nakat-t/items/4542e84c4b72b78740e8)。システムコールは[sched_setaffinity(2)](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/sched_setaffinity.2.html)、コマンドラインからはtaskset(1)で。

### Interrupt
Interrupt(割り込み)はその名のごとく何かの処理中に別の処理が割り込むので、割り込みハンドラとそこに登録する関数は注意深く精査する必要がある。割り込み頻度や割り込み優先度・ハンドラで処理する絶対量は常に意識しなければならない。

最近は、割り込みとはいえ直接ハンドラを呼ぶのではなく、割り込みから特定タスクを起こすことだけ行い、実際の処理を特定のタスクに任せるという割り込みスレッドを使った実装が流行っている。割り込みスレッドへcontext switchするコストがかかるものの、割り込みスレッドよりもさらに優先度したいタスクがある場合に、priorityベースでタスクの順を考えるようにすることができる。

### 割り込み/プリエンプト禁止
割り込み禁止(spin_lock_irqsave()系)やプリエンプト禁止(preempt_disable()系)を長い期間行うとレイテンシに響く。最低限のリソースへのアクセスの期間にとどめるのがよい。

### L1/L2キャッシュ状態・分岐予測・CPUの動的周波数変更・HyperVisor
最近はソフト制御の外のハードのレベルで行われるので、ソフト(OS)レベルでは影響を測ることが難しくなっている。結果、昔ながらの命令数を数えて処理時間を見積もるのが現実的でなくなってしまっている。L2キャッシュのway数をvcpuで割り振ったり、vcpuでなくて物理CPUレベルでHyperVisorがVMに割り当てたり、といったパーティショニングをするくらいしか今のところはないのかなぁ。


# あとがき
priorityの確認方法を書くだけのつもりだったのに、なにかリアルタイムシステム入門っぽくなってしまった。HPCも含め世の中は一般的にレイテンシよりもスループットを重視するため、意外とリアルタイム処理の視点で書かれている記事は多くない。nice値とpriority値を混同したようなレベルのものも未だに多い。

逆に、Linuxなんかではリアルタイム保証はムリという主張も多い(まぁ間違ってはいないけど)。「保証」のレベルにもよるんだろうけど、0,1で決めるんではなくて「おおむね保証する」レベルで許されるのなら、Linuxを使ったリアルタイム指向の設計という選択肢もある。

ARM CPUのマルチコアも当たり前になってきたので、これからこういう話がもっと必要になってくる・・・のかな？

# 参考サイト
### Documentation
 - [sched(7)](https://linuxjm.osdn.jp/html/LDP_man-pages/man7/sched.7.html)
 - [kernel/Documentation/scheduler/sched-bwc.txt](http://lxr.free-electrons.com/source/Documentation/scheduler/sched-bwc.txt?v=4.10)
 - [kernel/Documentation/scheduler/sched-deadline.txt](http://lxr.free-electrons.com/source/Documentation/scheduler/sched-deadline.txt?v=4.10)
 - [kernel/Documentation/scheduler/sched-design-CFS.txt](http://lxr.free-electrons.com/source/Documentation/scheduler/sched-design-CFS.txt?v=4.10)
 - [kernel/Documentation/scheduler/sched-rt-group.txt](http://lxr.free-electrons.com/source/Documentation/scheduler/sched-rt-group.txt?v=4.10)

### sched関係
 - [Linux スケジューラーのコア実装とシステムコール - Qiita](http://qiita.com/nhiroki/items/2fa7bb048118145b00cd)
 - [スケジューラについての調査 - オペレーティングシステム授業発表](https://www.slideshare.net/makotoshimazu/ss-33071336)
 - [Overview of the Linux Scheduler Framework](http://retis.sssup.it/rts-like/cesati.pdf)
 - [Introduction to Linux Scheduler](http://rtcc.hanyang.ac.kr/sitedata/2016_Grad_OS/Lecture_15_Linux_Scheduler.pdf)
 - ...Androidもそうだけど、ここ10年くらいはkernel分野のソフトは韓国・台湾強い...

### priority関係
 - [プロセスの優先度＠CetnOS 5.5 | Mazn.net](http://www.mazn.net/blog/2010/09/18/311.html)
 - [〆(.. )ｶﾘｶﾘｯ!! Linuxのidleスレッドめも - φ(・・*)ゞ ｳｰﾝ　カーネルとか弄ったりのメモ](http://kernhack.hatenablog.com/entry/2013/11/14/172702)
 - [CFSのnice値について : 革命の日々 その２](http://blog.gachapin-sensei.com/archives/618698.html)
 - [帰ってきたCon Kolivas、大論争を呼ぶの巻（3/3） － ＠IT](http://www.atmarkit.co.jp/flinux/rensai/watch2009/watch09c.html)

### リアルタイムシステム
 - [Real-Time Linux Wiki](https://rt.wiki.kernel.org/index.php/Main_Page)
 - [Linuxにおけるリアルタイムタスクの扱い方、および、関連技術の動向 (PDF)](http://tree.celinuxforum.org/CelfPubWiki/JapanET2007?action=AttachFile&do=get&target=scheduler071114celfet.pdf)
 - [Intel Edisonをリアルタイム化する - Qiita](http://qiita.com/yoneken/items/2f6fd50bf9837cf50f9d)