Å ajÄ rakstÄ ir aprakstÄ«ta konveijeru ievieÅ”ana Unix kodolÄ. Es biju nedaudz vÄ«lies, ka nesen tika publicÄts raksts ar nosaukumu "
Par ko mÄs runÄjam?
Cauruļvadi, "iespÄjams, vissvarÄ«gÄkais Unix izgudrojums", ir galvenÄ Unix filozofijas iezÄ«me, kas saistÄ«ta ar mazu programmu sasaisti, kÄ arÄ« pazÄ«stama zÄ«me komandrindÄ:
$ echo hello | wc -c
6
Å Ä« funkcionalitÄte ir atkarÄ«ga no kodola nodroÅ”inÄtÄ sistÄmas izsaukuma pipe
, kas ir aprakstÄ«ts dokumentÄcijas lapÄs
Cauruļvadi nodroÅ”ina vienvirziena kanÄlu starpprocesu komunikÄcijai. Konveijeram ir ieeja (rakstÄ«Å”anas beigas) un izvade (lasÄ«Å”anas beigas). Datus, kas ierakstÄ«ti cauruļvada ieejÄ, var nolasÄ«t izejÄ.
Cauruļvads tiek izveidots, izmantojot zvanu
pipe(2)
, kas atgriež divus failu deskriptorus: viens attiecas uz konveijera ievadi, otrs uz izvadi.
IepriekÅ” minÄtÄs komandas izsekoÅ”anas izvade parÄda cauruļvada izveidi un datu plÅ«smu caur to no viena procesa uz citu:
$ 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
VecÄku procesa zvani pipe()
lai iegÅ«tu uzstÄdÄ«to failu deskriptorus. Viens pakÄrtotais process raksta vienÄ rokturÄ, bet cits process nolasa tos paÅ”us datus no cita roktura. Apvalks izmanto dup2, lai "pÄrdÄvÄtu" deskriptorus 3 un 4, lai tie atbilstu stdin un stdout.
Bez caurulÄm apvalkam bÅ«tu jÄieraksta viena procesa rezultÄts failÄ un jÄnodod citam procesam, lai nolasÄ«tu datus no faila. RezultÄtÄ mÄs iztÄrÄtu vairÄk resursu un diska vietas. TomÄr cauruļvadi ir labi ne tikai tÄpÄc, ka tie ļauj izvairÄ«ties no pagaidu failu izmantoÅ”anas:
Ja process mÄÄ£ina nolasÄ«t no tukÅ”a cauruļvada, tad
read(2)
bloÄ·Äs, lÄ«dz dati kļūs pieejami. Ja process mÄÄ£ina rakstÄ«t pilnÄ konveijerÄ, tadwrite(2)
bloÄ·Äs, lÄ«dz no konveijera bÅ«s nolasÄ«ts pietiekami daudz datu, lai veiktu rakstÄ«Å”anu.
TÄpat kÄ POSIX prasÄ«ba, arÄ« Ŕī ir svarÄ«ga Ä«paŔība: rakstÄ«Å”ana konveijerÄ lÄ«dz PIPE_BUF
baitiem (vismaz 512) jÄbÅ«t atomiem, lai procesi varÄtu sazinÄties savÄ starpÄ, izmantojot konveijeru tÄdÄ veidÄ, kÄ parastie faili (kas nenodroÅ”ina Å”Ädas garantijas).
Izmantojot parastu failu, process var ierakstÄ«t tajÄ visu savu izvadi un nodot to citam procesam. Vai arÄ« procesi var darboties ļoti paralÄlÄ režīmÄ, izmantojot ÄrÄju signalizÄcijas mehÄnismu (piemÄram, semaforu), lai informÄtu viens otru, kad rakstÄ«Å”ana vai lasÄ«Å”ana ir pabeigta. Konveijeri mÅ«s glÄbj no visÄm Ŕīm grÅ«tÄ«bÄm.
Ko mÄs meklÄjam?
Es to paskaidroÅ”u vienkÄrÅ”i, lai jums bÅ«tu vieglÄk iedomÄties, kÄ var darboties konveijers. Jums bÅ«s jÄpieŔķir buferis un kÄds stÄvoklis atmiÅÄ. Jums bÅ«s nepiecieÅ”amas funkcijas, lai pievienotu un noÅemtu datus no bufera. Jums bÅ«s nepiecieÅ”ami daži lÄ«dzekļi, lai izsauktu funkcijas lasÄ«Å”anas un rakstÄ«Å”anas operÄciju laikÄ uz failu deskriptoriem. Un jums bÅ«s nepiecieÅ”amas slÄdzenes, lai Ä«stenotu iepriekÅ” aprakstÄ«to Ä«paÅ”o uzvedÄ«bu.
Tagad mÄs esam gatavi nopratinÄt kodola pirmkodu spilgtÄ lampas gaismÄ, lai apstiprinÄtu vai atspÄkotu mÅ«su neskaidro garÄ«go modeli. Bet vienmÄr esiet gatavs negaidÄ«tam.
Kur mÄs skatÄmies?
Es nezinu, kur atrodas mans slavenÄs grÄmatas eksemplÄrs "
KlÄ«st pa TUHS arhÄ«vu ir kÄ muzeja apmeklÄjums. MÄs varam aplÅ«kot mÅ«su kopÄ«go vÄsturi, un es cienu daudzu gadu pÅ«les, lai pamazÄm atgÅ«tu visu Å”o materiÄlu no vecÄm lentÄm un izdrukÄm. Un es ļoti labi apzinos tos fragmentus, kuru joprojÄm trÅ«kst.
ApmierinÄjuÅ”i savu zinÄtkÄri par konveijera seno vÄsturi, salÄ«dzinÄjumam varam aplÅ«kot mÅ«sdienu kodolus.
Starp citu, pipe
tabulÄ ir sistÄmas izsaukuma numurs 42 sysent[]
. NejauŔība?
TradicionÄlie Unix kodoli (1970ā1974)
Es neatradu nekÄdas pÄdas pipe(2)
ne iekÅ”Ä
TUHS norÄda, ka
Unix 1973rd Edition bija pÄdÄjÄ versija ar kodolu, kas rakstÄ«ts montÄžas valodÄ, bet arÄ« pirmÄ versija ar konveijeriem. XNUMX. gadÄ tika veikts darbs pie treÅ”Ä izdevuma uzlaboÅ”anas, kodols tika pÄrrakstÄ«ts C valodÄ, un tÄ parÄdÄ«jÄs ceturtais Unix izdevums.
KÄds lasÄ«tÄjs atrada skenÄtu dokumentu, kurÄ Dags Makilrojs ierosinÄja ideju "savienot programmas kÄ dÄrza Ŕļūteni".
Braiena Kernigana grÄmatÄ
Kad iznÄca Unix, mana aizrauÅ”anÄs ar korutÄ«nÄm lika man lÅ«gt operÄtÄjsistÄmas autoram Kenam Tompsonam ļaut datiem, kas ierakstÄ«ti procesÄ, nonÄkt ne tikai ierÄ«cÄ, bet arÄ« izvadÄ«t citÄ procesÄ. Kens nolÄma, ka tas ir iespÄjams. TomÄr kÄ minimÄlists viÅÅ” vÄlÄjÄs, lai katrai sistÄmas funkcijai bÅ«tu nozÄ«mÄ«ga loma. Vai tieÅ”Äm rakstÄ«Å”ana tieÅ”i starp procesiem ir liela priekÅ”rocÄ«ba salÄ«dzinÄjumÄ ar rakstÄ«Å”anu starpfailÄ? Tikai tad, kad es izteicu konkrÄtu priekÅ”likumu ar ÄÄ·Ä«go nosaukumu ācauruļvadsā un procesu mijiedarbÄ«bas sintakses aprakstu, Kens beidzot iesaucÄs: āEs to izdarÄ«Å”u!ā
Un darÄ«ja. KÄdÄ liktenÄ«gÄ vakarÄ Kens mainÄ«ja kodolu un Äaulu, laboja vairÄkas standarta programmas, lai standartizÄtu to, kÄ tÄs pieÅÄma ievadi (kas varÄtu bÅ«t no konveijera), kÄ arÄ« mainÄ«ja failu nosaukumus. NÄkamajÄ dienÄ cauruļvadus sÄka ļoti plaÅ”i izmantot lietojumos. LÄ«dz nedÄļas beigÄm sekretÄri tos izmantoja, lai nosÅ«tÄ«tu dokumentus no tekstapstrÄdes programmÄm uz printeri. Nedaudz vÄlÄk Kens aizstÄja sÄkotnÄjo API un sintaksi cauruļvadu izmantoÅ”anas iesaiÅoÅ”anai ar tÄ«rÄkÄm konvencijÄm, kas tiek izmantotas kopÅ” tÄ laika.
DiemžÄl treÅ”Ä izdevuma Unix kodola pirmkods ir pazaudÄts. Un, lai gan mums ir kodola pirmkods, kas rakstÄ«ts C valodÄ
Mums ir teksta dokumentÄcija pipe(2)
no abiem laidieniem, lai jÅ«s varÄtu sÄkt, meklÄjot dokumentÄciju pipe(2)
ir uzrakstÄ«ts montÄžas valodÄ un atgriež tikai vienu faila deskriptoru, taÄu jau nodroÅ”ina paredzÄto pamata funkcionalitÄti:
SistÄmas zvans caurule izveido ievades/izvades mehÄnismu, ko sauc par cauruļvadu. Atgriezto faila deskriptoru var izmantot lasÄ«Å”anas un rakstÄ«Å”anas darbÄ«bÄm. Kad konveijerÄ kaut kas tiek ierakstÄ«ts, tiek buferÄts lÄ«dz 504 baitiem datu, pÄc tam rakstÄ«Å”anas process tiek apturÄts. Lasot no konveijera, buferizÄtie dati tiek noÅemti.
NÄkamajÄ gadÄ kodols tika pÄrrakstÄ«ts C valodÄ un pipe(fildes)
"
SistÄmas zvans caurule izveido ievades/izvades mehÄnismu, ko sauc par cauruļvadu. Atgrieztos failu deskriptorus var izmantot lasÄ«Å”anas un rakstÄ«Å”anas operÄcijÄs. Kad konveijerÄ kaut kas tiek ierakstÄ«ts, tiek izmantots r1 atgrieztais rokturis (resp. fildes[1]), buferÄts lÄ«dz 4096 datu baitiem, pÄc tam rakstÄ«Å”anas process tiek apturÄts. Lasot no konveijera, rokturis, kas atgriezts uz r0 (resp. fildes[0]), Åem datus.
Tiek pieÅemts, ka pÄc konveijera definÄÅ”anas tiks veikti divi (vai vairÄki) saziÅas procesi (ko rada nÄkamie izsaukumi uz dakÅ”a) pÄrsÅ«tÄ«s datus no cauruļvada, izmantojot zvanus lasÄ«t Šø rakstÄ«t.
Apvalkam ir sintakse lineÄra procesu masÄ«va definÄÅ”anai, kas savienoti ar cauruļvadu.
AicinÄjumi lasÄ«t no tukÅ”a konveijera (kas nesatur buferÄtus datus), kuram ir tikai viens gals (visi rakstÄmÄ faila deskriptori ir aizvÄrti), atgriež "faila beigas". AicinÄjumi rakstÄ«t lÄ«dzÄ«gÄ situÄcijÄ tiek ignorÄti.
AgrÄkais
Sestais Unix izdevums (1975)
SÄksim lasÄ«t Unix pirmkodu
Daudzus gadus grÄmata Lauvas bija vienÄ«gais dokuments par Unix kodolu, kas pieejams Ärpus Bell Labs. Lai gan sestÄ izdevuma licence ļÄva skolotÄjiem izmantot tÄs pirmkodu, septÄ«tÄ izdevuma licence izslÄdza Å”o iespÄju, tÄpÄc grÄmata tika izplatÄ«ta nelegÄlu maŔīnrakstÄ«to kopiju veidÄ.
Å odien var iegÄdÄties grÄmatas atkÄrtotu izdruku, uz kuras vÄka redzami skolÄni pie kopÄtÄja. Pateicoties Vorenam Toomejam (kurÅ” uzsÄka TUHS projektu), jÅ«s varat lejupielÄdÄt
Pirms vairÄk nekÄ 15 gadiem es ierakstÄ«ju norÄdÄ«tÄ pirmkoda kopiju Lauvas, jo man nepatika manas kopijas kvalitÄte no nezinÄma skaita citu kopiju. TUHS vÄl neeksistÄja, un man nebija piekļuves vecajiem avotiem. Bet 1988. gadÄ es atradu vecu 9 celiÅu lenti, kurÄ bija dublÄjums no PDP11 datora. Bija grÅ«ti pateikt, vai tas darbojas, taÄu bija neskarts /usr/src/ koks, kurÄ lielÄkÄ daļa failu bija marÄ·Äti ar 1979. gadu, kas pat tad izskatÄ«jÄs sens. Tas bija septÄ«tais izdevums vai tÄ atvasinÄjums PWB, kÄ es ticÄju.
Par pamatu ÅÄmu atradumu un manuÄli rediÄ£Äju avotus lÄ«dz sestajam izdevumam. Daļa koda palika nemainÄ«gi, bet daži bija nedaudz jÄrediÄ£Ä, mainot moderno += marÄ·ieri uz novecojuÅ”o =+. Dažas lietas tika vienkÄrÅ”i izdzÄstas, un dažas bija pilnÄ«bÄ jÄpÄrraksta, bet ne pÄrÄk daudz.
Un Å”odien mÄs varam lasÄ«t tieÅ”saistÄ TUHS sestÄ izdevuma pirmkodu no
Starp citu, no pirmÄ acu uzmetiena C-koda galvenÄ iezÄ«me pirms Kernighan un Ritchie perioda ir tÄ Ä«sums. Retos gadÄ«jumos es varu ievietot koda daļas bez lielas rediÄ£ÄÅ”anas, lai tÄs ietilptu salÄ«dzinoÅ”i Å”aurÄ manas vietnes displeja apgabalÄ.
Agri
/*
* 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
Bufera lielums nav mainÄ«jies kopÅ” ceturtÄ izdevuma. TaÄu bez publiskas dokumentÄcijas mÄs redzam, ka konveijeri kÄdreiz izmantoja failus kÄ rezerves krÄtuvi!
Kas attiecas uz LARG failiem, tie atbilst
Å eit ir Ä«stais sistÄmas izsaukums 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;
}
KomentÄrs skaidri apraksta, kas Å”eit notiek. Bet saprast kodu nav tik viegli, daļÄji tÄpÄc, ka veids "R0
Šø R1
tiek nodoti sistÄmas izsaukuma parametri un atgrieÅ”anas vÄrtÄ«bas.
MÄÄ£inÄsim ar
pipe()
jÄiziet cauri R0
Šø R1
atgriezt faila deskriptora numurus lasīŔanai un rakstīŔanai. falloc()
atgriež rÄdÄ«tÄju uz faila struktÅ«ru, bet arÄ« "atgriež" caur u.u_ar0[R0]
un faila deskriptors. Tas ir, kods tiek saglabÄts r
faila deskriptors lasīŔanai un pieŔķir faila deskriptoru rakstīŔanai tieŔi no u.u_ar0[R0]
pÄc otrÄ zvana falloc()
.
AtzÄ«mÄt FPIPE
, ko iestatÄ«jÄm, veidojot konveijeru, kontrolÄ funkcijas darbÄ«bu
/*
* 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);
}
/* ā¦ */
}
PÄc tam funkcija readp()
Š² pipe.c
nolasa datus no konveijera. Bet labÄk ir izsekot ievieÅ”anai, sÄkot no writep()
. Atkal kods ir kļuvis sarežģītÄks argumentu nodoÅ”anas konvenciju dÄļ, taÄu dažas detaļas var izlaist.
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;
}
MÄs vÄlamies rakstÄ«t baitus cauruļvada ievadei u.u_count
. Vispirms mums ir jÄbloÄ·Ä inode (skatiet zemÄk plock
/prele
).
PÄc tam mÄs pÄrbaudÄm inode atsauces skaitÄ«tÄju. KamÄr abi cauruļvada gali paliek atvÄrti, skaitÄ«tÄjam jÄbÅ«t vienÄdam ar 2. MÄs turam vienu saiti (no rp->f_inode
), tÄdÄļ, ja skaitÄ«tÄjs ir mazÄks par 2, tas nozÄ«mÄ, ka nolasÄ«Å”anas process ir slÄdzis cauruļvada galu. Citiem vÄrdiem sakot, mÄs mÄÄ£inÄm rakstÄ«t slÄgtÄ konveijerÄ, un tÄ ir kļūda. Pirmo reizi kļūdas kods EPIPE
un signÄls SIGPIPE
parÄdÄ«jÄs Unix sestajÄ izdevumÄ.
Bet pat tad, ja konveijers ir atvÄrts, tas var bÅ«t pilns. Å ajÄ gadÄ«jumÄ mÄs atlaižam slÄdzeni un dodamies gulÄt, cerot, ka kÄds cits process nolasÄ«s no cauruļvada un atbrÄ«vos tajÄ pietiekami daudz vietas. PamoduÅ”ies atgriežamies sÄkumÄ, atkal noliekam slÄdzeni un sÄkam jaunu ierakstÄ«Å”anas ciklu.
Ja cauruļvadÄ ir pietiekami daudz brÄ«vas vietas, mÄs tajÄ ierakstÄm datus, izmantojot i_size1
inode (ja konveijers ir tukÅ”s, tas var bÅ«t vienÄds ar 0) norÄda tajÄ jau esoÅ”o datu beigas. Ja ir pietiekami daudz vietas ierakstÄ«Å”anai, mÄs varam aizpildÄ«t cauruļvadu no i_size1
līdz PIPESIZ
. PÄc tam atlaižam slÄdzeni un mÄÄ£inÄm pamodinÄt jebkuru procesu, kas gaida nolasÄ«Å”anu no konveijera. MÄs atgriežamies pie sÄkuma, lai redzÄtu, vai mÄs spÄjÄm ierakstÄ«t tik daudz baitu, cik mums nepiecieÅ”ams. Ja tas neizdodas, mÄs sÄkam jaunu ierakstÄ«Å”anas ciklu.
Parasti parametrs i_mode
inode tiek izmantots atļauju glabÄÅ”anai r
, w
Šø x
. Bet cauruļvadu gadÄ«jumÄ mÄs signalizÄjam, ka kÄds process gaida rakstÄ«Å”anu vai lasÄ«Å”anu, izmantojot bitus IREAD
Šø IWRITE
attiecīgi. Process nosaka karogu un zvanus sleep()
, un sagaidÄms, ka nÄkotnÄ izraisÄ«s kÄds cits process wakeup()
.
ÄŖstÄ maÄ£ija notiek iekÅ”Ä sleep()
Šø wakeup()
. Tie tiek ieviesti
/*
* 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) /* ā¦ */
Process, kas izraisa sleep()
konkrÄtam kanÄlam, vÄlÄk var tikt pamodinÄts ar citu procesu, kas izraisÄ«s wakeup()
tam paÅ”am kanÄlam. writep()
Šø readp()
koordinÄt savas darbÄ«bas, izmantojot Å”Ädus pÄra zvanus. pieraksti to pipe.c
vienmÄr dod priekÅ”roku PPIPE
kad sauc sleep()
, tÄ tas arÄ« viss sleep()
var tikt pÄrtraukts ar signÄlu.
Tagad mums ir viss, lai saprastu funkciju 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);
}
Jums var bÅ«t vieglÄk lasÄ«t Å”o funkciju no apakÅ”as uz augÅ”u. FiliÄle "lasÄ«t un atgriezt" parasti tiek izmantota, ja ir daži dati. Å ajÄ gadÄ«jumÄ mÄs izmantojam f_offset
lasÄ«Å”anu un pÄc tam atjauniniet atbilstoÅ”Äs nobÄ«des vÄrtÄ«bu.
TurpmÄkajos nolasÄ«jumos konveijers bÅ«s tukÅ”s, ja ir sasniegta nolasÄ«Å”anas nobÄ«de i_size1
inodÄ. MÄs atiestatÄm pozÄ«ciju uz 0 un mÄÄ£inÄm aktivizÄt jebkuru procesu, kas vÄlas rakstÄ«t konveijerÄ. MÄs zinÄm, ka tad, kad konveijers ir pilns, writep()
aizmigs tÄlÄk ip+1
. Un tagad, kad cauruļvads ir tukÅ”s, mÄs varam to pamodinÄt, lai atsÄktu tÄ rakstÄ«Å”anas ciklu.
Ja nav ko lasīt, tad readp()
var uzstÄdÄ«t karogu IREAD
un aizmigt tÄlÄk ip+2
. MÄs zinÄm, kas viÅu pamodinÄs writep()
, kad tas ieraksta dažus datus konveijerÄ.
KomentÄri par u
"MÄs varam tos uzskatÄ«t par parastajÄm I/O funkcijÄm, kas aizÅem failu, pozÄ«ciju, buferi atmiÅÄ un skaita lasÄmo vai rakstÄmo baitu skaitu.
/*
* 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;
/* ā¦ */
Kas attiecas uz "konservatÄ«vo" bloÄ·ÄÅ”anu, tad readp()
Šø writep()
bloÄ·Äjiet inode, lÄ«dz viÅi pabeidz darbu vai saÅem rezultÄtu (tas ir, zvaniet wakeup
). plock()
Šø prele()
strÄdÄt vienkÄrÅ”i: izmantojot citu zvanu kopu sleep
Šø wakeup
ļauj mums pamodinÄt jebkuru procesu, kam nepiecieÅ”ama tikko atbrÄ«votÄ slÄdzene:
/*
* 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);
}
}
SÄkumÄ es nevarÄju saprast, kÄpÄc readp()
neizraisa prele(ip)
pirms zvana wakeup(ip+1)
. PirmÄ lieta ir writep()
cÄloÅi savÄ ciklÄ, Å”is plock(ip)
, kas noved pie strupceļa, ja readp()
vÄl neesmu noÅÄmis savu bloku, tÄpÄc kodam kaut kÄ jÄdarbojas pareizi. Ja paskatÄs wakeup()
, tad kļūst skaidrs, ka tas tikai atzÄ«mÄ gulÄÅ”anas procesu kÄ gatavu izpildei, lai turpmÄk sched()
tieÅ”Äm to palaida. TÄtad readp()
cÄloÅi wakeup()
, noÅem slÄdzeni, uzstÄda IREAD
un zvani sleep(ip+2)
- tas viss iepriekÅ” writep()
atsÄk ciklu.
Tas pabeidz konveijeru aprakstu sestajÄ izdevumÄ. VienkÄrÅ”s kods, tÄlejoÅ”as sekas.
Xv6, vienkÄrÅ”s Unix lÄ«dzÄ«gs kodols
Lai izveidotu kodolu
Kods satur skaidru un pÄrdomÄtu ievieÅ”anu 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()
iestata pÄrÄjÄs ievieÅ”anas stÄvokli, kas ietver funkcijas piperead()
, pipewrite()
Šø pipeclose()
. Faktiskais sistÄmas zvans sys_pipe
ir iesaiÅojums, kas ieviests
Linux 0.01
Var atrast Linux 0.01 pirmkodu. BÅ«s pamÄcoÅ”i izpÄtÄ«t cauruļvadu ievieÅ”anu viÅa fs
/pipe.c
. Konveijera attÄloÅ”anai tiek izmantota inode, bet pats konveijers ir rakstÄ«ts mÅ«sdienu C valodÄ. Ja esat strÄdÄjis lÄ«dz 6. izdevuma kodam, jums Å”eit nebÅ«s problÄmu. Å Ädi izskatÄs funkcija 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;
}
Pat neapskatot struktÅ«ras definÄ«cijas, varat noskaidrot, kÄ inode atsauces skaits tiek izmantots, lai pÄrbaudÄ«tu, vai rakstÄ«Å”anas operÄcijas rezultÄtÄ SIGPIPE
. Papildus darbam pa baitam Å”o funkciju ir viegli salÄ«dzinÄt ar iepriekÅ” aprakstÄ«tajÄm idejÄm. Pat loÄ£ika sleep_on
/wake_up
neizskatÄs tik sveÅ”s.
MÅ«sdienu Linux kodoli, FreeBSD, NetBSD, OpenBSD
Es Ätri izskrÄju cauri dažiem mÅ«sdienu kodoliem. Nevienam no tiem vairs nav diska ievieÅ”anas (nav pÄrsteidzoÅ”i). Linux ir sava ievieÅ”ana. Lai gan trÄ«s mÅ«sdienu BSD kodoli satur implementÄcijas, kuru pamatÄ ir Džona Daisona uzrakstÄ«tais kods, gadu gaitÄ tie ir kļuvuÅ”i pÄrÄk atŔķirÄ«gi viens no otra.
Lasīt fs
/pipe.c
(uz Linux) vai sys
/kern
/sys_pipe.c
(uz *BSD), tas prasa patiesu centÄ«bu. Å odienas kods ir par veiktspÄju un atbalstu tÄdÄm funkcijÄm kÄ vektors un asinhronÄ I/O. Un informÄcija par atmiÅas pieŔķirÅ”anu, bloÄ·ÄÅ”anu un kodola konfigurÄciju ir ļoti atŔķirÄ«ga. Tas nav tas, kas koledžÄm ir vajadzÄ«gs operÄtÄjsistÄmu ievadkursam.
JebkurÄ gadÄ«jumÄ mani interesÄja izrakt dažus vecus modeļus (piemÄram, Ä£enerÄt SIGPIPE
un atgriezties EPIPE
rakstot slÄgtÄ cauruļvadÄ) visos Å”ajos dažÄdajos mÅ«sdienu kodolos. VisticamÄk, es nekad dzÄ«vÄ neredzÄÅ”u datoru PDP-11, taÄu joprojÄm ir daudz ko mÄcÄ«ties no koda, kas tika uzrakstÄ«ts gadus pirms manas dzimÅ”anas.
Divi Kapoor raksts 2011. gadÄ:
Avots: www.habr.com