Hierdie artikel beskryf die implementering van pyplyne in die Unix-kern. Ek was ietwat teleurgesteld dat 'n onlangse artikel getiteld "
Waaroor praat ons?
Pyplyne is "waarskynlik die belangrikste uitvinding in Unix" - 'n bepalende kenmerk van Unix se onderliggende filosofie om klein programme saam te stel, en die bekende opdragreël slagspreuk:
$ echo hello | wc -c
6
Hierdie funksionaliteit hang af van die kern-verskafde stelseloproep pipe
, wat op die dokumentasiebladsye beskryf word
Pyplyne bied 'n eenrigtingkanaal vir interproseskommunikasie. Die pyplyn het 'n inset (skryf einde) en 'n uitset (lees einde). Data wat na die invoer van die pyplyn geskryf is, kan by die uitset gelees word.
Die pyplyn word geskep deur te bel
pipe(2)
, wat twee lêerbeskrywers terugstuur: een verwys na die invoer van die pyplyn, die tweede na die uitvoer.
Die spooruitset van die bogenoemde opdrag toon die skepping van 'n pyplyn en die vloei van data daardeur van een proses na 'n ander:
$ 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
Die ouerproses roep pipe()
om aangehegte lêerbeskrywings te kry. Een kinderproses skryf na een beskrywer en 'n ander proses lees dieselfde data van 'n ander beskrywer. Die dop "hernoem" beskrywings 2 en 3 met dup4 om by stdin en stdout te pas.
Sonder pyplyne sal die dop die uitvoer van een proses na 'n lêer moet skryf en dit na 'n ander proses moet pyp om die data van die lêer te lees. As gevolg hiervan sal ons meer hulpbronne en skyfspasie mors. Pyplyne is egter goed vir meer as om net tydelike lêers te vermy:
As 'n proses uit 'n leë pyplyn probeer lees, dan
read(2)
sal blokkeer totdat die data beskikbaar is. As 'n proses probeer om na 'n volledige pyplyn te skryf, danwrite(2)
sal blokkeer totdat genoeg data vanaf die pyplyn gelees is om die skryfwerk te voltooi.
Soos die POSIX-vereiste, is dit 'n belangrike eienskap: skryf na die pyplyn tot PIPE_BUF
grepe (minstens 512) moet atoom wees sodat prosesse deur die pyplyn met mekaar kan kommunikeer op 'n manier wat normale lêers (wat nie sulke waarborge verskaf nie) nie kan nie.
Met 'n gewone lêer kan 'n proses al sy uitvoere daaraan skryf en dit aan 'n ander proses deurgee. Of prosesse kan in 'n harde parallelle modus werk, deur 'n eksterne seinmeganisme (soos 'n semafoor) te gebruik om mekaar in te lig oor die voltooiing van 'n skryf of lees. Transportbande red ons van al hierdie gesukkel.
Waarna soek ons?
Ek sal op my vingers verduidelik om dit vir jou makliker te maak om te dink hoe 'n vervoerband kan werk. Jy sal 'n buffer en 'n sekere toestand in die geheue moet toewys. Jy sal funksies nodig hê om data by die buffer by te voeg en te verwyder. Jy sal 'n mate van fasiliteit nodig hê om funksies te bel tydens lees- en skryfbewerkings op lêerbeskrywers. En slotte is nodig om die spesiale gedrag hierbo beskryf te implementeer.
Ons is nou gereed om die bronkode van die kern onder helder lamplig te ondersoek om ons vae verstandelike model te bevestig of te weerlê. Maar wees altyd voorbereid op die onverwagte.
Waar soek ons?
Ek weet nie waar my kopie van die bekende boek lê nie.
Om deur die TUHS-argiewe te dwaal is soos om 'n museum te besoek. Ons kan na ons gedeelde geskiedenis kyk en ek het respek vir die jare se poging om al hierdie materiaal bietjie vir bietjie van ou kassette en drukstukke te herwin. En ek is terdeë bewus van daardie fragmente wat nog vermis word.
Nadat ons ons nuuskierigheid oor die antieke geskiedenis van pypleidings bevredig het, kan ons na moderne kerns kyk vir vergelyking.
By the way, pipe
is stelseloproepnommer 42 in die tabel sysent[]
. Toeval?
Tradisionele Unix-pitte (1970–1974)
Ek het geen spoor gevind nie pipe(2)
ook nie in nie
TUHS beweer dit
Die derde uitgawe van Unix was die laaste weergawe met 'n kern geskryf in assembler, maar ook die eerste weergawe met pyplyne. Gedurende 1973 is daar gewerk om die derde uitgawe te verbeter, die kern is in C herskryf, en so is die vierde uitgawe van Unix gebore.
Een leser het 'n skandering van 'n dokument gevind waarin Doug McIlroy die idee voorgestel het om "programme soos 'n tuinslang te koppel."
In Brian Kernighan se boek
Toe Unix verskyn het, het my passie vir koroutines my die OS-outeur, Ken Thompson, laat vra om data wat na een of ander proses geskryf is, toe te laat om nie net na die toestel te gaan nie, maar ook na die uitgang na 'n ander proses. Ken het gedink dit is moontlik. As 'n minimalis wou hy egter hê dat elke stelselkenmerk 'n beduidende rol moes speel. Is direkte skryf tussen prosesse werklik 'n groot voordeel bo skryf na 'n tussenlêer? En eers toe ek 'n spesifieke voorstel met die pakkende naam "pyplyn" en 'n beskrywing van die sintaksis van die interaksie van prosesse maak, het Ken uiteindelik uitgeroep: "Ek sal dit doen!".
En het. Een noodlottige aand het Ken die kern en dop verander, verskeie standaardprogramme reggestel om te standaardiseer hoe hulle insette aanvaar (wat van 'n pyplyn kan kom), en lêername verander. Die volgende dag is pypleidings baie wyd in toepassings gebruik. Teen die einde van die week het die sekretaresses hulle gebruik om dokumente van woordverwerkers na die drukker te stuur. Ietwat later het Ken die oorspronklike API en sintaksis vir die gebruik van pyplyne vervang met skoner konvensies wat sedertdien gebruik is.
Ongelukkig is die bronkode vir die derde uitgawe Unix-kern verlore. En alhoewel ons die kernbronkode het wat in C geskryf is
Ons het dokumentasie teks vir pipe(2)
van beide vrystellings, sodat jy kan begin deur die dokumentasie te soek pipe(2)
is in assembler geskryf en gee slegs een lêerbeskrywer terug, maar verskaf reeds die verwagte kernfunksies:
Stelseloproep pyp skep 'n I/O-meganisme wat 'n pyplyn genoem word. Die teruggestuurde lêerbeskrywing kan gebruik word vir lees- en skryfbewerkings. Wanneer iets na die pyplyn geskryf word, buffer dit tot 504 grepe data, waarna die skryfproses opgeskort word. Wanneer die pyplyn gelees word, word die gebufferde data geneem.
Teen die volgende jaar is die kern herskryf in C, en pipe(fildes)
'
Stelseloproep pyp skep 'n I/O-meganisme wat 'n pyplyn genoem word. Die teruggestuurde lêerbeskrywings kan in lees- en skryfbewerkings gebruik word. Wanneer iets na die pyplyn geskryf word, word die beskrywer wat in r1 (resp. fildes[1]) teruggestuur word, gebruik, gebuffer tot 4096 grepe data, waarna die skryfproses opgeskort word. Wanneer die pyplyn gelees word, neem die beskrywer terug na r0 (resp. fildes[0]) die data.
Daar word aanvaar dat sodra 'n pyplyn gedefinieer is, twee (of meer) interaksie prosesse (geskep deur daaropvolgende oproepe vurk) sal data vanaf die pyplyn deur oproepe deurgee lees и skryf.
Die dop het 'n sintaksis om 'n lineêre reeks prosesse wat via 'n pyplyn verbind is, te definieer.
Oproepe om te lees vanaf 'n leë pyplyn (wat geen gebufferde data bevat nie) wat net een kant het (alle skryflêerbeskrywings gesluit) gee "einde van lêer" terug. Skryfoproepe in 'n soortgelyke situasie word geïgnoreer.
Vroegste
Unix sesde uitgawe (1975)
Begin om Unix-bronkode te lees
Vir baie jare die boek Lions was die enigste dokument oor die Unix-kern wat buite Bell Labs beskikbaar was. Alhoewel die sesde uitgawe lisensie onderwysers toegelaat het om sy bronkode te gebruik, het die sewende uitgawe lisensie hierdie moontlikheid uitgesluit, sodat die boek in onwettige getikte kopieë versprei is.
Vandag kan jy ’n herdrukkopie van die boek koop, waarvan die omslag studente by die kopieerder uitbeeld. En te danke aan Warren Toomey (wat die TUHS-projek begin het), kan jy aflaai
Meer as 15 jaar gelede het ek 'n kopie van die bronkode wat in verskaf is, ingetik Lionsomdat ek nie van die kwaliteit van my kopie van 'n onbekende aantal ander kopieë gehou het nie. TUHS het nog nie bestaan nie, en ek het nie toegang tot die ou bronne gehad nie. Maar in 1988 het ek 'n ou band met 9 snitte gekry wat 'n rugsteun van 'n PDP11-rekenaar gehad het. Dit was moeilik om te weet of dit werk, maar daar was 'n ongeskonde /usr/src/-boom waarin die meeste van die lêers 1979 gemerk is, wat selfs toe oud gelyk het. Dit was die sewende uitgawe, of 'n PWB-afgeleide, het ek gedink.
Ek het die vonds as basis geneem en die bronne handmatig geredigeer tot die toestand van die sesde uitgawe. 'n Deel van die kode het dieselfde gebly, 'n gedeelte moes effens geredigeer word, wat die moderne teken += verander het na die verouderde =+. Iets is eenvoudig uitgevee, en iets moes heeltemal oorgeskryf word, maar nie te veel nie.
En vandag kan ons aanlyn by TUHS die bronkode van die sesde uitgawe van lees
Terloops, met die eerste oogopslag is die hoofkenmerk van die C-kode voor die tydperk van Kernighan en Ritchie sy bondigheid. Dit is nie gereeld dat ek stukke kode kan invoeg sonder uitgebreide redigering om 'n relatief nou vertoonarea op my webwerf te pas nie.
Vroeë
/*
* 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
Die buffergrootte het nie verander sedert die vierde uitgawe nie. Maar hier sien ons, sonder enige publieke dokumentasie, dat pypleidings eens lêers as terugvalberging gebruik het!
Wat LARG lêers betref, stem hulle ooreen met
Hier is die regte stelseloproep 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;
}
Die kommentaar beskryf duidelik wat hier gebeur. Maar dit is nie so maklik om die kode te verstaan nie, deels as gevolg van hoe "R0
и R1
stelseloproepparameters en terugkeerwaardes word deurgegee.
Kom ons probeer met
pipe()
verskuldig deur R0
и R1
stuur lêerbeskrywernommers terug vir lees en skryf. falloc()
gee 'n wyser terug na 'n lêerstruktuur, maar "stuur ook terug" via u.u_ar0[R0]
en 'n lêerbeskrywing. Dit wil sê, die kode word gestoor in r
lêerbeskrywer vir lees en ken 'n beskrywer toe vir skryf direk vanaf u.u_ar0[R0]
na die tweede oproep falloc()
.
vlag FPIPE
, wat ons stel wanneer ons die pyplyn skep, beheer die gedrag van die funksie
/*
* 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);
}
/* … */
}
Dan die funksie readp()
в pipe.c
lees data uit die pyplyn. Maar dit is beter om die implementering op te spoor vanaf writep()
. Weereens, die kode het meer ingewikkeld geword as gevolg van die aard van die argument wat konvensie deurstaan, maar sommige besonderhede kan weggelaat word.
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;
}
Ons wil grepe na die pyplyninvoer skryf u.u_count
. Eerstens moet ons die inode sluit (sien hieronder plock
/prele
).
Dan gaan ons die inode-verwysingtelling na. Solank beide punte van die pyplyn oop bly, moet die toonbank 2 wees. Ons hou vas aan een skakel (vanaf rp->f_inode
), dus as die teller minder as 2 is, moet dit beteken dat die leesproses sy einde van die pyplyn gesluit het. Met ander woorde, ons probeer om na 'n geslote pyplyn te skryf, wat 'n fout is. Eerste foutkode EPIPE
en sein SIGPIPE
verskyn in die sesde uitgawe van Unix.
Maar selfs al is die vervoerband oop, kan dit vol wees. In hierdie geval maak ons die slot los en gaan slaap in die hoop dat 'n ander proses uit die pyplyn sal lees en genoeg spasie daarin sal vrystel. Wanneer ons wakker word, keer ons terug na die begin, hang die slot weer op en begin 'n nuwe skryfsiklus.
As daar genoeg vrye spasie in die pyplyn is, dan skryf ons data aan dit met behulp van i_size1
die inode'a (met 'n leë pyplyn kan gelyk wees aan 0) wys na die einde van die data wat dit reeds bevat. As daar genoeg spasie is om te skryf, kan ons die pyplyn van vul i_size1
aan PIPESIZ
. Dan los ons die slot en probeer om enige proses wat wag om uit die pyplyn te lees wakker te maak. Ons gaan terug na die begin om te sien of ons daarin geslaag het om soveel grepe te skryf as wat ons nodig gehad het. Indien nie, begin ons 'n nuwe opnamesiklus.
Gewoonlik parameter i_mode
inode word gebruik om toestemmings te stoor r
, w
и x
. Maar in die geval van pyplyne, beduie ons dat een of ander proses wag vir 'n skryf of lees met behulp van bisse IREAD
и IWRITE
onderskeidelik. Die proses stel die vlag en roep sleep()
, en daar word verwag dat 'n ander proses in die toekoms sal oproep wakeup()
.
Die ware magie gebeur in sleep()
и wakeup()
. Hulle word geïmplementeer 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) /* … */
Die proses wat roep sleep()
vir 'n spesifieke kanaal, kan later wakker gemaak word deur 'n ander proses, wat sal oproep wakeup()
vir dieselfde kanaal. writep()
и readp()
koördineer hul optrede deur sulke gepaarde oproepe. Let daarop dat pipe.c
altyd prioritiseer PPIPE
wanneer geroep word sleep()
, so almal sleep()
kan deur 'n sein onderbreek word.
Nou het ons alles om die funksie te verstaan 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);
}
Jy sal dit dalk makliker vind om hierdie funksie van onder na bo te lees. Die "lees en terug"-tak word gewoonlik gebruik wanneer daar data in die pyplyn is. In hierdie geval gebruik ons f_offset
lees, en werk dan die waarde van die ooreenstemmende offset op.
By daaropvolgende lesings sal die pyplyn leeg wees as die leesverskuiwing bereik het i_size1
by die inode. Ons stel die posisie terug na 0 en probeer enige proses wakker maak wat na die pyplyn wil skryf. Ons weet dat wanneer die vervoerband vol is, writep()
aan die slaap raak ip+1
. En noudat die pyplyn leeg is, kan ons dit wakker maak om sy skryfsiklus te hervat.
As daar niks is om te lees nie, dan readp()
'n vlag kan stel IREAD
en aan die slaap raak ip+2
. Ons weet wat hom sal wakker maak writep()
wanneer dit sommige data na die pyplyn skryf.
Kommentaar op u
» ons kan hulle behandel soos gewone I/O-funksies wat 'n lêer, 'n posisie, 'n buffer in geheue neem, en die aantal grepe tel om te lees of te skryf.
/*
* 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;
/* … */
Wat "konserwatiewe" blokkering betref, dan readp()
и writep()
sluit inodes totdat hulle klaar is of 'n resultaat kry (d.w.s. bel wakeup
). plock()
и prele()
werk eenvoudig: gebruik 'n ander stel oproepe sleep
и wakeup
laat ons toe om enige proses wakker te maak wat die slot nodig het wat ons pas vrygestel het:
/*
* 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);
}
}
Ek kon eers nie verstaan hoekom nie readp()
veroorsaak nie prele(ip)
voor die oproep wakeup(ip+1)
. Die eerste ding writep()
roep in sy lus, hierdie plock(ip)
, wat lei tot 'n dooiepunt as readp()
het nog nie sy blok verwyder nie, so die kode moet op een of ander manier korrek werk. As jy kyk na wakeup()
, word dit duidelik dat dit net die slaapproses as gereed vir uitvoering merk, sodat dit in die toekoms sched()
het dit regtig van stapel gestuur. Dus readp()
oorsake wakeup()
, ontsluit, stel IREAD
en oproepe sleep(ip+2)
- dit alles voorheen writep()
herbegin die siklus.
Dit voltooi die beskrywing van pypleidings in die sesde uitgawe. Eenvoudige kode, verreikende implikasies.
Xv6, 'n eenvoudige Unix-agtige kern
Om 'n kern te skep
Die kode bevat 'n duidelike en deurdagte implementering 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()
stel die toestand van al die res van die implementering, wat funksies insluit piperead()
, pipewrite()
и pipeclose()
. Die werklike stelsel oproep sys_pipe
is 'n omhulsel geïmplementeer in
Linux 0.01
U kan die bronkode vir Linux 0.01 vind. Dit sal leersaam wees om die implementering van pypleidings in sy te bestudeer fs
/pipe.c
. Hier word 'n inode gebruik om die pyplyn voor te stel, maar die pyplyn self is in moderne C geskryf. As jy jou pad deur die sesde uitgawe-kode gekap het, sal jy nie enige probleme hier hê nie. Dit is hoe die funksie lyk 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;
}
Selfs sonder om na die struktuurdefinisies te kyk, kan jy uitvind hoe die inode-verwysingtelling gebruik word om te kyk of 'n skryfbewerking tot gevolg het SIGPIPE
. Benewens greep-vir-greep-werk, is hierdie funksie maklik om met bogenoemde idees te vergelyk. Selfs logika sleep_on
/wake_up
lyk nie so uitheems nie.
Moderne Linux-pitte, FreeBSD, NetBSD, OpenBSD
Ek het vinnig oor 'n paar moderne pitte gegaan. Nie een van hulle het reeds 'n skyfgebaseerde implementering nie (nie verbasend nie). Linux het sy eie implementering. En hoewel die drie moderne BSD-pitte implementerings bevat gebaseer op kode wat deur John Dyson geskryf is, het hulle oor die jare te verskillend van mekaar geword.
Om te lees fs
/pipe.c
(op Linux) of sys
/kern
/sys_pipe.c
(op *BSD), dit verg werklike toewyding. Werkverrigting en ondersteuning vir kenmerke soos vektor en asinchroniese I/O is vandag belangrik in kode. En die besonderhede van geheuetoewysing, slotte en kernkonfigurasie verskil almal baie. Dit is nie wat universiteite nodig het vir 'n inleidende kursus oor bedryfstelsels nie.
Dit was in elk geval vir my interessant om 'n paar ou patrone op te grawe (byvoorbeeld generering SIGPIPE
en keer terug EPIPE
wanneer jy na 'n geslote pyplyn skryf) in al hierdie, so verskillende, moderne pitte. Ek sal seker nooit 'n PDP-11-rekenaar regstreeks sien nie, maar daar is nog baie om te leer uit die kode wat 'n paar jaar voor my geboorte geskryf is.
Geskryf deur Divi Kapoor in 2011, die artikel "
Bron: will.com