【Ubuntu/Linux】CPUコアのシールド分離 ============================== リアルタイム性が要求されるネットワーク通信を含むプログラムなど、さまざまなイベントに応答する際にレイテンシーを最小限に抑える必要がある。 これを行う為に、割り込み (IRQ) とさまざまな CPU 上のユーザープロセスを相互に分離する手法について備忘録を残す。 尚、シールドモデルの考え方など下記の資料が参考になる。 {{https://www.concurrent-rt.co.jp/external/TechSup/PDF/RedHawkLinux_UsersGuide8.2_Jpn.pdf}} プロセスのCPUコア分離 ----------------- リアルタイム性が必要なユーザプログラム(プロセス)を実行するCPUコアを分離する。 ### コア除外の設定 `/etc/default/grub`ファイルに以下設定をしコア`#2,#3`をスケジュールから除外。 `GRUB_CMDLINE_LINUX_DEFAULT="isolcpus=2,3"` (連番のCPU番号指定は`2-3`と書いてもよい) $ sudo update-grub $ sudo reboot 上記設定は、カーネルの起動引数に `isolcpus=2,3` を渡すもの。尚、カーネル起動コマンドに設定されているかの確認は下記。 $ cat /proc/cmdline BOOT_IMAGE=/vmlinuz-5.15.0-52-generic root=/dev/mapper/ubuntu--vg-ubuntu--lv ro isolcpus=2,3 尚、Ubuntu 22.04 Serverでは、何故か?上記では固定出来なかったので、下記コマンドで行った。 $ sudo ps -e -o pid= | xargs -n 1 taskset -p 0xFFFFFFF3 ### コア固定の設定 実行するプログラムを、コア`#2,#3`に固定し実行。 $ sudo taskset -c 2-3 ### 実行コアの確認 $ ps PID TTY TIME CMD 1067 pts/0 00:00:00 bash 2466 pts/0 00:00:00 ps $ taskset -pc 1067 プロセス ID 1067 の現在の親和性リスト: 0,1 割り込みのCPUコア固定 ----------------- 上記でコア分離したプロセスが使用するNICカードの割り込みを、分離したコア側に割り当てる。 ### IRQ バランスからの CPU の除外 `/etc/default/irqbalance` ファイルを編集して、コア`#2,#3`のCPUコアをIRQ バランシングから除外する。 IRQBALANCE_BANNED_CPUS=0000000c `IRQBALANCE_BANNED_CPUS`の指定がなければ、前記の`isolcpus=`の指定を参照する模様。 ### NICカードの IRQ への CPU アフィニティーの手動割り当て `/proc/interrupts` ファイルを参照して、各デバイスが使用している IRQ を確認する。 $ cat /proc/interrupts CPU0 CPU1 CPU2 CPU3 0: 17 0 0 0 IR-IO-APIC 2-edge timer 6: 0 0 0 1 IR-IO-APIC 6-edge ttyS2 8: 0 0 1 0 IR-IO-APIC 8-edge rtc0 9: 0 5 0 0 IR-IO-APIC 9-fasteoi acpi ... 該当するIRQ番号専用の`smp_affinity`ファイルに割り当てるCPUコアのbitマスク値を書き込む。このファイルは、`/proc/irq/(IRQ番号)/` ディレクトリにある。 例えば、IRQ 番号 130 の割り込みを CPU `#2,#3` でのみ実行するよう指示するには下記。 $ sudo echo c > /proc/irq/130/smp_affinity 尚、設定されたかどうかを、`smap_affinity_list`ファイルを表示して確認する。 $ cat /proc/irq/130/smp_affinity_list ### スクリプトによる IRQ への CPU アフィニティーの自動割り当て `smp_affinity`の指定は、再起動した時には元に戻るので、下記のようなPerlスクリプトを実行して自動設定する。 #!/usr/bin/perl # # 指定NICインタフェースの割り込みをコア2,3に割り当てる。 # use strict; use warnings; my $infile = "/proc/interrupts"; my $nicname = "enp3s0"; # 検索するNICインタフェース名 my $mode = 0; # 動作モード if (@ARGV >= 1) { $nicname = $ARGV[0]; } if (@ARGV >= 2) { if ($ARGV[1] eq "set") { $mode = 1; } if ($ARGV[1] eq "debug") { $mode = 2; } } open(IN, "$infile") || die "can not open $infile\n"; while ( ) { if ( $_ =~ /\b$nicname/ ) { my @a = split(); $a[0] =~ s/:$//; # IRQ番号、先頭ワードの末尾':'文字を削除 if ($mode == 0) { print; } else { if ($mode == 1) { # set ? system("echo c > /proc/irq/$a[0]/smp_affinity"); # コア2,3 } print "echo c > /proc/irq/$a[0]/smp_affinity\n"; } } } close(IN); 上記スクリプトを管理者権限で実行する。 $ sudo ./irq_shield.pl enp1s0 set その他 ----- ### CPUに負荷をかける stressコマンドで、CPUのコア#2に演算負荷をかける。 $ taskset -c 2 stress -c 1 ### CPU負荷状況の確認 下記コマンドでCPUの負荷状況を表示。 $ dstat -c -C 0,1,2,3 -----cpu0-usage----------cpu1-usage----------cpu2-usage----------cpu3-usage---- usr sys idl wai stl:usr sys idl wai stl:usr sys idl wai stl:usr sys idl wai stl 5 4 91 0 0: 7 2 90 0 0: 17 0 83 0 0: 0 0 100 0 0 11 6 83 0 0: 9 0 91 0 0: 99 1 0 0 0: 1 1 98 0 0 11 4 85 0 0: 8 0 92 0 0: 98 2 0 0 0: 0 1 99 0 0 9 4 86 0 0: 5 0 95 0 0: 99 1 0 0 0: 1 1 98 0 0 9 7 84 0 0: 8 1 91 0 0: 99 1 0 0 0: 0 2 98 0 0 14 5 81 0 0: 7 0 93 0 0:100 0 0 0 0: 0 1 99 0 0 12 5 83 0 0: 8 0 92 0 0: 99 1 0 0 0: 0 1 99 0 0 9 5 86 0 0: 4 1 95 0 0: 99 1 0 0 0: 0 2 98 0 0 14 6 80 0 0: 5 0 95 0 0: 98 2 0 0 0: 2 0 98 0 0 10 5 85 0 0: 6 0 94 0 0:100 0 0 0 0: 0 1 99 0 0 10 7 83 0 0: 4 0 96 0 0:100 0 0 0 0: 0 0 100 0 0 ### CPU Affinityの一覧表示 全プロセスのCPU Affinityを一覧表示するPerlスクリプト。 #!/usr/bin/perl # # CPU affinityの一覧を表示する。 # use strict; use warnings; my @result; my $mode = 0; if (@ARGV >= 1) { if ($ARGV[0] eq "tree") { $mode = 1; } } if ($mode == 0) { @result = `ps aux`; } else { @result = `ps auxf`; } foreach my $line(@result) { my @a = split(/\s+/, $line); # 複数の空白や改行でで分割 if ($a[1] eq "PID") { print "AFFIN " . $line; } else { my $affi = `taskset -pc $a[1] 2>&1`; # エラー出力は標準へ if ($? == 0) { # コマンド実行のリターンコード my @b = split(/\s+/, $affi); # 複数の空白や改行で分割 my $str = sprintf("%-6s", $b[$#b]); # CPU affinity print $str . $line; } } } 参考 ---- 1. [[https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux_for_real_time/8/html/optimizing_rhel_8_for_real_time_for_low_latency_operation/assembly_binding-interrupts-and-processes_optimizing-rhel8-for-real-time-for-low-latency-operation#doc-wrapper|第12章 割り込みとユーザープロセスを分離してシステムレイテンシーを最小限に抑える]] 2. [[https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux_for_real_time/7/html/tuning_guide/interrupt_and_process_binding|2.4. 割り込みおよびプロセスバインディング]] 3. [[https://docs.oracle.com/cd/E26853_01/coh.371/b71688/tune_perftune.htm|6 パフォーマンス・チューニング]] 4. [[https://www.slideshare.net/syuu1228/interrupt-affinity|Interrupt Affinityについて]] 5. [[https://web.mit.edu/rhel-doc/4/RH-DOCS/rhel-rg-ja-4/s1-proc-directories.html|5.3. /proc/配下のディレクトリ]] 6. [[https://qiita.com/knakahara/items/825d2f052b07c0a2599e|irqbalance を NetBSD に移植してみる]] 7. [[https://rheb.hatenablog.com/entry/nfv_tuning|レッドハットが考えるNFV環境向けのチューニング]] 8. [[https://www.web-dev-qa-db-ja.com/ja/linux/isolcpusがアクティブ化されているかどうかを検出する方法/962607069/|isolcpusがアクティブ化されているかどうかを検出する方法]] 9. [[https://qiita.com/nakat-t/items/4542e84c4b72b78740e8|プロセス・スレッドを特定のCPUコアでのみ動作させる方法]] 10. [[https://www.rcannings.com/systemd-core-isolation/|SYSTEMD CORE ISOLATION]] 11. [[https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/6/html/performance_tuning_guide/sect-red_hat_enterprise_linux-performance_tuning_guide-tool_reference-irqbalance|3.4. irqbalance]] 12. [[https://qiita.com/gyaneman/items/f77c2633a5ac92f05302|プロセス・スレッドにCPUアフィニティを設定する方法]] 13. [[https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/managing_monitoring_and_updating_the_kernel/assembly_configuring-cpu-affinity-and-numa-policies-using-systemd_managing-monitoring-and-updating-the-kernel|第28章 systemd を使用した CPU のアフィニティーおよび NUMA ポリシーの設定]] 14. [[https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/performance_tuning_guide/sect-red_hat_enterprise_linux-performance_tuning_guide-tuning_scheduling_policy-isolating_cpus|6.3.6.2. CPU の分離]] 15. [[https://blog.amedama.jp/entry/stress-command|stress コマンドを使ってマシンに負荷をかける]] 16. {{https://www.concurrent-rt.co.jp/external/TechSup/PDF/RedHawkLinux_UsersGuide8.2_Jpn.pdf}}