Within Erlang, all communication is done by asynchronous signaling. The communication between an Erlang node and the outside world is done through a port. A port is an interface between Erlang processes and an external resource. In early versions of Erlang a port behaved very much in the same way as a process and you communicated by sending and receiving signals. You can still communicate with ports this way but there are also a number of BIFs to communicate directly with a port.
In this chapter we will look at how ports are used as a common interface for all IO, how ports communicate with the outside world and how Erlang processes communicate with ports. But first we will look at how standard IO works on a higher level.
-
IO protocol
-
group leader
-
erlang:display — a BIF that sends directly to node’s std out
-
io:format — sends through io protocol and the group leader
-
Redirecting standard IO at startup (detached mode)
-
Standard in and out
A port is the process like interface between Erlang processes and everything that is not Erlang processes. The programmer can to a large extent pretend that everything in the world behaves like an Erlang process and communicate through message passing.
Each port has an owner, more on this later, but all processes who know about the port can send messages to the port. In figure REF we see how a processes can communicate with the port and how the port is communicating to the world outside the Erlang node.
Erlang Node +-------------------+ | __ | | / \ owner __ _______ | \__/ < - -- -> / \ <----> / / | P1 \__/ /______/ | 7 |Port1 file | / | | / | | __ / | | / \ | | \__/ | | P2 | +-------------------+ options: - ".*": {fill: [[0.7, 0.7, 0.7], no-shadow], frame: [[0.9, 0.9, 0.9], line]} - ".*": {text : ["Monospace 10", no-shadow]}
Process P1 has opened a port (Port1) to a file, and is the owner of the port and can receive messages from the port. Process P2 also has a handle to the port and can send messages to the port. The processes and the port reside in an Erlang node. The file lives in the file and operating system on the outside of the Erlang node.
If the port owner dies or is terminated the port is also killed. When a port terminates all external resources should also be cleaned up. This is true for all ports that come with Erlang and if you implement your own port you should make sure it does this cleanup.
There are three different classes of ports: file descriptors, external programs and drivers. A file descriptor port makes it possible for a process to access an already opened file descriptor. A port to an external program invokes the external program as a separate OS process. A driver port requires a driver to be loaded in the Erlang node.
All ports are created by a call to erlang:open_port(PortName, PortSettings).
A file descriptor port is opened with {fd, In, Out} as the PortName. This class of ports is used by some internal ERTS servers like the old shell. They are considered to not be very efficient and hence seldom used.
An external program port can be used to execute any program in the native OS of the Erlang node. To open an external program port you give either the argument {spawn, Command} or {spawn_executable, FileName} with the name of the external program. This is the easiest and one of the safest way to interact with code written in other programming languages. Since the external program is executed in its own OS process it will not bring down the Erlang node if it crashes. (It can of course use up all CPU or memory or do a number of other things to bring down the whole OS, but it is much safer than a linked in driver or a NIF).
A driver port requires that a driver program has been loaded with ERTS. Such a port is started with either {spawn, Command} or {spawn_driver, Command}. Writing your own linked in driver can be an efficient way to interface for example some C library code that you would like to use. Note that a linked in driver executes in the same OS process as the Erlang node and a crash in the driver will bring down the whole node. Details about how to write an Erlang driver in general can be found in [CH-C].
Erlang/OTP comes with a number port drivers implementing the
predefined port types. There are the common drivers available on all
platforms: tcp_inet
, udp_inet
, sctp_inet
, efile
, zlib_drv
,
ram_file_drv
, binary_filer
, tty_sl
. These drivers are used to
implement e.g. file handling and sockets in Erlang. On Windows there
is also a driver to access the registry: registry_drv
. And on most
platforms there are example drivers to use when implementing your own
driver like: multi_drv
and sig_drv
.
+-----------------------------------------------------------------------------------------------------+ | Entities on an Erlang Node | |--------------------------------------------------+--------------------------------------------------| | Processes | Ports | |--------------------------------------------------+---------++------++-------------------------------| | | FD || OS || Drivers | |--------------------------------------------------+---------++------++----------+++-------+++--------| | | (Heart) || (ls) || tcp_inet ||| efile ||| ... | +--------------------------------------------------+---------++------++----------+++-------+++--------+ options: - ".*": {fill: [[0.7, 0.7, 0.7], no-shadow], frame: [[0.9, 0.9, 0.9], line]} - ".*": {text : ["Monospace 10", no-shadow]}
Creating
Commands
Implementation
See chapter [CH-C] for details of how to implement your own linked in driver.