Questu articulu descrive l'implementazione di pipeline in u kernel Unix. Eru un pocu dispiaciutu chì un articulu recente intitulatu "
Di chì parlemu ?
Pipelines sò "probabilmente l'invenzione più impurtante in Unix" - una caratteristica di definizione di a filusufìa sottostante di Unix di mette inseme picculi prugrammi, è u slogan familiar di linea di cummanda:
$ echo hello | wc -c
6
Sta funziunalità dipende da a chjama di u sistema furnita da u kernel pipe
, chì hè descritta nantu à e pagine di documentazione
Pipelines furnisce un canale unidirezionale per a cumunicazione inter-processu. U pipeline hà un input (scrittura) è un output (lettura). I dati scritti à l'input di u pipeline ponu esse leghjite à u output.
U pipeline hè creatu da chjamà
pipe(2)
, chì torna dui descriptori di file: unu si riferisce à l'input di u pipeline, u sicondu à l'output.
L'output di traccia da u cumandamentu sopra mostra a creazione di una pipeline è u flussu di dati attraversu da un prucessu à l'altru:
$ strace -qf -e execve,pipe,dup2,read,write
sh -c 'echo hello | wc -c'
execve("/bin/sh", ["sh", "-c", "echo hello | wc -c"], …)
pipe([3, 4]) = 0
[pid 2604795] dup2(4, 1) = 1
[pid 2604795] write(1, "hellon", 6) = 6
[pid 2604796] dup2(3, 0) = 0
[pid 2604796] execve("/usr/bin/wc", ["wc", "-c"], …)
[pid 2604796] read(0, "hellon", 16384) = 6
[pid 2604796] write(1, "6n", 2) = 2
U prucessu parent chjama pipe()
per uttene descrittori di file allegati. Un prucessu di u zitellu scrive à un descrittore è un altru prucessu leghje i stessi dati da un altru descrittore. A shell "rinomina" i descrittori 2 è 3 cù dup4 per currisponde à stdin è stdout.
Senza pipeline, a cunchiglia duveria scrive l'output di un prucessu à un schedariu è u pipe à un altru prucessu per leghje e dati da u schedariu. In u risultatu, avariamu perdi più risorse è spaziu di discu. Tuttavia, i pipelines sò boni per più di evità i fugliali temporanei:
Se un prucessu prova di leghje da un pipeline viotu, allora
read(2)
bluccarà finu à chì i dati sò dispunibili. Se un prucessu prova di scrive à un pipeline cumpletu, allorawrite(2)
bluccarà finu à chì abbastanza dati hè statu lettu da u pipeline per compie a scrittura.
Cum'è u requisitu POSIX, questu hè una pruprietà impurtante: scrive à u pipeline finu à PIPE_BUF
bytes (almenu 512) deve esse atomicu per chì i prucessi ponu cumunicà cù l'altri attraversu u pipeline in una manera chì i schedarii normali (chì ùn furnisce micca tali garanzii) ùn ponu micca.
Cù un schedariu regulare, un prucessu pò scrive tuttu u so output à ellu è trasmette à un altru prucessu. O i prucessi ponu operà in un modu parallelu duru, utilizendu un mecanismu di signalamentu esternu (cum'è un semaforu) per informà l'altri nantu à a fine di una scrittura o lettura. I trasportatori ci salvanu da tuttu stu fastidiu.
Chì circhemu ?
Vi spiegheraghju nantu à e mo dite per fà più faciule per voi imagine cumu un trasportatore pò travaglià. Avete bisognu di assignà un buffer è qualchì statu in memoria. Avete bisognu di funzioni per aghjunghje è sguassà dati da u buffer. Averete bisognu di qualchì facilità per chjamà funzioni durante l'operazioni di lettura è scrittura nantu à i descrittori di file. È i chjusi sò necessarii per implementà u cumpurtamentu speciale descrittu sopra.
Avà simu pronti à interrogà u codice fonte di u kernel sottu un lume luminoso per cunfirmà o dispruvà u nostru vagu mudellu mentale. Ma sempre esse preparatu per l'imprevisu.
Induve circhemu ?
Ùn sò micca induve si trova a mo copia di u famosu libru.
Passà per l'archivi TUHS hè cum'è visità un museu. Pudemu guardà a nostra storia cumuna è aghju rispettu per l'anni di sforzu per ricuperà tuttu stu materiale pocu à pocu da vechji cassette è stampe. È sò assai cuscente di quelli frammenti chì sò sempre mancanti.
Dopu avè sappiutu a nostra curiosità nantu à a storia antica di i pipelines, pudemu fighjà nuclei muderni per paragunà.
In modu, pipe
hè u numeru di chjama di u sistema 42 in a tavula sysent[]
. Coincidenza ?
Kernels Unix tradiziunali (1970-1974)
Ùn aghju trovu traccia pipe(2)
nè in
TUHS dice chì
A terza edizione di Unix era l'ultima versione cù un kernel scrittu in assembler, ma ancu a prima versione cù pipelines. Duranti u 1973, u travagliu era in corso per migliurà a terza edizione, u kernel hè stata riscritta in C, è cusì hè nata a quarta edizione di Unix.
Un lettore hà truvatu una scansione di un documentu in u quale Doug McIlroy hà prupostu l'idea di "collega programmi cum'è una manguera di giardinu".
In u libru di Brian Kernighan
Quandu Unix apparsu, a mo passione per i coroutines m'hà fattu dumandà à l'autore di u SO, Ken Thompson, per permette à e dati scritti à qualchì prucessu per andà micca solu à u dispusitivu, ma ancu à a surtita à un altru prucessu. Ken hà pensatu chì era pussibule. Tuttavia, cum'è minimalista, vulia chì ogni funzione di u sistema ghjucassi un rolu significativu. A scrittura diretta trà i prucessi hè veramente un grande vantaghju nantu à a scrittura à un schedariu intermediu? È solu quandu aghju fattu una pruposta specifica cù u nome catchy "pipeline" è una descrizzione di a sintassi di l'interazzione di i prucessi, Ken infine esclamò: "Aghju da fà!".
È hà fattu. Una sera fatale, Ken hà cambiatu u kernel è a cunchiglia, hà riparatu parechji prugrammi standard per standardizà cumu accettanu l'input (chì puderia vene da una pipeline), è hà cambiatu i nomi di file. U ghjornu dopu, i pipelines eranu assai usati in l'applicazioni. À a fine di a settimana, i secretarii anu utilizatu per mandà documenti da i prucessori di testu à l'impresora. Un pocu dopu, Ken hà rimpiazzatu l'API è a sintassi originali per impacchendu l'usu di pipeline cù cunvenzioni più pulite chì sò state aduprate dapoi.
Sfurtunatamente, u codice fonte per a terza edizione kernel Unix hè stata persa. E ancu s'è avemu u codice fonte di u kernel scrittu in C
Avemu testu di documentazione per pipe(2)
da e duie versioni, cusì pudete principià per circà a documentazione pipe(2)
hè scrittu in assembler è torna solu un descrittore di u schedariu, ma furnisce digià a funziunalità di u core prevista:
Chjama di sistema Pipe crea un mecanismu I/O chjamatu pipeline. U descrittore di u schedariu restituitu pò esse usatu per operazioni di lettura è scrittura. Quandu qualcosa hè scrittu à u pipeline, buffers finu à 504 bytes di dati, dopu chì u prucessu di scrittura hè suspesu. Quandu leghje da u pipeline, i dati buffered sò pigliati.
À l'annu dopu, u kernel hè statu riscritto in C, è pipe(fildes)
»:
Chjama di sistema Pipe crea un mecanismu I/O chjamatu pipeline. I descrittori di u schedariu restituiti ponu esse utilizati in operazioni di lettura è scrittura. Quandu qualcosa hè scrittu à u pipeline, u descrittore torna in r1 (resp. fildes [1]) hè utilizatu, buffered up to 4096 bytes of data, dopu chì u prucessu di scrittura hè suspesu. Quandu leghje da u pipeline, u descrittore torna à r0 (resp. fildes[0]) piglia a dati.
Si assume chì una volta chì un pipeline hè statu definitu, dui (o più) prucessi interagiscendu (creati da invucazioni successive). Furcculu) passerà dati da u pipeline usendu chjamate leghje и scrivemu.
A shell hà una sintassi per definisce un array lineare di prucessi cunnessi via un pipeline.
Chjamate à leghje da un pipeline viotu (chì ùn cuntene micca dati buffered) chì hà solu una fine (tutti i descrittori di u schedariu di scrittura chjusi) tornanu "fine di u schedariu". Scrive chjamate in una situazione simili sò ignorati.
Prima
Sesta edizione Unix (1975)
Cumincià à leghje u codice fonte Unix
Per parechji anni u libru Lions era u solu documentu nantu à u kernel Unix dispunibule fora di Bell Labs. Ancu se a licenza di a sesta edizione hà permessu à i prufessori di utilizà u so codice fonte, a licenza di a sesta edizione escludeva sta pussibilità, cusì u libru hè statu distribuitu in copie illegale da tipografia.
Oghje pudete cumprà una copia di ristampa di u libru, a copertina di quale raffigura i studienti à a copiatrice. È grazia à Warren Toomey (chì hà iniziatu u prughjettu TUHS), pudete scaricà
Più di 15 anni fà, aghju scrittu una copia di u codice fonte furnitu Lionsperchè ùn mi piaceva micca a qualità di a mo copia da un numeru scunnisciutu di altre copie. TUHS ùn esiste micca ancu, è ùn aghju micca accessu à e vechji fonti. Ma in u 1988 aghju trovu una vechja cinta cù 9 piste chì avianu una copia di salvezza da un computer PDP11. Era difficiuli di sapè s'ellu hà travagliatu, ma ci era un arburu intactu /usr/src/ in quale a maiò parte di i schedari eranu marcati 1979, chì ancu allora parevanu anticu. Era a settima edizione, o un derivativu PWB, pensu.
Aghju pigliatu a ricerca cum'è una basa è editatu manualmente e fonti à u statu di a sesta edizione. Una parte di u codice hè stata a listessa, una parte hà da esse ligeramente editata, cambiendu u token mudernu += à l'obsoletu =+. Qualcosa hè stata simplicemente sguassata, è qualcosa avia da esse riscritta cumplettamente, ma micca troppu.
È oghje pudemu leghje in linea à TUHS u codice fonte di a sesta edizione di
In modu, à u primu sguardu, a funzione principale di u codice C prima di u periodu di Kernighan è Ritchie hè u so brevità. Ùn hè micca spessu chì sò capaci di inserisce snippets di codice senza una edizione estensiva per adattà una zona di visualizazione relativamente stretta in u mo situ.
À u principiu
/*
* Max allowable buffering per pipe.
* This is also the max size of the
* file created to implement the pipe.
* If this size is bigger than 4096,
* pipes will be implemented in LARG
* files, which is probably not good.
*/
#define PIPSIZ 4096
A dimensione di u buffer ùn hè micca cambiatu da a quarta edizione. Ma quì vedemu, senza alcuna ducumentazione publica, chì i pipelines usavanu una volta i fugliali cum'è almacenamentu di riserva!
In quantu à i schedarii LARG, currispondenu à
Eccu u veru sistema chjamatu pipe
:
/*
* The sys-pipe entry.
* Allocate an inode on the root device.
* Allocate 2 file structures.
* Put it all together with flags.
*/
pipe()
{
register *ip, *rf, *wf;
int r;
ip = ialloc(rootdev);
if(ip == NULL)
return;
rf = falloc();
if(rf == NULL) {
iput(ip);
return;
}
r = u.u_ar0[R0];
wf = falloc();
if(wf == NULL) {
rf->f_count = 0;
u.u_ofile[r] = NULL;
iput(ip);
return;
}
u.u_ar0[R1] = u.u_ar0[R0]; /* wf's fd */
u.u_ar0[R0] = r; /* rf's fd */
wf->f_flag = FWRITE|FPIPE;
wf->f_inode = ip;
rf->f_flag = FREAD|FPIPE;
rf->f_inode = ip;
ip->i_count = 2;
ip->i_flag = IACC|IUPD;
ip->i_mode = IALLOC;
}
U cumentu descrive chjaramente ciò chì succede quì. Ma ùn hè micca cusì faciule per capisce u codice, in parte per via di cumu "R0
и R1
i paràmetri di chjama di u sistema è i valori di ritornu sò passati.
Pruvemu cun
pipe()
dovutu à traversu R0
и R1
rinvià i numeri di descrittore di u schedariu per leghje è scrive. falloc()
torna un punteru à una struttura di u schedariu, ma ancu "ritorna" via u.u_ar0[R0]
è un descrittore di file. Questu hè, u codice hè guardatu in r
descriptore di file per leghje è assigna un descrittore per scrive direttamente da u.u_ar0[R0]
dopu a seconda chjama falloc()
.
Bandiera FPIPE
, chì avemu stabilitu quandu creanu u pipeline, cuntrolla u cumpurtamentu di a funzione
/*
* common code for read and write calls:
* check permissions, set base, count, and offset,
* and switch out to readi, writei, or pipe code.
*/
rdwr(mode)
{
register *fp, m;
m = mode;
fp = getf(u.u_ar0[R0]);
/* … */
if(fp->f_flag&FPIPE) {
if(m==FREAD)
readp(fp); else
writep(fp);
}
/* … */
}
Allora a funzione readp()
в pipe.c
leghje dati da u pipeline. Ma hè megliu à seguità a implementazione partendu da writep()
. In novu, u codice hè diventatu più cumplicatu per via di a natura di l'argumentu chì passa a cunvenzione, ma alcuni dettagli ponu esse omessi.
writep(fp)
{
register *rp, *ip, c;
rp = fp;
ip = rp->f_inode;
c = u.u_count;
loop:
/* If all done, return. */
plock(ip);
if(c == 0) {
prele(ip);
u.u_count = 0;
return;
}
/*
* If there are not both read and write sides of the
* pipe active, return error and signal too.
*/
if(ip->i_count < 2) {
prele(ip);
u.u_error = EPIPE;
psignal(u.u_procp, SIGPIPE);
return;
}
/*
* If the pipe is full, wait for reads to deplete
* and truncate it.
*/
if(ip->i_size1 == PIPSIZ) {
ip->i_mode =| IWRITE;
prele(ip);
sleep(ip+1, PPIPE);
goto loop;
}
/* Write what is possible and loop back. */
u.u_offset[0] = 0;
u.u_offset[1] = ip->i_size1;
u.u_count = min(c, PIPSIZ-u.u_offset[1]);
c =- u.u_count;
writei(ip);
prele(ip);
if(ip->i_mode&IREAD) {
ip->i_mode =& ~IREAD;
wakeup(ip+2);
}
goto loop;
}
Vulemu scrive byte à l'input di pipeline u.u_count
. Prima avemu bisognu di chjude l'inode (vede quì sottu plock
/prele
).
Allora cuntrollemu u conte di riferimentu inode. Mentre i dui estremità di u pipeline restanu aperti, u cuntatore deve esse 2. Tenemu à un ligame (da rp->f_inode
), dunque se u contatore hè menu di 2, allora questu significa chì u prucessu di lettura hà chjusu a fine di u pipeline. In altri palori, avemu pruvatu à scrive à un pipeline chjusu, chì hè un sbagliu. U primu codice d'errore EPIPE
è signale SIGPIPE
apparsu in a sesta edizione di Unix.
Ma ancu se u trasportatore hè apertu, pò esse pienu. In questu casu, liberamu a serratura è andemu à dorme in a speranza chì un altru prucessu leghjerà da u pipeline è liberà abbastanza spaziu in questu. Quandu avemu svegliatu, vultemu à u principiu, appiccà u serratura di novu è principià un novu ciclu di scrittura.
Se ci hè abbastanza spaziu liberu in u pipeline, allora scrivimu dati cù l'usu i_size1
l'inode'a (cù un pipeline viotu pò esse uguali à 0) punta à a fine di e dati chì cuntene digià. Se ci hè abbastanza spaziu per scrive, pudemu riempie u pipeline da i_size1
à PIPESIZ
. Allora liberamu a serratura è pruvate à sveglià ogni prucessu chì aspetta per leghje da u pipeline. Riturnemu à l'iniziu per vede s'ellu ci hè riesciutu à scrive quant'è byte avemu bisognu. Se no, allora avemu principiatu un novu ciclu di registrazione.
Di solitu paràmetru i_mode
inode hè utilizatu per almacenà i permessi r
, w
и x
. Ma in u casu di pipelines, signalemu chì qualchì prucessu aspetta per una scrittura o leghje cù bits IREAD
и IWRITE
rispettivamente. U prucessu stabilisce a bandiera è chjama sleep()
, è hè previstu chì in u futuru qualchì altru prucessu chjamarà wakeup()
.
A vera magia succede in sleep()
и wakeup()
. Sò implementati in
/*
* Give up the processor till a wakeup occurs
* on chan, at which time the process
* enters the scheduling queue at priority pri.
* The most important effect of pri is that when
* pri<0 a signal cannot disturb the sleep;
* if pri>=0 signals will be processed.
* Callers of this routine must be prepared for
* premature return, and check that the reason for
* sleeping has gone away.
*/
sleep(chan, pri) /* … */
/*
* Wake up all processes sleeping on chan.
*/
wakeup(chan) /* … */
U prucessu chì chjama sleep()
per un canale particulari, pò dopu esse svegliatu da un altru prucessu, chì chjamarà wakeup()
per u listessu canale. writep()
и readp()
coordinà e so azzioni attraversu tali chiamate accoppiate. nota chì pipe.c
sempre priorità PPIPE
quandu chjamatu sleep()
, cusì tuttu sleep()
pò esse interrotta da un signalu.
Avà avemu tuttu per capiscenu a funzione readp()
:
readp(fp)
int *fp;
{
register *rp, *ip;
rp = fp;
ip = rp->f_inode;
loop:
/* Very conservative locking. */
plock(ip);
/*
* If the head (read) has caught up with
* the tail (write), reset both to 0.
*/
if(rp->f_offset[1] == ip->i_size1) {
if(rp->f_offset[1] != 0) {
rp->f_offset[1] = 0;
ip->i_size1 = 0;
if(ip->i_mode&IWRITE) {
ip->i_mode =& ~IWRITE;
wakeup(ip+1);
}
}
/*
* If there are not both reader and
* writer active, return without
* satisfying read.
*/
prele(ip);
if(ip->i_count < 2)
return;
ip->i_mode =| IREAD;
sleep(ip+2, PPIPE);
goto loop;
}
/* Read and return */
u.u_offset[0] = 0;
u.u_offset[1] = rp->f_offset[1];
readi(ip);
rp->f_offset[1] = u.u_offset[1];
prele(ip);
}
Puderete truvà più faciule per leghje sta funzione da u fondu à a cima. U ramu "leghje è ritornu" hè generalmente utilizatu quandu ci sò qualchi dati in u pipeline. In questu casu, avemu aduprà f_offset
leghje, è dopu aghjurnà u valore di l'offset currispundenti.
In letture successive, u pipeline serà viotu se l'offset di lettura hà righjuntu i_size1
à l'inode. Resetemu a pusizione à 0 è pruvate à sveglià ogni prucessu chì vole scrive à u pipeline. Sapemu chì quandu u trasportatore hè pienu, writep()
dorme nantu ip+1
. È avà chì u pipeline hè viotu, pudemu svegliate per ripiglià u so ciclu di scrittura.
Se ùn ci hè nunda di leghje, allora readp()
pò mette una bandiera IREAD
è si addormenta ip+2
. Sapemu ciò chì u svegliarà writep()
quandu scrive qualchi dati à u pipeline.
Cumenti nantu à u
» Pudemu trattà cum'è funzioni I / O regulari chì piglianu un schedariu, una pusizioni, un buffer in memoria, è cuntà u numeru di bytes per leghje o scrive.
/*
* Read the file corresponding to
* the inode pointed at by the argument.
* The actual read arguments are found
* in the variables:
* u_base core address for destination
* u_offset byte offset in file
* u_count number of bytes to read
* u_segflg read to kernel/user
*/
readi(aip)
struct inode *aip;
/* … */
/*
* Write the file corresponding to
* the inode pointed at by the argument.
* The actual write arguments are found
* in the variables:
* u_base core address for source
* u_offset byte offset in file
* u_count number of bytes to write
* u_segflg write to kernel/user
*/
writei(aip)
struct inode *aip;
/* … */
In quantu à u bloccu "cunservatore", allora readp()
и writep()
bluccà inodi finu à ch'elli finiscinu o ottene un risultatu (vale à dì, chjamanu wakeup
). plock()
и prele()
travaglià simpliciamente: utilizendu un altru gruppu di chjama sleep
и wakeup
permettenu di sveglià ogni prucessu chì hà bisognu di a serratura chì avemu appena liberatu:
/*
* Lock a pipe.
* If its already locked, set the WANT bit and sleep.
*/
plock(ip)
int *ip;
{
register *rp;
rp = ip;
while(rp->i_flag&ILOCK) {
rp->i_flag =| IWANT;
sleep(rp, PPIPE);
}
rp->i_flag =| ILOCK;
}
/*
* Unlock a pipe.
* If WANT bit is on, wakeup.
* This routine is also used to unlock inodes in general.
*/
prele(ip)
int *ip;
{
register *rp;
rp = ip;
rp->i_flag =& ~ILOCK;
if(rp->i_flag&IWANT) {
rp->i_flag =& ~IWANT;
wakeup(rp);
}
}
In prima ùn pudia capisce perchè readp()
ùn causa micca prele(ip)
prima di a chjama wakeup(ip+1)
. A prima cosa writep()
chjama in u so ciclu, questu plock(ip)
, chì si traduce in un bloccu se readp()
ùn hà micca sguassatu u so bloccu ancu, cusì u codice deve in qualchì modu funziona bè. S'è vo guardate wakeup()
, diventa chjaru chì marca solu u prucessu di dorme cum'è prontu per l'esekzione, perchè in u futuru sched()
l'hà veramente lanciatu. Allora readp()
cause wakeup()
, sblocca, mette IREAD
è chjama sleep(ip+2)
- tuttu questu prima writep()
ripiglia u ciclu.
Questu cumpleta a descrizzione di pipelines in a sesta edizione. Codici simplice, implicazioni di vasta portata.
Xv6, un kernel simplice simile à Unix
Per creà un nucleu
U codice cuntene una implementazione chjara è pensativa pipealloc()
:
#define PIPESIZE 512
struct pipe {
struct spinlock lock;
char data[PIPESIZE];
uint nread; // number of bytes read
uint nwrite; // number of bytes written
int readopen; // read fd is still open
int writeopen; // write fd is still open
};
int
pipealloc(struct file **f0, struct file **f1)
{
struct pipe *p;
p = 0;
*f0 = *f1 = 0;
if((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0)
goto bad;
if((p = (struct pipe*)kalloc()) == 0)
goto bad;
p->readopen = 1;
p->writeopen = 1;
p->nwrite = 0;
p->nread = 0;
initlock(&p->lock, "pipe");
(*f0)->type = FD_PIPE;
(*f0)->readable = 1;
(*f0)->writable = 0;
(*f0)->pipe = p;
(*f1)->type = FD_PIPE;
(*f1)->readable = 0;
(*f1)->writable = 1;
(*f1)->pipe = p;
return 0;
bad:
if(p)
kfree((char*)p);
if(*f0)
fileclose(*f0);
if(*f1)
fileclose(*f1);
return -1;
}
pipealloc()
stabilisce u statu di tuttu u restu di l'implementazione, chì include e funzioni piperead()
, pipewrite()
и pipeclose()
. A chjama di u sistema attuale sys_pipe
hè un wrapper implementatu in
Linux 0.01
Pudete truvà u codice fonte per Linux 0.01. Serà istruttivu per studià l'implementazione di pipelines in u so fs
/pipe.c
. Quì, un inode hè utilizatu per rapprisintà u pipeline, ma u pipeline stessu hè scrittu in C mudernu. Se avete pirate u vostru modu à traversu u codice di a sesta edizione, ùn avete micca prublemi quì. Questu hè ciò chì a funzione pare write_pipe()
:
int write_pipe(struct m_inode * inode, char * buf, int count)
{
char * b=buf;
wake_up(&inode->i_wait);
if (inode->i_count != 2) { /* no readers */
current->signal |= (1<<(SIGPIPE-1));
return -1;
}
while (count-->0) {
while (PIPE_FULL(*inode)) {
wake_up(&inode->i_wait);
if (inode->i_count != 2) {
current->signal |= (1<<(SIGPIPE-1));
return b-buf;
}
sleep_on(&inode->i_wait);
}
((char *)inode->i_size)[PIPE_HEAD(*inode)] =
get_fs_byte(b++);
INC_PIPE( PIPE_HEAD(*inode) );
wake_up(&inode->i_wait);
}
wake_up(&inode->i_wait);
return b-buf;
}
Ancu senza guardà e definizioni di strutture, pudete capisce cumu u conte di riferimentu inode hè utilizatu per verificà se una operazione di scrittura risulta in SIGPIPE
. In più di u travagliu byte-by-byte, sta funzione hè faciule da paragunà cù l'idee sopra. Ancu a logica sleep_on
/wake_up
ùn pare micca cusì straneru.
Modern Linux Kernels, FreeBSD, NetBSD, OpenBSD
Aghju passatu rapidamente alcuni kernel muderni. Nisunu di elli hà digià una implementazione basata in discu (micca sorpresa). Linux hà a so propria implementazione. E ancu s'è i trè kernels BSD muderni cuntenenu implementazioni basate nantu à u codice chì hè statu scrittu da John Dyson, cù l'anni sò diventati troppu diffirenti l'un l'altru.
Per leghje fs
/pipe.c
(nantu à Linux) o sys
/kern
/sys_pipe.c
(nantu à *BSD), ci vole una vera dedicazione. U rendiment è u supportu per e funzioni cum'è l'I / O vettoriali è asincroni sò impurtanti in u codice oghje. È i dettagli di l'allocazione di memoria, i chjusi è a cunfigurazione di u kernel varianu assai. Ùn hè micca ciò chì l'università necessitanu per un cursu introduttivu à i sistemi operativi.
In ogni casu, era interessante per mè di scopre uni pochi di mudelli antichi (per esempiu, generà SIGPIPE
è torna EPIPE
quandu scrive à un pipeline chjusu) in tutti questi, cusì sfarenti, kernels muderni. Probabilmente ùn vi mai vede un urdinatore PDP-11 in diretta, ma ci hè ancu assai per amparà da u codice chì hè statu scrittu uni pochi d'anni prima ch'e sia natu.
Scrittu da Divi Kapoor in u 2011, l'articulu "
Source: www.habr.com