Erlang 21 - 4. Ports

4 पोर्ट




erlang

4 पोर्ट

यह अनुभाग एक उदाहरण का उपयोग करता है कि पोर्ट का उपयोग करके previous section में उदाहरण की समस्या को कैसे हल किया जाए।

परिदृश्य निम्न चित्र में चित्रित किया गया है:

चित्र 4.1: पोर्ट कम्युनिकेशन

4.1 एर्लांग कार्यक्रम

पोर्ट बनाकर एर्लांग और सी के बीच सभी संचार स्थापित किए जाने चाहिए। पोर्ट बनाने वाली Erlang प्रक्रिया को पोर्ट की कनेक्टेड प्रक्रिया कहा जाता है। पोर्ट से और उससे जुड़े सभी संचार कनेक्टेड प्रक्रिया से गुजरना चाहिए। यदि कनेक्टेड प्रक्रिया समाप्त हो जाती है, तो पोर्ट भी समाप्त हो जाता है (और बाहरी प्रोग्राम, यदि यह ठीक से लिखा गया है)।

पोर्ट को पहले तर्क के रूप में {spawn,ExtPrg} साथ BIF open_port/2 का उपयोग करके बनाया गया है। स्ट्रिंग ExtPrg किसी भी कमांड लाइन के तर्कों सहित बाहरी प्रोग्राम का नाम है। दूसरा तर्क विकल्पों की एक सूची है, केवल इस मामले में {packet,2} । यह विकल्प कहता है कि C और Erlang के बीच संचार को सरल बनाने के लिए एक 2 बाइट की लंबाई के संकेतक का उपयोग किया जाना है। Erlang पोर्ट स्वचालित रूप से लंबाई संकेतक जोड़ता है, लेकिन यह बाहरी C प्रोग्राम में स्पष्ट रूप से किया जाना चाहिए।

प्रक्रिया भी बाहर निकलने के लिए तैयार है, जो बाहरी कार्यक्रम की विफलता का पता लगाने में सक्षम है:

-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).

अब complex1:foo/1 और complex1:bar/1 को लागू किया जा सकता है। दोनों complex प्रक्रिया को संदेश भेजते हैं और निम्नलिखित उत्तर प्राप्त करते हैं:

foo(X) ->
  call_port({foo, X}).
bar(Y) ->
  call_port({bar, Y}).

call_port(Msg) ->
  complex ! {call, self(), Msg},
  receive
    {complex, Result} ->
      Result
  end.

complex प्रक्रिया निम्नलिखित करती है:

  • संदेश को बाइट्स के अनुक्रम में एन्कोड करता है।
  • इसे बंदरगाह पर भेजता है।
  • उत्तर की प्रतीक्षा करता है।
  • उत्तर को अस्वीकार करता है।
  • इसे वापस कॉलर को भेजता है:
loop(Port) ->
  receive
    {call, Caller, Msg} ->
      Port ! {self(), {command, encode(Msg)}},
      receive
        {Port, {data, Data}} ->
          Caller ! {complex, decode(Data)}
      end,
      loop(Port)
  end.

यह मानते हुए कि दोनों फ़ंक्शन और सी फ़ंक्शन के परिणाम 256 से कम हैं, एक साधारण एन्कोडिंग / डिकोडिंग योजना कार्यरत है। इस योजना में, foo को बाइट 1 द्वारा दर्शाया गया है, bar को 2 द्वारा दर्शाया गया है, और तर्क / परिणाम एक एकल बाइट द्वारा दर्शाया गया है:

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

decode([Int]) -> Int.

पोर्ट को रोकने और पोर्ट फेल्योर का पता लगाने के लिए कार्यक्षमता सहित परिणामी एरलांग कार्यक्रम निम्नानुसार है:

-module(complex1).
-export([start/1, stop/0, init/1]).
-export([foo/1, bar/1]).

start(ExtPrg) ->
    spawn(?MODULE, init, [ExtPrg]).
stop() ->
    complex ! stop.

foo(X) ->
    call_port({foo, X}).
