포트와 포트 드라이버
포트와 포트 드라이버 (Ports and Port Drivers)
Erlang은 외부 세계와 어떻게 소통할까요? 그 답이 바로 포트(port)예요. 포트는 외부 프로그램과 바이트 단위로 데이터를 주고받는 통로이고, 포트 드라이버는 이 통로를 C로 직접 구현해 Erlang 런타임에 연결한 형태를 말해요. 포트와 포트 드라이버를 다루는 예시는 Interoperability Tutorial에서 확인할 수 있고, 여기서 언급하는 BIF에 대한 정보는 ERTS의 m:erlang 모듈을 참고하세요.
포트 (Ports)
포트 는 Erlang 관점에서 외부 세계와 통신하기 위한 기본 메커니즘이에요. 포트는 외부 프로그램에 바이트 지향 인터페이스를 제공합니다. 포트가 생성되면 Erlang은 바이너리를 포함한 바이트 목록을 보내고 받는 방식으로 포트와 통신할 수 있어요.
포트를 생성하는 Erlang 프로세스는 그 포트의 포트 소유자(port owner) 또는 연결된 프로세스(connected process) 라고 해요. 포트로 들어오고 나가는 모든 통신은 포트 소유자를 거쳐야 합니다. 포트 소유자가 종료하면 포트도 (그리고 올바르게 작성됐다면 외부 프로그램도) 종료돼요.
외부 프로그램은 별도의 OS 프로세스에 있어요. 기본적으로 표준 입력(파일 디스크립터 0)에서 읽고 표준 출력(파일 디스크립터 1)으로 씁니다. 포트가 닫히면 외부 프로그램도 종료되어야 해요.
포트 드라이버 (Port Drivers)
특정 원칙에 따라 C로 드라이버를 작성해서 Erlang 런타임 시스템에 동적으로 연결할 수 있어요. 이렇게 연결된 드라이버는 Erlang 프로그래머 관점에서 포트처럼 보이고, 이를 포트 드라이버(port driver) 라고 부릅니다.
경고 {: .warning }
잘못된 포트 드라이버는 Erlang 런타임 시스템 전체에 메모리 누수, 멈춤, 크래시를 일으킬 수 있어요.
포트 드라이버에 대한 정보는 다음을 참고하세요:
- ERTS의 erl_driver
- ERTS의 driver_entry
- Kernel의
erl_ddll
포트 BIF (Port BIFs)
포트를 만들려면 open_port(PortName, PortSettings)을 호출해요. 이 호출은 새 포트를 여는 결과로 포트 식별자 Port를 반환합니다. 포트 식별자에는 PID처럼 메시지를 보내고 받을 수 있어요. 포트 식별자는 link/1로 링크하거나 register/2로 이름 아래에 등록할 수도 있습니다.
PortName은 보통 튜플 {spawn,Command}이고, 여기서 문자열 Command는 외부 프로그램의 이름이에요. 외부 프로그램은 Command라는 이름의 포트 드라이버를 찾지 못하는 한 Erlang 워크스페이스 밖에서 실행됩니다. Command를 찾으면 그 드라이버가 시작돼요.
PortSettings는 포트의 설정(옵션) 목록이에요. 이 목록은 보통 최소한 튜플 {packet,N}을 포함하는데, 이는 포트와 외부 프로그램 사이에 전송되는 데이터 앞에 N바이트 길이 표시자가 붙는다는 것을 지정해요. N의 유효한 값은 1, 2, 4입니다. 바이트 목록 대신 바이너리를 사용하려면 binary 옵션을 포함해야 해요.
포트 소유자 Pid는 메시지를 보내고 받는 방식으로 포트 Port와 통신할 수 있어요. (실은 아무 프로세스나 포트에 메시지를 보낼 수 있지만, 메시지에 포트 소유자를 명시해야 합니다.)
포트에 보내는 메시지는 비동기적으로 전달돼요.
변경 {: .info }
Erlang/OTP 16 이전에는 포트에 보내는 메시지가 동기 방식으로 전달됐어요.
다음 예시에서 Data는 I/O 목록이어야 합니다. I/O 목록은 바이너리이거나 (아마 깊은) 바이너리 또는 0에서 255 범위 정수들의 목록이에요.
포트에 보낼 수 있는 메시지는 다음과 같습니다:
-
{Pid,{command,Data}}-Data를 포트로 보냅니다. -
{Pid,close}- 포트를 닫아요. 포트가 이미 닫혀 있지 않다면, 모든 버퍼가 비워지고 포트가 실제로 닫힐 때 포트는{Port,closed}로 응답합니다. -
{Pid,{connect,NewPid}}-Port의 포트 소유자를NewPid로 설정해요. 포트가 이미 닫혀 있지 않다면, 포트는 이전 포트 소유자에게{Port,connected}로 응답합니다. 이전 포트 소유자는 여전히 포트에 링크되어 있지만, 새 포트 소유자는 링크되어 있지 않다는 점을 기억해 두세요.
포트로부터 받을 수 있는 메시지는 다음과 같습니다. 이 메시지들은 포트를 소유한 프로세스로 보내져요:
-
{Port,{data,Data}}- 외부 프로그램으로부터Data를 받았어요. -
{Port,closed}-Port ! {Pid,close}에 대한 응답입니다. -
{Port,connected}-Port ! {Pid,{connect,NewPid}}에 대한 응답이에요. -
{'EXIT',Port,Reason}- 포트가 어떤 이유로 종료된 경우입니다.
메시지를 주고받는 대신 사용할 수 있는 BIF도 몇 가지 있어요:
-
port_command(Port, Data)-Data를 포트로 보냅니다. -
port_close(Port)- 포트를 닫아요. -
port_connect(Port, NewPid)-Port의 포트 소유자를NewPid로 설정해요. 이전 포트 소유자Pid는 포트에 계속 링크되어 있고, 원하지 않는다면unlink(Port)을 호출해야 합니다. -
erlang:port_info(Port, Item)-Item이 지정하는 정보를 반환해요. -
erlang:ports()- 현재 노드의 모든 포트 목록을 반환합니다.
포트 드라이버에 적용되는 추가 BIF도 몇 가지 있어요:
port_control/3erlang:port_call/3.