Ist es möglich, den Kopiervorgang einer Struktur durch einen Interrupt in Embedded C zu unterbrechen?

Innerhalb des Treibers habe ich eine Funktion, um die Daten aus der internen Struktur in eine Struktur aus der Anwendung zu kopieren.

Kann dieser Vorgang durch einen Mikrocontroller-Interrupt-Trigger unterbrochen werden?

uint16_t getRawData(struct Data *Data_external)
{
  if(Data_external == NULL)
  {
    return ERR_PARA;
  }
  else
  {
    *Data_external = Data_internal;            // the copy process. Could this be interrupted? 
  }
  return ERR_NONE;
}
Es gibt nicht wirklich einen "Kopierprozess", es gibt Anweisungen zum Kopieren von Bytes, und wenn sie nicht atomar sind, können sie unterbrochen werden, abhängig von Ihrer Architektur, Ihrem Compiler, Ihren Einstellungen und Ihrer Ausrichtung
Es gibt in C keine Garantie, dass ein Strukturkopiervorgang nicht unterbrochen werden kann. Der Interrupt soll jedoch normalerweise zu dem Punkt im Code zurückkehren, an dem der Kopiervorgang abgeschlossen werden kann. Es spielt also wahrscheinlich keine Rolle, es sei denn, der Interrupt-Code hängt aus irgendeinem Grund davon ab (was in den meisten Fällen unwahrscheinlich ist). Wenn diese Strukturen jedoch Teil eines oberen/unteren Interrupt-Treiberpaars sind, könnte dies ein Problem auf der oberen Ebene sein dabei ist, einen "Chunk" aus einem Puffer zu entfernen oder einem Puffer hinzuzufügen, wenn ein Interrupt stattfindet, der auch mit "Chunks" in demselben Puffer fertig werden muss.
@jonk: Bitte beantworten Sie die Frage nicht in Kommentaren, da dies den normalen Überprüfungsprozess für Antworten umgeht, wie in Meta besprochen
@DaveTweed Dann verlieren Sie und diese Seite meine Kommentare. Ich schreibe keine Antworten, es sei denn, ich habe die Zeit, ausführlichere zu schreiben. Es ist meine einzige Arbeitsweise und ich werde sie nicht ändern. Wenn ich das Gefühl habe, genug Zeit für eine "angemessene Antwort" nach meiner Definition zu haben, werde ich wie bisher fortfahren. Aber ich arbeite sehr hart an mehreren verschiedenen aktiven Projekten und habe NICHT die Zeit für meinen üblichen Antwortkontext und -inhalt. Also entweder schreibe ich kurze Kommentare als Kommentar, oder die Seite verliert jeglichen Zugriff auf meine Zeit. Ihr Anruf. Meine Zeit wird zu meinen Bedingungen gegeben oder gar nicht.
@jonk: Es ist nichts falsch daran, eine kurze Antwort zu schreiben, solange sie vollständig und in sich geschlossen ist, wie Ihr Kommentar. Ihr Engagement für lange Antworten liegt ganz bei Ihnen. Die Seite hat Regeln und Prozesse, und ich versuche nur, Sie in die richtige Richtung zu schubsen. Letztendlich liegt es ganz bei Ihnen - Sie hätten 9 positive Stimmen (+90 Wiederholungen) für Ihre Antwort haben können ...
@DaveTweed Danke für den Schubs. Punkte spielen meist keine Rolle. Ich denke, sobald ich bei den "reduzierten Anzeigen" von 200 Punkten angekommen bin, habe ich mich nicht um den Rest gekümmert (noch verwendet). Ich glaube auch nicht, dass ich die hohen Zahlen verdiene, die ich jetzt habe. Ich bin nur ein Bastler, um Himmels willen. Und ich mache mir große Sorgen, dass die Leute diese Reputationszahl fälschlicherweise so interpretieren könnten, dass sie ihnen mehr bedeutet, als sie sollte. Ich möchte lieber, dass meine Worte immer "verdächtig" und kritikwürdig gehalten werden, nicht als eine Art Evangelium. Wenn ich meine Wiederholungszahlen reduzieren könnte, würde ich das tun.
@jonk Wenn Sie wirklich möchten, dass die Leute Ihre Antworten "verdächtigen" können, dann lassen Sie die Leute bitte richtig darüber abstimmen. In dem (unwahrscheinlichen!) Fall, dass Sie etwas Falsches als Kommentar posten, können Benutzer nicht abstimmen. Dies kann besonders ein Problem sein, wenn Leute einen Kommentar positiv bewerten, der zunächst richtig erscheint (wiederum nicht, dass ich die Qualität Ihrer Kommentare/Antworten in Frage stelle :) )
@mbrig Sie können mich falsch verstehen. Ich entscheide mich dafür, nur Antworten als solche und mit meinem Namen zu schreiben , wenn sie den vollständigen Kontext und Inhalt enthalten, oder wenn ich denke, dass ich einen ungewöhnlichen oder einzigartigen Ansatz bieten könnte. Sie werden auch immer recherchiert, in dem Sinne, dass ich keine Antwort poste, bevor ich mich selbst überprüft habe, indem ich Referenzen und unterstützendes Material gefunden habe, das sowohl mit meiner Position übereinstimmt als auch das ich auf Anfrage zitieren kann. Ich schreibe nicht nur. Ich schreibe, recherchiere und kreuze und verifiziere. Dann posten. Sonst erscheint es nicht als Antwort von mir.
@mbrig Schließlich ist meine Wiederholungszahl nicht aussagekräftig. Was ich schreibe, kann sein. Aber das ist ein anderes Thema. Ich wünschte nur, ich könnte meine Wiederholungszahl ausblenden oder loswerden. Ich freue mich, dass einige Leute eine Antwort von mir (oder zwei) als hilfreich empfunden haben. Aber es gibt eine natürliche Tendenz für Leute, sich angesichts meiner Wiederholungszahl etwas anderes vorzustellen, und ich wünschte, sie würden es nicht tun. Das ist alles. Ihr Vorschlag geht nicht auf meine Bedenken ein.
@jonk: Sie könnten eine Kopfgeldfrage starten und jemanden mit 29.000 Ruf belohnen, um ihn loszuwerden;)
@Rev1.0 Ich könnte das tun, wenn es nicht auch erforderlich wäre, zu sehr darüber nachzudenken, ein Problem / eine Frage zu erfinden, die es wert ist, und vielleicht nicht auch das Problem, auf das ich hinweise, nicht zu beseitigen, sondern es einfach weiterzugeben an jemand anderen (was meine Bedenken nicht anspricht). Gute kreative Darstellung, aber. ;)
@jonk: Hehe, lass uns verrückt werden ;) 1. Erstellen Sie ein zweites Konto 2. Schreiben Sie eine tolle Frage + Kopfgeld mit dem ersten Konto 3. Beantworten Sie die Frage, indem Sie eine tolle Antwort mit dem zweiten Konto posten 4. Löschen Sie das zweite Konto -> Ergebnis: Sie haben gut geschrieben Frage, eine gute Antwort und du hast dabei sogar deinen Ruf verloren ... alles, was du jemals wolltest ;)
@Rev1.0 (1) Ich habe das Gefühl, dass dies gegen eine Interpretation der Richtlinie verstoßen und mich sperren würde. (2) Ich möchte immer noch, dass die Leute wissen, wer ich bin, und das Ändern von Konten erschwert oder verfehlt dieses Ziel ziemlich. (3) Jemand (auch wenn ich es bin) endet immer noch mit diesen Wiederholungspunkten. (4) Ich liebe kreatives Denken! Vielen Dank.

