programming erlang ch12 - andstudy/forge GitHub Wiki

Chapt 12. ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ฒ•

์˜ˆ์ œ ์„ค๋ช…

  • example1.c

      int twice(int x) {
      	return 2 * x;
      }
      
      int sum(int x, int y) {
      	return x + y;
      }
    
  • erlang์—์„œ ์‹คํ–‰ํ•˜๋Š” ํ˜•ํƒœ

      X1 = example1:twice(23).
      Y1 = example1:sum(45, 32).
    

ํฌํŠธ

  • ์–ผ๋žญ ๋ณ‘ํ–‰ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ์†ก์ˆ˜์‹ ํ•˜๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•˜๊ฒŒ ๋™์ž‘

  • C ํ”„๋กœ๊ทธ๋žจ์€ ์–ผ๋žญ ์‹œ์Šคํ…œ ์™ธ๋ถ€์—์„œ ์‹คํ–‰๋˜๊ณ , ํฌํŠธ๋ฅผ ํ†ตํ•ด์„œ๋งŒ ๋ฉ”์‹œ์ง€๋ฅผ ์†ก์ˆ˜์‹ 

  • ๊ฐ€์žฅ ๊ถŒ์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•

  • ํฌํŠธ์ƒ์„ฑ

      @spec open_port(PortName, [Opt]) -> Port
    
  • ํฌํŠธ๋ฅผ ํ†ตํ•ด C ํ”„๋กœ๊ทธ๋žจ์œผ๋กœ ๋ฉ”์‹œ์ง€ ์ „์†ก ๋ฐ ์ˆ˜์‹ 

      Port ! {PidC, {command, Data}}
      Port ! {PidC, {connect, Pid1}}
      Port ! {PidC, close}
      
      receive
          {Port, {data, Data}} ->
             ...
    

Erlang ์ธก๋ฉด

  • example1.erl

      ...
      
      start() ->
      ...
      		  Port = open_port({spawn, "./example1"}, [{packet, 2}]),
      ...
      
      loop(Port) ->
          receive
      	{call, Caller, Msg} ->
      	    Port ! {self(), {command, encode(Msg)}}, 
      	    receive
      		{Port, {data, Data}} ->
      		    Caller ! {example1, decode(Data)}
      	    end,
      	    loop(Port);
      	stop ->
      	    Port ! {self(), close},
      	    receive
      		{Port, closed} ->
      		    exit(normal)
      	    end;
      	{'EXIT', Port, Reason} ->
      	    exit({port_terminated,Reason})
          end.
      
      encode({twice, X}) -> [1, X];  
      encode({sum, X, Y}) -> [2, X, Y]. 
      
      decode([Int]) -> Int. 
    
  • Port = open_port({spawn, "./example1"}, [{packet, 2}])

    • 2๋ฐ”์ดํŠธ ํ—ค๋” ์ž๋™ ์ถ”๊ฐ€ (1, 2, 4์ค‘ ํƒ์ผ)
    • twice(10) ์ผ ๊ฒฝ์šฐ, <<0, 2, 1, 10>>
  • {Port, {data, Data}}

    • ๋ฐ์ดํ„ฐ ์ˆ˜์‹ 
  • {Port, closed}

    • ํฌํŠธ๊ฐ€ ๋‹ซํ˜”์„ ๊ฒฝ์šฐ ์ˆ˜์‹ 
  • {'EXIT', Port, Reason}

    • ์—๋Ÿฌ๊ฐ€ ๋‚ฌ์„ ๊ฒฝ์šฐ ์ˆ˜์‹ 

