Newer posts are loading.
You are at the newest post.
Click here to check if anything new just came in.

May 08 2013

schlingel

May 31 2011

schlingel

April 26 2011

January 15 2010

schlingel

Creating Pipes in C

Tags: Coding C Linux
Reposted bymondkroetedziadek

December 09 2009

Errorhandling in C

Durch mein Studium habe ich auch die Pflicht hin und wieder etwas zu schreiben, was mich in einen Zwiespalt bringt: Denn zum einen bin ich mit Leib und Seele Programmierer und programmiere sehr gerne, auf der anderen Seite muss ich elendige Vorgaben von Leuten erfüllen, die sich mir entweder nicht erschließen oder die ich einfach nicht gut finde.

So ist das auch mit den Programmierrichtlinien die wir in der VL "Systemnahes Programmieren" haben. Was mich daran stört? Die Fehlerausgabe muss "*nix-like" sein und natürlich muss die Fehlerbehandlung auch ein sauberes Freigeben der allokierten Ressourcen garantieren.

In dieser Übung müssen wir 3 Programmierbeispiele lösen. Diese haben alle zwei Dinge gemein: Die Entwicklerplattform Linux und den Code zur Fehlerbehandlung.

Während ich die zweite Aufgabe löste, dachte ich mir, es ist eine Schande immer und immer wieder den selben Code zu kopieren und stumpfsinnig einzufügen. Warum also nicht ein eigenes Modul dafür schreiben, was ich einfach wieder verwenden kann? Gedacht getan.

Also ging ich an die Planung mit der Frage was ich brauche. Dabei kam ich auf folgende Punkte:

  • Normierte Fehlerausgabe. Dies beinhaltet einen Check ob eine Error-Nummer gesetzt wurde und gegebenem Falls die Ausgabe der jeweiligen Systemmeldung. Dazu gehört natürlich auch der Programname und die "persönliche" Meldung die man als Parameter übergeben kann.
  • Die Möglichkeit dynamisch Funktionen zu einer Liste von Callbacks hinzuzufügen, die im Fehlerfall ausgeführt werden um Aufräumarbeiten zu erledigen.
  • Vor allem muss mir als "Anwender" ein nettes Interface zur Verfügung gestellt werden, damit ich meine eigentliche Arbeit nicht mit dem Lesen von langen Dokumentationen verbringen muss.

Doch wie stelle ich das an? Ich bin folgendermaßen an die Sache herangegange: Zum einen ist mir klar, dass ich die Fehlerausgabe relativ schnell aus unserem Lehrbuch übernehmen kann aber gleichzeitig garantieren muss, dass diese Ausgabe überall verfügbar ist. Zum anderen muss ich irgendwie eine verkettete Liste anlegen um die Callbacks zu verwalten.

Diese Aufteilung erschien mir sinnvoll, also lagerte ich die verkettete Callback-Liste in ein eigenes Modul functions aus, auf dessen Interface ich aus meinem errorhandling Code zugreife.

Das heißt wir müssen zu aller erst die Basis für die Funktionsliste schaffen. Sehen wir uns dafür die Signatur bzw. das Header-File von functions an:
 1 /*** file functions.h
2 *
3 * Author: Martin
4 * Date: 4.12.2009
5 *
6 * Purpose: Contains the signations of the functions for the callbacks and
7 * includes the needed header files.
8 ******************************************************************************/
9
10 #include ;
11
12 /*** type NoArgsVoidFct
13 * Purpose: Provide easy interface for function pointers which take no arguments
14 * and the return value void.
15 */
16 typedef void (*NoArgsVoidFct)();
17
18 struct _CallbackElement_
19 {
20 NoArgsVoidFct pCallback;
21 struct _CallbackElement_ *next;
22 };
23
24 /*** type CallbackElement
25 * Purpose: Provide easy to use interface for chaining a callback element list.
26 */
27 typedef struct _CallbackElement_ CallbackElement;
28
29 struct _CallbackList_
30 {
31 CallbackElement *first;
32 CallbackElement *last;
33 int size;
34 };
35
36 /*** type CallbackList
37 * Purpose: Provide an container for a linked list for callbacks.
38 */
39 typedef struct _CallbackList_ CallbackList;
40
41 CallbackList* CreateCallbackList(void);
42
43 int AddCallback(CallbackList*, NoArgsVoidFct);
44
45 void RunCallbacks(CallbackList*);
46
47 void FreeCallbackList(CallbackList*);

Es ist sofort klar was hier passiert: Es werden die Elemente für die verkettete Callback-Liste definiert. Um die Arbeit zu erleichtern wird ein Typ für Funktions-Pointer die auf die auf void-Funktionen zeigen, wobei diese Funktionen keine Parameter erwarten.

