Clause
← Toute la documentation

Traduit de l'original anglais · 06042fa · 2026-10-09 · Lire en anglais

Ports

Décision du plan 11 step 57A (2026-10-08). Elle remplace la décision du step 53 (pas de ports, processus) : les programmes disposent des ports tels qu'OTP les définit, et les E/S externes passent par eux. Les steps 57B–57F l'implémentent ; chaque section indique son step. Ce contrat fixe la représentation, le modèle de pilotes et le thread d'E/S avant qu'une source puisse ouvrir un port.

Implémenté : les identités, la table des ports, les builtins et messages de ports, les liens, moniteurs, noms et signaux de sortie des ports, et les ports fd en sortie seule (step 57B, runtime/src/scheduler/ports.cpp, runtime/src/builtins/ports.cpp, runtime/src/ports/ ; golden OTP executables_port_identities) ; le thread d'E/S et l'entrée des ports fd avec découpage en flux, en paquets et en lignes (step 57C, runtime/src/ports/io*.cpp ; golden OTP executables_port_input) ; les ports de sous-processus, os:type/0, os:getenv/1 et os:cmd/1 (step 57D, runtime/src/ports/spawn*.cpp, library/stdlib/os.erl ; golden OTP executables_port_spawn) ; le pilote de fichiers, le sous-ensemble file de la bibliothèque et l'entrée standard via io:get_line/io:get_chars (step 57E, runtime/src/ports/file.cpp, library/stdlib/{file,io}.erl ; golden OTP executables_file_io) ; les sockets (step 57F, golden OTP executables_sockets) ; un thread d'E/S unique piloté par les événements pour tous les types de ports (step 57G1, runtime/src/ports/reactor.cpp ; golden OTP executables_many_ports, test du runtime runtime_port_io) ; les tâches de ports sur les workers de l'ordonnanceur (step 57G2, runtime/src/scheduler/ports.cpp ; golden OTP executables_port_fairness) ; les ports occupés et l'entrée bornée (step 57G3 ; goldens OTP executables_busy_ports, executables_slow_owner).

Identité

Table des ports et propriété

Builtins et messages de ports

Builtin ou messageStep
is_port/1 vrai pour les 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 sur des ports ; register/2 d'un port57B
port_control/3, port_call/357B (badarg pour les pilotes sans contrôle) ; utilisés par les pilotes de 57E/57F
open_port({fd, In, Out}, Opts)57B pour la sortie ; entrée à partir de 57C
open_port({spawn, Command} | {spawn_executable, File}, Opts)57D
Pilotes internes de la bibliothèque du projet (file, sockets)57E, 57F

Les builtins remplacent les diagnostics [ports] notimpl du step 53 ; la fonctionnalité ports devient implémentée en 57B. Les erreurs suivent OTP : badarg pour un port fermé ou invalide et pour de mauvais arguments, la raison POSIX (enoent, eacces) comme error pour une ouverture qui échoue.

Les messages d'un port vont à son processus connecté : {Port, {data, Data}}, {Port, eof} (option eof), {Port, {exit_status, Status}} (option exit_status), {Port, closed} (après {Pid, close}), {Port, connected} (à l'ancien propriétaire après une connexion).

Modes de données et options

