Dësen Artikel beschreift d'Ëmsetzung vu Pipelines am Unix Kernel. Ech war e bëssen enttäuscht datt e rezenten Artikel mam Titel "
Vu wat schwätz du?
Pipelines sinn "wahrscheinlech déi wichtegst Erfindung an Unix" - eng definéierend Feature vun der Unix senger Basisphilosophie fir kleng Programmer zesummenzestellen, an de vertraute Kommandozeil Slogan:
$ echo hello | wc -c
6
Dës Funktionalitéit hänkt vum Kernel geliwwert System Uruff of pipe
, déi op den Dokumentatiounssäiten beschriwwe gëtt
Pipelines bidden en een-Wee Kanal fir Inter-Prozess Kommunikatioun. D'Pipeline huet en Input (Schreifend) an en Ausgang (Liesend). Daten, déi un den Input vun der Pipeline geschriwwe sinn, kënnen um Ausgang gelies ginn.
D'Pipeline gëtt duerch Uruff erstallt
pipe(2)
, déi zwee Dateideskriptoren zréckginn: een bezitt sech op den Input vun der Pipeline, déi zweet op d'Ausgab.
De Spuerausgang vum uewe genannte Kommando weist d'Schafung vun enger Pipeline an de Flux vun Daten duerch et vun engem Prozess an en aneren:
$ 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
D'Elteren Prozess rifft pipe()
fir befestegt Dateideskriptoren ze kréien. Ee Kandprozess schreift un een Deskriptor an en anere Prozess liest déiselwecht Daten vun engem aneren Deskriptor. D'Schuel "benannt" Descriptoren 2 a 3 mat dup4 fir stdin an stdout ze passen.
Ouni Pipelines muss d'Schuel den Ausgang vun engem Prozess an eng Datei schreiwen an en an en anere Prozess schreiwen fir d'Donnéeën aus der Datei ze liesen. Als Resultat wäerte mir méi Ressourcen an Disk Space verschwenden. Wéi och ëmmer, Pipelines si gutt fir méi wéi just temporär Dateien ze vermeiden:
Wann e Prozess probéiert aus enger eidel Pipeline ze liesen, dann
read(2)
blockéiert bis d'Donnéeën verfügbar sinn. Wann e Prozess probéiert op eng voll Pipeline ze schreiwen, dannwrite(2)
wäert blockéieren bis genuch Daten aus der Pipeline gelies gi fir d'Schreiwen ze kompletéieren.
Wéi d'POSIX Fuerderung, ass dëst eng wichteg Eegeschafte: Schreiwen un d'Pipeline bis PIPE_BUF
Bytes (op d'mannst 512) mussen atomarer sinn, sou datt Prozesser matenee kënnen duerch d'Pipeline kommunizéieren op eng Manéier déi normal Dateien (déi keng esou Garantien ubidden) net kënnen.
Mat enger regulärer Datei kann e Prozess all seng Ausgab drop schreiwen an en an en anere Prozess weiderginn. Oder Prozesser kënnen an engem haarde parallele Modus operéieren, mat engem externen Signalmechanismus (wéi e Semaphore) fir géigesäiteg iwwer d'Réalisatioun vun engem Schreiwen oder Liesen z'informéieren. Conveyors retten eis vun all deem Stress.
Wat sichen mir?
Ech wäert op meng Fangeren erklären fir et méi einfach ze maachen fir Iech virzestellen wéi e Fërderband funktionnéiert. Dir musst e Puffer an e puer Staat an der Erënnerung verdeelen. Dir braucht Funktiounen fir Daten aus dem Puffer ze addéieren an ze läschen. Dir braucht e puer Ariichtungen fir Funktiounen ze ruffen wärend Lies- a Schreifoperatiounen op Dateideskriptoren. A Schleisen sinn néideg fir de spezielle Verhalen ëmzesetzen hei uewen beschriwwen.
Mir sinn elo prett fir de Quellcode vum Kernel ënner helle Luuchten ze interrogéieren fir eise vague mentale Modell ze bestätegen oder ze widderhuelen. Awer ëmmer virbereet op dat Onerwaart.
Wou sichen mir?
Ech weess net wou meng Kopie vum berühmte Buch läit.
Wandern duerch d'TUHS Archiven ass wéi e Musée ze besichen. Mir kënnen eis gemeinsam Geschicht kucken an ech hunn Respekt fir d'Jore vun Effort fir all dëst Material bëssen no bëssen aus alen Kassetten an Drécken ze recuperéieren. An ech sinn déi Fragmenter ganz bewosst, déi nach feelen.
Nodeems mir eis Virwëtz iwwer déi antik Geschicht vu Pipelines zefridden hunn, kënne mir modern Käre kucken fir ze vergläichen.
Iwwrégens, pipe
ass System Opruff Nummer 42 an der Tabell sysent[]
. Zoufall?
Traditionell Unix Kernels (1970–1974)
Ech hu keng Spuer fonnt pipe(2)
weder an
TUHS behaapt dat
Déi drëtt Editioun vun Unix war déi lescht Versioun mat engem Kärel geschriwwen am Assembler, awer och déi éischt Versioun mat Pipelines. Am Laf vun 1973 gouf geschafft fir déi drëtt Editioun ze verbesseren, de Kär gouf am C ëmgeschriwwen, an domat ass déi véiert Editioun vun Unix gebuer.
Ee Lieser huet e Scan vun engem Dokument fonnt, an deem den Doug McIlroy d'Iddi proposéiert huet "Programmer wéi e Gaardeschlauch ze verbannen."
Am Brian Kernighan sengem Buch
Wéi d'Unix erschéngt, huet meng Leidenschaft fir Coroutinen mech den OS Autor Ken Thompson gefrot fir Daten, déi an e puer Prozess geschriwwe goufen, net nëmmen op den Apparat ze goen, awer och op d'Ausfahrt an en anere Prozess. Ken decidéiert et méiglech. Wéi och ëmmer, als Minimalist wollt hien datt all System Feature eng bedeitend Roll spillt. Ass direkt Schreiwen tëscht Prozesser wierklech e grousse Virdeel iwwer Schreiwen op eng Zwëschendatei? An nëmmen wann ech eng spezifesch Propositioun mam opfällegen Numm "Pipeline" an enger Beschreiwung vun der Syntax vun der Interaktioun vu Prozesser gemaach hunn, huet de Ken endlech geruff: "Ech wäert et maachen!".
An huet. Ee schicksal Owend huet de Ken de Kärel an d'Schuel geännert, verschidde Standardprogrammer fixéiert fir ze standardiséieren wéi se Input akzeptéieren (wat aus enger Pipeline kënnt kommen), a geännert Dateinumm. Den nächsten Dag goufen Pipelines ganz wäit an Uwendungen benotzt. Bis Enn vun der Woch hunn d'Sekretären se benotzt fir Dokumenter vum Textveraarbechter un den Drécker ze schécken. E bësse méi spéit huet de Ken den ursprénglechen API an d'Syntax ersat fir d'Verwäertung vu Pipelines mat méi proppere Konventiounen ze wéckelen, déi zënterhier benotzt goufen.
Leider ass den Unix drëtt Editioun Kernel Quellcode verluer gaangen. An och wann mir de Kernel Quellcode an C geschriwwen hunn
Mir hunn Dokumentatioun Text fir pipe(2)
vu béide Verëffentlechungen, also kënnt Dir ufänken andeems Dir d'Dokumentatioun sicht pipe(2)
ass am Assembler geschriwwen a gëtt nëmmen ee Dateideskriptor zréck, awer liwwert schonn déi erwaart Kärfunktionalitéit:
System Opruff Päif ugeet erstellt en I / O Mechanismus genannt Pipeline. De zréckginn Dateideskriptor ka fir Lies- a Schreifoperatioune benotzt ginn. Wann eppes op d'Pipeline geschriwwe gëtt, puffert se bis zu 504 Bytes vun Daten, duerno gëtt de Schreifprozess suspendéiert. Wann Dir aus der Pipeline liest, ginn déi gebuffert Daten geholl.
Vum Joer duerno war de Kärel an C ëmgeschriwwe ginn, an pipe(fildes)
"
System Opruff Päif ugeet erstellt en I/O Mechanismus genannt Pipeline. Déi zréckginn Dateibeschreiwunge kënnen a Lies- a Schreifoperatioune benotzt ginn. Wann eppes op d'Pipeline geschriwwe gëtt, gëtt den Deskriptor, deen am r1 (bzw. Fildes[1]) zréckkomm ass, benotzt, bis zu 4096 Bytes vun Daten gebuffert, duerno gëtt de Schreifprozess suspendéiert. Wann Dir aus der Pipeline liest, hëlt den Deskriptor zréck op r0 (bzw. Fildes [0]) d'Donnéeën.
Et gëtt ugeholl datt eemol eng Pipeline definéiert ass, zwee (oder méi) interagéierend Prozesser (erstallt duerch spéider Invokatiounen Forschett) wäert Daten aus der Pipeline mat Uruff passéieren gelies и schreiwen.
D'Schuel huet eng Syntax fir eng linear Array vu Prozesser ze definéieren, déi iwwer eng Pipeline verbonne sinn.
Rufft fir aus enger eidel Pipeline ze liesen (keng gebufferten Donnéeën) déi nëmmen een Enn huet (all Schreifdateibeschreiwungen zougemaach) ginn "Enn vum Fichier" zréck. Schreiwen Appellen an enger ähnlecher Situatioun sinn ignoréiert.
Fréierst
Unix Sixth Edition (1975)
Start Unix Quellcode ze liesen
Fir vill Joer d'Buch Lions war dat eenzegt Dokument op der Unix Kernel verfügbar ausserhalb vu Bell Labs. Och wann déi sechst Editioun Lizenz erlaabt Enseignanten hire Quellcode ze benotzen, déi siwent Editioun Lizenz huet dës Méiglechkeet ausgeschloss, sou datt d'Buch an illegal typeschgeschriwwene Kopie verdeelt gouf.
Haut kënnt Dir eng Kopie vum Buch kafen, op deem Cover d'Studenten am Copier duerstellt. An dank dem Warren Toomey (deen den TUHS Projet ugefaang huet), kënnt Dir eroflueden
Viru méi wéi 15 Joer hunn ech eng Kopie vum Quellcode aginn Lionswell d'Qualitéit vu menger Kopie vun enger onbekannter Zuel vun aneren Exemplaren net gutt gefall huet. TUHS existéiert nach net, an ech hu keen Zougang zu den ale Quellen. Mee 1988 hunn ech eng al Band fonnt mat 9 Bunnen, déi e Backup vun engem PDP11 Computer haten. Et war schwéier ze wëssen ob et funktionnéiert, awer et war en intakten /usr/src/ Bam an deem déi meescht Dateien 1979 markéiert waren, deen och deemools antik ausgesinn huet. Et war déi siwent Editioun, oder eng PWB-Derivat, hunn ech geduecht.
Ech hunn d'Find als Basis geholl an d'Quellen manuell op den Zoustand vun der sechster Editioun geännert. En Deel vum Code ass d'selwecht bliwwen, en Deel huet misse liicht geännert ginn, de modernen += Token an den eelere =+ änneren. Eppes gouf einfach geläscht, an eppes huet misse komplett nei geschriwwe ginn, awer net ze vill.
An haut kënne mir online op TUHS de Quellcode vun der sechster Editioun vun liesen
Iwwregens, op den éischte Bléck, ass d'Haaptfunktioun vum C-Code virun der Period vu Kernighan a Ritchie seng kuerzheet. Et ass net dacks datt ech fäeg sinn Code-Snippets ouni extensiv Redaktioun anzeginn fir e relativ schmuele Displaygebitt op menger Säit ze passen.
Am Ufank
/*
* 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
D'Puffergréisst ass zënter der véierter Editioun net geännert. Awer hei gesi mir, ouni ëffentlech Dokumentatioun, datt Pipelines eemol Dateien als Réckfalllagerung benotzt hunn!
Wat LARG Dateien ugeet, entspriechen se
Hei ass de richtege Systemruff 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;
}
De Kommentar beschreift kloer wat hei geschitt. Awer et ass net sou einfach de Code ze verstoen, deelweis wéinst wéi "R0
и R1
System Call Parameteren a Retour Wäerter ginn passéiert.
Loosst d'probéieren mat
pipe()
wéinst duerch R0
и R1
Retour Datei Descriptor Zuelen fir Liesen a Schreiwen. falloc()
gëtt e Pointer op eng Dateistruktur zréck, awer och "zréck" via u.u_ar0[R0]
an e Fichier Descriptor. Dat ass, de Code ass gespäichert an r
Fichier Descriptor fir Liesen an zougewisen engem Descriptor fir Schreiwen direkt aus u.u_ar0[R0]
nom zweeten Opruff falloc()
.
Fändel FPIPE
, déi mir setzen beim Schafe vun der Pipeline, kontrolléiert d'Behuele vun der Funktioun
/*
* 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);
}
/* … */
}
Dann d'Funktioun readp()
в pipe.c
liest Daten aus der Pipeline. Awer et ass besser d'Ëmsetzung ze verfollegen ab writep()
. Nach eng Kéier ass de Code méi komplizéiert ginn wéinst der Natur vum Argument, deen d'Konventioun passéiert, awer e puer Detailer kënnen ausgelooss ginn.
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;
}
Mir wëllen Bytes un de Pipeline-Input schreiwen u.u_count
. Als éischt musse mir d'Inode spären (kuckt hei ënnen plock
/prele
).
Da kontrolléiere mir d'Inode Referenzzuel. Soulaang béid Enden vun der Pipeline oppe bleiwen, soll de Comptoir 2. Mir halen un engem Link (vun rp->f_inode
), also wann de Konter manner wéi 2 ass, da sollt dat heeschen datt de Liesprozess säin Enn vun der Pipeline zougemaach huet. An anere Wierder, mir probéieren op eng zougemaach Pipeline ze schreiwen, wat e Feeler ass. Éischt Feeler Code EPIPE
an Signal SIGPIPE
erschéngt an der sechster Editioun vun Unix.
Awer och wann de Fërderband op ass, kann et voll sinn. An dësem Fall befreien mir d'Schloss a schlofen an der Hoffnung datt en anere Prozess aus der Pipeline liest a genuch Plaz dran befreit. Wa mir erwächen, komme mir zréck an den Ufank, hänken d'Spär erëm op a starten en neie Schreifzyklus.
Wann et genuch fräi Plaz an der Pipeline ass, da schreiwen mir Daten op et mat i_size1
d'Inode'a (mat enger eidel Pipeline kann gläich 0 sinn) weist op d'Enn vun den Donnéeën, déi et scho enthält. Wann et genuch Plaz ass fir ze schreiwen, kënne mir d'Pipeline ausfëllen i_size1
ze PIPESIZ
. Da loosse mir d'Schloss eraus a probéieren all Prozess z'erwächen, dee waart fir aus der Pipeline ze liesen. Mir ginn zréck an den Ufank fir ze kucken ob mir et fäerdeg bruecht hunn esou vill Bytes ze schreiwen wéi mir gebraucht hunn. Wann net, da starten mir en neien Opnamzyklus.
Normalerweis Parameter i_mode
inode gëtt benotzt fir Permissiounen ze späicheren r
, w
и x
. Mä am Fall vun Pipelines, mir Signal, datt e puer Prozess fir eng schreiwen gewaart oder liesen Bits benotzt IREAD
и IWRITE
respektiv. De Prozess setzt de Fändel a rifft sleep()
, an et gëtt erwaart datt an Zukunft e puer anere Prozess ruffen wakeup()
.
Déi richteg Magie geschitt an sleep()
и wakeup()
. Si sinn ëmgesat an
/*
* 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) /* … */
De Prozess deen rifft sleep()
fir e bestëmmte Kanal, kann spéider vun engem anere Prozess erwächt ginn, dee wäert ruffen wakeup()
fir déi selwecht Kanal. writep()
и readp()
koordinéieren hir Handlungen duerch sou gepaart Uruff. notéiert dat pipe.c
ëmmer Prioritéit PPIPE
wann genannt sleep()
, also all sleep()
kann duerch e Signal ënnerbrach ginn.
Elo hu mir alles fir d'Funktioun ze verstoen 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);
}
Dir kënnt et méi einfach fannen dës Funktioun vun ënnen no uewen ze liesen. D'Branche "liesen an zréck" gëtt normalerweis benotzt wann et e puer Daten an der Pipeline ass. An dësem Fall benotze mir f_offset
liesen, an dann update de Wäert vun der entspriechend Offset.
Bei spéider Liesungen ass d'Pipeline eidel wann de Liesoffset erreecht ass i_size1
an der Inod. Mir setzen d'Positioun op 0 zréck a probéieren all Prozess z'erwächen, deen op d'Pipeline schreiwen wëllt. Mir wëssen datt wann de Fërderband voll ass, writep()
schlofen op ip+1
. An elo, datt d'Pipeline eidel ass, kënne mir et erwächen fir säi Schreifzyklus erëmzefannen.
Wann et näischt ze liesen ass, dann readp()
kann e Fändel setzen IREAD
a schlofen op ip+2
. Mir wësse wat hien erwächt writep()
wann et e puer Donnéeën un d'Pipeline schreift.
Kommentaren op u
» Mir kënnen se wéi normal I/O Funktiounen behandelen, déi e Fichier, eng Positioun, e Puffer an der Erënnerung huelen an d'Zuel vun de Bytes zielen fir ze liesen oder ze schreiwen.
/*
* 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 "konservativ" Blockéierung ugeet, dann readp()
и writep()
blockéieren Inoden bis se fäerdeg sinn oder e Resultat kréien (dat ass, se ruffen wakeup
). plock()
и prele()
Aarbecht einfach: eng aner Formatioun vun Uriff benotzt sleep
и wakeup
erlaabt eis all Prozess z'erwächen, deen de Spär brauch, dee mir just verëffentlecht hunn:
/*
* 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);
}
}
Am Ufank konnt ech net verstoen firwat readp()
net verursaacht prele(ip)
virum Opruff wakeup(ip+1)
. Déi éischt Saach writep()
rifft a senger Loop, dëst plock(ip)
, wat zu engem Deadlock resultéiert wann readp()
huet säi Block nach net geläscht, also muss de Code iergendwéi richteg funktionnéieren. Wann Dir kuckt wakeup()
, gëtt kloer datt et nëmmen de Schlofprozess als fäerdeg fir d'Ausféierung markéiert, sou datt an Zukunft sched()
wierklech lancéiert et. Also readp()
verursaacht wakeup()
, Spär, setzt IREAD
an rifft sleep(ip+2)
- all dëst virun writep()
Restart den Zyklus.
Dëst fäerdeg d'Beschreiwung vu Pipelines an der sechster Editioun. Einfach Code, wäitreegend Konsequenzen.
Xv6, en einfachen Unix-ähnlechen Kernel
Fir e Kär ze kreéieren
De Code enthält eng kloer an Duerchduechte Ëmsetzung 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()
setzt den Zoustand vun all de Rescht vun der Ëmsetzung, déi Funktiounen enthält piperead()
, pipewrite()
и pipeclose()
. Déi aktuell System Opruff sys_pipe
ass e Wrapper implementéiert an
Linux 0.01
Dir fannt de Quellcode fir Linux 0.01. Et wäert léierräich sinn der Ëmsetzung vun Pipelines ze studéieren a sengem fs
/pipe.c
. Hei gëtt eng Inode benotzt fir d'Pipeline ze representéieren, awer d'Pipeline selwer ass am modernen C geschriwwen. Wann Dir de Wee duerch de Code vun der sechster Editioun gehackt hutt, hutt Dir hei keng Problemer. Dëst ass wéi d'Funktioun ausgesäit 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;
}
Och ouni d'Strukt Definitiounen ze kucken, kënnt Dir erausfannen wéi d'Inode Referenzzuel benotzt gëtt fir ze kontrolléieren ob eng Schreifoperatioun resultéiert SIGPIPE
. Zousätzlech zu Byte-by-Byte-Aarbecht ass dës Funktioun einfach mat den uewe genannten Iddien ze vergläichen. Souguer Logik sleep_on
/wake_up
gesäit net sou alien aus.
Modern Linux Kernels, FreeBSD, NetBSD, OpenBSD
Ech sinn séier iwwer e puer modern Kärelen gaang. Keen vun hinnen huet schonn eng Disk-baséiert Implementatioun (net iwwerraschend). Linux huet seng eege Implementatioun. An och wann déi dräi modern BSD Kernelen Implementatiounen enthalen baséiert op Code dee vum John Dyson geschriwwe gouf, si si iwwer d'Joren ze ënnerschiddlech vuneneen ginn.
Liesen fs
/pipe.c
(op Linux) oder sys
/kern
/sys_pipe.c
(op *BSD), et hëlt wierklech Engagement. Leeschtung an Ënnerstëtzung fir Funktiounen wéi Vecteure an asynchronous I / O si wichteg am Code haut. An d'Detailer vun der Erënnerungsallokatioun, Spären a Kernelkonfiguratioun variéieren all immens. Dëst ass net wat d'Universitéiten brauchen fir en Aféierungscours iwwer Betribssystemer.
Op alle Fall war et interessant fir mech e puer al Musteren z'entdecken (zum Beispill generéieren SIGPIPE
an zréck EPIPE
wann Dir op eng zougemaach Pipeline schreift) an all dësen, sou ënnerschiddlech, modernen Kären. Ech wäert wahrscheinlech ni e PDP-11 Computer live gesinn, awer et gëtt nach vill ze léieren aus dem Code deen e puer Joer geschriwwe gouf ier ech gebuer sinn.
Geschriwwen vum Divi Kapoor am Joer 2011, den Artikel "
Source: will.com