Um die Verwendung einer Liste zu vereinfachen, definiere ich weiters einen Typ der auf das erste sowie das letzte Element der Liste zeigt und zudem einen Counter für die Anzahl der Elemente führt.

Die Signaturen der vier Funktionen sagen bereits aus was getan werden soll:

  • CreateCallbackList() erzeugt eine neue Liste und allokiert den dafür nötigen Speicher.
  • AddCallback() fügt eine void-Funktion in der Liste ein und kümmert sich unter anderem um das hochzählen des Elementzählers im List-Pointer.
  • RunCallbacks() führt einen Callback nach dem anderen aus.
  • FreeCallbackList() gibt iterativ den Speicher für die Listenelemente frei um am Ende den allokierten Speicher für den Container auch noch zu befreien.

Damit wäre auch schon die wichtigste Arbeit für unsere dynamische Funktionsverwaltung getan. Doch wie soll es weitergehen? Nun geht es an die Arbeit für das tatsächliche Errorhandling. Ich wähle hier den Weg eines kontrollierten Ausstiegs: Soll heißen, dass der Benutzer die Möglichkeit hat, eine Fehlermeldung auszugeben, alle OnError-Callbacks auszuführen, damit sich dann das Programm "sauber" mit -1 als Rückgabewert verabschieden kann.

Wie sieht dort wieder der Code aus?

 1 /*** file errorhandling.h
2 *
3 * Author: Martin
4 * Date: 4.12.2009
5 *
6 * Purpose: Contains the signations of the errorhandling functions and
7 * includes the needed header files. For further information check the
8 * comments of errorhandling.c
9 ******************************************************************************/
10
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <errno.h>
14 #include <string.h>
15 #include "functions.h"
16
17 void InitErrorHandling(void);
18
19 void FreeErrorHandling(void);
20
21 void SetOnErrorCommandName(const char*);
22
23 void AddOnErrorCallback(NoArgsVoidFct);
24
25 void PrintError(const char*);
26
27 void BailOut(const char *);
28
29 void RunOnErrorCallbacks(void);

Wie funktioniert das ganze jetzt? Mittels InitErrorHandling() wird, falls noch nicht geschehen, die Liste der OnError-Callbacks initialisiert. Um den jeweiligen Programmnamen in der Fehlerausgabe zu haben, kann mittels SetOnErrorCommandName() der jeweilige String gesetzt werden. Tritt nun ein Fehler auf und der Benutzer möchte beenden, ruft er BailOut() mit der Fehlermeldung als Parameter auf.

Dabei ist darauf zu achten, dass in einem OnError-Callback nicht BailOut verwendet wird! Das würde zu einer rekursiven Endlosschleife führen. In einem OnError-Callback kann mittels PrintError() die formatierte Fehlerausgabe verwendet werden. Benötigt man die OnError-Callback Liste nicht mehr, kann mittels FreeErrorHandling() die Liste wieder freigegeben werden. (Passiert automatisch in BailOut())

Das Ausführen der Callbacks sowie das Hinzufügen von neuen Funktionen ist, denke ich, selbsterklärend. Falls doch noch Fragen auftauchen, bitte zögert nicht sie zu stellen.

Aber wie linke ich das? In meinem Makefile für das Beispielprojekt, sieht das so aus:

# Compile the code which is needed for using callbacks.
functions: functions.h functions.c
gcc -ansi -pedantic -Wall -g -c functions.c -o functions.o

# Compile the code which is used project wide for error handling.
errorhandling: errorhandling.c errorhandling.h functions
gcc -ansi -pedantic -Wall -g -c errorhandling.c -o errorhandling.o

Den restlichen Code des Beispielprojekts findet ihr hier: Errorhandling u. SharedMemory Es handelt sich dabei um ein kl. Client/Server-Projekt bei dem ein Server log-Einträge auf den Stdout schreibt die er über einen Shared Memory Bereich erhält. Zur Synchronisation werden Posix-Semaphore eingesetzt.

schlingel

Errorhandling.SharedMemory

(RAR, 8.72 KB)
Die letzte Aufgabe aus Systemnahes Programmieren. Ziel war es, den Zugriff auf Shared Memory mittels Semaphore zu synchronisieren.
Neben der eigentlichen Angabe habe ich ein kleines Errorhandling-Modul geschrieben.
Tags: Coding C Linux
Older posts are this way If this message doesn't go away, click anywhere on the page to continue loading posts.
Could not load more posts
Maybe Soup is currently being updated? I'll try again automatically in a few seconds...
Just a second, loading more posts...
You've reached the end.

Don't be the product, buy the product!

Schweinderl