bar(Y) ->
    call_port({bar, Y}).

call_port(Msg) ->
    complex ! {call, self(), Msg},
    receive
	{complex, Result} ->
	    Result
    end.

init(ExtPrg) ->
    register(complex, self()),
    process_flag(trap_exit, true),
    Port = open_port({spawn, ExtPrg}, [{packet, 2}]),
    loop(Port).

loop(Port) ->
    receive
	{call, Caller, Msg} ->
	    Port ! {self(), {command, encode(Msg)}},
	    receive
		{Port, {data, Data}} ->
		    Caller ! {complex, decode(Data)}
	    end,
	    loop(Port);
	stop ->
	    Port ! {self(), close},
	    receive
		{Port, closed} ->
		    exit(normal)
	    end;
	{'EXIT', Port, Reason} ->
	    exit(port_terminated)
    end.

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

decode([Int]) -> Int.

4.2 सी कार्यक्रम

C की तरफ, यह 2 बाइट लंबाई संकेतक के साथ / से Erlang तक डेटा प्राप्त करने और भेजने के लिए फ़ंक्शन लिखना आवश्यक है। डिफ़ॉल्ट रूप से, सी प्रोग्राम को मानक इनपुट (फ़ाइल डिस्क्रिप्टर 0) से पढ़ना है और मानक आउटपुट (फाइल डिस्क्रिप्टर 1) पर लिखना है। ऐसे कार्यों के उदाहरण, read_cmd/1 और write_cmd/2 , इस प्रकार हैं:

/* erl_comm.c */

typedef unsigned char byte;

read_cmd(byte *buf)
{
  int len;

  if (read_exact(buf, 2) != 2)
    return(-1);
  len = (buf[0] << 8) | buf[1];
  return read_exact(buf, len);
}

write_cmd(byte *buf, int len)
{
  byte li;

  li = (len >> 8) & 0xff;
  write_exact(&li, 1);
  
  li = len & 0xff;
  write_exact(&li, 1);

  return write_exact(buf, len);
}

read_exact(byte *buf, int len)
{
  int i, got=0;

  do {
    if ((i = read(0, buf+got, len-got)) <= 0)
      return(i);
    got += i;
  } while (got<len);

  return(len);
}

write_exact(byte *buf, int len)
{
  int i, wrote = 0;

  do {
    if ((i = write(1, buf+wrote, len-wrote)) <= 0)
      return (i);
    wrote += i;
  } while (wrote<len);

  return (len);
}

ध्यान दें कि stdin और stdout बफर इनपुट / आउटपुट के लिए हैं और stdout साथ संचार के लिए उपयोग नहीं किया जाना चाहिए।

main फ़ंक्शन में, सी प्रोग्राम एरलंग के एक संदेश को सुनना है और चयनित एन्कोडिंग / डिकोडिंग योजना के अनुसार, पहले बाइट का उपयोग करके निर्धारित करें कि किस फ़ंक्शन को कॉल करना है और दूसरा बाइट फ़ंक्शन के तर्क के रूप में है। फ़ंक्शन को कॉल करने का परिणाम फिर से एर्लांग में भेजा जाना है:

/* 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);
  }
}

ध्यान दें कि C प्रोग्राम while लिए है, read_cmd/1 के रिटर्न मान की जाँच कर रहा है। ऐसा इसलिए है क्योंकि पोर्ट बंद होने और समाप्त होने पर C प्रोग्राम का पता लगाना चाहिए।

4.3 उदाहरण चल रहा है

चरण 1. सी कोड संकलित करें:

unix> gcc -o extprg complex.c erl_comm.c port.c

चरण 2. Erlang प्रारंभ करें और Erlang कोड संकलित करें:

unix> erl
Erlang (BEAM) emulator version 4.9.1.2

Eshell V4.9.1.2 (abort with ^G)
1> c(complex1).
{ok,complex1}

चरण 3. उदाहरण चलाएँ:

2> complex1:start("extprg").
<0.34.0>
3> complex1:foo(3).
4
4> complex1:bar(5).
10
5> complex1:stop().
stop