Die Antwort ist die Verwendung von CPU-Sets. Das Dienstprogramm python cpuset macht es einfach, sie zu konfigurieren.
Grundkonzepte
3 CPU-Sätze
root
:in allen Konfigurationen vorhanden und enthält alle CPUs (ungeschirmt )system
:enthält CPUs, die für Systemaufgaben verwendet werden - diejenigen, die ausgeführt werden müssen, aber nicht "wichtig" sind (unshielded )user
:enthält CPUs, die für "wichtige" Aufgaben verwendet werden - diejenigen, die wir im "Echtzeit"-Modus ausführen möchten (abgeschirmt )
Die shield
Befehl verwaltet diese 3 CPU-Sets.
Während des Setups verschiebt es alle beweglichen Tasks in den ungeschirmten CPU-Satz (system
) und verschiebt beim Teardown alle beweglichen Tasks in den root
cpuset.Nach der Einrichtung können Sie mit dem Unterbefehl Aufgaben in das Schild verschieben (user
) cpuset und zusätzlich spezielle Tasks (Kernel-Threads) von root
zu verschieben bis system
(und damit aus dem user
CPU-Satz).
Befehle:
Zuerst erstellen wir einen Schild. Natürlich ist das Layout des Schildes maschinen-/aufgabenabhängig. Angenommen, wir haben einen Nicht-NUMA-Computer mit 4 Kernen:Wir möchten dem Schild 3 Kerne widmen , und lassen Sie 1 Kern für unwichtige Aufgaben übrig; Da es sich nicht um NUMA handelt, müssen wir keine Speicherknotenparameter angeben, und wir lassen die Kernel-Threads in root
laufen cpuset (also:über alle CPUs hinweg)
$ cset shield --cpu 1-3
Einige Kernel-Threads (solche, die nicht an bestimmte CPUs gebunden sind) können in den system
verschoben werden CPU-Satz. (Im Allgemeinen ist es keine gute Idee, Kernel-Threads zu verschieben, die an eine bestimmte CPU gebunden sind)
$ cset shield --kthread on
Lassen Sie uns nun auflisten, was im Shield läuft (user
) oder ungeschirmt (system
) CPU-Sätze:(-v
für verbose, das die Prozessnamen auflistet) (fügen Sie ein zweites -v
hinzu um mehr als 80 Zeichen anzuzeigen)
$ cset shield --shield -v
$ cset shield --unshield -v -v
Wenn wir den Schild stoppen wollen (Teardown)
$ cset shield --reset
Lassen Sie uns nun einen Prozess im Schild ausführen (Befehle nach '--'
werden an den auszuführenden Befehl übergeben, nicht an cset
)
$ cset shield --exec mycommand -- -arg1 -arg2
Wenn wir bereits einen laufenden Prozess haben, den wir in den Schild verschieben möchten (beachten Sie, dass wir mehrere Prozesse verschieben können, indem wir eine durch Kommas getrennte Liste oder Bereiche übergeben (jeder Prozess im Bereich wird verschoben, auch wenn es Lücken gibt))
$ cset shield --shield --pid 1234
$ cset shield --shield --pid 1234,1236
$ cset shield --shield --pid 1234,1237,1238-1240
Erweiterte Konzepte
cset set/proc
- Diese geben Ihnen eine feinere Kontrolle über CPU-Sets
Einstellen
CPU-Sets erstellen, anpassen, umbenennen, verschieben und zerstören
Befehle
Erstellen Sie einen CPU-Satz mit den CPUs 1-3, verwenden Sie den NUMA-Knoten 1 und nennen Sie ihn "my_cpuset1"
$ cset set --cpu=1-3 --mem=1 --set=my_cpuset1
Ändern Sie "my_cpuset1", um nur die CPUs 1 und 3 zu verwenden
$ cset set --cpu=1,3 --mem=1 --set=my_cpuset1
Einen CPU-Satz zerstören
$ cset set --destroy --set=my_cpuset1
Benennen Sie einen bestehenden CPU-Satz um
$ cset set --set=my_cpuset1 --newname=your_cpuset1
Erstellen Sie einen hierarchischen CPU-Satz
$ cset set --cpu=3 --mem=1 --set=my_cpuset1/my_subset1
Vorhandene CPU-Sätze auflisten (Tiefe von Level 1)
$ cset set --list
Vorhandenes CPU-Set und seine Kinder auflisten
$ cset set --list --set=my_cpuset1
Alle existierenden CPU-Sets auflisten
$ cset set --list --recurse
Proc
Threads und Prozesse verwalten
Befehle
Aufgaben auflisten, die in einem CPU-Satz laufen
$ cset proc --list --set=my_cpuset1 --verbose
Eine Aufgabe in einem CPU-Set ausführen
$ cset proc --set=my_cpuset1 --exec myApp -- --arg1 --arg2
Eine Aufgabe verschieben
$ cset proc --toset=my_cpuset1 --move --pid 1234
$ cset proc --toset=my_cpuset1 --move --pid 1234,1236
$ cset proc --toset=my_cpuset1 --move --pid 1238-1340
Eine Aufgabe und alle ihre Geschwister verschieben
$ cset proc --move --toset=my_cpuset1 --pid 1234 --threads
Verschieben Sie alle Aufgaben von einem CPU-Satz zu einem anderen
$ cset proc --move --fromset=my_cpuset1 --toset=system
Verschieben Sie unfixierte Kernel-Threads in einen CPU-Satz
$ cset proc --kthread --fromset=root --toset=system
Verschieben Sie Kernel-Threads (einschließlich derer, die an eine bestimmte CPU gepinnt sind) zwangsweise in einen CPU-Satz (Hinweis:Dies kann schwerwiegende Folgen für das System haben - stellen Sie sicher, dass Sie wissen, was Sie tun)
$ cset proc --kthread --fromset=root --toset=system --force
Hierarchiebeispiel
Wir können hierarchische CPU-Sätze verwenden, um priorisierte Gruppierungen zu erstellen
- Erstellen Sie einen
system
CPU-Set mit 1 CPU (0) - Erstellen Sie einen
prio_low
CPU-Set mit 1 CPU (1) - Erstellen Sie einen
prio_met
CPU-Set mit 2 CPUs (1-2) - Erstellen Sie einen
prio_high
CPU-Set mit 3 CPUs (1-3) - Erstellen Sie einen
prio_all
cpuset mit allen 4 CPUs (0-3) (beachten Sie, dass dies dasselbe wie root ist; es wird als gute Praxis angesehen, eine Trennung von root beizubehalten)
Um das Obige zu erreichen, erstellen Sie prio_all und dann eine Teilmenge prio_high unter prio_all usw.
$ cset set --cpu=0 --set=system
$ cset set --cpu=0-3 --set=prio_all
$ cset set --cpu=1-3 --set=/prio_all/prio_high
$ cset set --cpu=1-2 --set=/prio_all/prio_high/prio_med
$ cset set --cpu=1 --set=/prio_all/prio_high/prio_med/prio_low
Es gibt zwei andere Möglichkeiten, die ich mir vorstellen kann (wenn auch nicht so elegant wie cset, das von Redhat nicht so fantastisch unterstützt zu werden scheint):
1) Taskset alles einschließlich PID 1 - nett und einfach (aber angeblich - ich habe selbst noch nie Probleme gesehen - kann zu Ineffizienzen im Scheduler führen). Das folgende Skript (das als root ausgeführt werden muss) führt taskset auf allen laufenden Prozessen aus, einschließlich init (PID 1); Dadurch werden alle laufenden Prozesse an einen oder mehrere „Junk-Kerne“ geheftet, und indem auch init gepinnt wird, wird sichergestellt, dass alle zukünftigen Prozesse ebenfalls in der Liste der „Junk-Kerne“ gestartet werden:
#!/bin/bash
if [[ -z $1 ]]; then
printf "Usage: %s '<csv list of cores to set as junk in double quotes>'", $0
exit -1;
fi
for i in `ps -eLfad |awk '{ print $4 } '|grep -v PID | xargs echo `; do
taskset -pc $1 $i;
done
2) Verwenden Sie den Kernel-Parameter isolcpus (hier ist die Dokumentation von https://www.kernel.org/doc/Documentation/kernel-parameters.txt):
isolcpus= [KNL,SMP] Isolate CPUs from the general scheduler.
Format:
<cpu number>,...,<cpu number>
or
<cpu number>-<cpu number>
(must be a positive range in ascending order)
or a mixture
<cpu number>,...,<cpu number>-<cpu number>
This option can be used to specify one or more CPUs
to isolate from the general SMP balancing and scheduling
algorithms. You can move a process onto or off an
"isolated" CPU via the CPU affinity syscalls or cpuset.
<cpu number> begins at 0 and the maximum value is
"number of CPUs in system - 1".
This option is the preferred way to isolate CPUs. The
alternative -- manually setting the CPU mask of all
tasks in the system -- can cause problems and
suboptimal load balancer performance.
Ich habe diese beiden plus die cset-Mechanismen für mehrere Projekte verwendet (übrigens bitte verzeihen Sie die offensichtliche Eigenwerbung :-)), ich habe gerade ein Patent für ein Tool namens Pontus Vision ThreadManager angemeldet, das optimale Pinning-Strategien für alle entwickelt gegebene x86-Plattform für beliebige gegebene Software-Arbeitslasten; Nachdem ich es an einem Kundenstandort getestet hatte, erzielte ich wirklich gute Ergebnisse (270 % Reduzierung der Spitzenlatenzen), daher lohnt es sich, Pinning und CPU-Isolierung durchzuführen.