はじめに
Linuxは多くのサーバやシステムの基盤として使われており、インフラエンジニアにとって欠かせない技術です。その中でも「プロセス管理」は、システムが安定して動作するために重要な知識のひとつですが、Linux OSがどのような方法でプロセスを管理しているのか、詳細に理解できていない方も多いのではないでしょうか。
本記事では、Linuxのプロセス管理におけるスケジューラの機能について初心者の方でも理解できるように、基本からわかりやすく解説します。ぜひ最後までお読みいただき、日々の業務に役立ててください。
スケジューラによる並列処理の仕組み
Linuxにおいて、1つの論理CPUは同時に1つのプロセスしか処理できません。複数のプロセスがあたかも同時に動作しているように見えるのは、CPUリソースの実行権限を短い時間単位で切り替えながら、複数のプロセスに順番に割り当てているためです。このようにCPU時間を複数のプロセスで分け合う仕組みはタイムシェアリングシステム(time-sharing system)と呼ばれます。ここでは、こうしたプロセス切り替えを担うスケジューラの基本機能について解説します。

※なお、複数のCPUコアを搭載するマルチコア環境では、実際に複数のプロセスが物理的に同時実行される(並列処理)ことも可能です。
早速ですが、複数プロセスがどのように処理されるのか実機で試してみましょう。「Linuxのしくみ―実験と図解で学ぶOS、仮想マシン、コンテナの基礎知識」で提供されているスクリプトを利用して、並列度別の経過時間とタスクの進捗度を示す図を作成しています。複数プロセスに対する処理が切り替えられながら進捗していることが確認できます。

