ProgrammierungSystementwickler

Erklären Sie, wie das System FFI (Foreign Function Interface) in Rust funktioniert. Welche Anforderungen und Fallstricke gibt es für den sicheren Aufruf von Funktionen aus C?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort

FFI in Rust ermöglicht es, Funktionen, die in externen Bibliotheken (z.B. C/C++) deklariert sind, aufzurufen und Rust-Funktionen nach außen zu exportieren. Dazu wird das Schlüsselwort extern verwendet. Anforderungen:

  • Alle FFI-Aufrufe müssen in eine Deklaration mit unsafe eingewickelt sein;
  • ABI (Application Binary Interface) muss übereinstimmen (üblicherweise "C");
  • Datentypen müssen kompatibel sein – es ist wichtig, primitive Datentypen mit festen Größen (i32, u64 usw.) zu verwenden;
  • Speicherverwaltung – sehr vorsichtig mit Besitz, Speicherlecks und doppelter Freigabe.

Beispiel für eine Wrapper:

extern "C" { fn abs(input: i32) -> i32; } fn main() { unsafe { println!("{}", abs(-5)); } }

Eine Funktion aus Rust für die Verwendung in C kann so exportiert werden:

#[no_mangle] pub extern "C" fn sum(a: i32, b: i32) -> i32 { a + b }

Fangfrage

Frage: Garantiert Rust, dass, wenn man den Aufruf einer C-Funktion in unsafe einwickelt, alles in Bezug auf Threadsicherheit und den Umgang mit UB korrekt funktioniert?

Antwort: Nein! unsafe ist ein Versprechen an den Compiler, dass Sie für die Korrektheit aller Sicherheitsanforderungen (Aliasing, Threadsicherheit, Speicherzugriff) selbst garantieren. Rust führt keine Prüfungen des Codes innerhalb von C durch. Beispielsweise kann ein Race Condition oder UB im Bibliothekscode zur "Zerschlagung" der Runtime einer Rust-Anwendung führen. Selbst wenn Rust-Code kompiliert wird, kann die tatsächliche Ausführung zu einem Absturz führen.


Geschichte

Bei einem Finanzdatenanbieter integrierte das Team eine C-Bibliothek über FFI, ohne die Größen von Typen zu überprüfen. Auf x86_64 wurde erwartet, dass long und i64 übereinstimmen, aber es stellte sich heraus, dass die Größen auf anderen Plattformen nicht übereinstimmten – falsches Lesen von Speicher führte zu Bugs in den Berechnungen.

Geschichte

Ein Entwickler erstellte einen Wrapper für die C-API und vergaß, die übergebenen Zeiger auf Puffer zu berücksichtigen. Da Rust den Speicher automatisch freigab, arbeitete die C-Funktion manchmal mit bereits freigegebenem Speicher, was zu Abstürzen führte.

Geschichte

In einem Client-Server-Produkt sprach das Rust-Modul mit einer C-Bibliothek, ohne die Multithread-Sicherheit zu berücksichtigen. Die Bibliothek war nicht threadsicher, und Rust-Programme sprachen gleichzeitig aus verschiedenen Threads darauf zu, was zu Abstürzen und Datenbeschädigung führte.