Heim  >  Artikel  >  Backend-Entwicklung  >  Sicherheit von Rostfäden: Ein Vergleich mit C.

Sicherheit von Rostfäden: Ein Vergleich mit C.

Susan Sarandon
Susan SarandonOriginal
2024-11-19 11:54:02769Durchsuche

In diesem POC (Proof of Concept) werden wir untersuchen, wie die Rust-Sprache Race Conditions behandelt, und sie mit C , eine weit verbreitete Sprache, aber mit weniger Sicherheitsgarantien für den Wettbewerb.

Sicherheit von Rostfäden: Ein Vergleich mit C

Threads-Sicherheit: Datenrennen von C nach Rust

Index

    1. Einführung
  • 2. Threads
  • 3. Implementierung in C
    • 3.1. Code ohne Schutz vor Rennbedingungen
    • 3.2. Behebung mit Mutex
  • 4. Implementierung in Rust
    • 4.1. Problem mit den Rennbedingungen
    • 4.2. Auflösung mit Mutex und Arc
    • 4.3. Mutex vs. RwLock
  • 5. Fazit
  • 6. Referenzen

1. Einführung

In der Informatik werden

Threads verwendet, um Softwareaufgaben in Teilaufgaben zu unterteilen, die gleichzeitig ausgeführt werden können. Durch die Verwendung von Threads gewinnen wir Verarbeitungszeit und nutzen die Ressourcen der Maschine besser aus, aber dieser Wettbewerb bringt Herausforderungen mit sich, wie z. B. Rennbedingungen, die zu schwerwiegenden Inkonsistenzen in den Daten führen können.


2. Threads

Threads sind Ausführungseinheiten, die es Ihnen ermöglichen, Aufgaben gleichzeitig zu bearbeiten. Wir können uns Threads als unabhängige Ausführungsflüsse innerhalb eines Programms vorstellen, wie im Bild unten dargestellt:

Rust Threads safety: Uma comparação com C.

Threads bringen zwar Leistungsvorteile, bergen aber auch Risiken, insbesondere beim Zugriff auf gemeinsam genutzte Ressourcen.

Darüber hinaus können Threads zur Implementierung von Parallelität verwendet werden, bei der mehrere Aufgaben gleichzeitig auf verschiedenen CPU-Kernen ausgeführt werden. Dadurch kann das Programm die verfügbare Hardware besser nutzen und die Ausführung unabhängiger Aufgaben beschleunigen.


3. Implementierung in C

Lassen Sie uns ein einfaches System in

C erstellen:

    Ein Anfangssaldo von 1000.
  1. Eine Reihe von Transaktionen, bei denen es sich um Gutschriften oder Belastungen handeln kann.
  2. Parallele Verarbeitung dieser Transaktionen mithilfe von Threads.
3.1. Code ohne Schutz vor Rennbedingungen

int saldo = 1000; 

void creditar(int valor) {
    int tmp_saldo = saldo;

    sleep(1); // Delay simulado

    saldo += tmp_saldo + valor;
}

void debitar(int valor) {
    int temp = saldo;

    sleep(1); // Delay simulado

    if (temp >= valor) {
        saldo = temp - valor;
    }
}

void* processar_transacao(void* arg) {
    int valor = *(int*)arg;

    if (valor > 0) {
        creditar(valor);
    } else {
        debitar(abs(valor));
    }

    return NULL;
}

int main() {
    int transactions[] = {100, -50, 200, -150, 300, -200, 150, -100, 50, -50};
    int num_transactions = sizeof(transactions) / sizeof(transactions[0]);

    pthread_t threads[num_transactions];

    for (int i = 0; i < num_transactions; i++) {
        pthread_create(&threads[i], NULL, processar_transacao, &transactions[i]); // Cria uma thread para cada transação
    }

    for (int i = 0; i < num_transactions; i++) {
        pthread_join(threads[i], NULL); // Aguarda todas as threads terminarem
    }

    printf("Saldo final da conta: %d\n", saldo);
    return 0;
}
Wenn wir uns für eine Umgebung mit

