Clause
← Toute la documentation

L'original anglais a changé depuis cette traduction ; certaines parties peuvent donc être obsolètes. Lire en anglais

Traduit de l'original anglais · 1e3a39c8-dirty · 2026-10-26

Ports

Les programmes disposent des ports tels qu'OTP les définit, et les E/S externes passent par eux. La table des ports, les règles de sortie et les tâches de ports se trouvent dans runtime/src/scheduler/ports.cpp, les builtins dans runtime/src/builtins/erlang/ports.cpp, les pilotes et le thread d'E/S dans runtime/src/ports/. Goldens OTP : executables_port_identities, executables_port_input, executables_port_spawn, executables_file_io, executables_sockets, executables_many_ports, executables_port_fairness, executables_busy_ports, executables_slow_owner ; test du runtime runtime_port_io.

Identité

Table des ports et propriété

Builtins et messages de ports

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 (sous-processus)
{busy_limits_port, {Low, High} | disabled}Octets de sortie en file qui rendent le port occupé (ports occupés)

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, des sockets et du registre 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) 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. Le thread d'E/S transmet l'entrée telle qu'elle a été lue : octets bruts, fin d'entrée, erreur de lecture, code de sortie d'un programme. Les événements de sockets (messages, connexions acceptées) sont transmis de la même façon.

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.

Tâches de ports

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.

Ports occupés

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

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

Sockets

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/2,3,4 (un hôte et un port, ou une map d'adresse de socket), listen/2, accept/1,2, send/2, recv/2,3, close/1, controlling_process/2, shutdown/2 ; gen_udp:open/1,2, connect/2,3, send/2,3,4,5 (formes de destination et données auxiliaires d'OTP), recv/2,3, close/1 ; inet:setopts/2, getopts/2, getstat/1,2, info/1, i/0,1,2, monitor/1, cancel_monitor/1, port/1, peername/1, peernames/1,2, sockname/1, socknames/1,2 ; modes {active, true | false | once}, binary/list ({mode, M}), {packet, 0 | 1 | 2 | 4 | raw}, {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 autres que le mode de données (ports/socket_options.cpp, library/kernel/clause_socket_options.erl) sont des records de pilote contenant un code et une valeur, donnés à l'ouverture d'une socket (définis avant sa liaison ou sa connexion) ou par setopts/2 ; toutes les options d'un appel setopts/2 sont vérifiées avant d'en définir une seule. Les options du système (reuseaddr, keepalive, dontroute, linger, broadcast, sndbuf, recbuf, nodelay, tos, tclass, ipv6_v6only, multicast_ttl, multicast_loop, multicast_if, add_membership, drop_membership, exclusiveaddruse sous Windows, priority et reuseport là où le système les propose) sont transmises à setsockopt et relues avec getsockopt ; les options que l'inet_drv d'OTP garde lui-même (buffer, header, deliver, exit_on_close, send_timeout, seuils, packet_size, ...) sont conservées avec les valeurs par défaut d'inet_drv et non appliquées (différences). Comme inet_drv, un tampon de réception augmente buffer tant que cette option n'est pas définie, et getopts/2 omet les options absentes d'un type de socket (broadcast pour une socket TCP, nodelay pour une UDP, tclass pour IPv4) et refuse les options inconnues avec {error, einval}. Une connexion acceptée reprend les options de sa socket d'écoute. send/5 avec des données auxiliaires définit tos, tclass ou ttl pour ce seul datagramme ; sous Windows, il donne {error, einval}, comme OTP.

Chaque socket compte son trafic comme inet_drv pour getstat/1,2 : les paquets reçus sont des lectures (TCP) ou des datagrammes, les paquets envoyés sont des appels send (avec leur en-tête de paquet) ou des datagrammes, avec les moyennes courantes d'inet_drv ; send_pend est la sortie en file non écrite. getstatus donne les indicateurs d'état d'inet_drv (open, connected, connecting, listen, accepting), que info/1 et i/0,1,2 affichent avec les noms et la colonne de module d'OTP (inet_tcp, inet6_udp, ...). Les requêtes sur une socket fermée répondent {error, einval}, comme OTP.

Le module socket (library/kernel/socket.erl) utilise les mêmes ports de pilotes en mode binaire passif. Une socket de flux peut être liée avant d'écouter ou de se connecter (l'opération 22 ouvre et lie la socket système, que listen transforme en socket d'acceptation ou que connect connecte) ; une socket de datagrammes est liée par udp_open, implicitement à son premier envoi. La fin d'une connexion répond à chaque recv en attente (y compris celui qui demande plus d'octets qu'il n'en reste). socket garde dans son propre tampon les octets qu'un recv n'a pas pris ; recv/2,3,4 donne donc les réponses {error, {timeout, Data}} et {error, {closed, Data}} d'OTP.

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

Le registre

win32reg ouvre {spawn_driver, "clause_registry"} sous Windows (runtime/src/ports/registry_windows.cpp). Le pilote conserve l'état du pilote de registre d'OTP (l'accès demandé, la clé courante et sa racine, initialement HKEY_CLASSES_ROOT) et répond aux neuf commandes d'OTP par des opérations port_control/3 synchrones avec les réponses que le pilote d'OTP envoie comme données de port : o, e et le nom errno (un registre endommagé donne eio, une clé supprimée einval, les autres erreurs Windows selon la conversion du runtime C), k et un nom de clé, v, le type, le nom et les données (chaînes sans leur zéro final, DWORDs big-endian), s et la clé courante. Les énumérations renvoient chaque réponse, précédée de sa taille sur 32 bits. Les noms et les chaînes utilisent la page de codes ANSI, comme le pilote d'OTP.

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.