Zurück zur Startseite

Streaming-Datenverarbeitung in C: Architektur und Beispiele

Der Artikel beschreibt die Architektur der Streaming-Datenverarbeitung in C, einschließlich der Auswahl der Graph-Topologie, der Plugin-Implementierung und des Speichermanagements. Ein Beispiel-Pipeline für Video-Analyse wird bereitgestellt.

Streaming-Verarbeitung in C: Von der Theorie zur funktionsfähigen Pipeline
Advertisement 728x90

Streamdatenverarbeitung in C: Architektur und Implementierung

Streamverarbeitung erfordert minimale Latenz, vorhersehbare Ausführungszeiten und Flexibilität bei Konfigurationsänderungen. Im Gegensatz zur Batch-Verarbeitung, bei der Daten vor der Analyse akkumuliert werden und damit unvermeidbare Verzögerungen entstehen, reagieren Streaming-Systeme unmittelbar auf eingehende Eingaben. Das macht sie unverzichtbar für Videoanalysen, Netzwerkinspektion und andere Echtzeit-Anwendungen. C eignet sich ideal für diese Systeme, da es keinen Garbage Collection besitzt und vollständige Kontrolle über Speicher und Ausführungszeitpunkte bietet.

Architekturabstraktionen für Streamverarbeitung

Es ist praktisch, ein Streaming-System als Rechengraph zu modellieren, wobei Knoten Datenverarbeiter darstellen und Kanten die Datenflussrichtungen angeben. Jeder Knoten hat einen strikten Typ:

  • INPUT_NODE — Datenquelle (Netzwerk, Festplatte, Sensor);
  • PROCESSING_NODE — Transformer (Analyse, Filterung, Kodierung);
  • OUTPUT_NODE — Konsument (Speicherung, Übertragung, Protokollierung).

Die Graph-Topologie bestimmt das Gesamtverhalten des Systems. Die einfachste Form ist eine einfach verkettete Liste für lineare Pipelines. Komplexere Aufgaben erfordern Baumstrukturen (Verzweigung nach Datentyp) oder beliebige Graphen (zyklische Abhängigkeiten, Verbindungszustände).

Google AdInline article slot

Die richtige Topologie für die Aufgabe wählen

Die Auswahl der passenden Graphstruktur wirkt sich direkt auf Leistung und Skalierbarkeit aus:

  • Einfach verkettete Liste — ideal für homogene Eingabedaten und lineare Verarbeitung. Beispiel: JPEG-Dekodierung → Filterung → Speichern als BMP.
  • Baum — für Routing basierend auf Eingabetyp. Zum Beispiel das Leiten von H.264- oder HEVC-Videoströmen zu verschiedenen Dekodern.
  • Graph — erforderlich für zustandsbehaftete Operationen oder Feedback-Schleifen. TCP-Traffic-Verarbeitung benötigt Speicherung von Sitzungskontexten und erneutes Durchlaufen von Knoten.

Implementierung in C

Das System basiert auf dynamisch geladenen Modulen (.so unter Linux). Jedes Plugin exportiert eine node_t-Struktur, die sein Verhalten und seine Verbindungen definiert. Die Kerninfrastruktur befindet sich im Header infra.h:

typedef enum NODE_TYPE_C {
    INPUT_NODE,
    OUTPUT_NODE,
    PROCESSING_NODE
} NODE_TYPE_T;

typedef struct matrix_s {
  unsigned int width_;
  unsigned int height_;
  unsigned char *data_;
} matrix_t;

typedef struct data_s {
  matrix_t matrix_;
  char *metadata_;
} data_t;

typedef void (*init_function_t)(void);
typedef unsigned short (*processing_function_t)(data_t**, unsigned short);

typedef struct node_s {
  char *name_;
  NODE_TYPE_T type_;
  char *prev_;
  char *next_;
  init_function_t init;
  processing_function_t processing;
} node_t;

typedef node_t* (*get_node_structure)(void);

#define REGISTER_NODE(plugin_name, type, processing_function, init_function, prev, next) \
    static node_t node = {.name_ = plugin_name, .type_ = type, \
                          .init = init_function, .processing = processing_function, \
                          .prev_ = prev, .next_ = next}; \
    node_t* getnode_structure() { return &node; }

Jedes Plugin implementiert zwei Funktionen: init() für die Einrichtung von Ressourcen und processing() für die Kernlogik. Das Makro REGISTER_NODE erzeugt den Export automatisch.

Google AdInline article slot

Beispiel: Videoanalysen-Pipeline

Betrachten Sie ein synthetisches Szenario: Empfangen von JPEG-Frames über das Netzwerk, Tauschen der R- und G-Kanäle, dann Speichern als BMP und CSV.

Eingabeplugin (input_plugin.c) erfasst bis zu fünf Frames pro Iteration, dekodiert sie und leitet sie weiter:

static unsigned short processing(data_t **data, unsigned short count) {
  if(data == NULL) return 0;
  unsigned short i = 0;
  for (; i < 5; ++i) {
    unsigned char *jpeg_image = get_image();
    if (jpeg_image == NULL) break;
    decode_image(data[i], jpeg_image);
    free(jpeg_image);
  }
  return i;
}
REGISTER_NODE("input-node", INPUT_NODE, processing, init, NULL, "processing-node");

Verarbeitungsplugin (processing_plugin.c) tauscht Pixelkanäle:

Google AdInline article slot
static unsigned short processing(data_t **data, unsigned short count) {
  for (unsigned short i = 0; i < count; ++i) {
    unsigned int sz = data[i].width_ * data[i].height_;
    for (unsigned int j = 0; j < sz; j += 3) {
      unsigned char pix = data[i].data_[j];
      data[i].data_[j] = data[i].data_[j + 1];
      data[i].data_[j + 1] = pix;
    }
  }
  return count;
}
REGISTER_NODE("processing-node", PROCESSING_NODE, processing, init, "input-node", "output-node");

Ausgabeplugin (output_plugin.c) speichert Ergebnisse und gibt Speicher frei:

static unsigned short processing(data_t **data, unsigned short count) {
  for (unsigned short i = 0; i < count; ++i) {
    save_data(data[i]);
    free(data[i].data_);
    free(data[i].metadata_);
  }
  free(data);
  return count;
}
REGISTER_NODE("output-node", OUTPUT_NODE, processing, init, "processing-node", NULL);

Die Hauptschleife in main.c lädt Plugins, richtet die Ausführungsreihenfolge ein und führt die Verarbeitung in einer Endlosschleife durch, um Leerlaufzeiten zu minimieren.

Wichtige Erkenntnisse

  • Streaming-Systeme auf C-Basis vermeiden unvorhersehbare Pausen, die in GC-Sprachen üblich sind.
  • Wählen Sie die Graph-Topologie beim Design passend zum spezifischen Anwendungsfall.
  • Dynamisches Plugin-Laden ermöglicht Erweiterbarkeit ohne Neukompilierung des Kerns.
  • Jeder Knoten übernimmt genau eine Rolle: Eingabe, Verarbeitung oder Ausgabe.
  • Das Speichermanagement liegt vollständig in der Verantwortung des Entwicklers – es erfordert Disziplin, bietet aber höchste Präzision.

— Editorial Team

Advertisement 728x90

Weiterlesen