In diesem Dokument wird beschrieben, wie Sie CPU-Bus-Sperren in Linux-Gastbetriebssystemen identifizieren und beheben. Es werden die Symptome von CPU-Bus-Sperren, die Diagnose dieser Probleme mithilfe von Kernel-Logmeldungen, die Suche nach dem fehlerhaften Code und die Anwendung von Maßnahmen oder Korrekturen beschrieben.
Übersicht
Eine CPU-Bus-Sperre tritt auf, wenn ein Prozessor ein Hardware-LOCK#-Signal senden muss, um exklusiven Zugriff auf den systemweiten Speicherbus zu erhalten. Dieses Problem tritt in der Regel in einem der folgenden Fälle auf:
- Eine atomare Anweisung wird für nicht ausgerichteten Speicher ausgeführt, der eine Cachezeilengrenze überschreitet (Split Lock).
- Eine atomare Anweisung wird für Speicher ausgeführt, der als
uncacheable(UC) gekennzeichnet ist, z. B.Memory-Mapped I/O(MMIO).
Da die CPU eine globale Bussperre durchsetzt, müssen alle anderen Prozessoren und Geräte im Gastbetriebssystem warten, bis der Speichervorgang abgeschlossen ist. Eine hohe Anzahl von Bus-Locks kann die CPU-Leistung erheblich beeinträchtigen.
Bei älteren Prozessoren wurden Bus-Sperren nicht erfasst. Moderne x86-Prozessoren wie Intel Sapphire Rapids und höher oder AMD Zen 5 und höher verfügen jedoch über eine Hardwarefunktion, die CPU-Bus-Sperren erkennt. Wenn eine Anweisung eine CPU-Bus-Sperre auslöst, gibt die CPU sofort nach Abschluss der Anweisung eine Debug-Ausnahme (#DB) aus.
Ab Linux-Kernelversion 5.13 auf Intel und 6.13 auf AMD fängt der Linux-Kernel diese #DB-Ausnahme ab und wendet eine Gegenmaßnahme an, in der Regel durch Ratenbegrenzung des fehlerhaften Prozesses. Indem der Thread absichtlich in den Ruhezustand versetzt wird, verhindert der Kernel, dass eine einzelne Anwendung den Speicherbus überlastet. So wird die Systemleistung für den Rest der Compute-Instanz aufrechterhalten, allerdings auf Kosten der Leistung der fehlerhaften Anwendung.
Symptome
Wenn ein Prozess in Ihrem Linux-Gastsystem CPU-Bus-Sperren auslöst, können die folgenden Symptome auftreten:
- Beeinträchtigte Anwendungsleistung: CPU-Bus-Sperren können zu unerwarteten Latenzen bei Anwendungen führen.
- Systemweite Lastspitzen: Die Reaktionsfähigkeit des Gesamtsystems kann abnehmen.
- Unerwartete Abstürze von Anwendungen: Wenn Sie den Kernel so konfigurieren, dass er Split- oder Bus-Locks streng behandelt (
split_lock_detect=fatal), kann die fehlerhafte Anwendung mit einemSIGBUS-Fehler abstürzen.
CPU-Bus-Sperren identifizieren
So stellen Sie fest, ob auf Ihrer Compute-Instanz CPU-Bus-Sperren auftreten:
- Wenn Sie das Logging der Ausgabe des seriellen Ports für Ihre Compute-Instanz aktiviert haben, sehen Sie sich die Ausgabe des seriellen Ports an, um nach einem CPU-Bus-Lock-Trace zu suchen.
- Sehen Sie sich die Betriebssystemlogs Ihrer Compute-Instanz (
/var/log/messages) an, um nach einem CPU-Bus-Lock-Trace zu suchen.
Beispiel für einen CPU-Bus-Lock-Trace
x86/split lock detection: #DB: <process_name>/<pid> took a bus_lock trap at
address: 0x<address>
So erkennen Sie zukünftige CPU-Bus-Sperren:
- Logging der Ausgabe des seriellen Ports aktivieren.
Logbasierte Benachrichtigungsrichtlinie erstellen für das folgende Log:
resource.type="gce_instance" log_id("serialconsole.googleapis.com/serial_port_1_output") textPayload=~"took a bus_lock trap"Dieser Logeintrag enthält den Prozessnamen (
<process_name>) und die Prozess-ID (<pid>), die für die CPU-Bus-Sperre verantwortlich sind, sowie die Adresse des Befehlszeigers, an der der Fehler aufgetreten ist.
Fehlerbehebung bei CPU-Bus-Sperren
Wenn Sie die fehlerhafte Anwendung entwickeln oder kompilieren, können Sie bestimmte C- oder C++-Compilerwarnungen verwenden, um Variablen und Strukturen zu identifizieren, die möglicherweise Split Locks verursachen.
Compilerwarnungen
Wenn Sie GCC oder Clang verwenden, kompilieren Sie Ihren Code mit den folgenden Flags, um Ausrichtungsprobleme zu erkennen:
-Wcast-alignoder-Wcast-align=strict: Diese Flags warnen Sie, wenn durch eine Pointer-Umwandlung die erforderliche Ausrichtung des Ziels erhöht wird. Das Casten eines generischenchar*-Puffers in einenuint64_t*und das Ausführen eines atomaren Vorgangs darauf ist eine klassische Ursache für Split Locks.-Waddress-of-packed-member: Dieses Flag warnt Sie, wenn Sie die Adresse eines gepackten Strukturmembers abrufen (z. B. mit#pragma pack(1)oder__attribute__((packed))). Da bei gepackten Strukturen die natürliche Speicherausrichtung ignoriert wird, überschreitet jede atomare Operation für ein Member einer gepackten Struktur mit hoher Wahrscheinlichkeit eine 64‑Byte-Cachezeilengrenze.
Nicht cachefähige (UC) Arbeitsspeicher-Locks erkennen
Wenn atomare Operationen auf nicht im Cache speicherbaren Speicher den CPU-Bus sperren, werden Compilerwarnungen nicht ausgegeben. Dieses Problem tritt normalerweise auf, wenn Sie mit dem Gerätespeicher interagieren:
- Speicherzuordnungen prüfen: Suchen Sie in Ihrem Code nach Verwendungen von
mmapmit Flags wieO_SYNCoder direktem Zugriff auf/dev/memoder/dev/uio. - Vermeiden Sie atomare Operationen auf MMIO: Verwenden Sie keine atomaren Operationen wie
__sync_fetch_and_addoderstd::atomicfür Speicherbereiche, die Geräte Registern oder nicht cachefähigen Speicherpuffern zugeordnet sind.
CPU-Bus-Sperren beheben
Probleme mit der CPU-Bus-Sperre können Sie beheben, indem Sie die Speicherausrichtung im Quellcode der Anwendung korrigieren.
- Vermeiden Sie die Verwendung von
#pragma packoder__attribute__((packed))für Strukturen, die atomare Variablen, Mutexe oder Spinlocks enthalten. - Verwenden Sie Standardausrichtungsanweisungen (z. B.
alignas(64)in C++11 oder__attribute__((aligned(64)))in C), um Variablen, die häufig in atomaren Operationen verwendet werden, an Cachezeilengrenzen auszurichten. - Achten Sie darauf, dass während der Kompilierung keine Warnungen im Zusammenhang mit der Ausrichtung angezeigt werden.
- Verwenden Sie nur standardmäßige Sperrmechanismen (Mutexes, Spinlocks) oder atomare Anweisungen für standardmäßigen, cachefähigen RAM, niemals für MMIO- oder UC-Speicher.
Wenn das Problem durch die Schritte zur Fehlerbehebung nicht behoben werden konnte, wenden Sie sich an Cloud Customer Care und geben Sie alle Informationen an, die Sie bei der Fehlerbehebung gesammelt haben.