C ์ธก๋ฉด

  • example_driver1.c

      int main() {
      	int fn, arg1, arg2, result;
      	byte buff[100];
      
      	while (read_cmd(buff) > 0) {
      		fn = buff[0];
      
      		if (fn == 1) {
      			arg1 = buff[1];
      			result = twice(arg1);
      		} else if (fn == 2) {
      			arg1 = buff[1];
      			arg2 = buff[2];
      			result = sum(arg1, arg2);
      		}
      
      		buff[0] = result;
      		write_cmd(buff, 1);
      	}
      }
    
  • erl_comm.c

      int 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);
      }
      
      int 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);
      }
      
      int 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);
      }
      
      int 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);
      }
    
  • read(0, buf+got, len-got)

    • ํ‘œ์ค€ ์ž…๋ ฅ์„ ํ†ตํ•ด erlang์œผ๋กœ๋ถ€ํ„ฐ ๋ฉ”์‹œ์ง€ ์ˆ˜์‹ 
  • write(1, buf+wrote, len-wrote)

    • ํ‘œ์ค€ ์ถœ๋ ฅ์„ ํ†ตํ•ด erlang์œผ๋กœ ๋ฉ”์‹œ์ง€ ์†ก์‹ 
  • erlang๊ณผ์˜ ํŒจํ‚ท์„ ๋งž์ถ”๊ธฐ ์œ„ํ•ด write_cmd ์— 2๋ฐ”์ดํŠธ ํ—ค๋” ์ˆ˜๋™์œผ๋กœ ์ถ”๊ฐ€

์‹คํ–‰

    1> example1:start().
    <0.32.0>
    2> example1:sum(43, 32).
    77
    3> example1:twice(10).
    20

๋งํฌ-์ธ ๋“œ๋ผ์ด๋ฒ„

  • ํฌํŠธ ํ”„๋กœ๊ทธ๋žจ๊ณผ ์ •ํ™•ํ•˜๊ฒŒ ๋˜‘๊ฐ™์€ ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅธ๋‹ค
  • ์–ผ๋žญ ์‹œ์Šคํ…œ "๋‚ด๋ถ€" ์—์„œ ์‹คํ–‰๋œ๋‹ค๋Š” ์ ์ด ํฌํŠธ์™€์˜ ์ฐจ์ด์ 
  • ํšจ์œจ์„ฑ ์ธก๋ฉด์—์„œ ์ข‹์„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ๊ฒฝ์šฐ ์–ผ๋žญ ์‹œ์Šคํ…œ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ์œ„ํ—˜์„ฑ

Erlang ์ธก๋ฉด

  • example1_lid.erl

      ...
      
      start() ->
          start("example1_drv").
      
      start(SharedLib) ->
          case erl_ddll:load_driver(".", SharedLib) of
      	ok -> ok;
      	{error, already_loaded} -> ok;
      	_ -> exit({error, could_not_load_driver})
          end,
          spawn(fun() -> init(SharedLib) end).
      
      init(SharedLib) ->
          register(example1_lid, self()),
          Port = open_port({spawn, SharedLib}, []),
          loop(Port).
      
      ...
    
  • erl_ddll:load_driver(".", SharedLib)

    • C ๋“œ๋ผ์ด๋ฒ„๋ฅผ (Unix์—์„œ๋Š” so, Windows์—์„œ๋Š” dll) ์–ผ๋žญ ์‹œ์Šคํ…œ ๋‚ด๋ถ€์— ๋กœ๋”ฉ
  • open_port({spawn, SharedLib}, [])

    • ํฌํŠธ ์ƒ์„ฑ

C ์ธก๋ฉด

  • example1_lid.c

      #include <stdio.h>
      #include "erl_driver.h"
      
      typedef struct {
          ErlDrvPort port;
      } example_data;
      
      static ErlDrvData example_drv_start(ErlDrvPort port, char *buff)
      {
          example_data* d = (example_data*)driver_alloc(sizeof(example_data));
          d->port = port;
          return (ErlDrvData)d;
      }
      
      static void example_drv_stop(ErlDrvData handle)
      {
          driver_free((char*)handle);
      }
      
      static void example_drv_output(ErlDrvData handle, char *buff, int bufflen)
      {
          example_data* d = (example_data*)handle;
          char fn = buff[0], arg = buff[1], res;
          if (fn == 1) {
            res = twice(arg);
          } else if (fn == 2) {
            res = sum(buff[1], buff[2]);
          }
          driver_output(d->port, &res, 1);
      }
      
      ErlDrvEntry example_driver_entry = {
          NULL,               /* F_PTR init, N/A */
          example_drv_start,  /* L_PTR start, called when port is opened */
          example_drv_stop,   /* F_PTR stop, called when port is closed */
          example_drv_output, /* F_PTR output, called when erlang has sent
      			   data to the port */
          NULL,               /* F_PTR ready_input, 
                                 called when input descriptor ready to read*/
          NULL,               /* F_PTR ready_output, 
                                 called when output descriptor ready to write */
          "example1_drv",     /* char *driver_name, the argument to open_port */
          NULL,               /* F_PTR finish, called when unloaded */
          NULL,               /* F_PTR control, port_command callback */
          NULL,               /* F_PTR timeout, reserved */
          NULL                /* F_PTR outputv, reserved */
      };
      
      DRIVER_INIT(example_drv) /* must match name in driver_entry */
      {
          return &example_driver_entry;
      }
    

}}}

  • driver_alloc
    • thread-safe, driver_free๋ฅผ ํ†ตํ•ด ๋ช…์‹œ์ ์œผ๋กœ ํ•ด์ œํ•ด์•ผ ํ•จ
  • driver_free
    • thread-safe, driver_alloc์„ ํ†ตํ•ด ์ƒ์„ฑํ•œ ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ
  • driver_output
    • Port Owner๋กœ ๋ฉ”์‹œ์ง€ ์†ก์‹ 

