From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- system/doc/tutorial/c_portdriver.xmlsrc | 183 ++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 system/doc/tutorial/c_portdriver.xmlsrc (limited to 'system/doc/tutorial/c_portdriver.xmlsrc') diff --git a/system/doc/tutorial/c_portdriver.xmlsrc b/system/doc/tutorial/c_portdriver.xmlsrc new file mode 100644 index 0000000000..f875fa80d2 --- /dev/null +++ b/system/doc/tutorial/c_portdriver.xmlsrc @@ -0,0 +1,183 @@ + + + + +
+ + 20002009 + Ericsson AB. All Rights Reserved. + + + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + + + Port drivers + + + + + c_portdriver.xml +
+

This is an example of how to solve the example problem by using a linked in port driver.

+ + Port Driver Communication. + + +
+ Port Drivers +

A port driver is a linked in driver, that is accessible as a + port from an Erlang program. It is a shared library (SO in Unix, + DLL in Windows), with special entry points. The Erlang runtime + calls these entry points, when the driver is started and when + data is sent to the port. The port driver can also send data to + Erlang.

+

Since a port driver is dynamically linked into the emulator + process, this is the fastest way of calling C-code from Erlang. + Calling functions in the port driver requires no context + switches. But it is also the least safe, because a crash in the + port driver brings the emulator down too.

+
+ +
+ Erlang Program +

Just as with a port program, the port communicates with a Erlang + process. All communication goes through one Erlang process that + is the connected process of the port + driver. Terminating this process closes the port driver.

+

Before the port is created, the driver must be loaded. This is + done with the function erl_dll:load_driver/1, with the + name of the shared library as argument.

+

The port is then created using the BIF open_port/2 with + the tuple {spawn, DriverName} as the first argument. The + string SharedLib is the name of the port driver. The second + argument is a list of options, none in this case.

+
+-module(complex5).
+-export([start/1, init/1]).
+
+start(SharedLib) ->
+    case erl_ddll:load_driver(".", SharedLib) of
+        ok -> ok;
+\011{error, already_loaded} -> ok;
+\011_ -> exit({error, could_not_load_driver})
+    end,
+    spawn(?MODULE, init, [SharedLib]).
+
+init(SharedLib) ->
+  register(complex, self()),
+  Port = open_port({spawn, SharedLib}, []),
+  loop(Port).
+

Now it is possible to implement complex5:foo/1 and + complex5:bar/1. They both send a message to the + complex process and receive the reply.

+
+foo(X) ->
+    call_port({foo, X}).
+bar(Y) ->
+    call_port({bar, Y}).
+
+call_port(Msg) ->
+    complex ! {call, self(), Msg},
+    receive
+        {complex, Result} ->
+            Result
+    end.
+

The complex process encodes the message into a sequence + of bytes, sends it to the port, waits for a reply, decodes the + reply and sends it back to the caller. +

+
+loop(Port) ->
+    receive
+        {call, Caller, Msg} ->
+            Port ! {self(), {command, encode(Msg)}},
+            receive
+\011        {Port, {data, Data}} ->
+                    Caller ! {complex, decode(Data)}
+            end,
+            loop(Port)
+    end.
+

Assuming that both the arguments and the results from the C + functions will be less than 256, a very simple encoding/decoding + scheme is employed where foo is represented by the byte + 1, bar is represented by 2, and the argument/result is + represented by a single byte as well. +

+
+encode({foo, X}) -> [1, X];
+encode({bar, Y}) -> [2, Y].
+      
+decode([Int]) -> Int.
+

The resulting Erlang program, including functionality for + stopping the port and detecting port failures is shown below.

+ +
+ +
+ C Driver +

The C driver is a module that is compiled and linked into a + shared library. It uses a driver structure, and includes the + header file erl_driver.h.

+

The driver structure is filled with the driver name and function + pointers. It is returned from the special entry point, declared + with the macro )]]>.

+

The functions for receiving and sending data, are combined into + a function, pointed out by the driver structure. The data sent + into the port is given as arguments, and the data the port + sends back is sent with the C-function driver_output.

+

Since the driver is a shared module, not a program, no main + function should be present. All function pointers are not used + in our example, and the corresponding fields in the + driver_entry structure are set to NULL.

+

All functions in the driver, takes a handle (returned from + start), that is just passed along by the erlang + process. This must in some way refer to the port driver + instance.

+

The example_drv_start, is the only function that is called with + a handle to the port instance, so we must save this. It is + customary to use a allocated driver-defined structure for this + one, and pass a pointer back as a reference.

+

It is not a good idea to use a global variable; since the port + driver can be spawned by multiple Erlang processes, this + driver-structure should be instantiated multiple times. +

+ +
+ +
+ Running the Example +

1. Compile the C code.

+
+unix> gcc -o exampledrv -fpic -shared complex.c port_driver.c
+windows> cl -LD -MD -Fe exampledrv.dll complex.c port_driver.c
+

2. Start Erlang and compile the Erlang code.

+
+> erl
+Erlang (BEAM) emulator version 5.1
+
+Eshell V5.1 (abort with ^G)
+1> c(complex5).
+{ok,complex5}
+

3. Run the example.

+
+2> complex5:start("example_drv").
+<0.34.0>
+3> complex5:foo(3).
+4
+4> complex5:bar(5).
+10
+5> complex5:stop().
+stop
+
+
+ -- cgit v1.2.3