Portar
Program får portar så som OTP definierar dem, och extern I/O går genom dem.
Porttabellen, reglerna för avslut och portuppgifterna finns i
runtime/src/scheduler/ports.cpp, de inbyggda funktionerna i
runtime/src/builtins/erlang/ports.cpp, drivrutinerna och I/O-tråden i
runtime/src/ports/. OTP-referenstester (goldens):
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; runtime-test
runtime_port_io.
Identitet
- En port är ett omedelbart ord (immediate): tagg
0x7(TermKind2::portunder den primära taggensee_termkind2, bredvid pid:ars0x3), med ett portnummer som nyttolast, hämtat ur en processgemensam sekvens som aldrig återanvänds, precis som pid-nummer (pid:ar). En runtime godtar endast de nummer den själv har utfärdat; förfalskade och främmande portord avvisas. - Utskrift:
#Port<0.N>;port_to_list/1ger den texten ochlist_to_port/1tolkar den (badarg för allt annat). - Ordning: tal < atomer < referenser < funs < portar < pid:ar < tupler, som i OTP; portar jämförs efter nummer.
- En stängd ports identitet förblir giltig:
is_port/1förblir sant,port_info/1,2svararundefined, och sändningar till den kastas. - Portar är omedelbara värden, så kopiering, skräpsamling och meddelanden behöver inget nytt.
Porttabell och ägarskap
- Varje runtime har en porttabell (nummer → port) i sin exekutor, skyddad av exekutorns mutex, liksom processernas länkar och namn (arbetstrådar).
- En port registrerar sin drivrutin, sin anslutna process (den som öppnade
porten, ändras av
port_connect/2eller{Pid, {connect, New}}), sina länkar, de monitorer som hålls på den, ett valfritt registrerat namn, sina alternativ samt sina bytesräknare för indata och utdata. open_port/2länkar den nya porten till den som öppnade den. När den anslutna processen avslutas stängs dess port; en port som stängs skickar exitsignaler till sina länkar och'DOWN'-meddelanden till sina monitorer med sin orsak (normalför en stängning som inte är ett fel, annars en POSIX-atom somepipe).- En exitsignal som når en port stänger den med signalens orsak (
killfrånexit/2somkilled), utomnormalgenom en länk från en annan process än den anslutna, vilket endast tar bort länken.exit(Port, normal)stänger porten. En ansluten process som har tagit bort länken lämnar sin port öppen när den avslutas. - Ett begäransmeddelande från en annan process än den anslutna, eller ett
felformat meddelande, skickar exitsignalen
badsigfrån porten till den anslutna processen; porten förblir öppen. - En ports exitsignaler och meddelanden till en process som körs på en annan
arbetstråd väntar tills den processens tidsskiva tar slut (de innehåller
endast atomer, pid:ar och portar); ett
exit(Port, Reason)med en orsaksterm i avsändarens heap gör att den inbyggda funktionen körs igen tills ingen länkad eller övervakande process körs någon annanstans, som för processignaler (arbetstrådar). - Portar kan få registrerade namn (
register/2) ochmonitor(port, Port);link/1ochunlink/1accepterar dem.
Inbyggda funktioner och portmeddelanden
is_port/1(sant för portar),port_to_list/1,list_to_port/1,ports/0.port_info/1,2:name,links,id,connected,input,output,os_pid,monitors,monitored_by,registered_name,queue_size(upptagna portar).port_close/1,port_connect/2,port_command/2,3; meddelandenaPort ! {Pid, {command, Data}},{Pid, close},{Pid, {connect, New}}.link/1,unlink/1,monitor(port, P),exit/2på portar;register/2av en port.port_control/3,port_call/3:badargför drivrutiner utan styrning; drivrutinerna förfileoch socketar styrs medport_control/3.open_port({fd, In, Out}, Opts),open_port({spawn, Command}, Opts),open_port({spawn_executable, File}, Opts)och de interna drivrutinerna i projektets bibliotek (file, socketar).
Fel följer OTP: badarg för en stängd eller ogiltig port och felaktiga
argument, POSIX-orsaken (enoent, eacces) som ett error för en öppning som
misslyckas.
Meddelanden från en port går till dess anslutna process:
{Port, {data, Data}}, {Port, eof} (alternativet eof),
{Port, {exit_status, Status}} (alternativet exit_status),
{Port, closed} (efter {Pid, close}), {Port, connected} (till den
tidigare ägaren efter en anslutning).
Datalägen och alternativ
| Alternativ | Effekt |
|---|---|
stream (standard), {packet, N} (N = 1, 2, 4) | Bytes allteftersom de anländer, eller meddelanden inramade av en N-bytes längd i big-endian som även utdata får |
{line, L} | {eol, Line} per rad, {noeol, Part} för delar längre än L eller ett oavslutat slut |
binary | Data som binaries i stället för bytelistor |
eof | {Port, eof} vid slutet av indata; porten förblir öppen tills den stängs |
exit_status | {Port, {exit_status, S}} när programmet avslutas (spawn-portar) |
use_stdio (standard), nouse_stdio, stderr_to_stdout, in, out, hide | Som i OTP (hide har ingen effekt) |
{args, List}, {arg0, A}, {env, Env}, {cd, Dir} | Spawn-portar (delprocesser) |
{busy_limits_port, {Low, High} | disabled} | Köade utdatabytes som gör porten upptagen (upptagna portar) |
Utan eof stänger slutet av indata porten med orsaken normal, efter
meddelandet exit_status om ett sådant begärdes. Okända alternativ ger
badarg.
Drivrutiner
En drivrutin är ett C++-objekt bakom en port (runtime/src/ports/): den
öppnar resursen, tar emot utdata (port_command), svarar på port_control/3
när den stöder styrning, rapporterar indata och fel som händelser och stänger.
Drivrutiner:
-
fd: befintliga deskriptorer, utdata skrivs synkront, indata genom I/O-tråden. -
spawn: ett barnprogram med sin stdin och stdout som rör (pipes) (delprocesser). -
file: en öppen fil i projektbibliotekets modulfile; dess operationer är synkronaport_control/3-anrop som kan blockera arbetstråden som kör anroparen under diskoperationens varaktighet, som OTP:s smutsiga I/O-schemaläggare (dirty I/O schedulers) gör (filer). -
tcp_inet,udp_inet: socketar för projektbiblioteketsgen_tcp,gen_udpochinetovanpå Boost.Asio; anslutningar, accepteranden och mottagningar är asynkrona (socketar). -
clause_registry(Windows): registret förwin32reg(registret).
De interna drivrutinerna för file, socketarna och registret öppnas med
{spawn_driver, Name} under Clause-namn, och deras port_control/3-operationer
är ett Clause-protokoll: program använder biblioteksmodulerna, inte OTP:s
protokoll prim_inet/efile.
I/O-tråd
En I/O-tråd per runtime (detail::Reactor, runtime/src/ports/reactor.hpp)
kör en Boost.Asio-io_context: en I/O-kompletteringsport (I/O completion
port) på Windows, epoll på Linux, kqueue på macOS. Den första porten som
behöver den startar den; den betjänar alla sorters portar, så en port kostar
ingen egen tråd:
- Rör för startade program: överlappade namngivna rör på Windows
(
windows::stream_handle; anonyma rör kan inte överlappas), vanliga rör annars (posix::stream_descriptor). Utdata köas och skrivs i ordning med asynkrona skrivningar. fd-indata på Linux och macOS: tråden väntar tills deskriptorn är läsbar, sedan tar ettread()det som finns, så att programmets egna deskriptorer behåller sitt blockerande läge; en vanlig fil, som inte går att vänta på, läses direkt.- Programavslut: på Windows väntar systemets trådpool för väntan
(
RegisterWaitForSingleObject, som Asiosobject_handleanvänder) på processhandtaget och avslutet rapporteras på I/O-tråden; på Linux och macOS tar enSIGCHLD-hanterare (signal_set) ochwaitpid(WNOHANG)hand om varje bevakat program, även efter att dess port har stängts. - Socketar (socketar).
- Undantag: ett
fd-indatahandtag på Windows (en konsol eller ett ärvt anonymt rör) kan inte överlappas, så det läses av en egen blockerande tråd, som libuv och ERTS gör; att stänga porten avbryter läsningen (CancelSynchronousIo) och släpper tråden.
runtime/src/ports/io*.cpp innehåller port-I/O:n (detail::IoService): dess
metoder lägger endast upp arbete på I/O-tråden, där allt I/O-tillstånd finns.
I/O-tråden lämnar över indata så som den lästes: råa bytes, slutet av indata,
ett läsfel, ett programs avslutsstatus. Sockethändelser (meddelanden,
accepterade anslutningar) lämnas över på samma sätt.
Prototypen tests/prototypes/poller/ (run.py --wsl) visar väckningen på
Windows (kompletteringsport) och WSL Linux (poll()): en ledig
schemaläggartråd vaknar 9–91 µs efter indata, och nedstängning stoppar
I/O-tråden utan indata.
Vid programmets slut stängs varje port (barnprogram ser slutet av indata; de
dödas inte, som i OTP) och I/O-tråden stoppas: den förenas (join) utanför
exekutorns mutex, eftersom en pågående leverans tar mutexen; en Windows-läsare
för fd som är blockerad i en läsning som inte kan avbrytas kopplas loss och
levererar inget mer.
Portuppgifter
En port schemaläggs som en process: det som I/O-tråden lämnar över väntar i porten, och porten väntar i exekutorns portkö tills en arbetstråd i schemaläggaren kör dess uppgift.
- Arbetstrådar tar växelvis en portuppgift och en processtidsskiva medan båda köerna har arbete, så en port som översvämmas av indata kan inte svälta processer, och processer kan inte svälta portar; en ledig arbetstråd tar det som står i kö.
- En uppgift körs under exekutorns mutex i
PORT_TASK_REDUCTIONS(en tidsskivas 4 000): varje meddelande den levererar kostar 100 reduktioner plus en per 64 bytes som det bär. En port med arbete kvar köas igen längst bak. - Uppgiften ramar in indata (
InputDecoder): strömindata kommer i de bitar som läsningar returnerar;{packet, N}håller kvar bytes tills ett helt paket har anlänt (ett ofullständigt paket vid slutet av indata kastas, som i OTP);{line, L}delar vid\n, skickar en rad längre änLsom{noeol, Part}-delar och ett oavslutat slut som{noeol, Rest}.port_info(P, input)räknar varje läst byte, inklusive radbrytningar och pakethuvuden, när den läses. - Slutet av indata skickar
{Port, eof}med alternativeteof, annars stängs porten med orsakennormal; ett läsfel stänger den medeio. - Ett meddelande blir ett meddelande till dess process direkt när den processen inte körs på en arbetstråd (och väcker den som vilket meddelande som helst), annars när dess tidsskiva tar slut, så att heapen hos en körande process aldrig berörs av en annan tråd.
- Kommandon, stängningar, anslutningar och exitsignaler som skickas till en
port verkar direkt på avsändarens arbetstråd, som ERTS gör för en port som
inte är upptagen: en
fd-port skriver sin utdata direkt på anroparens arbetstråd; rör- och socketdrivrutinerna köar utdata och låter I/O-tråden skriva den; en upptagen port suspenderar avsändaren (upptagna portar).
Upptagna portar
Utdata som en drivrutin köar för I/O-tråden (rör för startade program) räknas
mot portens gränser för upptagenhet, OTP:s busy_limits_port
(standardvärden: hög 8 192 bytes, låg 4 096;
{busy_limits_port, {Low, High}} eller disabled som ett alternativ till
open_port/2, gränser minst 1, en låg gräns över den höga sänks till den):
- En skrivning som tar den köade utdatan till den höga gränsen eller över skickas ändå och gör porten upptagen; den förblir upptagen tills I/O-tråden har skrivit så mycket att mindre än den låga gränsen står i kö.
port_command/2ochPort ! {Pid, {command, Data}}till en upptagen port suspenderar avsändaren, som inte skriver något; när porten inte längre är upptagen, eller stängs, kör avsändaren sin inbyggda funktion igen (en stängd port ger dåbadarg, som i OTP). En suspenderad process tar fortfarande emot exitsignaler.port_command/3mednosuspendreturnerarfalseför en upptagen port och skriver inget;forcekastarnotsup, eftersom OTP:s spawn- ochfd-drivrutiner inte tillåter det.port_info(P, queue_size)är den utdata som står i kö och ännu inte har skrivits.fd-portar skriver direkt och socketutdata går genomport_control/3, så ingen av dem är någonsin upptagen.
En port begränsar även den indata den håller:
- När mer än 64 KiB rå indata väntar på portens uppgift slutar I/O-tråden att läsa porten (ett program som skriver till den blockeras då, som vid ett fullt rör) tills uppgiften har tagit den under 32 KiB.
- Medan den anslutna processen kan köras (den körs eller står i kö, väntar
inte i en
receive, är inte suspenderad eller blockerad) och redan håller 1 024 meddelanden (eller har lika många portsignaler som väntar på att dess tidsskiva ska ta slut), levererar portens uppgift inget mer förrän den processens tidsskiva har tagit slut. En process som väntar i enreceivefår alltid indata, så en receive efter ett senare meddelande från porten kan inte vänta för evigt.
Delprocesser
open_port({spawn, Command}, Options) och
open_port({spawn_executable, File}, Options) startar ett program vars stdin
och stdout är portens rör:
{spawn, Command}: på Linux och macOS/bin/sh -c Command; på WindowsCreateProcessWmedCommandsom kommandorad (den hittar programmet i sökvägen).{spawn_executable, File}körFileutan skal, med argv[0]{arg0, A}(standardFile) och{args, List}; på Windows citeras argument enligt reglerna förCommandLineToArgvW.{env, [{Name, Value | false}]}sätter eller tar bort variabler för barnet,{cd, Dir}dess katalog,stderr_to_stdoutslår ihop stderr med porten; annars delar barnet programmets stderr. Enbartinöppnar inget stdin-rör och enbartoutinget stdout-rör (null-enheten i stället); med båda eller ingetdera har porten båda, som i OTP.hideochoverlapped_iohar ingen effekt.- Ett program som inte kan startas kastar
error:Reasonmed POSIX-orsaken (enoent,eacces,enoexec); felaktiga namn och alternativ kastarbadarg. - Utdata köas och skrivs av I/O-tråden, med portens gränser för upptagenhet (upptagna portar); att stänga porten låter den slutföra det som står i kö och stänger sedan programmets stdin. Programmet dödas aldrig; det avslutas vanligtvis vid slutet av indata.
- Alternativet
exit_statusskickar{Port, {exit_status, S}}när programmet har avslutats (dess avslutskod; på Linux och macOS 128 plus signalen för ett program som en signal avslutade) och före{Port, eof}eller stängningen vid slutet av indata; en port som inte läser något rapporterar det när programmet avslutas och stängs sedan. port_info(P, os_pid)är programmets process-id;nameär kommandot eller filen.- Bibliotekets
os:cmd/1körCommandmedCOMSPEC /cpå Windows (cmdom den inte är satt) eller/bin/sh -c, samlar in stdout och stderr tills programmet stänger dem och returnerar bytes som en lista;os:type/0är{win32, nt},{unix, linux}eller{unix, darwin};os:getenv/1returnerar en sträng ellerfalse.
Standard-I/O och filer
io:format,io:put_charsocherlang:displayskriver direkt till standard-utdata (io). Standard-indata läses av en serverprocess i biblioteket, registrerad somclause_stdinoch startad vid första användning, som äger en{fd, 0, 1}-port:io:get_line/1,2skriver prompten och returnerar sedan nästa rad med dess radbrytning, resten av indata utan radbrytning i slutet, och sedaneof;io:get_chars/2,3returnerar upp tillNtecken, sedaneof. Begäranden från flera processer besvaras i ankomstordning. Indata avkodas som UTF-8 till tecken, så som OTP läser sin terminal; servern behåller OTP:s noshell-alternativ förstandard_io(binary,encoding,echo,log;io:getopts/0,1,io:setopts/1,2): binärläge returnerar binaries,echoskriver förbrukad indata till standard-utdata,get_passwordär{error, enotsup}utan skal, som i OTP.- Filservrar besvarar
getoptsochsetoptssom OTP:s fil-I/O-server (binary/listochencoding;file:open/2tar{encoding, E}): I/O-begäranden (io:get_line,get_chars,put_chars,get_until) kodar och avkodar enhetens kodning, ochfile:read/2,read_line/1på en kodad fil svarar som latin1-begäranden av typenget_chars/get_line(no_translationöver 255, vilket stoppar servern). Utdata frånio:format/3,io:put_chars/2,io:nl/1till valfri pid är enput_chars-begäran i I/O-protokollet. - Bibliotekets modul
filedriver runtimens fildrivrutin,{spawn_driver, "clause_file"}, medport_control/3. Dess operationer (ports/file.cpp: open, read, write, position, read_line, sync, close och sökvägsoperationerna read_file, write_file, delete, rename, list_dir, make_dir, del_dir och read_file_info,ports/file_info.cpp; get_cwd, set_cwd, read_link, read_link_info, make_link och make_symlink,ports/file_paths.cpp) är synkrona systemanrop på anroparens arbetstråd, utanför exekutorns mutex; deras svar börjar med en statusbyte (0 ok, 1 fel och dess POSIX-orsak, 2 filslut). Protokollet är Clauses eget. read_line läser 64 KB i förväg och returnerar rader från den bufferten; varje annan operation på filen söker först tillbaka över de bytes den inte har returnerat, så att positionerna förblir exakta. - Sökvägsoperationer gör OTP:s systemanrop och inget mer: en sökvägs status
läses först när en öppning eller borttagning misslyckats (för att svara
eisdirellereperm), delete tar bort en fillänk och aldrig en katalog, och read_file läser en fil upp till den storlek dess deskriptor rapporterar, som OTP gör, utan att prova att läsa efter slutet. port_control/3lämnar över en binary eller[]direkt till drivrutinen (annan iodata plattas ut först), och ett svar blir resultatets binary utan kopiering: svar ärByteBuffers (runtime/include/byte_buffer.hpp), bytevektorer som växer utan nollställning, så en läsning fyller svarets bytes en gång.file:open/2returnerar en I/O-servers pid som äger en filport och stängs när den som öppnade avslutas, som i OTP (på Windows delas filen för läsning, skrivning och borttagning, så som OTP öppnar den, så en öppen fil kan tas bort eller byta namn) (lägenaread,write,append,exclusive,binary,raw; andra lägen ignoreras, som OTP ignorerar alternativ den inte använder;writetrunkerar om inte ävenappendanges).file:write_file/3öppnar med sina lägen plusbinaryochwrite;file:read_file_info/1,2returnerar OTP:s#file_info{}från sökvägens status (länkar följs), med tider som lokalt eller universellt datum och tid eller POSIX-sekunder ({time, local | universal | posix};rawgodtas). På Windows ärinode,uidochgid0 ochmajor_deviceär enhetsnumret, så som OTP rapporterar dem.read/2,read_line/1,write/2,position/2ochio:get_line/2,io:get_chars/3på den är begäranden till den servern; efterclose/1returnerar begäranden{error, terminated}ochclose/1förblirok.- Positionerad I/O och filändringar:
pread/2,3ochpwrite/2,3positionerar och läser eller skriver sedan på en filserver (curläser vidare, positionen flyttas), som OTP:s server gör; enraw-fil tar endast heltalsoffsetar (badargannars) och behåller sin position.truncate/1kapar vid positionen;advise/4tar OTP:s rådgivningsatomer (posix_fadvisepå Linux, ignoreras annars, som i OTP);allocate/3reserverar utrymme (fallocate, macOSF_PREALLOCATE,{error, enotsup}på Windows), med argumenten kontrollerade först (badargför annat än tal,einvalför negativa tal och flyttal). Som OTP:s filserver svarar en server vars skrivning, positionerade skrivning, sync, trunkering eller rådgivning misslyckas med felet och avslutas, så senare begäranden får{error, terminated}; en raw-fil fortsätter.write_file_info/2,3ochchange_mode/2,change_owner/2,3,change_group/2,change_time/2,3ändrar ägare, behörigheter och tider i prim_file:s ordning (Windows: ägare ignoreras, endast skrivskyddsattributet från ägarens skrivbit, även skapelsetiden sätts).sendfile/2,5läser en raw-fil i block (högst 1 MiB,chunk_size) och skickar dem medgen_tcp:send/2eller angiven fun, OTP:s reservlösning: Clause har ingen native sendfile.format_error/1ger OTP:s texter, POSIX-texterna frånerl_posix_msg:message/1. - Rader och misslyckade läsningar:
read_line/1ochio:get_lineläser en CRLF-radbrytning som LF (positionen räknar CR), som OTP; en filserver vars läsning misslyckas svarar med felet och avslutas, och en vars radläsning misslyckas avslutas utan svar (anroparen får{error, terminated}), som OTP:s. compressedochcompressed_one: skrivning går genom en gzip deflate-ström (zlib, window bits 31) som avslutas vid stängning, även när servern stannar eller den som öppnade avslutas; läsning packar upp en fil som börjar med gzip:s magiska bytes (compressedläser sammanfogade delar som en,compressed_onestannar efter den första) och läser alla andra filer som de är. Positioner räknar okomprimerade bytes: framåt genom att läsa eller skriva nollor, bakåt endast vid läsning (från början igen),eofäreinval. Att läsa en skriven fil eller skriva en läst ärebadf;allocate/3och, på raw-filer, positionerad I/O ärenotsup; en filserver stannar eftertruncate,adviseochsync(enotsup) som OTP:s. Samtidig läsning och skrivning eller tillägg är{error, einval}.{zstd, Options}: somcompressed, genom en zstd-kontext som skapats medOptions({error, badarg}när den avvisar dem): varje skrivning är en egen ram och stängning lägger till en tom, så som OTP:s lager skriver; läsning packar upp ramarna som läses i block om 8 KiB.ram: filen är sitt iodata-innehåll i minnet, betjänas av en process som andra filer och stängs med den som öppnade; endast lägenaread,writeochbinary(readnär ingetdera anges, allt annatbadarg,rawmedramkastarbadarg). Läsningar, skrivningar, positioner,pread/pwrite(positionen behålls),truncate,sync,adviseochallocatesvarar som OTP:s ram-fildrivrutin: skrivningar bortom slutet fyller mellanrummet med nollor, offsetar och storlekar utanför 32-bitars heltal med tecken äreinval, skrivning till en skrivskyddad fil ärebadf, trunkering av deneacces.- OTP:s I/O-protokoll: standard-indataservern och servern för varje öppen fil
besvarar
{io_request, From, ReplyAs, Request}med{io_reply, ReplyAs, Reply}, somio:request/1,2ochio:requests/1,2skickar (standard_ioochusernamnger standard-indataservern, en annan atom en registrerad process;{error, arguments}när den inte är registrerad,{error, calling_self}för anroparen själv). Begäranden:get_until(M:F(Cont, Chars | eof, Extra...)frånCont = [], matad med det som är buffrat eller filblock om 4 KiB tills{done, Result, Rest};Restläses igen av nästa begäran: det behålls i bufferten, eller för en fil flyttas positionen tillbaka över det),get_line,get_chars,put_chars(tecken ellerM:F(A)), de gamla formerna utan kodning (latin1) och{requests, List}(i ordning, stannar vid första{error, _}). Tecken är bytes, som ovan; en fil i binärläge returnerar ett listresultat frånget_untilsom en binary. Andra får{error, {request, Request}}. Enget_until- ellerput_chars-funktion som misslyckas får en fils server att svara{error, Function}och avsluta med felet, som OTP:s gör; den som öppnade lever vidare (OTP:s filserver är inte länkad till den). Indatabegäranden på standard-indata skriver sin prompt. - Kataloger, länkar och kopior:
get_cwd/0stavar den aktuella katalogen som OTP gör (snedstreck framåt, liten enhetsbokstav),get_cwd/1tar en Windows-enhet (enotsuppå andra system);set_cwd/1ändrar den för hela programmet;read_link/1,read_link_all/1(einvalför en sökväg som inte är en länk),read_link_info/1,2(en slutlig länks egen status: POSIXlstat, på Windows målets fält med typensymlink),make_link/2,make_symlink/2(kräver en behörighet på Windows:eperm, som OTP),list_dir_all/1,del_dir_r/1,copy/2,3(namn,{Name, Modes}eller öppna filer;{ok, BytesCopied}),raw_read_file_info/1,read_file/2,delete/2,native_name_encoding/0(utf8).del_dir/1svararenoentför en saknad sökväg ochenotdirför en fil. - Clauses
fileär fillagret; Clausesfilelibochfilenamekörs ovanpå det (filelib:fold_files/5använderre);erl_prim_loadertillhandahålls ovanpåfilemed OTP:s naknaerror-svar och en laddningssökväg["."](set_path/1), dess laddnings- och arkivfunktioner avböjs;prim_fileochprim_buffersaknas fortfarande, så OTP:s anropare avraw_file_io*kommer aldrig in i ett program. - Fel följer OTP:
{error, enoent},eexist,eisdir(en katalog som öppnas eller läses som en fil),einval(en negativ position;badargför något som inte är en position),ebadf(läsning av en fil som bara är skrivbar eller skrivning till en som bara är läsbar),badargför felaktiga namn och data. Filnamn är strängar, binaries eller atomer, kodade som UTF-8.
Socketar
En socket är en port, som med OTP:s backend inet_drv: is_port(Socket) är
sant och meddelanden i aktivt läge är {tcp, Socket, Data},
{tcp_closed, Socket}, {tcp_error, Socket, Reason},
{udp, Socket, Address, Port, Data}. Delmängden:
gen_tcp:connect/2,3,4 (en värd och port, eller en socketadress som map),
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 (OTP:s destinationsformer och tilläggsdata), 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; lägena {active, true | false | once},
binary/list ({mode, M}), {packet, 0 | 1 | 2 | 4 | raw}, {backlog, N},
{ip, Address}/{ifaddr, Address}, inet/inet6; IPv4- och IPv6-adresser
som tupler, värdnamn som strängar eller atomer (loopback inräknat).
Alternativ utöver dataläget (ports/socket_options.cpp,
library/kernel/clause_socket_options.erl) är drivrutinsrecord med en kod och
ett värde, angivna när en socket öppnas (sätts innan den binds eller ansluts)
eller med setopts/2; varje alternativ i ett setopts/2-anrop kontrolleras
innan något sätts. Systemets alternativ (reuseaddr, keepalive, dontroute,
linger, broadcast, sndbuf, recbuf, nodelay, tos, tclass,
ipv6_v6only, multicast_ttl, multicast_loop, multicast_if,
add_membership, drop_membership, exclusiveaddruse på Windows, priority
och reuseport där systemet har dem) går till setsockopt och läses tillbaka
med getsockopt; de alternativ OTP:s inet_drv behåller självt (buffer,
header, deliver, exit_on_close, send_timeout, vattenmärken,
packet_size, ...) behålls med inet_drv:s standardvärden och tillämpas inte
(skillnader). Som i inet_drv höjer en mottagningsbuffert
buffer tills det sätts, och getopts/2 utelämnar alternativ som en
sockettyp saknar (broadcast för en TCP-socket, nodelay för en UDP-socket,
tclass för IPv4) och avvisar okända med {error, einval}. En accepterad
anslutning övertar sin lyssnande sockets alternativ. send/5 med tilläggsdata
sätter tos, tclass eller ttl enbart för det datagrammet; på Windows är
det {error, einval}, som i OTP.
Varje socket räknar sin trafik som inet_drv gör för getstat/1,2: mottagna
paket är läsningar (TCP) eller datagram, skickade paket är send-anrop (med
sitt pakethuvud) eller datagram, med inet_drv:s löpande medelvärden;
send_pend är köad utdata som inte skrivits. getstatus ger inet_drv:s
statusflaggor (open, connected, connecting, listen, accepting), som
info/1 och i/0,1,2 skriver ut med OTP:s namn och modulkolumn (inet_tcp,
inet6_udp, ...). Frågor om en stängd socket besvaras med {error, einval},
som i OTP.
Modulen socket (library/kernel/socket.erl) använder samma drivrutinsportar
i passivt binärläge. En strömsocket kan bindas innan den lyssnar eller
ansluter (operation 22 öppnar och binder systemsocketen, som listen gör
till accepterare eller connect ansluter); en datagramsocket binds av
udp_open, implicit vid sin första sändning. Slutet på en anslutning besvarar
varje väntande recv (även en som begär fler bytes än som återstår).
socket behåller de bytes som en recv inte tog i sin egen buffert, så
recv/2,3,4 ger OTP:s {error, {timeout, Data}} och
{error, {closed, Data}}.
Implementation (runtime/src/ports/sockets.cpp):
- Runtimens I/O-tråd (I/O-tråd) betjänar socketarna; varje
sockets tillstånd finns på den tråden. Biblioteket öppnar en port med
{spawn_driver, "tcp_inet" | "udp_inet"}och driver den medport_control/3; den anropande arbetstråden lägger upp operationen på I/O-tråden och väntar på dess synkrona svar (statusbyte 0 och ett resultat, eller 1 och en POSIX-orsak). - Operationer som väntar (connect, accept, recv) svarar senare med ett
meddelande
{clause_socket, Socket, Reply}till sin anropare, så att anroparen blockeras i en vanligreceiveoch andra processer fortsätter att köras. En tidsgräns avbryter begäran; själva avbrottet svararcancelledefter allt som socketen skickat dessförinnan, så att ett svar som vann kapplöpningen returneras i stället för att gå förlorat. - Socketmeddelanden beskrivs utanför heapen (
ports/value.hpp) och byggs i mottagarens heap när de levereras (portuppgifter). En accepterad anslutning blir en ny port som ägs av och är länkad till den som anropadeaccept, med den lyssnande socketens läge. controlling_process/2stoppar aktiv leverans, flyttar socketens meddelanden som redan skickats till den tidigare ägaren till den nya, och ansluter sedan om porten, som OTP:sinetgör; endast ägaren får anropa den ({error, not_owner}).- Utdata köas utan tak och skrivs i ordning på I/O-tråden. Att stänga en port
skickar det som står i kö och stänger sedan anslutningen på ett ordnat sätt
(FIN); en stängd port svarar varje väntande anropare
{error, closed}. Namnuppslagning körs på anroparens arbetstråd, eftersom den blockerar. - Socketportar schemaläggs som andra portar (portuppgifter): en sockets meddelanden väntar i dess port tills portens uppgift levererar dem.
- Adresser och värdar:
inet:parse_address/1,2,parse_strict_address/1,2,parse_ipv4_address/1,parse_ipv4strict_address/1,parse_ipv6_address/1,parse_ipv6strict_address/1(OTP:s tillåtande former: korta och oktala eller hexadecimala IPv4-fält,::, IPv4-svansar,%scopeförfe80::ochff02::),ntoa/1(OTP:s stavning: hexadecimalt med gemener, den första längsta nollsekvensen som::),is_ipv4_address/1,is_ipv6_address/1,is_ip_address/1är Erlang;gethostname/0(en socketoperation),getaddr/2,getaddrs/2(systemets resolver,getaddrinfo) ochgethostbyname/1,2(h_nameär det efterfrågade namnet, inga alias) svarar genom en socketport som öppnas för förfrågan. - Tjänster, omvända uppslagningar och gränssnitt (
inet:getservbyname/2,getservbyport/2,gethostbyaddr/1,getifaddrs/0,1) är inbyggdaclause_net-funktioner som delas mednet(nätverksnamn). - Clauses
gen_tcp,gen_udp,inetochnetär nätverkslagret; OTP:s modulsocket(socket-NIF-API:t) saknas, liksomprim_socket,prim_net,prim_inetochsocket_registry.
Registret
win32reg öppnar {spawn_driver, "clause_registry"} på Windows
(runtime/src/ports/registry_windows.cpp). Drivrutinen behåller tillståndet
hos OTP:s registerdrivrutin (begärd åtkomst, aktuell nyckel och dess rot,
inledningsvis HKEY_CLASSES_ROOT) och besvarar OTP:s nio kommandon som
synkrona port_control/3-operationer med de svar OTP:s drivrutin skickar som
portdata: o, e och errno-namnet (registerskada är eio, en borttagen
nyckel einval, andra Windows-fel så som C-runtime mappar dem), k och ett
nyckelnamn, v, typ, namn och data (strängar utan sin avslutande nolla,
DWORD-värden i big-endian), s och aktuell nyckel. Listningar returnerar
varje svar, vart och ett föregånget av sin 32-bitars storlek. Namn och
strängar använder ANSI-teckentabellen, som OTP:s drivrutin.
Tillhandahålls inte
Inlänkade drivrutiner (linked-in drivers) och NIF:ar,
erlang:open_port({spawn_driver, Name}) för OTP-drivrutinsnamn,
distributionsportar, port_call/3 på de tillhandahållna drivrutinerna utöver
bibliotekets eget protokoll, upptagna socketportar samt alternativen
overlapped_io, parallelism och busy_limits_msgq.
Clause