OptionEffet
stream (par défaut), {packet, N} (N = 1, 2, 4)Octets tels qu'ils arrivent, ou messages délimités par une longueur big-endian de N octets que la sortie reçoit aussi
{line, L}{eol, Line} par ligne, {noeol, Part} pour les parties plus longues que L ou une fin non terminée
binaryDonnées sous forme de binaries au lieu de listes d'octets
eof{Port, eof} en fin d'entrée ; le port reste ouvert jusqu'à sa fermeture
exit_status{Port, {exit_status, S}} lorsque le programme se termine (ports spawn)
use_stdio (par défaut), nouse_stdio, stderr_to_stdout, in, out, hideComme dans OTP (hide n'a aucun effet)
{args, List}, {arg0, A}, {env, Env}, {cd, Dir}Ports spawn (57D)
{busy_limits_port, {Low, High} | disabled}Octets de sortie en file qui rendent le port occupé (ports occupés, 57G3)

Sans eof, la fin de l'entrée ferme le port avec la raison normal, après le message exit_status lorsqu'il a été demandé. Les options inconnues donnent badarg.

Pilotes

Un pilote (driver) est un objet C++ derrière un port (runtime/src/ports/) : il ouvre la ressource, accepte la sortie (port_command), répond à port_control/3 lorsqu'il prend en charge le contrôle, signale l'entrée et les erreurs sous forme d'événements et se ferme. Pilotes :

Les pilotes internes de file et des sockets sont ouverts avec {spawn_driver, Name} sous des noms propres à Clause, et leurs opérations port_control/3 forment un protocole de Clause : les programmes utilisent les modules de la bibliothèque, pas les protocoles prim_inet/efile d'OTP.

Thread d'E/S

Un thread d'E/S par runtime (detail::Reactor, runtime/src/ports/reactor.hpp, step 57G1) exécute un io_context de Boost.Asio : un port de complétion d'E/S sous Windows, epoll sous Linux, kqueue sous macOS. Le premier port qui en a besoin le démarre ; il sert tous les types de ports, de sorte qu'un port ne coûte aucun thread propre :

runtime/src/ports/io*.cpp contiennent les E/S des ports (detail::IoService) : ses méthodes ne font que poster du travail vers le thread d'E/S, où réside tout l'état des E/S.

Tâches de ports

Step 57G2. Un port est ordonnancé comme un processus : ce que le thread d'E/S transmet attend dans le port, et le port attend dans la file des ports de l'exécuteur jusqu'à ce qu'un worker de l'ordonnanceur exécute sa tâche.

Le prototype tests/prototypes/poller/ (run.py --wsl) montre le réveil sous Windows (port de complétion) et sous Linux WSL (poll()) : un thread d'ordonnanceur inactif se réveille 9 à 91 µs après l'entrée, et l'arrêt stoppe le thread d'E/S sans entrée.

À la fin du programme, chaque port est fermé (les programmes enfants voient la fin de l'entrée ; ils ne sont pas tués, comme dans OTP) et le thread d'E/S s'arrête : il est joint en dehors du mutex de l'exécuteur, car une livraison en cours prend ce mutex ; un lecteur fd Windows bloqué dans une lecture qui ne peut pas être annulée est détaché et ne délivre plus rien.

Ports occupés

Step 57G3. La sortie qu'un pilote met en file pour le thread d'E/S (tubes des programmes lancés) est comptée par rapport aux limites d'occupation du port, le busy_limits_port d'OTP (par défaut : haute 8 192 octets, basse 4 096 ; {busy_limits_port, {Low, High}} ou disabled comme option de open_port/2, limites d'au moins 1, une limite basse supérieure à la haute étant abaissée à celle-ci) :

Un port borne aussi l'entrée qu'il retient :

Sous-processus

Step 57D. open_port({spawn, Command}, Options) et open_port({spawn_executable, File}, Options) démarrent un programme dont stdin et stdout sont des tubes du port :

E/S standard et fichiers

Step 57E.

Sockets (57F)

Une socket est un port, comme avec le backend inet_drv d'OTP : is_port(Socket) est vrai et les messages du mode actif sont {tcp, Socket, Data}, {tcp_closed, Socket}, {tcp_error, Socket, Reason}, {udp, Socket, Address, Port, Data}. Le sous-ensemble : 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 ; modes {active, true | false | once}, binary/list, {packet, 0 | 1 | 2 | 4 | raw}, {reuseaddr, Bool}, {backlog, N}, {ip, Address}/{ifaddr, Address}, inet/inet6 ; adresses IPv4 et IPv6 sous forme de tuples, noms d'hôtes sous forme de chaînes ou d'atomes (loopback compris). Les options de réglage nodelay, keepalive, send_timeout, send_timeout_close, delay_send et exit_on_close sont acceptées et non appliquées ; toute autre option donne exit(badarg), comme pour une option invalide dans OTP.

Implémentation (runtime/src/ports/sockets.cpp) :

Non fourni

Les pilotes liés (linked-in drivers) et les NIF, erlang:open_port({spawn_driver, Name}) pour les noms de pilotes d'OTP, les ports de distribution, port_call/3 sur les pilotes fournis en dehors du protocole propre à la bibliothèque, les ports de sockets occupés, et les options overlapped_io, parallelism et busy_limits_msgq.