Antworten (7)

Ja. So ziemlich alles in einer MCU kann durch eine Interrupt-Anfrage unterbrochen werden. Wenn der Interrupt-Handler abgeschlossen ist, wird der vorherige Code einfach fortgesetzt, sodass dies normalerweise kein Problem darstellt.

In einem Sonderfall können die Interrupt-Handler selbst durch Interrupts höherer Priorität (verschachtelte Interrupts) unterbrochen werden.

Wenn eine Reihe von Anweisungen nicht unterbrochen werden darf, müssen Sie einen kritischen Abschnitt implementieren (grundsätzlich Interrupts global deaktivieren, den Job erledigen, wieder aktivieren).

Denken Sie daran, dass je nach Architektur der Ziel-CPU eine einzelne C-Zeile zu vielen Assembleranweisungen kompiliert werden kann. Eine einfache i++auf einem AVR wird zu mehreren Anweisungen kompiliert, wenn izum Beispiel uint32_t.

In Bezug auf Ihren letzten Absatz gehe ich davon aus, dass dies nur auf 16/8-Bit-Systemen zutrifft, oder? Ich weiß, du sagst AVR ... nur um sicherzugehen.
memcpy einer Struktur (wie in der Frage) sind auch mehrere Anweisungen auf 32-Bit-ARM. Die Inkrementierung von uint64_t auf 32-Bit-ARM ist ebenfalls nicht atomar. Bitweise Operationen sind auch Read-Modify-Write (es sei denn, es wird Bit-Banding verwendet).
@HarrySvensson nein - auf jeder RMW-Architektur und die meisten uCs sind Read - Modify - Write (die meisten RISC-Prozessoren sind RMW). Daher generiert i ++ in vielen Fällen (außer wenn der Wert nur im Register gehalten wird, was hier nicht der Fall ist) auch auf den modernen 32/64-Bit-ARM-uCs drei Anweisungen.
Ah, hier liegt ein kleines Missverständnis vor. Ich habe darüber nachgedacht, mich um den Übertrag zu kümmern, wenn Inkremente (Überlauf) ausgeführt werden, und Sie beide sprechen über das Abrufen aus dem Speicher und das Zurückschreiben. - Nun ja.
Generell gilt: Alle Operationen auf Typen, die breiter als die Datenbusbreite sind, sind "funky".

