| The Managed Resource Interface: Erlang
|
|
This chapter gives the necessary background on Erlang, the programming language used to develop the Managed Resource Interface described in this paper. It goes into detail on the aspects of the language needed to understand the managed resource interface. Language Abstract Erlang is a symbolic high-level functional programming language. It was originally developed for programming and prototyping telecommunication systems, but due to its properties quickly proved to be ideal for other industrial applications dependant on features such as fault tolerance, concurrency, distribution, and soft real time properties. Programs in Erlang consist of functions stored in modules. Functions can execute concurrently in lightweight processes, and communicate with each other through asynchronous message passing. The creation and deletion of processes require little memory and computation time. In Erlang, memory is dynamically allocated. A real time garbage collector deallocates memory when it is no longer referenced to. Shared memory does not exist, forcing processes to exchange data through message passing. An error handling mechanism allows a process to trap exceptions created from incorrect expression evaluations, attempts to execute undefined functions, and failing match operations. Exceptions can also be generated by the user changing the program’s flow of control. Processes can monitor each other through links, and either be deleted or receive notifications depending on their execution state, and that of the processes they are linked to. An important property of the language is its distribution mechanism based on TCP/IP. Processes can interact with each other on different physical machines, or on the same machine running different Erlang Systems. The syntax for the interaction is the same as the system used for local process communication, allowing programs written for a single environment to be easily ported to a multi-processor system. The distribution mechanism ensures transparency between the environments, even when the machines are running different operating systems. The Erlang system offers a set of built in functions called BIFs. They are used to execute tasks not expressible with Erlang’s primitives. They include data type conversions, distribution mechanisms, system information retrieval, and operations dealing with ports and processes. In the development environment, functions are evaluated using the shell. It is a simple interactive command interpreter allowing the user to load, compile and evaluate expressions. It also allows the user to inspect and control the state of the system. Final programs are executed without the shell. They are started up through scripts where start functions and necessary parameters are specified. The target environment can be anything from a PC to an embedded system.
Variables in Erlang are single assignment. They are instantiated when created, and may not be assigned new values. Variables are dynamically typed, and need not be declared. Atomic storables are integers, floats, atoms, refs [17] and pids [18]. Four compound data types are provided. Tuples, records [19] and binaries are of fixed size, while lists are of variable size. Macros can also be defined.
Records are a key concept in the MRI. Together with actions, are used to specify the system specific parts of the interface [20]. When records are defined, their name and fields are specified. A definition can appear anywhere in the code, as long as it is before a clause using the record. It is good practice to put record definitions in separate files included by the modules using them. A default value can be assigned in the field declaration. If no value is specified, the field is automatically assigned the atom undefined. Records are bound to variables, and when a field in a record is changed, it has to be bound to a new variable. Guards exist on records, allowing checks to see if a record is of a specific type.
Functions and Modules
Pattern Matching Pattern matching is a key concept in Erlang. It is used for variable assignment and clause choices, altering the flow of the program. Patterns have the same structures as Erlang data objects, but can include unbound variables that are bound when the matching occurs. A function with the same name can be defined several times, where through pattern matching, the clause chosen depends on the parameters and arguments passed to it. Examining example 3, if the argument in the function call is not 0, the second clause is automatically chosen, and the argument is bound to N. If the argument is 0, the first clause is chosen, and 1 is returned. Flow of control
In if and case statements, should no match exist, the process will terminate with an error. In case statements, the user can avail himself of the special variable ‘_’, used as a placeholder for cases where we are not interested in the value. This match will always succeed. In if statements, the atom true can be used instead of a guard, executing that sequence should none of the guards succeed. Concurrency Erlang processes are created dynamically by evaluating the spawn/3 BIF [21]. The spawn BIF takes a module name, a function name, and a list of parameters for the function as arguments. This is the first function that is called by the new process. The BIF call returns a PID, which is used to identify this process. Messages are sent to the process through the Pid ! Message construct. A message can be any Erlang expression or term. They are received as terms in a receive clause, and bound through pattern matching. If messages cannot be matched, they remain in the processes’ mailboxes until the flow leads into another receive construct where an appropriate match occurs. Time-outs are allowed in receive statements, should no message match or be received.
In order to facilitate message passing, processes are allowed to have static identifiers represented as atoms. These identifiers are assigned through the register/2 BIF, allowing this identifier to be used instead of the Pid. One process at the most may be registered with a specific identifier at any one time in an Erlang system.
In Example 7, a process executing the function start/1, located in the module server, is created. The atom initial is passed as a parameter. This process is then assigned the name server. Two messages are sent to it, the atoms hello and bye. hello is sent through the use of the process name assigned to it, while bye is sent to it through the use of the process identifier. Distribution An Erlang node can be thought of as a complete Erlang system. We call a network of two or more Erlang nodes interacting with each other a distributed system. The nodes are identified by a name given to the node during the start-up of the Erlang system, and by the name of the machine they are executing on. The BIF node/0 returns the name of the node the expression is evaluated in. Assume the node with the name server is running on the machine ramones.ericsson.se, the name of the node would be server@ramones.ericsson.se [22]. The primitives for communication between processes in different
nodes are the same if Pids are used. Should the user want to send a message
to a registered process on another node, however, the node name must be
specified.
The programmer does not need to set up connections
between nodes. They are set up by the run-time system when a node, for
the first time, is referred to by another node. When the connection has
successfully been set up, the newly connected node adds its peer to the
list of known nodes, spreading the information to other nodes they were
previously connected to. This creates a loosely coupled system, where
node configuration can change dynamically, whenever nodes are added or
removed. The creation of loosely coupled systems involves some security
aspects that are only partially handled by the underlying system. Every
node has a secret cookie assigned to it. If a set of nodes shares the
same cookie, they can freely communicate with each other, and no restrictions
are imposed as to what operations can be performed on the individual nodes
by processes in other nodes. Open Telecom Platform The Erlang system has a set of libraries that provide building primitives for larger systems. They include routines for I/O, file management, and list handling. Other libraries handle lists of ASCII [23], parsing, generic servers, etc. The GS library provides a set of graphical event driven routines, while the ets [24] library provides a hash table implementation for accessing data in constant time. This set of standard libraries is seen as an application in the Open Telecom Platform. Large systems written in Erlang use the Open Telecom Platform. It consists of a set of design principles, which together with middleware applications yields building blocks for scalable robust real time systems. Supervision, restart, and configuration mechanisms are provided for included applications, and specified for user-defined applications allowing a standardization of behaviors between systems. Provided applications are constantly expanding and today
include SNMP agents, complete with MIB compilers and guidelines for instrumentation
functions. A fault tolerant HTTP server facilitates the development and
integration of web-based interfaces. A distributed relational database
called Mnesia, with its own query language mnemosyne, allows transaction
handling in a distributed environment. An Orb facilitates the development
of CORBA based management systems. Interfaces towards other languages
include Jive, a Java interface, IG, an interface allowing Erlang programs
to call c modules, and c modules to call Erlang. These interfaces are
complemented with the possibility of defining IDL interfaces, through
which code can be generated. Other included applications involve deal
with error handling, alarm handling, monitoring, and more
[25]. Like any tool used to solve a problem, there are correct and incorrect ways of going around in problem solving. Using a methodology proven successful in other types of development is no guarantee that it will work when developing systems in Erlang/OTP. Erlang’s properties as a high level language introduces a simple edit compile and test cycle. Prototypes can thus be quickly and efficiently implemented, testing ideas at an early stage of development. Features such as dynamic typing, flat module, process concepts and application structures allows for an incremental development of the system. Functionality can be added either as components on already existing modules, or as layers on the software. This facilitates testing in between the increments, and allows errors and inconsistencies to be detected at early stages of the development. As Erlang is a higher-level language, there is a no need to document as many levels between the requirements and the implementation. Documents necessary are subsystem descriptions, with their respective interfaces. Interfaces should remain stable between the increments. This has proven to be possible due to the polymorphic properties of the language. An application description describes how the subsystems interact between each other, and a high-level system-architecture document describes how the applications interact among each other on one processor. For more in-depth reading on how Erlang / OTP promotes
new design techniques, see [Keisu, 1998].
[17] A unique reference for the Erlang system. [18] A process identifier. [19] Records are translated by the pre-processor to tuples, and operations on them to functions applied to these tuples. [20] The information
model.
[21] The /3 in the spawn BIF denotes that the call takes three arguments. [23] Strings do not exist in Erlang, and are instead represented as lists of ASCII characters. [24] Erlang term storage [25] For an up to date list, see the OTP reference
manual and user guide. |
|||||||||||||||||||||||||||||||||