Artikulu honek kanalizazioen inplementazioa deskribatzen du Unix nukleoan. Zertxobait etsita nengoen azken artikulu batek "
Zertaz ari gara?
Pipelineak, "seguruenik Unix-en asmakizun garrantzitsuena", programa txikiak elkarrekin lotzeko Unix-en filosofiaren ezaugarri definitzaile bat dira, baita komando-lerroko seinale ezagun bat ere:
$ echo hello | wc -c
6
Funtzio hau nukleoak emandako sistema-deiaren araberakoa da pipe
, dokumentazio orrietan azaltzen dena
Pipelinek norabide bakarreko kanala eskaintzen dute prozesuen arteko komunikaziorako. Kanalizazioak sarrera (idazketa amaiera) eta irteera (irakurketa amaiera) ditu. Hodiaren sarreran idatzitako datuak irteeran irakur daitezke.
Deia erabiliz kanalizazioa sortzen da
pipe(2)
, bi fitxategi deskribatzaile itzultzen dituena: bata kanalizazioaren sarrerari erreferentzia egiten diona, bigarrena irteerari.
Goiko komandoaren arrastoaren irteerak kanalizazioaren sorrera eta prozesu batetik besterako datuen fluxua erakusten du:
$ 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
Guraso-prozesuak deiak egiten ditu pipe()
muntatutako fitxategien deskribatzaileak lortzeko. Haur-prozesu batek helduleku batean idazten du, eta beste prozesu batek beste helduleku batetik datu berak irakurtzen ditu. Shell-ak dup2 erabiltzen du 3 eta 4 deskribatzaileak "berriendatzeko" stdin eta stdout bat etortzeko.
Tuturik gabe, shell-ak prozesu baten irteera fitxategi batean idatzi beharko luke eta beste prozesu batera pasatu beharko luke fitxategiko datuak irakurtzeko. Ondorioz, baliabide eta disko espazio gehiago xahutuko genituzke. Hala ere, kanalizazioak onak dira aldi baterako fitxategiak erabiltzea saihesteko aukera ematen dutelako ez ezik:
Prozesu bat kanalizazio huts batetik irakurtzen saiatzen ari bada
read(2)
blokeatu egingo da datuak eskuragarri egon arte. Prozesu bat kanalizazio oso batean idazten saiatzen bada, orduanwrite(2)
blokeatuko du kanaletik idazketa egiteko behar adina datu irakurri arte.
POSIX eskakizuna bezala, propietate garrantzitsu bat da: kanalizaziora idaztea PIPE_BUF
byteek (gutxienez 512) atomikoak izan behar dute, prozesuek kanalizazioaren bidez elkarren artean komunikatu ahal izateko, fitxategi arruntek (berme horiek ematen ez dituztenek) ezin duten moduan.
Fitxategi arrunt bat erabiltzean, prozesu batek bere irteera guztia idatzi eta beste prozesu batera pasa dezake. Edo prozesuek modu oso paraleloan funtziona dezakete, kanpoko seinaleztapen-mekanismo bat erabiliz (semaforo bat bezala) elkarri jakinarazteko idazketa edo irakurketa amaitutakoan. Garraiogailuek traba guzti honetatik salbatzen gaituzte.
Zer bilatzen ari gara?
Termino sinpleetan azalduko dut, errazagoa izan dadin irudikatzea garraiatzaile batek nola funtziona dezakeen. Buffer bat eta egoera batzuk esleitu beharko dituzu memorian. Bufferretik datuak gehitzeko eta kentzeko funtzioak beharko dituzu. Fitxategien deskribatzaileetan irakurtzeko eta idazteko eragiketetan funtzioei deitzeko baliabide batzuk beharko dituzu. Eta sarrailak beharko dituzu goian deskribatutako portaera berezia ezartzeko.
Orain prest gaude nukleoaren iturburu-kodea argi distiratsuaren azpian galdetzeko, gure eredu mental lausoa baieztatzeko edo ezeztatzeko. Baina beti prest egon ezustekoetarako.
Nora bilatzen ari gara?
Ez dakit non dagoen liburu ospetsuaren kopiaβ
TUHS artxiboetan barrena ibiltzea museo bat bisitatzea bezalakoa da. Partekatutako historiari erreparatu diezaiokegu, eta errespetua daukat material hori guztia zinta eta grabatu zaharretatik pixkanaka-pixkanaka berreskuratzeko urte askotako ahalegina. Eta oraindik ere falta diren zati horietaz ohartzen naiz.
Zinta garraiatzaileen antzinako historiari buruz dugun jakin-mina ase ondoren, nukleo modernoak aldera ditzakegu.
Bide batez, pipe
taulako sistemaren 42 zenbakia da sysent[]
. Kasualitatea?
Unix kernel tradizionalak (1970β1974)
Ez nuen arrastorik aurkitu pipe(2)
ezta barruan ere
TUHSek dio
Unix 1973. Edizioa muntaia-lengoaian idatzitako nukleoa zuen azken bertsioa izan zen, baina baita kanalizazioekin lehen bertsioa ere. XNUMXan zehar, hirugarren edizioa hobetzeko lanak egin ziren, nukleoa C-n berridatzi zen, eta horrela Unix-en laugarren edizioa agertu zen.
Irakurle batek dokumentu baten eskaneatu bat aurkitu zuen, non Doug McIlroyk "lorategiko mahuka bat bezalako programak konektatzeko" ideia proposatu zuen.
Brian Kernighanen liburuan
Unix atera zenean, koroutinekiko lilurak eraman ninduen Ken Thompson sistema eragilearen egileari galdetzera prozesu batean idatzitako datuak gailura ez ezik, beste prozesu batera ateratzeko ere uzteko. Kenek erabaki zuen posible zela. Hala ere, minimalista den heinean, sistemaren funtzio guztiek paper garrantzitsua izatea nahi zuen. Prozesuen artean zuzenean idaztea abantaila handia al da benetan tarteko fitxategi batean idaztearen aldean? "Pipeline" izen erakargarriarekin eta prozesuen arteko elkarrekintzarako sintaxiaren deskribapenarekin proposamen zehatz bat egin nuenean bakarrik esan zuen Kenek azkenean: "Egin dut!"
Eta egin zuen. Arratsalde zoritxarreko batean, Ken-ek nukleoa eta shell-a aldatu zituen, hainbat programa estandar konpondu zituen sarrera nola onartzen zuten estandarizatzeko (hodibide batetik etor zitekeen), eta fitxategien izenak ere aldatu zituen. Hurrengo egunean, hodiak oso zabal erabiltzen hasi ziren aplikazioetan. Aste bukaerarako, idazkariek testu-prozesadoreetatik inprimagailura dokumentuak bidaltzeko erabiltzen zituzten. Pixka bat geroago, Ken-ek jatorrizko APIa eta sintaxia ordezkatu zituen kanalizazioen erabilera konbentzio garbiagoekin biltzeko, orduz geroztik erabili izan direnak.
Zoritxarrez, hirugarren edizioko Unix nukleoaren iturburu-kodea galdu egin da. Eta nukleoaren iturburu kodea C-n idatzita badugu ere
Testu-dokumentazioa dugu pipe(2)
bi argitalpenetatik, dokumentazioa bilatuz has zaitezke pipe(2)
mihiztadura-lengoaian idatzita dago eta fitxategi deskribatzaile bakarra itzultzen du, baina dagoeneko espero den oinarrizko funtzionaltasuna eskaintzen du:
Sistema-deia kanalizazio pipeline izeneko sarrera/irteera mekanismo bat sortzen du. Itzulitako fitxategi deskribatzailea irakurtzeko eta idazteko eragiketak egiteko erabil daiteke. Hodibidean zerbait idazten denean, 504 byte-ko datuak bufferean gordetzen dira, ondoren idazketa-prozesua eten egiten da. Hoditik irakurtzean, buffer-eko datuak kentzen dira.
Hurrengo urtean nukleoa C-n berridatzita zegoen, eta pipe(fildes)
'
Sistema-deia kanalizazio pipeline izeneko sarrera/irteera mekanismo bat sortzen du. Itzulitako fitxategien deskribatzaileak irakurtzeko eta idazteko eragiketetan erabil daitezke. Pipelinean zerbait idazten denean, r1-n itzultzen den heldulekua (esp. fildes[1]) erabiltzen da, 4096 byte-ko datuetan gordeta, eta ondoren idazketa-prozesua eten egiten da. Tutorialetik irakurtzean, r0-ra itzuli den heldulekuak (resp. fildes[0]) hartzen ditu datuak.
Suposatzen da kanalizazio bat definitu ondoren, komunikazio-prozesu bi (edo gehiago) (ondorengo deien bidez sortuak). fork) kanaletik datuak transferituko ditu deiak erabiliz irakurri ΠΈ idatzi.
Shell-ak kanalizazio baten bidez konektatutako prozesuen matrize lineal bat definitzeko sintaxia du.
Mutur bakarra duen kanalizazio huts batetik (daturik ez duena) irakurtzeko deiek (idazketa-fitxategien deskribatzaile guztiak itxita daude) "fitxategiaren amaiera" itzultzen dute. Antzeko egoera batean idazteko deiak ez dira aintzat hartzen.
Lehenago
Unix-en seigarren edizioa (1975)
Has gaitezen Unix iturburu-kodea irakurtzen
Urte askotan liburua Lions Bell Labs-etik kanpo zegoen Unix kernel-eko dokumentu bakarra zen. Seigarren edizioko lizentziak irakasleei bere iturburu-kodea erabiltzea ahalbidetzen bazuen ere, zazpigarren edizioko lizentziak aukera hori baztertzen zuen, beraz, liburua makinaz idatzitako kopia ilegalen moduan banatu zen.
Gaur liburuaren berrargitalpena eros dezakezu, zeinaren azalean ikasleak kopia makina batean ageri dira. Eta Warren Toomey-ri esker (TUHS proiektua hasi zuena) deskarga dezakezu
Duela 15 urte baino gehiago, emandako iturburu-kodearen kopia bat idatzi nuen Lions, ez zitzaidalako gustatu nire kopiaren kalitatea beste kopia kopuru ezezagun batetik. TUHS oraindik ez zen existitzen eta ez nuen iturri zaharretarako sarbiderik. Baina 1988an, 9 pistako zinta zahar bat aurkitu nuen PDP11 ordenagailu baten babeskopia zuena. Zaila zen funtzionatzen zuen ala ez esatea, baina bazegoen /usr/src/ zuhaitz osorik eta bertan fitxategi gehienak 1979 urtearekin etiketatuta zeuden, orduan ere antzinako itxura zuena. Zazpigarren edizioa edo bere eratorria PWB zen, nik uste nuen bezala.
Aurkikuntza oinarritzat hartu nuen eta eskuz editatu nituen iturriak seigarren ediziorako. Kode batzuk berdin jarraitzen zuten, baina batzuk apur bat editatu behar izan ziren, += token modernoa =+ zaharkituta aldatuz. Gauza batzuk besterik gabe ezabatu ziren, eta beste batzuk guztiz berridatzi behar izan ziren, baina ez gehiegi.
Eta gaur sarean irakur dezakegu TUHS-en seigarren edizioaren iturburu kodea
Bide batez, lehen begiratuan, Kernighan eta Ritchieren garaiaren aurretik C-kodearen ezaugarri nagusia berea da. laburtasuna. Ez da sarritan kode zatiak txertatzeko gai naizela, editatu gabe, nire webguneko bistaratzeko eremu estu samarrera egokitzeko.
Goiz
/*
* 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
Buffer-aren tamaina ez da aldatu laugarren ediziotik. Baina hemen ikusten dugu, inolako dokumentazio publikorik gabe, kanalizazioak behinola fitxategiak erabiltzen zituela babeskopiko biltegiratze gisa!
LARG fitxategiei dagokienez, horri dagozkio
Hona hemen benetako sistema deia 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;
}
Iruzkinak argi deskribatzen du hemen gertatzen ari dena. Baina kodea ulertzea ez da hain erraza, neurri batean moduagatik "R0
ΠΈ R1
sistema-deiaren parametroak eta itzultzeko balioak pasatzen dira.
Saia gaitezen
pipe()
bidez egin behar da R0
ΠΈ R1
irakurtzeko eta idazteko fitxategi deskribatzaile-zenbakiak itzultzeko. falloc()
Erakuslea itzultzen du fitxategi-egiturara, baina baita "itzultzen" bidez u.u_ar0[R0]
eta fitxategi deskribatzailea. Hau da, kodea gordetzen du r
irakurtzeko fitxategi deskribatzailea eta zuzenean idazteko fitxategi deskribatzailea esleitzen du u.u_ar0[R0]
bigarren deialdiaren ostean falloc()
.
bandera FPIPE
, kanalizazioa sortzean ezartzen duguna, funtzioaren portaera kontrolatzen du
/*
* 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);
}
/* β¦ */
}
Ondoren funtzioa readp()
Π² pipe.c
kanaletik datuak irakurtzen ditu. Baina hobe da ezarpena bertatik abiatuta trazatzea writep()
. Berriz ere, kodea konplexuagoa bihurtu da argumentuak pasatzearen konbentzioengatik, baina xehetasun batzuk ezaba daitezke.
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;
}
Pipelinearen sarreran byteak idatzi nahi ditugu u.u_count
. Lehenik eta behin inodoa blokeatu behar dugu (ikus behean plock
/prele
).
Ondoren, inodoaren erreferentzia-kontagailua egiaztatuko dugu. Hodiaren bi muturrak irekita jarraitzen duten bitartean, kontagailuak 2ren berdina izan behar du. Esteka bat edukitzen dugu (tik. rp->f_inode
), beraz, kontagailua 2 baino txikiagoa bada, irakurketa prozesuak hodiaren amaiera itxi duela esan behar du. Beste era batera esanda, kanalizazio itxi batean idazten saiatzen ari gara, eta hau errore bat da. Lehen aldiz errore-kodea EPIPE
eta seinale SIGPIPE
Unix-en seigarren edizioan agertu zen.
Baina garraiatzailea irekita egon arren, beteta egon daiteke. Kasu honetan, blokeoa askatu eta lotara joango gara beste prozesu batek kanaletik irakurriko duen eta bertan nahikoa leku askatuko duenaren esperantzarekin. Esnatu ondoren, hasierara itzuliko gara, berriro blokeoa eskegi eta grabazio-ziklo berri bati ekingo diogu.
Kanpoan nahikoa leku libre badago, datuak idatziko ditugu erabiliz i_size1
inodoak (kanalizazioa hutsik badago 0-ren berdina izan daiteke) dagoeneko dituen datuen amaiera adierazten du. Grabatzeko leku nahikoa badago, kanalizazioa bertatik bete dezakegu i_size1
to PIPESIZ
. Ondoren, blokeoa askatzen dugu eta kanaletik irakurtzeko zain dagoen edozein prozesu esnatzen saiatzen gara. Hasierara itzuliko gara, behar adina byte idazteko gai garen ikusteko. Huts egiten badu, grabazio-ziklo berri bat hasiko dugu.
Normalean parametroa i_mode
inodoa baimenak gordetzeko erabiltzen da r
, w
ΠΈ x
. Baina kanalizazioen kasuan, prozesuren bat bitak erabiliz idazteko edo irakurtzeko zain dagoela adierazten dugu IREAD
ΠΈ IWRITE
hurrenez hurren. Prozesuak bandera eta deiak ezartzen ditu sleep()
, eta etorkizunean beste prozesuren batek eragingo duela espero da wakeup()
.
Benetako magia bertan gertatzen da sleep()
ΠΈ wakeup()
. urtean ezartzen dira
/*
* 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) /* β¦ */
Sortzen duen prozesua sleep()
kanal jakin baterako, geroago beste prozesu batek esnatu dezake, eta horrek eragingo du wakeup()
kanal bererako. writep()
ΠΈ readp()
haien ekintzak koordinatzea parekatutako deien bidez. apuntatu hori pipe.c
beti ematen du lehentasuna PPIPE
deituta sleep()
, beraz, hori da sleep()
seinale batek eten dezake.
Orain dena dugu funtzioa ulertzeko 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);
}
Baliteke funtzio hau behetik goitik irakurtzea errazagoa izatea. "Irakurri eta itzuli" adarra erabili ohi da kanalean datu batzuk daudenean. Kasu honetan, erabiltzen dugu f_offset
irakurri, eta, ondoren, eguneratu dagokion desplazamenduaren balioa.
Ondorengo irakurketetan, kanalizazioa hutsik egongo da irakurritako desplazamendura iritsi bada i_size1
inodoan. Posizioa 0-ra berrezartzen dugu eta kanalizazioan idatzi nahi duen edozein prozesu esnatzen saiatzen gara. Badakigu garraiatzailea beteta dagoenean, writep()
gainean lo hartuko du ip+1
. Eta orain kanalizazioa hutsik dagoenez, esna dezakegu bere idazketa-zikloa berriro hasteko.
Irakurtzeko ezer ez baduzu, orduan readp()
bandera bat ezarri dezake IREAD
eta lo hartu ip+2
. Badakigu zerk esnatuko duen writep()
, kanalean datu batzuk idazten dituenean.
Iruzkinak u
"Fitxategi bat, posizio bat, memorian buffer bat hartzen duten eta irakurtzeko edo idazteko byte kopurua zenbatzen duten I/O funtzio arrunt gisa trata ditzakegu.
/*
* 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;
/* β¦ */
Blokeo βkontserbadoreariβ dagokionez, bada readp()
ΠΈ writep()
blokeatu inodoa lana amaitu arte edo emaitza bat jaso arte (hau da, deitu wakeup
). plock()
ΠΈ prele()
besterik gabe lan egin: dei-multzo ezberdin bat erabiliz sleep
ΠΈ wakeup
askatu berri dugun blokeoa behar duen edozein prozesu esna dezagun:
/*
* 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);
}
}
Hasieran ezin nuen ulertu zergatik readp()
ez du eragiten prele(ip)
deialdiaren aurretik wakeup(ip+1)
. Lehenengo gauza da writep()
bere zikloan eragiten du, hau plock(ip)
, blokeoa dakar bada readp()
oraindik ez dut nire blokeoa kendu, beraz, nolabait, kodeak behar bezala funtzionatu behar du. Begiratuz gero wakeup()
, orduan argi geratzen da lo egiteko prozesua exekutatzeko prest bezala markatzen duela soilik, etorkizunean sched()
benetan jarri zuen martxan. Beraz readp()
arrazoiak wakeup()
, sarraila kentzen du, ezartzen IREAD
eta deiak sleep(ip+2)
- hau guztia aurretik writep()
zikloari berrekingo dio.
Honek seigarren edizioko garraiatzaileen deskribapena osatzen du. Kode sinplea, ondorio zabalak.
Xv6, Unix itxurako kernel sinplea
Nukleoa sortzeko
Kodeak inplementazio argi eta pentsakor bat dauka 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()
funtzioak barne hartzen dituen gainerako ezarpenen egoera ezartzen du piperead()
, pipewrite()
ΠΈ pipeclose()
. Benetako sistema-deia sys_pipe
urtean inplementatutako bilgarri bat da
Linux 0.01
Linux 0.01 iturburu kodea aurki daiteke. Irakasgarria izango da bere hoditegien ezarpena aztertzea fs
/pipe.c
. Honek inodo bat erabiltzen du kanalizazioa irudikatzeko, baina kanalizazioa bera C modernoan idatzita dago. 6. edizioko kodean lan egin baduzu, ez duzu arazorik izango hemen. Hau da funtzioaren itxura 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;
}
Egituraren definizioak begiratu ere egin gabe, inodoaren erreferentzia-zenbaketa nola erabiltzen den jakin dezakezu idazketa-eragiketa batek sortzen duen ala ez egiaztatzeko. SIGPIPE
. Bytez byte lan egiteaz gain, funtzio hau erraza da goiko deskribatutako ideiekin alderatzea. Baita logika ere sleep_on
/wake_up
ez dirudi hain arrotza.
Linux kernel modernoak, FreeBSD, NetBSD, OpenBSD
Azkar kernel moderno batzuk zeharkatu nituen. Horietako inork ez du jada disko inplementaziorik (ez da harritzekoa). Linuxek bere inplementazioa du. Hiru BSD nukleo modernoek John Dysonek idatzitako kodean oinarritutako inplementazioak badituzte ere, urteen poderioz elkarrengandik oso desberdindu dira.
Irakurri fs
/pipe.c
(Linux-en) edo sys
/kern
/sys_pipe.c
(*BSD-n), benetako dedikazioa behar da. Gaurko kodea errendimenduari eta laguntzari buruzkoa da, hala nola, I/O bektoriala eta asinkronoa bezalako funtzioetarako. Eta memoriaren esleipenaren, blokeoen eta nukleoaren konfigurazioaren xehetasunak asko aldatzen dira. Hau ez da unibertsitateek sistema eragileen hastapeneko ikastaro baterako behar dutena.
Dena den, eredu zahar batzuk aurkitzea interesatzen zitzaidan (sortzea adibidez SIGPIPE
eta itzuli EPIPE
kanalizazio itxi batean idaztean) nukleo moderno ezberdin hauetan guztietan. Ziurrenik ez dut inoiz PDP-11 ordenagailurik ikusiko bizitza errealean, baina oraindik asko dago ikasteko ni jaio baino urte lehenago idatzitako kodetik.
Divi Kapoor-ek 2011n idatzitako artikulu bat:
Iturria: www.habr.com