Clause
← Toda a documentação

Traduzido do original em inglês · 06042fa · 2026-10-09 · Ler em inglês

Portas

Decisão do plano 11, step 57A (2026-10-08). Substitui a decisão do step 53 (sem portas, processos): os programas obtêm portas tal como o OTP as define, e a E/S externa passa por elas. Os steps 57B–57F implementam-na; cada secção indica o seu step. Este contrato fixa a representação, o modelo de drivers e a thread de E/S antes de qualquer código-fonte poder abrir uma porta.

Implementado: as identidades, a tabela de portas, os builtins e as mensagens de portas, as ligações, os monitores, os nomes e os sinais de saída das portas, e as portas fd só de saída (step 57B, runtime/src/scheduler/ports.cpp, runtime/src/builtins/ports.cpp, runtime/src/ports/; golden OTP executables_port_identities); a thread de E/S e a entrada de portas fd com enquadramento em fluxo, por pacotes e por linhas (step 57C, runtime/src/ports/io*.cpp; golden OTP executables_port_input); as portas de subprocessos, os:type/0, os:getenv/1 e os:cmd/1 (step 57D, runtime/src/ports/spawn*.cpp, library/stdlib/os.erl; golden OTP executables_port_spawn); o driver de ficheiros, o subconjunto file da biblioteca e a entrada padrão através de io:get_line/io:get_chars (step 57E, runtime/src/ports/file.cpp, library/stdlib/{file,io}.erl; golden OTP executables_file_io); os sockets (step 57F, golden OTP executables_sockets); uma única thread de E/S orientada a eventos para todos os tipos de portas (step 57G1, runtime/src/ports/reactor.cpp; golden OTP executables_many_ports, teste do runtime runtime_port_io); as tarefas de portas nos workers do escalonador (step 57G2, runtime/src/scheduler/ports.cpp; golden OTP executables_port_fairness); as portas ocupadas e a entrada limitada (step 57G3; goldens OTP executables_busy_ports, executables_slow_owner).

Identidade

Tabela de portas e propriedade

Builtins e mensagens de portas

Builtin ou mensagemStep
is_port/1 true para portas, port_to_list/1, list_to_port/1, ports/057B
port_info/1,2 (name, links, id, connected, input, output, os_pid, monitors, monitored_by, registered_name)57B
port_close/1, port_connect/2, port_command/2,357B
Port ! {Pid, {command, Data}}, {Pid, close}, {Pid, {connect, New}}57B
link/1, unlink/1, monitor(port, P), exit/2 sobre portas; register/2 de uma porta57B
port_control/3, port_call/357B (badarg para drivers sem controlo); usados pelos drivers de 57E/57F
open_port({fd, In, Out}, Opts)57B saída; entrada a partir de 57C
open_port({spawn, Command} | {spawn_executable, File}, Opts)57D
Drivers internos da biblioteca do projeto (file, sockets)57E, 57F

Os builtins substituem os diagnósticos [ports] notimpl do step 53; a funcionalidade ports passa a estar implementada em 57B. Os erros seguem o OTP: badarg para uma porta fechada ou inválida e para argumentos inválidos, e a razão POSIX (enoent, eacces) como error para uma abertura que falhe.

As mensagens de uma porta vão para o seu processo conectado: {Port, {data, Data}}, {Port, eof} (opção eof), {Port, {exit_status, Status}} (opção exit_status), {Port, closed} (depois de {Pid, close}), {Port, connected} (para o antigo dono depois de um connect).

Modos de dados e opções

OpçãoEfeito
stream (por omissão), {packet, N} (N = 1, 2, 4)Bytes tal como chegam, ou mensagens enquadradas por um comprimento big-endian de N bytes, que a saída também recebe
{line, L}{eol, Line} por linha, {noeol, Part} para partes com mais de L ou para um fim sem terminador
binaryDados como binaries em vez de listas de bytes
eof{Port, eof} no fim da entrada; a porta continua aberta até ser fechada
exit_status{Port, {exit_status, S}} quando o programa sai (portas spawn)
use_stdio (por omissão), nouse_stdio, stderr_to_stdout, in, out, hideComo no OTP (hide não tem efeito)
{args, List}, {arg0, A}, {env, Env}, {cd, Dir}Portas spawn (57D)
{busy_limits_port, {Low, High} | disabled}Bytes de saída em fila que tornam a porta ocupada (portas ocupadas, 57G3)

Sem eof, o fim da entrada fecha a porta com a razão normal, depois da mensagem exit_status quando esta tiver sido pedida. As opções desconhecidas dão badarg.

Drivers

Um driver é um objeto C++ por trás de uma porta (runtime/src/ports/): abre o recurso, aceita saída (port_command), responde a port_control/3 quando suporta controlo, indica a entrada e os erros como eventos e fecha. Drivers:

Os drivers internos de file e dos sockets são abertos com {spawn_driver, Name} sob nomes do Clause, e as suas operações de port_control/3 são um protocolo do Clause: os programas usam os módulos da biblioteca, não os protocolos prim_inet/efile do OTP.

Thread de E/S

Uma thread de E/S por runtime (detail::Reactor, runtime/src/ports/reactor.hpp, step 57G1) executa um io_context do Boost.Asio: uma I/O completion port no Windows, epoll no Linux, kqueue no macOS. A primeira porta que precisa dela inicia-a; serve todos os tipos de portas, pelo que uma porta não custa uma thread própria:

runtime/src/ports/io*.cpp contêm a E/S das portas (detail::IoService): os seus métodos apenas publicam trabalho na thread de E/S, onde reside todo o estado de E/S.

Tarefas de portas

Step 57G2. Uma porta é escalonada como um processo: o que a thread de E/S entrega espera na porta, e a porta espera na fila de portas do executor até um worker do escalonador executar a sua tarefa.

O protótipo tests/prototypes/poller/ (run.py --wsl) mostra o despertar no Windows (completion port) e no Linux em WSL (poll()): uma thread do escalonador inativa acorda 9–91 µs após a entrada, e o encerramento para a thread de E/S sem entrada.

No fim do programa, todas as portas são fechadas (os programas filhos veem o fim da entrada; não são mortos, como no OTP) e a thread de E/S para: é juntada (join) fora do mutex do executor, porque uma entrega em curso o obtém; um leitor fd do Windows bloqueado numa leitura que não pode ser cancelada é desanexado e não entrega mais nada.

Portas ocupadas

Step 57G3. A saída que um driver põe em fila para a thread de E/S (pipes de programas lançados) conta para os limites de ocupação da porta, o busy_limits_port do OTP (por omissão: alto 8192 bytes, baixo 4096; {busy_limits_port, {Low, High}} ou disabled como opção de open_port/2, limites de pelo menos 1, um limite baixo acima do alto é reduzido a este):

Uma porta também limita a entrada que retém:

Subprocessos

Step 57D. open_port({spawn, Command}, Options) e open_port({spawn_executable, File}, Options) iniciam um programa cujo stdin e stdout são pipes da porta:

E/S padrão e ficheiros

Step 57E.

Sockets (57F)

Um socket é uma porta, como no backend inet_drv do OTP: is_port(Socket) é true e as mensagens em modo ativo são {tcp, Socket, Data}, {tcp_closed, Socket}, {tcp_error, Socket, Reason}, {udp, Socket, Address, Port, Data}. O subconjunto: gen_tcp:connect/3,4, listen/2, accept/1,2, send/2, recv/2,3, close/1, controlling_process/2, shutdown/2; gen_udp:open/1,2, send/4, recv/2,3, close/1; inet:setopts/2, inet:port/1, inet:peername/1, inet:sockname/1; modos {active, true | false | once}, binary/list, {packet, 0 | 1 | 2 | 4 | raw}, {reuseaddr, Bool}, {backlog, N}, {ip, Address}/{ifaddr, Address}, inet/inet6; endereços IPv4 e IPv6 como tuplos, nomes de anfitrião como strings ou átomos (incluindo loopback). As opções de afinação nodelay, keepalive, send_timeout, send_timeout_close, delay_send e exit_on_close são aceites e não aplicadas; qualquer outra opção dá exit(badarg), como para uma opção inválida no OTP.

Implementação (runtime/src/ports/sockets.cpp):

Não fornecido

Drivers linked-in e NIFs, erlang:open_port({spawn_driver, Name}) para nomes de drivers do OTP, portas de distribuição, port_call/3 sobre os drivers fornecidos para além do protocolo próprio da biblioteca, portas de sockets ocupadas, e as opções overlapped_io, parallelism e busy_limits_msgq.