Multithreading-Verarbeitung entscheiden, können sogenannte Race Conditions auftreten. Wenn zwei Threads auf denselben Wert zugreifen und ihn ändern, liegt eine Race Condition vor. Dieses Problem tritt auf, weil die Synchronisierung des Werts, auf den in jedem Thread zugegriffen wird, aufgrund der Konkurrenz zwischen Aufrufen nicht garantiert ist.

Wenn dieser Code mehrmals ausgeführt wird, variiert der endgültige Saldo, da Threads gleichzeitig auf den Saldo zugreifen und ihn ändern.

Rust Threads safety: Uma comparação com C.


3.2. Behebung mit Mutex

int saldo = 1000; 

void creditar(int valor) {
    int tmp_saldo = saldo;

    sleep(1); // Delay simulado

    saldo += tmp_saldo + valor;
}

void debitar(int valor) {
    int temp = saldo;

    sleep(1); // Delay simulado

    if (temp >= valor) {
        saldo = temp - valor;
    }
}

void* processar_transacao(void* arg) {
    int valor = *(int*)arg;

    if (valor > 0) {
        creditar(valor);
    } else {
        debitar(abs(valor));
    }

    return NULL;
}

int main() {
    int transactions[] = {100, -50, 200, -150, 300, -200, 150, -100, 50, -50};
    int num_transactions = sizeof(transactions) / sizeof(transactions[0]);

    pthread_t threads[num_transactions];

    for (int i = 0; i < num_transactions; i++) {
        pthread_create(&threads[i], NULL, processar_transacao, &transactions[i]); // Cria uma thread para cada transação
    }

    for (int i = 0; i < num_transactions; i++) {
        pthread_join(threads[i], NULL); // Aguarda todas as threads terminarem
    }

    printf("Saldo final da conta: %d\n", saldo);
    return 0;
}

Mutex ist ein Synchronisationsprimitiv, das sicherstellt, dass jeweils nur ein Thread Zugriff auf eine gemeinsam genutzte Ressource hat. Das Akronym mutex kommt vom englischen Begriff mutual exclusion, was „gegenseitiger Ausschluss“ bedeutet.

Wenn ein Thread einen Mutex erwirbt, wird jeder andere Thread, der versucht, denselben Mutex zu erwerben, angehalten, bis der erste Thread den Mutex freigibt. Dadurch wird verhindert, dass zwei oder mehr Prozesse (Threads) gleichzeitig auf die gemeinsam genutzte Ressource zugreifen.

Rust Threads safety: Uma comparação com C.

4. Implementierung in Rust

int saldo = 1000; 
pthread_mutex_t saldo_mutex; // Mutex para proteger o saldo

void creditar(int valor) { 
    pthread_mutex_lock(&saldo_mutex); // Bloqueia o mutex
    int tmp_saldo = saldo;

    sleep(1); // Delay simulado

    saldo = tmp_saldo + valor;

    pthread_mutex_unlock(&saldo_mutex); // Libera o mutex
}

void debitar(int valor) {
    pthread_mutex_lock(&saldo_mutex); // Bloqueia o mutex
    int tmp_saldo = saldo;

    sleep(1); // Delay simulado

    if (tmp_saldo >= valor) {
        saldo = tmp_saldo - valor;
    }

    pthread_mutex_unlock(&saldo_mutex);  // Libera o mutex
}

Rust als eine Sprache zu betrachten, die im Datenrennen fehlt, ist nicht produktiv, aber wir können verstehen, wie Strukturen und ihr Compiler dazu beitragen, indem sie großartige Funktionen für Speicher- und Thread-Sicherheit bieten.

