**文書の過去の版を表示しています。**
目次
【Ubuntu/Linux】CPUコアのシールド分離
リアルタイム性が要求されるネットワーク通信を含むプログラムなど、さまざまなイベントに応答する際にレイテンシーを最小限に抑える必要がある。 これを行う為に、割り込み (IRQ) とさまざまな CPU 上のユーザープロセスを相互に分離する手法について備忘録を残す。
尚、シールドモデルの考え方など下記の資料が参考になる。
プロセスの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 <progfile>
実行コアの確認
$ 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 バランシングから除外する。
- /etc/default/irqbalance
IRQBALANCE_BANNED_CPUS=0000000c
IRQBALANCE_BANNED_CPUS
の指定がなければ、前記のisolcpus=
の指定を参照する模様。
NICカードの IRQ への CPU アフィニティーの手動割り当て
/proc/interrupts
ファイルを参照して、各デバイスが使用している IRQ を確認する。
$ cat /proc/interrupts
該当する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スクリプトを実行して自動設定する。
- irq_shield.pl
#!/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 ( <IN> ) { 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