Jede Operation, die nicht atomar ist, kann durch einen Interrupt gestört werden. Diese Art der Programmierung unterscheidet sich oft stark von den meisten anderen Programmierungen und kann für Menschen verwirrend sein, die sich nicht mit Prozessordesign oder Computerarchitektur befasst haben.

Sie können sich denken: "Das wird eigentlich nie passieren, wie lange dauert das Kopieren dieses Codes und wie wahrscheinlich ist eine Unterbrechung?" Aber bei den meisten eingebetteten Produktionsanwendungen wird es passieren, weil das Produkt jahrelang ohne Updates läuft.

Das andere Problem bei Strukturkopien wie dieser ist, dass sie, wenn sie passieren, außerordentlich schwer zu debuggen sind, weil sie nur passieren, wenn der Interrupt genau zum richtigen Zeitpunkt auftritt (was so wenig wie ein Zyklus sein kann).

Der springende Punkt bei Interrupts ist, dass sie die ganze Zeit auftreten können (und dies auch tun) und so konzipiert sind, dass sie keinen Einfluss auf jeden Code haben, der gerade ausgeführt wird, wenn sie auftreten. Alle Register werden gespeichert, und je nach CPU-Architektur kann ein völlig anderer Registersatz ausgetauscht werden, der Interrupt tut seine Sache, und dann werden die ursprünglichen Register wiederhergestellt und der Code läuft normal weiter.

Probleme können auftreten, wenn die Interrupt-Serviceroutine selbst versucht, auf Speicher zuzugreifen, auf den der laufende, unterbrochene Code zugreift. Noch subtilere Fehler können auftreten, wenn ein zeitkritischer I/O-Prozess unterbrochen wird. Diese Probleme sind bei älteren, einfacheren und weniger sicheren Architekturen üblich, bei denen möglicherweise nur eine geringe Trennung zwischen "Benutzer"- und "Supervisor/Kernel"-Moduscode besteht.

Diese Art von Problem kann schwer zu identifizieren und oft schwer zu reproduzieren sein, aber wenn sie einmal identifiziert sind, sind sie oft ziemlich trivial zu beheben, indem sie defensive Programmierung, Mutexe/Semaphore oder einfach durch Deaktivieren von Interrupts in kritischen Codeabschnitten verwenden.

Die allgemeine Problemklasse wurde ausführlich untersucht, und moderne Mehrkern-CPUs und sogar Multitasking-Betriebssysteme wären nicht möglich, wenn nicht bereits mehrere Lösungen erprobt und getestet worden wären.

Ich gehe einfach davon aus, dass Sie dies aus einem sehr guten Grund gefragt haben.

*Data_external = Data_internal;

Kann geteilt werden (mit Ausnahme einiger Grenzfälle, die hier wahrscheinlich nicht im Spiel sind).

Ich kenne Ihre CPU nicht, aber ich habe noch keine CPU gesehen, die das moralische Äquivalent von:

cli(); /* mask all interrupts */
*Data_external = Data_internal;
sti(); /* restore interrupt mask */

Jetzt kann es nicht auf eine einzelne Kern-CPU aufgeteilt werden, da nichts unterbrechen kann, während Interrupts ausgeschaltet sind. Ob das eine gute Idee ist oder nicht, hängt von vielen Dingen ab, die ich einfach nicht beurteilen kann.

Wenn Sie Multi-Core sind (und ich habe mich endlich daran erinnert, dass es eine eingebettete Multi-Core-CPU auf dem Markt gibt), tun Sie dies nicht. Es ist wertlos. Sie müssten die richtige Verriegelung entwickeln.

