1 Software ubiquitärer Systeme Anwendungsentwicklung in C/C++ Olaf Spinczyk Arbeitsgruppe Eingebettete Systemsoftware Lehrstuhl für Informatik 12 TU Dortmund [email protected]http://ess.cs.uni-dortmund.de/~os/ http://ess.cs.tu-dortmund.de/DE/Teaching/SS2010/SuS/
47
Embed
Software ubiquitärer Systeme - uni-dortmund.de · 2021. 1. 29. · C Datentypen werden direkt auf die Hardwaredatentypen abgebildet-In Java wird von der realen Hardware abstrahiert.
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
11
Software ubiquitärer SystemeAnwendungsentwicklung in C/C++
Olaf SpinczykArbeitsgruppe Eingebettete Systemsoftware
Lehrstuhl für Informatik 12TU Dortmund [email protected]://ess.cs.uni-dortmund.de/~os/
● Sicheres Programmieren in C und C++● MISRA C● Embedded C++● Cyclone
● Ressourcenverbrauch von C++ gegenüber C● Laufzeit-Polymorphie● Parametrische Polymorphie
● Zusammenfassung
HardwareHardware
BetriebssystemBetriebssystem
MiddlewareMiddleware
DatenhaltungDatenhaltung
Anwendung/ProgrammierungAnwendung/Programmierung
06.1 – Anwendungsentwicklung in C/C++ 33
Inhalt● Motivation
● Sicheres Programmieren in C und C++● MISRA C● Embedded C++● Cyclone
● Ressourcenverbrauch von C++ gegenüber C● Laufzeit-Polymorphie● Parametrische Polymorphie
● Zusammenfassung
06.1 – Anwendungsentwicklung in C/C++ 44
Verbreitung von C und C++● Statistik über den Einsatz von Programmiersprachen bei
der Entwicklung eingebetteter Systeme● Daten beruhen auf einer Umfrage bei 74 Unternehmen der Branche
CC++
AssemblerJava.Net
SystemCIEC 61131
4GL SprachenDSL
sonstige
0 10 20 30 40 50 60 70
5858
5858
4343
4242
3232
1818
1818
1212
77
88
Einsatz in %
Quelle: Computer Zeitung Nr. 23, Juni 2009, S. 16
06.1 – Anwendungsentwicklung in C/C++ 55
Warum C und C++? (1)● Transparency and Control [1]● Beispiele
● Datenstrukturen wie Arrays von Structs werden einfach linear im Speicher abgelegt. Zugriffe sind durch Cache-Lokalität sehr schnell.- In Java bestimmen der Compiler und das Laufzeitsystem die
Objektplazierung (keine Transparenz).
● C Datentypen werden direkt auf die Hardwaredatentypen abgebildet- In Java wird von der realen Hardware abstrahiert.
● C Programme verwalten den Heap-Speicher manuell.Dies gibt dem Programmierer mehr Kontrolle über die Performance.- Java arbeitet mit einem Garbage Collector. Die Strategie bleibt dem
Laufzeitsystem überlassen.
● C/C++ Programme ...● lassen sich manuell optimieren. Man sieht ihnen die verursachten
Kosten direkt an.● sind in der Regel effizienter als zum Beispiel Java Programme.
06.1 – Anwendungsentwicklung in C/C++ 66
Warum C und C++?● Vergleich beim „Great Programming Language Shootout“
● siehe http://shootout.alioth.debian.org/
● C Code hier bis zu einem Faktor von 36 schneller als Java („mandelbrot“ Benchmark) und bis zu 23 mal kleiner.- Mit JIT Compiler ist Java deutlich schneller (als mit Interpreter)
aber auch deutlich größer.● C und C++ liegen dicht beieinander. C Code war etwas langsamer,
Und warum lieber nicht?● Undefiniertes Verhalten, z.B.
● Zugriffe über die Grenzen eines Arrays hinweg● Lesen einer uninitialisierten Variablen● Dereferenzieren eines Zeigers, der auf kein gültiges zum Typ des
● Portabilität, z.B.● Wertebereich von Objekten variiert je nach Compiler/Zielplattform
➔ Stabilitäts- und Sicherheitsprobleme, z.B.● Buffer Overflows können genutzt werden, um die Kontrolle über
privilegierte Programme zu übernehmen.
06.1 – Anwendungsentwicklung in C/C++ 88
Inhalt● Motivation
● Sicheres Programmieren in C und C++● MISRA C● Embedded C++● Cyclone
● Ressourcenverbrauch von C++ gegenüber C● Laufzeit-Polymorphie● Parametrische Polymorphie
● Zusammenfassung
06.1 – Anwendungsentwicklung in C/C++ 99
MISRA-C [2] (1)(Motor Industry Software Reliability Association)
● Über 100 Programmierregeln der Automobilindustrie● Vermeidung von undefiniertem Verhalten
● Einhaltung des Standards (ISO C)
● Guter Programmierstil zur Vermeidung von Fehlern und Missverständnissen
● Werkzeugunterstützung zur Überprüfung der Einhaltung● „MISRA-Checker“
● Statische Code-Analyse
● Weite Verbreitung in der Industrie● Compiler-Hersteller integrieren Checker● Integratoren verlangen die Einhaltung der Regeln von Zulieferern
06.1 – Anwendungsentwicklung in C/C++ 1010
MISRA-C (2)● Beispiel
● Problem: Es können trotzdem diverse Fehler auftreten● Memory Leaks● Zugriffe über Array-Grenzen hinaus● …
➔ Nur der Stil wird verbessert, nicht die Sprache
Rule 34 (required)The operands of a logical && or || shall be primary expressions.
Invalid: if ( x == 0 && ishigh )Valid: if ( ( x == 0 ) && ishigh )
Primary expressions are constants, a single identifier such as ishigh, or a parenthesized expression. Parentheses are important for readability and ensuring that the behavior is what the programmer intends.
Rule 34 (required)The operands of a logical && or || shall be primary expressions.
Invalid: if ( x == 0 && ishigh )Valid: if ( ( x == 0 ) && ishigh )
Primary expressions are constants, a single identifier such as ishigh, or a parenthesized expression. Parentheses are important for readability and ensuring that the behavior is what the programmer intends.
06.1 – Anwendungsentwicklung in C/C++ 1111
Embedded C++ (EC++)● Teilmenge von C++ für eingebettete Systeme
● Festgelegt von einem japanischen Industriekonsortium,u.a. Hitachi, NEC, Fujitsu, Toshiba
● Weglassen wurden ...● kostenbehaftete C++ Sprachmerkmale
- RTTI
- Exceptions
● komplexe Merkmale, die somit Fehlerquellen sind- Mehrfachvererbung (teils auch Kostengründe)
- Templates
- mutable-Qualifizierer
● Merkmale, die man einfach für unnötig hielt- Namespaces, New-style Casts
● Einige C++ Compiler erlauben Einschränkung auf EC++● oder auch nur EC++ (kein C++)
06.1 – Anwendungsentwicklung in C/C++ 1212
Cyclone: Ein sicheres C [1]● C-Spracherweiterung, die undefiniertes Verhalten
vermeiden hilft.● Ein erweitertes Typsystem und statische Analyse
● Laufzeitabsicherungen
● Konkrete Maßnahmen● Spezielle sichere Zeigertypen (Fat, Thin und Bounded)
● Sicherer Umgang mit NULL
● Erzwungene Initialisierung
● Sichere Unions
● Region-based Type System
● Verschiedene Strategien zur Heap-Verwaltung (inkl. Garbage Coll.)
● Exceptions, Namespaces, Subtyping, u.v.m.
06.1 – Anwendungsentwicklung in C/C++ 1313
Cyclone: Beispiele (1)● Fat Pointers
● Enthalten implizit den erlaubten Adressbereich● Dereferenzierungen außerhalb des Bereich führen zu Exception
Das Speicher-Layout der Arrays ist nicht anders als bei C. Die fetten Zeiger enthalten die Information über die Dimension. Zeigerarithmetik ist erlaubt aber kontrolliert.
Das Speicher-Layout der Arrays ist nicht anders als bei C. Die fetten Zeiger enthalten die Information über die Dimension. Zeigerarithmetik ist erlaubt aber kontrolliert.
06.1 – Anwendungsentwicklung in C/C++ 1414
Cyclone: Beispiele (2)● Thin Pointers
● Verweisen auf ein einzelnes Objekt● Es finden keine Laufzeitüberprüfungen statt● Zeigerarithmetik ist aber komplett verboten
● Garantierte Initialisierung● Realisiert durch statische Kontrollflussanalyse
int x = 3;int *y = &x;
Form *f;switch (event->eType) {case frmOpenEvent: f = FrmGetActiveForm(); ...case ctlSelectEvent: i = FrmGetObjectIndex(f, field); ...}
Fehler! 'f' ist hiernicht initialisiert.Fehler! 'f' ist hiernicht initialisiert.
06.1 – Anwendungsentwicklung in C/C++ 1515
Cyclone: Beispiele (3)● Tagged Unions
● Optionale Erweiterung von Unions● Das Union-Objekt merkt sich seinen aktuellen Typ● Lesezugriffe werden kontrolliert● Der Typ kann auch explizit abgefragt werden
@tagged union U { int i; int *p; };void pr(union U x) { if (tagcheck(x.i)) printf("int(%d)",x.i); else printf("ptr(%d)",*x.p);}
06.1 – Anwendungsentwicklung in C/C++ 1616
Cyclone: Kosten● Die Sicherheit hat ihren Preis
● Im Vergleich zu anderen sicheren Sprachen ist es jedoch effizient
06.1 – Anwendungsentwicklung in C/C++ 1717
Fazit: Sichere/bessere C/C++ Dialekte● MISRA C und Embedded C++ reduzieren die
Fehlerwahrscheinlichkeit durch die Beschränkung auf eine Teilmenge von C bzw. C++.
● Der Compiler oder separate Werkzeuge können die Einhaltung der Regeln zur Übersetzungszeit prüfen
● Trotzdem bleiben viele zentrale Probleme bestehen
● Virtuelle Funktionsaufrufe wie inBase::b() und main() bedingen eine Indirektion● kein Inlining solcher Aufrufe möglich!● selbst leere virtuelle Funktionen müssen angelegt werden
● Dynamisches Binden kostet deutlich mehr als statisches!
● Faustregel: In ressourcenbeschränkten Domänen das virtual Schlüsselwort nur verwenden, wenn es wirklich nötig ist
06.1 – Anwendungsentwicklung in C/C++ 3333
● Häufig werden die selben Algorithmen für verschiedene Datentypen benötigt, z.B. quicksort() für int, float, u.s.w. oder Listen von int, Foo oder Bar Objekten.
● Wie kann der Programmierer damit umgehen, z.B. in C oder Java < 5?
● Mehrfachimplementierung des Algorithmus- Probleme bei der Wartung, Wiederholung von Fehlern, Mühe!
● Gemeinsame Basis- fehlende Typsicherheit bzw. Typüberprüfung erst zur Laufzeit
● Präprozessoren (z.B. C Makros)- blinde Textersetzung, Scopes und Typen werden ignoriert
● Templates sind ein standardisierter [2] C++ Mechanismus, der alle diese Probleme vermeidet!
Warum Templates?
06.1 – Anwendungsentwicklung in C/C++ 3434
● als Template-Parameter kann im Prinzip jeder Typ verwendet werden (keine gemeinsame Basis nötig!)
● Einschränkungen definiert das Template implizit, hier:● T benötigt operator < (const T&) const● die Aufrufparameter von max() müssen den selben Typ haben,
damit T „deduziert“ werden kann- int i = max(2,3); // ok
- int k = max(4,4.2); // Fehler
Ein erstes Funktions-Template
max.hmax.h
template <typename T>inline const T& max (const T &a, const T &b) { return a < b ? b : a;}
template <typename T>inline const T& max (const T &a, const T &b) { return a < b ? b : a;}
T kann wie ein normaler Typverwendet werdenT kann wie ein normaler Typverwendet werden
06.1 – Anwendungsentwicklung in C/C++ 3535
Überladen von Funktions-Templatesmax.hmax.h
template <typename T>inline const T& max (const T &a, const T &b) {...}
template <typename T>inline const T& max (const T &a, const T &b, const T &c) {...}
inline const int& max (const int &a, const int &b) {...}
template <typename T>inline const T& max (const T &a, const T &b) {...}
template <typename T>inline const T& max (const T &a, const T &b, const T &c) {...}
inline const int& max (const int &a, const int &b) {...}
main.ccmain.cc
#include “max.h“int main() { max(1,2,3); // Template mit 3 Argumenten max(1.0,2.0); // max<double> per Deduktion max('X','Y'); // max<char> per Deduktion max(1,2); // nicht-Template Variante bevorzugt max<>(1,2); // max<int> per Deduktion max<double>(1,2); // max<double> ohne Deduktion max('X',3.14); // nicht-Template Variante für 2 ints}
#include “max.h“int main() { max(1,2,3); // Template mit 3 Argumenten max(1.0,2.0); // max<double> per Deduktion max('X','Y'); // max<char> per Deduktion max(1,2); // nicht-Template Variante bevorzugt max<>(1,2); // max<int> per Deduktion max<double>(1,2); // max<double> ohne Deduktion max('X',3.14); // nicht-Template Variante für 2 ints}
06.1 – Anwendungsentwicklung in C/C++ 3636
● Klassen-Templates sind perfekt für Container-Klassen● darum gibt es auch die Standard Template Library (STL)
Ein erstes Klassen-TemplateVector.hVector.h
template <typename T>class Vector { T *data; int dim;public: // Konstruktor, Copy-Konstruktor(!), ... // Zugriffsfunktionen z.B. mit Indexprüfung: void set (int index, const T& obj); T get (int index) const;};
template <typename T>class Vector { T *data; int dim;public: // Konstruktor, Copy-Konstruktor(!), ... // Zugriffsfunktionen z.B. mit Indexprüfung: void set (int index, const T& obj); T get (int index) const;};
● Wenn Methoden von Klassen-Templates nicht im Klassenrumpf definiert werden, müssen sie ähnlich wie ein Funktions-Template formuliert werden:
Methoden von Klassen-Templates
vector.hvector.h
#include <assert.h>template <typename T>void Vector<T>::set (int index, const T& obj) { assert (index < dim); // wird nur in der Debug Variante // geprüft data[index] = obj; // erfordert operator = in T}
template <typename T>T Vector<T>::get (int index) { assert (index < dim); // wird nur in der Debug Variante // geprüft return data[index]; // erfordert Copy Konstructor}
#include <assert.h>template <typename T>void Vector<T>::set (int index, const T& obj) { assert (index < dim); // wird nur in der Debug Variante // geprüft data[index] = obj; // erfordert operator = in T}
template <typename T>T Vector<T>::get (int index) { assert (index < dim); // wird nur in der Debug Variante // geprüft return data[index]; // erfordert Copy Konstructor}
06.1 – Anwendungsentwicklung in C/C++ 3838
● neben Typen können auch konstante Ausdrücke als Template-Parameter benutzt werden
Nicht-Typ Template-Parameter
Vector2.hVector2.h
template <typename T, int DIM>class Vector2 { T data[DIM]; // in jedem Objekt steckt ein Arraypublic: // Konstruktor, Copy-Konstruktor(!), ... // Zugriffsfunktionen z.B. mit Indexprüfung: void set (int index, const T& obj); T get (int index) const;};
template <typename T, int DIM>class Vector2 { T data[DIM]; // in jedem Objekt steckt ein Arraypublic: // Konstruktor, Copy-Konstruktor(!), ... // Zugriffsfunktionen z.B. mit Indexprüfung: void set (int index, const T& obj); T get (int index) const;};
● aber Achtung:
Vector<int> v1(10)Vector<int> v2(20); // v1 und v2 haben den selben TypVector2<int,10> v2_1; // v2_1 und v2_2 habenVector2<int,20> v2_2; // unterschiedliche Typen!
Vector<int> v1(10)Vector<int> v2(20); // v1 und v2 haben den selben TypVector2<int,10> v2_1; // v2_1 und v2_2 habenVector2<int,20> v2_2; // unterschiedliche Typen!
06.1 – Anwendungsentwicklung in C/C++ 3939
● Templates, die Templates benutzen sollen, können auch Templates als Parameter bekommen
Template Template-Parameter
DataCollector.hDataCollector.h
template <template <typename> class Container>class DataCollector { Container<double> collected_data; // Container wird benutztpublic: // ...};
template <template <typename> class Container>class DataCollector { Container<double> collected_data; // Container wird benutztpublic: // ...};
● so wird’s benutzt:
DataCollector<Vector> dc; // alles OK
DataCollector<Vector2> dc2; // geht nicht! // (Vector2 erwartet 2 Parameter)
DataCollector<Vector> dc; // alles OK
DataCollector<Vector2> dc2; // geht nicht! // (Vector2 erwartet 2 Parameter)
Parameter ist ein Template mit einem ParameterParameter ist ein Template mit einem Parameter
06.1 – Anwendungsentwicklung in C/C++ 4040
● ähnlich wie bei Default-Argumenten von Funktionen können auch Template-Parameter ein Default haben● wie bei Funktionen müssen dann nachfolgende Parameter auch ein
Default haben
Default Template-Parameter
DataCollector2.hDataCollector2.h
template <template <typename,int> class Container=Vector2, int DIM=32>class DataCollector2 { Container<double, DIM> collected_data; // Container und DIMpublic: // ...};
template <template <typename,int> class Container=Vector2, int DIM=32>class DataCollector2 { Container<double, DIM> collected_data; // Container und DIMpublic: // ...};
● so wird’s benutzt:DataCollector2<> c1; // auf <> kann man nicht // verzichtenDataCollector2<Vector2> c2; // DIM ist 32 per DefaultDataCollector2<FooBar, 10> c3; // Default nicht genutzt
DataCollector2<> c1; // auf <> kann man nicht // verzichtenDataCollector2<Vector2> c2; // DIM ist 32 per DefaultDataCollector2<FooBar, 10> c3; // Default nicht genutzt
zwei Parameter mit Defaultszwei Parameter mit Defaults
06.1 – Anwendungsentwicklung in C/C++ 4141
● mit Hilfe der Template Spezialisierung können unterschiedliche Template Implementierungen in Abhängigkeit von den Parametern gewählt werden
● Explizite Template Spezialisierung● für einen bestimmten Parametersatz, z.B. Vector<bool>
● Partielle Template Spezialisierung● für eine Parametermenge, z.B. Vector<const T*> mit beliebigem T● geht nicht bei Funktions-Templates
● wenn sowohl eine explizite als auch eine partielle Spezialisierung passen, wird die explizite gewählt
● passt keine Spezialisierung, wird das primäre Template genommen
● Template-Spezialisierung bewirkt eine Fallunterscheidung● wichtige Grundlage für Template-Metaprogrammierung
Template Spezialisierung
06.1 – Anwendungsentwicklung in C/C++ 4242
● kann z.B. zur Optimierung benutzt werden:
Explizite Template Spezialisierung
● mit der Spezialisierung für bool wird für Vector<bool> auf dem Heap nur ein Achtel des Speicherplatzes angefordert
Vector.hVector.h
template <>class Vector<bool> { unsigned *data; // bool wird als Bit gespeichert int dim;public: // Konstruktor Vector(int d) { data = new unsigned[1 + (d-1) / (sizeof(unsigned)*8)]; } // Copy-Konstruktor(!), Zugriffsfunktionen, ...};
template <>class Vector<bool> { unsigned *data; // bool wird als Bit gespeichert int dim;public: // Konstruktor Vector(int d) { data = new unsigned[1 + (d-1) / (sizeof(unsigned)*8)]; } // Copy-Konstruktor(!), Zugriffsfunktionen, ...};
explizite Spezialisierung für boolexplizite Spezialisierung für bool
06.1 – Anwendungsentwicklung in C/C++ 4343
● kann z.B. die gleiche Optimierung für Vector2 realisieren:
● Sicheres Programmieren in C und C++● MISRA C● Embedded C++● Cyclone
● Ressourcenverbrauch von C++ gegenüber C● Laufzeit-Polymorphie● Parametrische Polymorphie
● Zusammenfassung
06.1 – Anwendungsentwicklung in C/C++ 4646
Zusammenfassung● C und C++ sind nicht typsicher
● Quelle für diverse Fehler und Sicherheitslücken● Teilmengen wie MISRA C und Embedded C++ helfen nur bedingt
● Dafür bieten C und C++ …● (Kosten-)transparenz
- Man „sieht“ wofür die Ressourcen verbraucht werden
- Das Systemverhalten ist vorhersagbar (z.B. kein Garbage Collector)
● Kontrolle über den Ressourcenverbrauch- Man kann den Ressourcenverbrauch beeinflussen (tuning)
… und sind daher dominierend in eingebetteten Systemen● C++ erweitert C primär um zwei neue Paradigmen
● Objektorientierung- Vorsicht Kostenfalle
● Generische Programmierung- Komplex, aber keine Laufzeitkosten → geeignet für eingeb. Produktlinien
06.1 – Anwendungsentwicklung in C/C++ 4747
Literatur[1] T. Jim, J. G. Morrisett, D. Grossman, M. W. Hicks, J. Cheney,
and Y. Wang. Cyclone: A Safe Dialect of C. In Proceedings of the General Track of the Annual Conference on USENIX Annual Technical Conference (C. S. Ellis, Ed.). USENIX Association, Berkeley, CA, pages 275-288, June 2002.
[2] Guidelines for the Use of the C Language in Critical Systems, ISBN 0 9524156 2 3 (paperback), ISBN 0 9524156 4 X (PDF), October 2004.
[3] P. Wegner. Classification in Object-Oriented Systems, ACM, SIGPLAN Notices, 21(10):173-182, 1986.
[4] B. Liskov. Data Abstraction and Hierarchy, ACM, SIGPLAN Notices, 23(5), 1988.
[5] C++ ABI Summary, http://www.codesourcery.com/cxx-abi