1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- This document was generated using DocBuilder 3.3.3 -->
<HTML>
<HEAD>
<TITLE>Ports</TITLE>
<SCRIPT type="text/javascript" src="../../doc/erlresolvelinks.js">
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#FF00FF"
ALINK="#FF0000">
<CENTER>
<A HREF="http://www.erlang.se"><IMG BORDER=0 ALT="[Ericsson AB]" SRC="min_head.gif"></A>
</CENTER>
<A NAME="4"><!-- Empty --></A>
<H2>4 Ports</H2>
<P>This is an example of how to solve the <A HREF="example.html">example problem</A> by using a port.
<P>
<CENTER>
<IMG ALT="port" SRC="port.gif"><BR>
<EM>Port Communication.</EM>
</CENTER>
<A NAME="4.1"><!-- Empty --></A>
<H3>4.1 Erlang Program</H3>
<P>First of all communication between Erlang and C must be established by creating the port. The Erlang process which creates a port is said to be <STRONG>the connected process</STRONG> of the port. All communication to and from the port should go via the connected process. If the connected process terminates, so will the port (and the external program, if it is written correctly).
<P>The port is created using the BIF <CODE>open_port/2</CODE> with <CODE>{spawn,ExtPrg}</CODE> as the first argument. The string <CODE>ExtPrg</CODE> is the name of the external program, including any command line arguments. The second argument is a list of options, in this case only <CODE>{packet,2}</CODE>. This option says that a two byte length indicator will be used to simplify the communication between C and Erlang. Adding the length indicator will be done automatically by the Erlang port, but must be done explicitly in the external C program.
<P>The process is also set to trap exits which makes it possible to detect if the external program fails.
<PRE>
-module(complex1).
-export([start/1, init/1]).
start(ExtPrg) ->
spawn(?MODULE, init, [ExtPrg]).
init(ExtPrg) ->
register(complex, self()),
process_flag(trap_exit, true),
Port = open_port({spawn, ExtPrg}, [{packet, 2}]),
loop(Port).
</PRE>
<P>Now it is possible to implement <CODE>complex1:foo/1</CODE> and <CODE>complex1:bar/1</CODE>. They both send a message to the <CODE>complex</CODE> process and receive the reply.
<PRE>
foo(X) ->
call_port({foo, X}).
bar(Y) ->
call_port({bar, Y}).
call_port(Msg) ->
complex ! {call, self(), Msg},
receive
{complex, Result} ->
Result
end.
</PRE>
<P>The <CODE>complex</CODE> 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.
<PRE>
loop(Port) ->
receive
{call, Caller, Msg} ->
Port ! {self(), {command, encode(Msg)}},
receive
{Port, {data, Data}} ->
Caller ! {complex, decode(Data)}
end,
loop(Port)
end.
</PRE>
<P>Assumong 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 <CODE>foo</CODE> is represented by the byte 1, <CODE>bar</CODE> is represented by 2, and the argument/result is represented by a single byte as well.
<PRE>
encode({foo, X}) -> [1, X];
encode({bar, Y}) -> [2, Y].
decode([Int]) -> Int.
</PRE>
<P>The resulting Erlang program, including functionality for stopping the port and detecting port failures, can be found in
<A TARGET="_top" HREF="complex1.erl">complex1.erl.</A><A NAME="4.2"><!-- Empty --></A>
<H3>4.2 C Program</H3>
<P>On the C side, it is necessary to write functions for receiving and sending
data with two byte length indicators from/to Erlang. By default, the C program
should read from standard input (file descriptor 0) and write to standard output
(file descriptor 1). Examples of such functions, <CODE>read_cmd/1</CODE> and
<CODE>write_cmd/2</CODE>, can be found in the file
<A TARGET="_top" HREF="erl_comm.c">erl_comm.c</A>
.
<P> Note that <CODE>stdin</CODE> and <CODE>stdout</CODE> are for buffered input/output and should not be used for the communication with Erlang!
<P>In the <CODE>main</CODE> function, the C program should listen for a message from Erlang and, according to the selected encoding/decoding scheme, use the first byte to determine which function to call and the second byte as argument to the function. The result of calling the function should then be sent back to Erlang.
<PRE>
/* port.c */
typedef unsigned char byte;
int main() {
int fn, arg, res;
byte buf[100];
while (read_cmd(buf) > 0) {
fn = buf[0];
arg = buf[1];
if (fn == 1) {
res = foo(arg);
} else if (fn == 2) {
res = bar(arg);
}
buf[0] = res;
write_cmd(buf, 1);
}
}
</PRE>
<P>Note that the C program is in a <CODE>while</CODE>-loop checking for the return value of <CODE>read_cmd/1</CODE>. The reason for this is that the C program must detect when the port gets closed and terminate.<A NAME="4.3"><!-- Empty --></A>
<H3>4.3 Running the Example</H3>
<P>1. Compile the C code.
<PRE>
unix> gcc -o extprg complex.c erl_comm.c port.c
</PRE>
<P>2. Start Erlang and compile the Erlang code.
<PRE>
unix> erl
Erlang (BEAM) emulator version 4.9.1.2
Eshell V4.9.1.2 (abort with ^G)
1> c(complex1).
{ok,complex1}
</PRE>
<P>3. Run the example.
<PRE>
2> complex1:start("extprg").
<0.34.0>
3> complex1:foo(3).
4
4> complex1:bar(5).
10
5> complex1:stop().
stop
</PRE>
<CENTER>
<HR>
<SMALL>
Copyright © 1991-2006
<A HREF="http://www.erlang.se">Ericsson AB</A><BR>
</SMALL>
</CENTER>
</BODY>
</HTML>
|