Was sind CLI und STI? Von Google scheinen sie x86-Anweisungen zu sein?
@Sam: Nun ja, das sind die Namen der Anweisungen in x86. Ich denke, sie haben im PDP-11 die gleichen Namen, weil einige Codes, die dort entstanden sind, die gleichen Funktionsnamen für ein logisches Äquivalent im Benutzermodus hatten (Interprozesssignale maskieren). Hier ist eine Anleitung für Arduino: tldp.org/LDP/tlk/dd/interrupts.html
Normalerweise würden Sie Interrupts deaktivieren, aber nur dann wieder aktivieren, wenn sie tatsächlich aktiviert waren. Andernfalls hat Ihr Code den Nebeneffekt, Interrupts immer zu aktivieren, selbst wenn sie deaktiviert wurden, bevor der Code aufgerufen wurde.

Der Code, wie Sie ihn präsentiert haben, kann tatsächlich unterbrochen werden. Bevor Sie jedoch anfangen, überall kritische Abschnitte zu erstellen, sollten Sie ein paar Dinge überprüfen:

  • Sie sagen, diese Funktion befindet sich "in einem Treiber". Sind Interrupts bereits deaktiviert, wenn diese Funktion aufgerufen wird? Oder wird es in einem Interrupt-Handler aufgerufen, der verhindert, dass andere Interrupts ausgelöst werden? Falls ja, kann der Betrieb tatsächlich nicht unterbrochen werden.

  • Wird Data_internaljemals innerhalb eines Interrupt-Handlers zugegriffen? Wenn nicht, gibt es keinen Schaden, selbst wenn der Betrieb unterbrochen werden kann.

[Nicht genug Repräsentant zum Kommentieren]

Ein weiteres Problem mit dieser Art von Strukturkopie ist, dass es sich um eine flache Kopie handelt . Möglicherweise benötigen Sie stattdessen eine tiefe Kopie.

Eine flache Kopie könnte möglicherweise, aber wahrscheinlich nicht atomar sein, abhängig von der Maschinenarchitektur. Eine tiefe Kopie ist mit ziemlicher Sicherheit auf keiner Architektur atomar.

Ein fairer Punkt; aber eine tiefe Kopie ist eher atomar, da sie fast immer durch einen Zeigeraustausch eingerichtet wird. Doch in diesem Fall, wenn es eine tiefe Kopie hätte sein sollen, wird sie definitiv nicht atomar sein.

Eine für die Verwendung in eingebetteten Systemen geeignete Qualitätsimplementierung dokumentiert, wie volatile-qualifizierte Lese- oder Schreibvorgänge verschiedener Typen ausreichend detailliert ausgeführt werden, um anzuzeigen, ob und wie sie durch Interrupts "geteilt" werden können, und auch, ob flüchtige Lese- und Schreibvorgänge sind oder nicht hinsichtlich nicht qualifizierter Lese- und Schreibvorgänge sequenziert. Während sich einige Implementierungen so verhalten können, als ob alle Lese- und Schreibvorgänge volatile-qualifiziert wären, wird allgemein erwartet, dass Implementierungen frei sind, Sequenzen von nicht qualifizierten Lese- und Schreibvorgängen auf jede Art und Weise zu verarbeiten, die am effizientesten wäre, wenn es keine dazwischenliegenden volatileZugriffe gibt.

Auf einem typischen 32-Bit-Mikrocontroller Lese- und Schreibvorgänge von volatile-qualifizierten Integer-Typen, die 32 Bit und kleiner sind; eine Zuweisung besteht aus einem atomaren Lesen, gefolgt von einem atomaren Schreiben. Wenn Sie sicherstellen möchten, dass eine 32-Bit-Struktur atomar kopiert wird, platzieren Sie sie in einer Vereinigung mit einem uint32_tund lesen oder schreiben Sie dieses Element, um die Struktur als Ganzes zu lesen oder zu schreiben. Qualitativ hochwertige Implementierungen, die so konfiguriert sind, dass sie für die Verwendung in eingebetteten Systemen geeignet sind, ermöglichen die Verwendung von Unions auf diese Weise, ohne Rücksicht darauf, ob der Standard Implementierungen verlangen würde, die nicht für eine solche Verwendung vorgesehen sind, dies ebenfalls zu tun. Beachten Sie, dass sich gcc und clang nicht zuverlässig als hochwertige Implementierungen verhalten, die für eingebettete Systeme geeignet sind, es sei denn, verschiedene Optimierungen sind deaktiviert.