์‹คํ–‰

    1> c(example1_lid).
    {ok, example1_lid}
    2> example1_lid:start().
    <0.41.0>
    3> example1_lid:twice(50).
    100
    4> example1_lid:sum(10, 20).
    30

C ๋…ธ๋“œ

  • ๋ถ„์‚ฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ์‹คํ–‰
  • ์–ผ๋žญ ํ”„๋กœ๊ทธ๋žจ ๊ด€์ ์—์„œ C ํ”„๋กœ๊ทธ๋žจ์ด ํ•˜๋‚˜์˜ ๋…ธ๋“œ์ฒ˜๋Ÿผ ํ–‰๋™
  • erl_interface๋ฅผ ํ†ตํ•ด C ํ”„๋กœ๊ทธ๋žจ ์ž‘์„ฑ
  • ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋งค์šฐ ๋“œ๋ฌผ๋‹ค (๋Œ€๋ถ€๋ถ„ ํฌํŠธ๋กœ ์ถฉ๋ถ„)

Erlang ์ธก๋ฉด

  • example1_cnode.erl

      -module(example1_cnode).
      -export([twice/1, sum/2]).
      
      twice(X) -> X * 2.
      
      sum(X, Y) -> X + Y.
    

C ์ธก๋ฉด

  • example1_cnode.c

      #define __WIN32__
      
      #include "C:\Program Files\erl5.6.3\lib\erl_interface-3.5.7\include\erl_interface.h"
      #include "C:\Program Files\erl5.6.3\lib\erl_interface-3.5.7\include\ei.h"
      
      int main(int argc, char **argv) {
        int fd;                                  /* fd to Erlang node */
        int res;
      
        ETERM *rpcp, *resp, *argp;
        
        erl_init(NULL, 0);
      
        if (erl_connect_init(1, "secretcookie", 0) == -1)
          erl_err_quit("erl_connect_init");
      
        if ((fd = erl_connect("erlang_node@SSB-PC")) < 0)
          erl_err_quit("erl_connect");
        fprintf(stderr, "Connected to erlang_node@SSB-PC\n\r");
      
      /* twice(10) */
      	argp = erl_format("[~i]", 10);
      	rpcp = erl_rpc(fd, "example1_cnode", "twice", argp);
      	res = ERL_INT_VALUE(rpcp);
      
      	fprintf(stderr, "rpc:call(erlang_node@SSB-PC, example1_cnode, twice, [10])\r\n%d\r\n", res);
      
          erl_free_term(argp);
          erl_free_term(rpcp);
      
      /* sum(11, 22) */
      	argp = erl_format("[~i, ~i]", 11, 22);
      	rpcp = erl_rpc(fd, "example1_cnode", "sum", argp);
      	res = ERL_INT_VALUE(rpcp);
      
      	fprintf(stderr, "rpc:call(erlang_node@SSB-PC, example1_cnode, sum, [11, 22])\r\n%d\r\n", res);
      
          erl_free_term(argp);
          erl_free_term(rpcp);
      }
    

์‹คํ–‰

    d:\>erl -sname erlang_node -setcookie secretcookie
    d:\>example1_cnode.exe
    Connected to erlang_node@SSB-PC
    rpc:call(erlang_node@SSB-PC, example1_cnode, twice, [10])
    20
    rpc:call(erlang_node@SSB-PC, example1_cnode, sum, [11, 22])
    33
โš ๏ธ **GitHub.com Fallback** โš ๏ธ