実行順序の決定(スケジューリングポリシー)
実行可能状態のプロセスの中から次にCPUを割り当てるプロセスを選択する処理は、スケジューリングポリシーに従って行われます。各プロセスごとに異なるポリシーを設定できるため、極めて高いパフォーマンスが求められるシステムではチューニングを検討してください。Linuxでは大きく分けて通常ポリシーとリアルタイムポリシーが用意されています。リアルタイムポリシーは通常ポリシーよりも優先して実行されます。
通常ポリシー
• SCHED_OTHER(SCHED_NORMAL)
デフォルトのスケジューリングポリシーです。通常のユーザープロセスに使われる CFS(Completely Fair Scheduler) によって実装されています。
• SCHED_BATCH
バッチ処理向け処理に適しています。インタラクティブ性が不要な長時間実行されるプロセスに最適です。優先度は低めで、ユーザー入力を想定していないポリシーとなります。
• SCHED_IDLE
非常に優先度が低く、CPUが他にやることがないときだけ実行します。
リアルタイムポリシー
• SCHED_FIFO(First In, First Out)
リアルタイムスケジューリング 実行可能な中で一番優先度が高いプロセスが実行されます。自発的に終了するか、ブロックされるまで CPUを使い続けます。優先度は 1~99 のリアルタイム優先度で制御します。(数値が大きいほど高優先度)
• SCHED_RR(Round Robin)
SCHED_FIFOと似ていますが、タイムスライスを持ち、同じ優先度のプロセス間で順番に実行されます。タイムスライスが終了すると次のプロセスに切り替えを行います。
• SCHED_DEADLINE
リアルタイム性が非常に高い処理向けのポリシーとなります。例えば、メディア処理や制御系の処理に使われることが多いです。 EDF(Earliest Deadline First)というポリシーをベースとしており、設定されたデッドラインが一番近いタスクから実行していきます。
ここで、適用されているスケジューリングポリシーの確認方法および変更方法をご紹介します。まず、下記の通りloop処理のシェルを作成して実行します。
[root@appserver-dev ~]# cd /tmp/
[root@appserver-dev tmp]#
[root@appserver-dev tmp]# vi loop.sh
[root@appserver-dev tmp]# cat loop.sh
#!/bin/bash
while true; do
# ファイル作成
touch /tmp/testfile
# 任意でファイルに何か書き込む場合
echo "hello" > /tmp/testfile
# ファイル削除
rm -f /tmp/testfile
done
[root@appserver-dev tmp]#
[root@appserver-dev tmp]# chmod +x loop.sh
[root@appserver-dev tmp]#
[root@appserver-dev tmp]# ll loop.sh
-rwxr-xr-x 1 root root 206 Jul 5 14:48 loop.sh
[root@appserver-dev tmp]#
[root@appserver-dev tmp]# ./loop.sh別のTerminalを開いて、スケジューリングポリシーを確認してみましょう。下記の通り、デフォルトのSCHED_OTHERポリシーが適用されており、優先度設定は0になっていることが確認できます。
[root@appserver-dev ~]# ps aux |grep loop.sh |grep -v grep
root 1544 8.8 0.0 4732 3584 pts/0 R+ 14:48 0:03 /bin/bash ./loop.sh
[root@appserver-dev ~]# chrt -p 1544
pid 1544's current scheduling policy: SCHED_OTHER
pid 1544's current scheduling priority: 0
[root@appserver-dev ~]#続いて、スケジュールポリシーを指定してシェルを実行してみましょう。一例として、SCHED_FIFOポリシーで優先度50を指定してみます。
[root@appserver-dev tmp]# chrt -f 50 ./loop.sh下記の通り、指定したポリシーと優先度になっていることが確認できるかと思います。
[root@appserver-dev ~]# ps aux |grep loop.sh |grep -v grep
root 32414 9.4 0.0 4732 3584 pts/0 S+ 14:56 0:06 /bin/bash ./loop.sh
[root@appserver-dev ~]# chrt -p 32414
pid 32414's current scheduling policy: SCHED_FIFO
pid 32414's current scheduling priority: 50
[root@appserver-dev ~]#実行順序の決定(優先度変更)
タスクの優先度を変更することで実行順序を制御することができます。たとえば、SCHED_FIFOポリシーを指定するときに優先度50を指定していましたが、50より大きな値を設定することで優先度を上げることができます。
よくあるケースとしては、通常ポリシーに対してnice値という優先度のパラメータを設定することがあります。nice値は「-20」~「+19」が設定することができ、値が小さいほうが優先度が高くなります。ただし、通常ポリシーよりもリアルタイムポリシーの方が優先されるため、nice値を低い値に設定したとしても、通常ポリシーのタスクがリアルタイムポリシーのタスクよりも優先されることはないということはご留意ください
ここでは、実機をつかって動作を確認してみましょう。まず、デフォルトの優先度で先ほどのループ処理を実行してみます。
[root@appserver-dev tmp]# ./loop.shループ処理に指定されているnice値をtopコマンドで確認してみましょう。NIという項目がnice値となります。デフォルトの値である「0」が指定されています。
root@ubuntu20:~# top
top - 22:25:15 up 1:00, 2 users, load average: 0.22, 0.06, 0.02
Tasks: 127 total, 2 running, 124 sleeping, 0 stopped, 1 zombie
%Cpu(s): 23.3 us, 23.3 sy, 0.0 ni, 46.7 id, 0.0 wa, 0.0 hi, 6.7 si, 0.0 st
MiB Mem : 3919.9 total, 2723.9 free, 231.7 used, 964.3 buff/cache
MiB Swap: 2290.0 total, 2290.0 free, 0.0 used. 3458.6 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
2491 root 20 0 7024 3724 3292 R 26.7 0.1 0:03.38 loop.sh
続いて、nice値を指定してループ処理を実行します。下記の通りniceコマンドを付与したうえで実行して下さい。
# nice -n {nice値} {実行コマンド}
[root@git-server ~]# nice -n -5 ./loop.sh再度topコマンドを実行します。 NIの値が「-5」になっていることが確認できます。
root@ubuntu20:~# top
top - 22:25:50 up 1:00, 2 users, load average: 1.05, 0.29, 0.10
Tasks: 127 total, 2 running, 124 sleeping, 0 stopped, 1 zombie
%Cpu(s): 9.7 us, 32.6 sy, 0.0 ni, 52.1 id, 0.0 wa, 0.0 hi, 5.7 si, 0.0 st
MiB Mem : 3919.9 total, 2724.5 free, 231.2 used, 964.3 buff/cache
MiB Swap: 2290.0 total, 2290.0 free, 0.0 used. 3459.1 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
22015 root 15 -5 7024 3672 3240 R 40.9 0.1 0:02.59 loop.sh下記コマンドを実行することで、一度実行したタスクのnice値を変更することも可能です。
root@ubuntu20:~# renice -10 -p 22015
22015 (process ID) old priority -5, new priority -10
root@ubuntu20:~#再度topコマンドを実行します。 NIの値が「-10」になっていることが確認できます。
root@ubuntu20:~# top
top - 22:27:56 up 1:03, 2 users, load average: 1.51, 0.74, 0.29
Tasks: 126 total, 1 running, 125 sleeping, 0 stopped, 0 zombie
%Cpu(s): 21.9 us, 18.8 sy, 0.0 ni, 43.8 id, 0.0 wa, 0.0 hi, 15.6 si, 0.0 st
MiB Mem : 3919.9 total, 2724.1 free, 231.4 used, 964.5 buff/cache
MiB Swap: 2290.0 total, 2290.0 free, 0.0 used. 3459.0 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
22015 root 10 -10 7024 3672 3240 S 25.0 0.1 0:51.23 loop.shnice値を設定することで、どのようにタスクが処理されていくかを視覚的に理解しましょう。「Linuxのしくみ―実験と図解で学ぶOS、仮想マシン、コンテナの基礎知識」で提供されているスクリプトを利用して経過時間とタスクの進捗度を示す図を作成しています。負荷処理0はnice値「0」、負荷処理0はnice値「5」となります。nice値が低い負荷処理0が優先されていることがわかるかと思います。

さいごに
Linuxのプロセス管理におけるスケジューラについての説明は以上となります。本内容を理解していなくても普段の業務に支障はないかもしれませんが、いざトラブルが起きた時には非常に役立つ知識になります。本記事を通じて理解を深めていただけると幸いです。最後まで読んでいただき、ありがとうございました。
Linuxの処理概要については下記の記事でまとめてるので、興味ある方はこちらも参照ください。
インフラエンジニア入門|Linuxの歴史・機能概要・特徴をわかりやすく解説
参考文献



コメント