Академический Документы
Профессиональный Документы
Культура Документы
Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards
Introduction
Why the language matters Language taxonomies Cardellis metrics Schedulability analysis
Language taxonomies
Imperative languages involve assignment of variables (most widely used language are imperative, e.g. C, Java, Ada 95, Fortran, BASIC). Functional (applicative) languages employ repeated application of some function (e.g. LISP). Logic programming involves a formal statement of the problem without any specification of how it is solved (e.g. PROLOG). Procedural languages are defined by a series of operations (most languages you can think of). Procedural also describes a style of programming that is not object-oriented. Declarative (non-procedural) languages involve specification of rules defining the solution to a problem (PROLOG, Spreadsheets). Object-oriented describes a style of programming. Languages written to support this are called object-oriented.
Cardellis metrics
Economy of execution. How fast does a program run? Economy of compilation. How long does it take to go from sources to executables? Economy of small-scale development. How hard must an individual programmer work? Economy of large-scale development. How hard must a team of programmers work? Economy of language features. How hard is it to learn or use a programming language?
Cardelli, L. Bad Engineering Properties of Object-Oriented Languages, ACM Computing Surveys, Volume 28A, Number 4, 1996, pp. 150-158.
Schedulability analysis
The compile time prediction of execution time performance is known as a schedulability analysis. Can be achieved in three ways:
Eliminate or remove language constructs that destroy determinism such as unbounded recursion and unbounded while loops. Provide typesafe language mechanisms that provide for complete control of time. Build the programming language in conjunction with the operating system in such a way as to provide explicit interaction and control.
Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards
Assembly language
In terms of Cardellis criteria:
Excellent economy of execution. Excellent economy of compilation (vacuously). Poor economies of small- and large-scale development, language features.
Assembly language
Used only in cases where the compiler does not support certain macroinstructions, or when the timing constraints are tight. Requires complex prologues and epilogues to prepare an assembly language program. Often a shell of the program is written in the high-order language and compiled to an assembly file, which is then massaged. Some languages such as Ada provide a pragma pseudo-op, which allows for assembly code to be placed in-line with the high-order language code.
Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards
Procedural languages
Overview Parameter passing techniques Global variables Recursion Dynamic memory allocation Typing Exception handling Modularity Cardellis metrics and languages that support the procedural style of programming
Overview
In the programming sense, those languages in which the program is organized into a sequence of steps. These languages are characterized by facilities that allow for instructions to be grouped together into sub-programs or procedures (modules). Appropriate structuring of the sub-programs allow for achievement of desirable properties of the software (e.g. modularity, reliability, reuse, etc.) Some languages, notably Ada 95 and C++ can be used as procedural and/or object-oriented languages.
Global variables
Variables that are within the scope of all code. References can be made in direct mode and thus are faster than references to variables passed via parameter lists. Global variables are dangerous because references to them may be made by unauthorized code, thus introducing subtle faults. Even in languages like Fortran, where blocks of global variables can be defined via named COMMON declarations, access is still not wellcontrolled. For this and other reasons, unwarranted use of global variables is to be avoided. Only recommended when timing warrants, or if the use of parameter passing leads to convoluted code. The use of global variables must be clearly documented.
Recursion
When a procedure can either call itself or use itself in its construction. Is elegant and is often necessary, but has negative performance impact. Procedure calls require the allocation of storage on one or more stacks for the passing of parameters and for storage of local variables this uses large numbers of expensive memory and register indirect instructions. Precautions need to be taken to insure that the recursive routine will terminate otherwise the run-time stack will overflow. Use of recursion often makes it impossible to determine the size of run-time memory requirements. Iterative techniques such as while and for loops must be used where performance and determinism are crucial. Recursion theorem guarantees that any recursive code can be written iteratively and vice versa.
Is important in the construction and maintenance of many data structures needed in real-time systems. Can be time consuming but is necessary, especially in the construction of interrupt handlers, memory managers. Linked lists, trees, heaps and other dynamic data structures can benefit from the clarity and economy introduced by dynamic allocation. In cases where just a pointer is used to pass a data structure, then the overhead for dynamic allocation can be quite reasonable. Care should be taken to ensure that the compiler will pass pointers to large data structures and not the data structure itself. Languages that do not allow dynamic allocation of memory require data structures of fixed size. While this may be faster, flexibility is sacrificed and memory requirements must be predetermined. Languages such as C, Pascal, Ada, and Modula-2 have dynamic allocation facilities while BASIC and old versions of Fortran do not.
Typing
Typed languages require that each variable and constant be of a specific type declared before use. Strongly typed languages prohibit the mixing of types, forcing the programmer to be precise. Strong typing can prevent corruption of data through unwanted or unnecessary type conversion. Compiler type-checking is an important way to find errors at compile time, rather than at run-time, when they are more costly to repair. Abstract data types may contribute an execution time penalty as complicated internal representations are often needed to support the abstraction. Some languages are typed, but do not prohibit mixing of types in arithmetic operations. This causes performance impacts due to hidden promotions and/or loss of accuracy.
Exception handling
Exceptions are errors or other anomalous conditions that arise during program execution. Includes the obvious, such as floating-point overflow, square root of a negative, divide-by-zero, and user defined ones. Exception handling in the high-level language aids in the incorporation of fault-tolerance. Poor handling of exceptions can degrade performance. For example, floating-point overflow errors can propagate bad data through an algorithm and instigate time-consuming error recovery routines. Languages such as C++ and Java provide excellent exception handling through the throw-try-catch mechanisms. C provides raise and signal combination.
Modularity
Procedural-oriented languages that support the principle of Information Hiding tend to make it easy to construct high-integrity real-time systems. E.g. the Ada package consists of a specification and declarations that include its public or visible interface and its invisible or private parts. Packages are separately compliable entities, which further enhances their application as black boxes. In Fortran there is the notion of a SUBROUTINE and separate compilation of source files. The C language provides for separately compiled modules and other features that promote a good modular design. There is a price to pay for modularity in the overhead associated with procedure calls and parameter passing.
Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards
Object-oriented languages
Overview Synchronizing objects Garbage collection Cardellis metrics and object-oriented languages Object-oriented versus procedural languages
Overview
Languages that support a high degree of data abstraction, inheritance, polymorphism, and messaging. The benefits of OO languages include increasing programmer efficiency, reliability, and the potential for extension and reuse. Objects are an effective way to manage system complexity they provide a natural environment for information hiding, protected variation, and encapsulation.
Synchronizing objects
Synchronized objects: e.g., mutex is associated with an object that can be concurrently accessed by multiple threads.
Internal locking : each public method acquires a lock on the synchronization object upon method entry and releases the lock on exit. External locking : clients are responsible for acquiring a lock on the associated synchronization object before accessing the object and subsequently releasing the lock when finished.
Encapsulated objects: No synchronization needed the lock of the enclosing object also protects the encapsulated object. Thread-local objects: Objects that are only accessed by a single thread require no synchronization. Objects migrating between threads: Ownership of a migrating object is transferred between threads. Require no synchronization the transfer of ownership does require synchronization. Immutable objects: An immutable objects state can never be modified after it is instantiated. Immutable objects require no synchronization when accessed by multiple threads since all accesses are read-only. Unsynchronized objects: Objects in a single-threaded program require no synchronization.
Garbage collection
Garbage refers to allocated memory that is no longer being used but is not otherwise available. Garbage is generally associated with object-oriented languages like C++ and Java. Excessive garbage accumulation can be detrimental to real-time perforamence. Garbage collection algorithms generally have unpredictable performance and are detrimental in real-time. Garbage can be created in procedural languages. For example, in Pascal or C garbage can be created by improper deallocation of memory. Java standard environment incorporates GC, whereas C++ does not. In those languages that do not provide GC, it must be implemented.
Less efficient in execution every routine is a method. Introduces additional indirections through method tables and prevents optimizations such as inlining. Economy of compilation is low often there is no distinction between the code and the interface of a class. Some OO languages are not sufficiently modular and require recompilation of superclasses when compiling subclasses. Superior with respect to economy of small-scale development. E.g. individual programmers can take good advantage of class libraries and frameworks. Economy of large-scale development is low sometimes these languages have poor modularity properties. E.g. it is easy to override a method that should not be overridden, or to re-implement a class in a way that causes problems in subclasses. OO languages have low economy of language features what starts as economical and uniform ("everything is an object") ends up as a menagerie of class varieties.
Most opponents of OO languages for real-time assert that concurrency and synchronization are poorly supported.
When built-in language support for concurrency does not exist it is standard practice to create "wrapper facade" classes to encapsulate system concurrency APIs (e.g., wrapper classes in C++ for POSIX threads). We already noted several concurrency patterns for OO real-time systems. While concurrency may be poorly supported at the language level in practice it is not an issue since developers use libraries instead.
Bottom line there are no clear guidelines for where OO approaches and languages should be used in real-time. Each situation needs to be considered separately.
Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards
Survey of languages
Ada 95 C C++ C# Fortran Java Real-time Java Occam 2 Special real-time languages
Ada 95
Ada (1983) was originally intended to be the mandatory language for all U.S. Department of Defense projects, which included many embedded real-time systems. Ada 83, had significant problems and Ada (1995) was introduced to deal with these problems. Ada 95 was the first internationally standardized object-oriented programming language. Ada 95 is both procedural and object-oriented depending on how the programmer uses it. Ada 95 includes extensions to help deal with scheduling, resource contention, and synchronization. Other language extensions made Ada 95 an OO language (tagged types, packages, and protected units). Ada has never lived up to its promise of universality because users have found it to be too bulky and inefficient.
Invented around 1971, is a good language for low-level programming. C provides special variable types such as register, volatile, static, and constant, which allows for control of code generation at the high-order language level. C supports call-by-value only call-by-reference can be implemented by passing a pointer to anything as a value. Variables declared as type volatile are not optimized by the compiler. This is useful in handling memory-mapped I/O and other instances where the code should not be optimized. Automatic coercion (implicit casting of data types) can lead to mysterious problems. C provides for exception handling through the use of signals and setjmp and longjmp, constructs to allow a procedure to return quickly from a deep level of nesting. Overall C is good for embedded programming it provides for structure and flexibility without complex language restrictions.
C++
C++ is a hybrid OO language that was originally implemented as a macro-extension of C. Exhibits all characteristics of an OO language. C++ extends C functions with classes and class methods, which are functions that are connected to classes. C++ still allows for low-level control using inline methods rather than a runtime call. The C++ preprocessor can lead to unnecessary complexity. Preprocessors also do weak type checking and validation. Misuse of pointers causes many bugs in C/C++ programming standard libraries of dynamic data structures (e.g. the Standard Template Language) help minimize pointer problems.
C++
Multiple inheritance allows a class to be derived from multiple parent classes. Multiple inheritance is powerful, but it is difficult to use correctly, is subsumed by composition, and is very complicated to implement. C++ does not provide automatic GC, which mean dynamic memory must be managed manually or GC must be implemented. Tendency to take existing C code and objectify it by wrapping the procedural code very bad because brings all the disadvantages of C++ and none of the benefits. Bad C++ programmers use a mixture of function and methods leading to confusing programs. C versus C++ is a tradeoff between a "lean and mean" C program and a slower and unpredictable C++ program that will be easier to maintain.
C#
C# is a C++-like language that along with its operating environment, has similarities to the Java and JVM. C# is associated with Microsofts .NET Framework for scaled down operating systems like Windows CE 3.0. C# and the .NET platform may not be appropriate for hard real-time systems unbounded execution of its GC environment and lack of threading constructs to adequately support schedulability and determinism. C# and .NET might be appropriate for some soft and firm real-time systems.
Fortran
Fortran is the oldest high-order language still used in modern real-time systems (developed circa 1955). Old versions of Fortran did not support re-entrant code. Fortran was developed in an era when efficient code was essential to optimizing performance in small, slow machines. Fortran is weakly typed, but it still can be used to design highly structured code. Fortran has no built-in exception handling or abstract data types. Fortran is still used in many legacy and even new real-time applications (A new version of Visual Fortran was recently released for .NET). Embedded systems written in Fortran typically include a large portion of assembly language code to handle interrupts and scheduling, and communication with external devices.
Java
Java is an OO language that looks very much like C++. Java is interpreted the code compiles into machine-independent code which runs in a managed execution environment. This environment is the Java Virtual Machine, which executes object code instructions as a series of program directives. Java code can run on any device that implements the virtual machine write once, run anywhere. There are native-code Java compilers byte code executes directly on the bare metal. Like C, Java supports call-by-value, but the value is the reference to an object, which appears as call-by-reference for all objects. One of the best-known problems with Java involves its garbage collection utility.
Real-time Java
National Institute of Standards and Technology (NIST) task force was charged with developing a version of Java that was suitable for embedded real-time applications. The final report (1999), defines core requirements for the real-time specification for Java (RTSJ).
Any garbage collection that is provided shall have a bounded preemption latency. Defines the relationships among real-time Java threads. Include APIs to allow communication and synchronization between Java and nonJava tasks. Includes handling of both internal and external asynchronous events. Incorporates some form of asynchronous thread termination. Provides mechanisms for enforcing mutual exclusion without blocking. Provides a mechanism to allow code to query whether it is running under a realtime Java thread or a non-real-time Java thread. Defines the relationships that exist between real-time Java and non-real-time Java threads.
Occam 2
Occam 2 is based on the Communicating Sequential Processes (CSP) formalism. The basic entity in Occam 2 is the process there are four fundamental types, assignment, input, output, and wait. More complex processes are constructed from these by specifying sequential or parallel execution or by associating a process with an input from a channel. The Occam 2 language was designed to support concurrency on transputers but compilers are available for other architectures. Occam-2 has found some practical implementation in the U.K.
Several specialized languages for real-time have appeared and disappeared over the last 30 years. PEARL: Process and Experiment Automation real-time Language developed in the early 1970s. Has fairly wide application in Germany, especially in industrial controls settings. Real-time Euclid: An experimental language that is completely suited for schedulability analysis. This is achieved through language restriction. Real-time C: A generic name for any of a variety of C macroextension packages. These macroextensions typically provide timing and control constructs that are not found in standard C. Real-time C++: A generic name for one of several object class libraries specifically developed for C++. These libraries augment standard C++ to provide an increased level of timing and control. Others include MACH, Eiffel, MARUTI, ESTEREL. Most of these languages are used for highly specialized applications or in research only.
Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards
Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards
Coding standards
Is a set of stylistic conventions. Complying with coding standards will not foster portability, but rather in many cases, readability and maintainability. May even increase reliability. May also be used to foster improved performance by encouraging or mandating the use of language constructs that are known to generate more efficient code. Many agile methodologies embrace coding standards, for example, Extreme Programming.
Coding standards
Standard or boilerplate header format. Frequency, length, and style of comments. Naming of classes, methods, procedures, variable names, data, file names and so forth. Formatting of program source code including use of white space and indentation. Size limitations on code units including maximum and minimum lines of code, number of methods and so forth. Rules about the choice of language construct to be used; for example, when to use case statements instead of nested if-then-else statements (previously discussed). Example: Hungarian notation -- a set of rules about naming variables, it has been used with C++, Ada, Java and even C.
Coding standards
They can promote very mangled variable names. Their very strength can be its own undoing. E.g. what if the type information embedded in the object name is, in fact, wrong? There is no way for a compiler to check this. Adoption of coding standards is not recommended midproject. It is much easier to start conforming than to be required to change existing code to comply. A bad coding standard is better than none at all.