Rust behandelt Race-Bedingungen mit Garantien zur Kompilierungszeit und verwendet Funktionen wie Ownership, Borrowing und nebenläufigkeitssichere Strukturen:

  • Arc: Sicherer Austausch unveränderlicher Daten.
  • Mutex und RwLock: Zugriffskontrolle für veränderbare Daten.

4.1. Problem mit den Rennbedingungen

Ohne die Verwendung von Arc- und Mutex-Strukturen

Rust’s rich type system and ownership model guarantee memory-safety and thread-safety — enabling you to eliminate many classes of bugs at compile-time.

Rust erlaubt keinen direkten Zugriff auf veränderliche Daten (Balance) von mehreren Threads ohne Schutz.
Der Compiler generiert einen Fehler, da der Ausgleich ohne einen sicheren Mechanismus auf mehrere Threads (handle1 und handle2) verschoben wird.
Die angezeigte Fehlermeldung lautet:

fn main() {
    let mut saldo = 1000; // saldo mutável, mas sem proteção

    let handle1 = thread::spawn(move || {
        saldo += 100;  // erro: `saldo` é movido para esta thread sem proteção
    });

    let handle2 = thread::spawn(move || {
        saldo -= 50;  // erro: `saldo` é movido para esta thread sem proteção
    });

    handle1.join().unwrap();
    handle2.join().unwrap();
}

4.2. Auflösung mit Mutex und Arc

Mithilfe von Mutex und Arc konnten wir unseren Code kompilieren und ausführen, wobei die Race Condition-Probleme behoben wurden.

error[E0382]: use of moved value: `saldo`

4.3. Mutex vs. RwLock

Mutex und RwLock werden verwendet, um Race-Bedingungen zu handhaben, jeweils mit spezifischen Vorteilen:

Mutex: Garantiert den exklusiven Zugriff auf eine Ressource für einen Thread und blockiert den Zugriff für andere, bis er freigegeben wird. Es ist einfach und effektiv, aber selbst Lesevorgänge blockieren die Ressource, was sie in leseintensiven Szenarien weniger effizient macht.

RwLock: Ermöglicht mehrere gleichzeitige Lesevorgänge mit .read() und schränkt das exklusive Schreiben mit .write() ein. Es ist Ideal für Szenarien mit überwiegend Lesevorgängen, da es die Leistung verbessert, indem es Parallelität bei Lesevorgängen ermöglicht.


5. Fazit

Der Vergleich zwischen C und Rust zeigt unterschiedliche Ansätze zur Lösung von Race Conditions. Während C Aufmerksamkeit erfordert, um Race-Condition-Fehler zu vermeiden, reduziert Rust diese Risiken zur Kompilierungszeit durch Tools wie Mutex, RwLock und Arc zusätzlich zum Besitzmodell. Dies macht den Code nicht nur sicherer, sondern reduziert auch die mentale Belastung des Programmierers, indem stille Fehler vermieden werden.

Zusammenfassend lässt sich sagen, dass Rust sich als ausgezeichnete Wahl für die Entwicklung konkurrierender Systeme positioniert und Sicherheit und Zuverlässigkeit bietet.


6. Referenzen

  • Repo mit Codes: https://github.com/z4nder/rust-data-races
  • https://en.wikipedia.org/wiki/Race_condition
  • https://blog.bughunt.com.br/o-que-sao-vulnerabilidades-race-condition/
  • https://medium.com/cwi-software/spring-boot-race-condition-e-ambiente-multi-thread-263b21e0042e
  • https://learn.microsoft.com/en-us/troubleshoot/developer/visualstudio/visual-basic/lingual-compilers/race-conditions-deadlocks
  • https://www.reddit.com/r/rust/comments/18faxjg/understanding_threadsafety_vs_race_conditions/?rdt=52263
  • https://doc.rust-lang.org/nomicon/races.html
  • https://news.ycombinator.com/item?id=23599598

Das obige ist der detaillierte Inhalt vonSicherheit von Rostfäden: Ein Vergleich mit C.. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn