Clause
← Gesamte Dokumentation

Übersetzt aus dem englischen Original · 06042fa · 2026-10-09 · Auf Englisch lesen

Ports

Entscheidung von Plan 11, step 57A (2026-10-08). Sie ersetzt die Entscheidung aus step 53 (keine Ports, Prozesse): Programme erhalten Ports so, wie OTP sie definiert, und externe I/O läuft über sie. Die Steps 57B–57F setzen sie um; jeder Abschnitt nennt seinen Step. Dieser Vertrag legt die Darstellung, das Treibermodell und den I/O-Thread fest, bevor irgendein Quelltext einen Port öffnen kann.

Umgesetzt: Identitäten, die Port-Tabelle, die Port-Builtins und -Nachrichten, Links, Monitore, Namen und Exit-Signale von Ports sowie reine Ausgabe-fd-Ports (step 57B, runtime/src/scheduler/ports.cpp, runtime/src/builtins/ports.cpp, runtime/src/ports/; OTP-Golden executables_port_identities); der I/O-Thread und fd-Port-Eingabe mit Stream-, Paket- und Zeilen-Framing (step 57C, runtime/src/ports/io*.cpp; OTP-Golden executables_port_input); Unterprozess-Ports, os:type/0, os:getenv/1 und os:cmd/1 (step 57D, runtime/src/ports/spawn*.cpp, library/stdlib/os.erl; OTP-Golden executables_port_spawn); der Dateitreiber, die Teilmenge file der Bibliothek und Standardeingabe über io:get_line/io:get_chars (step 57E, runtime/src/ports/file.cpp, library/stdlib/{file,io}.erl; OTP-Golden executables_file_io); Sockets (step 57F, OTP-Golden executables_sockets); ein ereignisgesteuerter I/O-Thread für jede Port-Art (step 57G1, runtime/src/ports/reactor.cpp; OTP-Golden executables_many_ports, Runtime-Test runtime_port_io); Port-Tasks auf den Scheduler-Workern (step 57G2, runtime/src/scheduler/ports.cpp; OTP-Golden executables_port_fairness); ausgelastete Ports (busy ports) und begrenzte Eingabe (step 57G3; OTP-Goldens executables_busy_ports, executables_slow_owner).

Identität

Port-Tabelle und Besitz

Builtins und Port-Nachrichten

Builtin oder NachrichtStep
is_port/1 wahr für Ports, 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 auf Ports; register/2 eines Ports57B
port_control/3, port_call/357B (badarg für Treiber ohne Steuerung); genutzt von den Treibern aus 57E/57F
open_port({fd, In, Out}, Opts)57B Ausgabe; Eingabe ab 57C
open_port({spawn, Command} | {spawn_executable, File}, Opts)57D
Interne Treiber der Projektbibliothek (file, Sockets)57E, 57F

Die Builtins ersetzen die Diagnosen [ports] notimpl aus step 53; das Feature ports gilt ab 57B als umgesetzt. Fehler folgen OTP: badarg für einen geschlossenen oder ungültigen Port und ungültige Argumente, der POSIX-Grund (enoent, eacces) als error für ein fehlgeschlagenes Öffnen.

Nachrichten eines Ports gehen an seinen verbundenen Prozess: {Port, {data, Data}}, {Port, eof} (Option eof), {Port, {exit_status, Status}} (Option exit_status), {Port, closed} (nach {Pid, close}), {Port, connected} (an den alten Besitzer nach einem Connect).

Datenmodi und Optionen

OptionWirkung
stream (Standard), {packet, N} (N = 1, 2, 4)Bytes, wie sie ankommen, oder Nachrichten, gerahmt durch eine N Byte lange Big-Endian-Länge, die auch die Ausgabe erhält
{line, L}{eol, Line} pro Zeile, {noeol, Part} für Teile länger als L oder ein nicht abgeschlossenes Ende
binaryDaten als binaries statt Byte-Listen
eof{Port, eof} am Ende der Eingabe; der Port bleibt offen, bis er geschlossen wird
exit_status{Port, {exit_status, S}}, wenn das Programm endet (Spawn-Ports)
use_stdio (Standard), nouse_stdio, stderr_to_stdout, in, out, hideWie in OTP (hide hat keine Wirkung)
{args, List}, {arg0, A}, {env, Env}, {cd, Dir}Spawn-Ports (57D)
{busy_limits_port, {Low, High} | disabled}Gepufferte Ausgabe-Bytes, die den Port auslasten (ausgelastete Ports, 57G3)

Ohne eof schließt das Ende der Eingabe den Port mit Grund normal, nach der Nachricht exit_status, falls diese angefordert wurde. Unbekannte Optionen ergeben badarg.

Treiber

Ein Treiber ist ein C++-Objekt hinter einem Port (runtime/src/ports/): Er öffnet die Ressource, nimmt Ausgabe an (port_command), beantwortet port_control/3, wenn er Steuerung unterstützt, meldet Eingabe und Fehler als Ereignisse und schließt. Treiber:

Die internen Treiber von file und den Sockets werden mit {spawn_driver, Name} unter Clause-Namen geöffnet, und ihre port_control/3-Operationen sind ein Clause-Protokoll: Programme nutzen die Bibliotheksmodule, nicht die Protokolle prim_inet/efile von OTP.

I/O-Thread

Ein I/O-Thread pro Runtime (detail::Reactor, runtime/src/ports/reactor.hpp, step 57G1) betreibt einen Boost.Asio-io_context: einen I/O Completion Port unter Windows, epoll unter Linux, kqueue unter macOS. Der erste Port, der ihn braucht, startet ihn; er bedient jede Port-Art, sodass ein Port keinen eigenen Thread kostet:

runtime/src/ports/io*.cpp enthalten die Port-I/O (detail::IoService): Ihre Methoden übergeben Arbeit nur an den I/O-Thread, wo der gesamte I/O-Zustand liegt.

Port-Tasks

Step 57G2. Ein Port wird wie ein Prozess eingeplant: Was der I/O-Thread übergibt, wartet im Port, und der Port wartet in der Port-Warteschlange des Executors, bis ein Scheduler-Worker seinen Task ausführt.

Der Prototyp tests/prototypes/poller/ (run.py --wsl) zeigt das Aufwecken unter Windows (Completion Port) und WSL Linux (poll()): Ein untätiger Scheduler-Thread wacht 9–91 µs nach der Eingabe auf, und das Herunterfahren stoppt den I/O-Thread ohne Eingabe.

Am Programmende wird jeder Port geschlossen (Kindprogramme sehen das Ende der Eingabe; sie werden nicht beendet, wie in OTP), und der I/O-Thread stoppt: Er wird außerhalb des Executor-Mutex gejoint, weil eine laufende Zustellung ihn nimmt; ein Windows-fd-Leser, der in einem nicht abbrechbaren Lesevorgang blockiert, wird abgekoppelt und stellt nichts mehr zu.

Ausgelastete Ports

Step 57G3. Ausgabe, die ein Treiber für den I/O-Thread puffert (Pipes gestarteter Programme), zählt gegen die Auslastungsgrenzen des Ports, busy_limits_port von OTP (Standardwerte: hoch 8.192 Bytes, niedrig 4.096; {busy_limits_port, {Low, High}} oder disabled als Option von open_port/2, Grenzen mindestens 1, eine niedrige Grenze über der hohen wird auf diese gesenkt):

Ein Port begrenzt auch die Eingabe, die er hält:

Unterprozesse

Step 57D. open_port({spawn, Command}, Options) und open_port({spawn_executable, File}, Options) starten ein Programm, dessen stdin und stdout Pipes des Ports sind:

Standard-I/O und Dateien

Step 57E.

Sockets (57F)

Ein Socket ist ein Port, wie beim inet_drv-Backend von OTP: is_port(Socket) ist wahr, und Nachrichten im aktiven Modus sind {tcp, Socket, Data}, {tcp_closed, Socket}, {tcp_error, Socket, Reason}, {udp, Socket, Address, Port, Data}. Die Teilmenge: 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; Modi {active, true | false | once}, binary/list, {packet, 0 | 1 | 2 | 4 | raw}, {reuseaddr, Bool}, {backlog, N}, {ip, Address}/{ifaddr, Address}, inet/inet6; IPv4- und IPv6-Adressen als Tupel, Hostnamen als Strings oder Atome (loopback eingeschlossen). Die Tuning-Optionen nodelay, keepalive, send_timeout, send_timeout_close, delay_send und exit_on_close werden akzeptiert und nicht angewendet; jede andere Option ergibt exit(badarg), wie bei einer ungültigen in OTP.

Implementierung (runtime/src/ports/sockets.cpp):

Nicht bereitgestellt

Linked-in-Treiber und NIFs, erlang:open_port({spawn_driver, Name}) für OTP-Treibernamen, Distributions-Ports, port_call/3 auf den bereitgestellten Treibern außer dem eigenen Protokoll der Bibliothek, ausgelastete Socket-Ports sowie die Optionen overlapped_io, parallelism und busy_limits_msgq.