Conas píblínte a chur i bhfeidhm in Unix

Conas píblínte a chur i bhfeidhm in Unix
Déanann an t-alt seo cur síos ar chur i bhfeidhm píblínte san eithne Unix. Bhí díomá beag orm go raibh alt le déanaí dar teideal “Conas a oibríonn píblínte in Unix?"iompaithe amach aon faoin struchtúr inmheánach. D’éirigh mé fiosrach agus thochail mé isteach i seanfhoinsí chun an freagra a fháil.

Cad faoi a bhfuilimid ag caint?

Is sainairíonna iad píblínte, “an t-aireagán is tábhachtaí in Unix, is dócha,” d’fhealsúnacht bhunúsach Unix maidir le cláir bheaga a nascadh le chéile, chomh maith le comhartha coitianta ar an líne ceannais:

$ echo hello | wc -c
6

Braitheann an fheidhmiúlacht seo ar an nglao córais a sholáthair an eithne pipea bhfuil cur síos air ar na leathanaigh doiciméadaithe píopa(7) и píopa(2):

Soláthraíonn píblínte cainéal aontreoch le haghaidh cumarsáide idirphróisis. Tá ionchur (deireadh scríofa) agus aschur (deireadh léite) ag an bpíblíne. Is féidir sonraí a scríobhtar chuig ionchur na píblíne a léamh ag an aschur.

Cruthaítear an phíblíne ag baint úsáide as an nglao pipe(2), a thugann dhá thuairisceoir comhaid ar ais: ceann amháin ag tagairt d'ionchur na píblíne, an dara ceann don aschur.

Léiríonn an t-aschur rian ón ordú thuas cruthú na píblíne agus an sreabhadh sonraí tríd ó phróiseas amháin go próiseas eile:

$ 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

Glaonn an próiseas tuismitheora pipe()chun tuairisceoirí comhaid gléasta a fháil. Scríobhann próiseas páiste amháin chuig hanla amháin, agus léann próiseas eile na sonraí céanna ó láimhseáil eile. Úsáideann an bhlaosc dup2 chun tuairisceoirí 3 agus 4 a "athainmniú" chun stdin agus stdout a mheaitseáil.

Gan píopaí, bheadh ​​ar an mblaosc aschur próiseas amháin a scríobh chuig comhad agus é a chur ar aghaidh chuig próiseas eile chun na sonraí ón gcomhad a léamh. Mar thoradh air sin, chuirfimis níos mó acmhainní agus spás diosca amú. Mar sin féin, tá píblínte go maith, ní hamháin toisc go gceadaíonn siad duit úsáid comhaid shealadacha a sheachaint:

Má tá próiseas ag iarraidh léamh ó phíblíne folamh ansin read(2) bacfaidh sé go dtí go mbeidh na sonraí ar fáil. Má dhéanann próiseas iarracht scríobh chuig píblíne iomlán, ansin write(2) blocfaidh sé go dtí go mbeidh go leor sonraí léite ón bpíblíne chun an scríobh a dhéanamh.

Cosúil le ceanglas POSIX, is maoin thábhachtach é seo: scríobh chuig an bpíblíne suas go dtí PIPE_BUF caithfidh beart (512 ar a laghad) a bheith adamhach ionas gur féidir le próisis cumarsáid a dhéanamh lena chéile tríd an bpíblíne ar bhealach nach féidir le comhaid rialta (nach dtugann ráthaíochtaí dá leithéid).

Agus gnáthchomhad á úsáid, is féidir le próiseas a aschur go léir a scríobh chuige agus é a chur ar aghaidh chuig próiseas eile. Nó is féidir le próisis oibriú ar mhodh an-chomhthreomhar, ag baint úsáide as meicníocht chomharthaíochta sheachtrach (cosúil le semaphore) chun fógra a thabhairt dá chéile nuair a bhíonn scríobh nó léamh críochnaithe. Sábhálann iompróirí sinn ón hassle seo go léir.

Cad atá á lorg againn?

Míneoidh mé i dtéarmaí simplí é ionas go mbeidh sé níos éasca duit a shamhlú conas is féidir le iompróir oibriú. Beidh ort maolán agus staid éigin a leithdháileadh i gcuimhne. Beidh feidhmeanna uait chun sonraí a chur leis agus a bhaint as an maolán. Beidh roinnt bealaí ag teastáil uait chun feidhmeanna a ghlaoch le linn oibríochtaí léite agus scríofa ar thuairisceoirí comhaid. Agus beidh glais ag teastáil uait chun an iompar speisialta a thuairiscítear thuas a chur i bhfeidhm.

Táimid réidh anois an cód foinse eithne a cheistiú faoi sholas lampa geal chun ár samhail mheabhrach doiléir a dhearbhú nó a bhréagnú. Ach a ullmhú i gcónaí le haghaidh an gan choinne.

Cá bhfuil muid ag féachaint?

Níl a fhios agam cá bhfuil mo chóip den leabhar cáiliúil “Leabhar lions" le cód foinse Unix 6, ach a bhuíochas sin do Cumann Oidhreachta Unix is féidir leat cuardach a dhéanamh ar líne ag cód foinse leaganacha níos sine fós de Unix.

Is ionann siúl trí chartlanna TUHS agus cuairt a thabhairt ar mhúsaem. Is féidir linn breathnú ar ár stair chomhroinnte, agus tá meas agam ar na blianta fada a rinne mé iarracht an t-ábhar seo go léir a aisghabháil beagán ar bheagán ó shean-téipeanna agus priontaí. Agus is eol dom go géar na blúirí sin atá fós in easnamh.

Tar éis dúinn ár bhfiosracht faoi stair ársa na n-iompróirí a shásamh, is féidir linn breathnú ar eithne nua-aimseartha chun comparáid a dhéanamh.

Dála an scéil, pipe is é córas glao-uimhir 42 sa tábla sysent[]. Chomhtharlú?

Eithne Traidisiúnta Unix (1970–1974)

Ní bhfuair mé aon rian pipe(2) ná i PDP-7 Unix (Eanáir 1970), ná i Unix an chéad eagrán (Samhain 1971), ná sa chód foinse neamhiomlán dara heagrán (Meitheamh 1972).

Deir TUHS go bhfuil an tríú eagrán de Unix (Feabhra 1973) an chéad leagan le iompróirí:

Ba é Unix 1973rd Edition an leagan deireanach le eithne scríofa i dteanga tionóil, ach freisin an chéad leagan le píblínte. Le linn XNUMX, rinneadh obair chun an tríú eagrán a fheabhsú, athscríobhadh an eithne i C, agus mar sin bhí an ceathrú eagrán de Unix le feiceáil.

Fuair ​​​​léitheoir amháin scanadh ar dhoiciméad inar mhol Doug McIlroy an smaoineamh “cláir a nascadh cosúil le hose gairdín.”

Conas píblínte a chur i bhfeidhm in Unix
I leabhar Brian KernighanUnix: Stair agus Cuimhneachán“, i stair theacht chun cinn na n-iompróirí, luaitear an doiciméad seo freisin: “... crochadh sé ar an mballa i m’oifig ag Bell Labs ar feadh 30 bliain.” Anseo agallamh le McIlroy, agus scéal eile ó Saothar McIlroy, a scríobhadh in 2014:

Nuair a tháinig Unix amach, thug mo spéis le coroutines orm iarraidh ar údar an OS, Ken Thompson, cead a thabhairt do shonraí scríofa chuig próiseas dul ní hamháin chuig an bhfeiste, ach freisin le haschur chuig próiseas eile. Chinn Ken go raibh sé indéanta. Mar íoslach, áfach, bhí sé ag iarraidh go mbeadh ról suntasach ag gach feidhm chórais. An buntáiste mór é scríobh go díreach idir próisis seachas scríobh chuig comhad idirmheánach? Is nuair a rinne mé moladh sonrach leis an ainm catchy “píblíne” agus cur síos ar an chomhréir don idirghníomhaíocht idir próisis a d’éiligh Ken ar deireadh: “Déanfaidh mé é!”

Agus rinne. Tráthnóna cinniúnach amháin, d'athraigh Ken an t-eithne agus an bhlaosc, shocraigh sé roinnt clár caighdeánach chun caighdeánú a dhéanamh ar an gcaoi ar ghlac siad le hionchur (a d'fhéadfadh teacht ó phíblíne), agus d'athraigh sé ainmneacha comhaid freisin. An lá dár gcionn, thosaigh píblínte a úsáid go forleathan in iarratais. Faoi dheireadh na seachtaine, bhí rúnaithe ag baint úsáide as iad chun doiciméid a sheoladh ó phróiseálaithe focal chuig an printéir. Beagán níos déanaí, chuir Ken in ionad an API bunaidh agus an chomhréir chun úsáid píblínte a fhilleadh le coinbhinsiúin níos glaine, a úsáideadh ó shin i leith.

Ar an drochuair, tá an cód foinse don tríú eagrán Unix eithne caillte. Agus cé go bhfuil an cód foinse eithne againn scríofa i C ceathrú eagrán, a scaoileadh i mí na Samhna 1973, ach tháinig sé amach roinnt míonna roimh an scaoileadh oifigiúil agus níl feidhmithe píblíne ann. Is mór an náire go bhfuil an cód foinse don fheidhm legendary Unix seo caillte, b'fhéidir go deo.

Tá doiciméadú téacs againn le haghaidh pipe(2) ón dá eisiúint, ionas gur féidir leat tosú trí chuardach a dhéanamh ar an doiciméadú tríú eagrán (i gcás focail áirithe, cuir béim ar “láimh”, teaghrán litriúil ^H, agus béim ina dhiaidh!). an proto-pipe(2) scríofa i dteanga tionóil agus ní sheolann sé ach tuairisceoir comhaid amháin ar ais, ach cuireann sé an chroífheidhmíocht ionchais ar fáil cheana féin:

Glao córais píopa cruthaíonn sé meicníocht ionchuir/aschuir ar a dtugtar píblíne. Is féidir an tuairisceoir comhaid a cuireadh ar ais a úsáid le haghaidh oibríochtaí léamh agus scríobh. Nuair a scríobhtar rud éigin chuig an bpíblíne, maolaítear suas le 504 bytes de shonraí, agus cuirtear an próiseas scríbhneoireachta ar fionraí ina dhiaidh sin. Nuair a bhíonn tú ag léamh ón bpíblíne, tógtar na sonraí maolánacha ar shiúl.

Faoin mbliain ina dhiaidh sin bhí an eithne athscríofa in C, agus píopa(2) sa cheathrú eagrán fuair sé a chuma nua-aimseartha leis an fhréamhshamhail"pipe(fildes)»:

Glao córais píopa cruthaíonn sé meicníocht ionchuir/aschuir ar a dtugtar píblíne. Is féidir na tuairisceoirí comhaid a cuireadh ar ais a úsáid in oibríochtaí léite agus scríofa. Nuair a scríobhtar rud éigin chuig an bpíblíne, úsáidtear an hanla a chuirtear ar ais in r1 (resp. fildes[1]), arna mhaolánú go 4096 beart sonraí, agus ina dhiaidh sin cuirtear an próiseas scríofa ar fionraí. Agus tú ag léamh ón bpíblíne, glacann an hanla a chuirtear ar ais go r0 (resp. fildes[0]) na sonraí.

Glactar leis, a luaithe a shainmhínítear píblíne, go ndéantar dhá phróiseas cumarsáide (nó níos mó) (a chruthaítear le glaonna ina dhiaidh sin chuig forc) aistreoidh sé sonraí ón bpíblíne trí úsáid a bhaint as glaonna léamh и scríobh.

Tá comhréir ag an mblaosc chun sraith líneach próiseas a shainiú atá nasctha le píblíne.

Glaonna le léamh ó phíblíne folamh (nach bhfuil aon sonraí maolánacha ann) nach bhfuil ach foirceann amháin aici (tá gach tuairisceoir comhaid scríofa dúnta) “deireadh comhaid” ar ais. Déantar neamhaird de ghlaonna chun scríobh i gcás comhchosúil.

is luaithe cur i bhfeidhm píblíne caomhnaithe i bhfeidhm go dtí an cúigiú eagrán de Unix (Meitheamh 1974), ach tá sé beagnach mar an gcéanna leis an gceann a bhí le feiceáil sa chéad eisiúint eile. Tá tuairimí díreach curtha leis, ionas gur féidir leat an cúigiú eagrán a scipeáil.

An séú eagrán de Unix (1975)

Tosaímid ag léamh cód foinse Unix séú heagrán (Bealtaine 1975). Den chuid is mó a bhuíochas sin do Lions tá sé i bhfad níos éasca a fháil ná foinsí na leaganacha níos luaithe:

Le blianta fada a an leabhar Lions an t-aon doiciméad ar an eithne Unix atá ar fáil lasmuigh de Bell Labs. Cé gur thug ceadúnas an séú heagrán cead do mhúinteoirí a cód foinse a úsáid, níor cuireadh an fhéidearthacht seo as an gceadúnas seachtú heagrán, mar sin dáileadh an leabhar i bhfoirm cóipeanna mídhleathacha clóscríofa.

Sa lá atá inniu is féidir leat athchló den leabhar a cheannach, agus taispeánann a chlúdach na mic léinn ag meaisín cóipe. Agus buíochas le Warren Toomey (a chuir tús leis an tionscadal TUHS) is féidir leat a íoslódáil Comhad pdf le cód foinse don séú eagrán. Ba mhaith liom smaoineamh a thabhairt duit ar an méid iarracht a rinneadh an comhad a chruthú:

Breis is 15 bliain ó shin, chlóscríobh mé cóip den chód foinse a tugadh isteach Lions, mar níor thaitin cáilíocht mo chóip liom ó líon anaithnid cóipeanna eile. Ní raibh TUHS ann go fóill agus ní raibh rochtain agam ar na seanfhoinsí. Ach i 1988, fuair mé sean-téip 9-rian ina raibh cúltaca ó ríomhaire PDP11. Ba dheacair a rá an raibh sé ag obair, ach bhí crann /usr/src/ slán ann inar lipéadaíodh an chuid is mó de na comhaid leis an mbliain 1979, a bhí cuma ársa fiú ansin. Ba é an seachtú eagrán nó a dhíorthaigh PWB, mar a chreid mé.

Ghlac mé leis an bhfionnachtain mar bhonn agus chuir mé na foinsí in eagar go dtí an séú eagrán de láimh. D’fhan cuid den chód mar a chéile, ach b’éigean roinnt eile a chur in eagar, rud a d’athraigh an comhartha += nua-aimseartha go dtí an seanfhocal =+. Scriosadh roinnt rudaí go simplí, agus b'éigean roinnt eile a athscríobh go hiomlán, ach ní raibh an iomarca.

Agus inniu is féidir linn cód foinse an séú eagrán a léamh ar líne ar TUHS cartlann, a raibh lámh ag Dennis Ritchie uirthi.

Dála an scéil, ar an gcéad amharc, is é príomhghné an chóid C roimh thréimhse Kernighan agus Ritchie a gontacht. Ní minic a bhíonn mé in ann píosaí cód a chur isteach gan eagarthóireacht fhairsing chun limistéar taispeána sách cúng a chur ar mo shuíomh.

Luath /usr/sys/ken/pipe.c tá trácht míniúcháin (agus tá, tá níos mó /usr/sys/dmr):

/*
 * 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

Níl aon athrú ar an méid maoláin ón gceathrú eagrán. Ach anseo feicimid, gan aon doiciméadú poiblí, gur úsáideadh píblínte comhaid uair amháin mar stóráil chúltaca!

Maidir le comhaid LARG, comhfhreagraíonn siad do bratach inoide LARG, a úsáideann an "algartam seoltaí mór" chun próiseáil a dhéanamh bloic indíreacha chun tacú le córais comhad níos mó. Ós rud é go ndúirt Ken go bhfuil sé níos fearr gan iad a úsáid, beidh mé sásta a fhocal a ghlacadh ar a shon.

Seo é an fíorghlao córais 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;
}

Déanann an trácht cur síos go soiléir ar a bhfuil ar siúl anseo. Ach níl sé chomh héasca an cód a thuiscint, i bpáirt mar gheall ar an mbealach "struchtúr úsáideora u» agus cláir R0 и R1 déantar paraiméadair ghlao córais agus luachanna tuairisceáin a rith.

Déanaimis iarracht le ialloc() chur ar diosca ionóid (láimhseáil innéacs), agus le cabhair falloc() - cuir beirt i gcuimhne comhad. Má éiríonn go maith leis, socróimid bratacha chun na comhaid seo a aithint mar dhá cheann na píblíne, cuir in iúl iad chuig an inóid chéanna (a socrófar a chomhaireamh tagartha go 2), agus marcáil an t-ionóid mar atá modhnaithe agus in úsáid. Tabhair aird ar iarratais ar chuir mé() i slite earráide chun an comhaireamh tagartha san inóid nua a laghdú.

pipe() ní mór tríd R0 и R1 uimhreacha tuairisceora comhaid a sheoladh ar ais le haghaidh léamh agus scríobh. falloc() cuireann sé pointeoir ar ais chuig an struchtúr comhaid, ach freisin "tuairiscíonn" via u.u_ar0[R0] agus tuairisceoir comhaid. Is é sin, sábhálann an cód i r tuairisceoir comhaid le léamh agus sannann sé tuairisceoir comhaid lena scríobh go díreach uaidh u.u_ar0[R0] tar éis an dara glaoch falloc().

Bratach FPIPE, a shocraigh muid nuair a chruthaíonn an phíblíne, rialaíonn iompar na feidhme rdwr() in sys2.cgnáthaimh I/O ar leith a ghlaoch:

/*
 * 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);
    }
        /* … */
}

Ansin an fheidhm readp() в pipe.c léann sonraí ón bpíblíne. Ach is fearr an cur i bhfeidhm a rianú ag tosú ó writep(). Arís, tá an cód tar éis éirí níos casta mar gheall ar choinbhinsiúin na hargóintí a rith, ach is féidir roinnt sonraí a fhágáil ar lár.

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;
}

Ba mhaith linn bearta a scríobh chuig an ionchur píblíne u.u_count. Ar dtús ní mór dúinn an t-ionóid a ghlasadh (féach thíos plock/prele).

Ansin déanaimid seiceáil ar an gcuntar tagartha inode. Chomh fada agus a fhanann an dá cheann den phíblíne ar oscailt, ba chóir go mbeadh an gcuntar comhionann le 2. Coinnímid nasc amháin (ó rp->f_inode), mar sin má tá an gcuntar níos lú ná 2, caithfidh sé a chiallaíonn go bhfuil deireadh an phíblíne dúnta ag an bpróiseas léitheoireachta. I bhfocail eile, táimid ag iarraidh scríobh chuig píblíne dúnta, agus is earráid é seo. Cód earráide don chéad uair EPIPE agus comhartha SIGPIPE le feiceáil sa séú eagrán de Unix.

Ach fiú má tá an conveyor oscailte, féadfaidh sé a bheith iomlán. Sa chás seo, scaoilimid an glas agus téann muid a chodladh le súil go léifear próiseas eile ón bpíblíne agus go scaoilfear go leor spáis ann. Tar éis dúinn dúiseacht, filleann muid ar an tús, crochaimid an glas arís agus cuirimid tús le timthriall taifeadta nua.

Má tá go leor spáis saor in aisce ar an bpíblíne, scríobhaimid sonraí chuige ag baint úsáide as scríobhann()... Paraiméadar i_size1 Léiríonn an inóid (má tá an phíblíne folamh is féidir é a bheith comhionann le 0) deireadh na sonraí atá ann cheana féin. Má tá go leor spáis taifeadta ann, is féidir linn an phíblíne a líonadh ó i_size1 до PIPESIZ. Ansin scaoilimid an glas agus déanaimid iarracht aon phróiseas atá ag fanacht le léamh ón bpíblíne a mhúscailt. Téim ar ais go dtí an tús féachaint an raibh muid in ann a scríobh oiread beart agus is gá dúinn. Má theipeann air, ansin cuirimid tús le timthriall taifeadta nua.

De ghnáth an paraiméadar i_mode úsáidtear inode chun ceadanna a stóráil r, w и x. Ach i gcás píblínte, tugaimid le fios go bhfuil próiseas éigin ag fanacht le scríobh nó léamh ag baint úsáide as giotán IREAD и IWRITE faoi ​​seach. Leagann an próiseas an bhratach agus glaonna sleep(), agus táthar ag súil go gcuirfidh próiseas éigin eile sa todhchaí faoi deara wakeup().

Tarlaíonn an draíocht fíor i sleep() и wakeup(). Cuirtear i bhfeidhm iad i slp.c, foinse an tráchtaireacht cháiliúil “Ní mheastar go dtuigeann tú é seo”. Ar ámharaí an tsaoil, ní gá dúinn an cód a thuiscint, níl le déanamh ach breathnú ar roinnt tuairimí:

/*
 * 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) /* … */

An próiseas is cúis le sleep() le haghaidh cainéal ar leith is féidir, a dhúiseacht níos déanaí le próiseas eile, rud a chuirfidh faoi deara wakeup() don chainéal céanna. writep() и readp() a gcuid gníomhaíochtaí a chomhordú trí ghlaonna péireáilte den sórt sin. tabhair faoi deara go pipe.c tugann tosaíocht i gcónaí PPIPE nuair a ghlaoitear air sleep(), mar sin sin é sleep() féadfaidh comhartha cur isteach air.

Anois tá gach rud againn chun an fheidhm a thuiscint 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);
}

Seans go mbeidh sé níos fusa duit an fheidhm seo a léamh ó bhun go barr. Úsáidtear an brainse "léamh agus filleadh" de ghnáth nuair a bhíonn roinnt sonraí ar an bpíblíne. Sa chás seo, úsáidimid léigh() léimid an oiread sonraí agus atá ar fáil ag tosú ón gceann reatha f_offset léamh, agus ansin nuashonraigh luach an fhritháireamh comhfhreagrach.

Ar léamha ina dhiaidh sin, beidh an phíblíne folamh má tá an fritháireamh léite sroichte i_size1 ag inóid. Déanaimid an seasamh a athshocrú go 0 agus déanaimid iarracht aon phróiseas atá ag iarraidh scríobh chuig an bpíblíne a mhúscailt. Tá a fhios againn nuair a bhíonn an iompair lán. writep() beidh titim ina chodladh ar ip+1. Agus anois go bhfuil an phíblíne folamh, is féidir linn é a mhúscailt chun a timthriall scríbhneoireachta a atosú.

Mura bhfuil aon rud le léamh agat, ansin readp() is féidir bratach a shocrú IREAD agus titim ina chodladh ar ip+2. Tá a fhios againn cad a dhúisíonn sé writep(), nuair a scríobhann sé roinnt sonraí chuig an bpíblíne.

Tuairimí chuig readi() and writei() cabhróidh sé leat é sin a thuiscint seachas paraiméadair a rith trí "u“Is féidir linn caitheamh leo mar ghnáthfheidhmeanna I/O a thógann comhad, suíomh, maolán sa chuimhne, agus líon na mbeart atá le léamh nó le scríobh a chomhaireamh.

/*
 * 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;
/* … */

Maidir leis an "coimeádach" blocáil, mar sin readp() и writep() bac a chur ar an inode go dtí go gcríochnóidh siad a gcuid oibre nó go bhfaighidh siad toradh (is é sin, glaoigh wakeup). plock() и prele() oibriú go simplí: ag baint úsáide as sraith eile glaonna sleep и wakeup lig dúinn aon phróiseas a mhúscailt a dteastaíonn an glas a scaoileadh uainn:

/*
 * 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);
    }
}

Ar dtús ní raibh mé in ann a thuiscint cén fáth readp() ní cúis prele(ip) roimh an nglao wakeup(ip+1). Is é an chéad rud writep() cúiseanna ina timthriall, seo plock(ip), as a dtagann tsáinn má readp() níor bhain mé mo bhloc fós, mar sin caithfidh an cód oibriú i gceart ar bhealach éigin. Má fhéachann tú ar wakeup(), ansin bíonn sé soiléir go marcálann sé ach an próiseas codlata mar réidh le forghníomhú, ionas go mbeidh sa todhchaí sched() sheol sé i ndáiríre. Mar sin readp() cúiseanna wakeup(), baintear an glas, leagann sé IREAD agus glaonna sleep(ip+2)- seo go léir roimhe seo writep() atosaíonn an timthriall.

Críochnaíonn sé seo an cur síos ar iompróirí sa séú eagrán. Cód simplí, iarmhairtí forleathana.

Seachtú eagrán de Unix (Eanáir 1979) mór-eisiúint nua (ceithre bliana ina dhiaidh sin) a thug isteach go leor feidhmchlár nua agus gnéithe eithne. Rinneadh athruithe suntasacha air freisin maidir le húsáid cineál-theilgthe, aontaí agus leideanna clóscríofa ar struchtúir. ach cód iompair beagnach gan athrú. Is féidir linn an t-eagrán seo a scipeáil.

Xv6, eithne simplí cosúil le Unix

Chun an eithne a chruthú Xv6 tionchar ag an séú eagrán de Unix, ach tá sé scríofa i C nua-aimseartha a reáchtáil ar phróiseálaithe x86. Tá an cód éasca le léamh agus intuigthe. Ina theannta sin, murab ionann agus foinsí Unix le TUHS, is féidir leat é a thiomsú, é a mhodhnú, agus é a rith ar rud éigin eile seachas PDP 11/70. Dá bhrí sin, úsáidtear an eithne seo go forleathan in ollscoileanna mar ábhar oideachais ar chórais oibriúcháin. Foinsí atá ar Github.

Tá cur i bhfeidhm soiléir agus tuisceanach sa chód píob.c, tacaithe ag maolán sa chuimhne seachas inóid ar diosca. Anseo ní thugaim ach an sainmhíniú ar "phíblíne struchtúrach" agus an fheidhm 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() leagann sé amach staid an chuid eile den chur i bhfeidhm, lena n-áirítear na feidhmeanna piperead(), pipewrite() и pipeclose(). Fíorghlao córais sys_pipe is wrapper i bhfeidhm i sysfile.c. Molaim a chód iomlán a léamh. Tá an chastacht ag leibhéal cód foinse an séú eagrán, ach tá sé i bhfad níos éasca agus níos taitneamhaí é a léamh.

Linux 0.01

Is féidir cód foinse Linux 0.01 a fháil. Beidh sé oiliúnach chun staidéar a dhéanamh ar chur i bhfeidhm píblínte ina fs/pipe.c. Úsáideann sé seo inóid chun an phíblíne a léiriú, ach tá an phíblíne féin scríofa i C nua-aimseartha. Má d'oibrigh tú do bhealach a dhéanamh tríd an gcód 6ú eagrán, ní bheidh aon deacracht agat anseo. Seo mar a bhreathnaíonn an fheidhm 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;
}

Gan fiú breathnú ar na sainmhínithe struchtúir, is féidir leat a dhéanamh amach conas a úsáidtear an comhaireamh tagartha ionóid chun a sheiceáil cibé an dtagann oibríocht scríofa SIGPIPE. Chomh maith le beart-ar-beart a oibriú, is furasta an fheidhm seo a chur i gcomparáid leis na smaointe a gcuirtear síos orthu thuas. Fiú loighic sleep_on/wake_up cuma chomh coimhthíoch sin.

Eithne Linux nua-aimseartha, FreeBSD, NetBSD, OpenBSD

Rith mé go tapa trí roinnt kernels nua-aimseartha. Níl aon cheann acu i bhfeidhm diosca níos mó (ní nach ionadh). Tá a fhorfheidhmiú féin ag Linux. Cé go bhfuil feidhmiúcháin bunaithe ar chód a scríobh John Dyson sna trí eithne BSD nua-aimseartha, thar na blianta tá siad tar éis éirí ró-dhifriúil óna chéile.

A léamh fs/pipe.c (ar Linux) nó sys/kern/sys_pipe.c (ar *BSD), caithfidh sé fíor-thiomantas. Baineann cód an lae inniu le feidhmíocht agus tacaíocht do ghnéithe cosúil le veicteoir agus I/O asincrónach. Agus tá éagsúlacht mhór idir na sonraí maidir le leithdháileadh cuimhne, glais agus cumraíocht eithne. Ní hé seo an rud a theastaíonn ó choláistí le haghaidh cúrsa tosaigh ar chórais oibriúcháin.

Ar aon nós, bhí suim agam roinnt seanphatrúin a thochailt (cosúil le giniúint SIGPIPE agus ar ais EPIPE agus tú ag scríobh chuig píblíne dúnta) sna kernels nua-aimseartha éagsúla seo go léir. Is dócha nach bhfeicfidh mé ríomhaire PDP-11 sa saol fíor, ach tá go leor le foghlaim fós ó chód a scríobhadh blianta sular rugadh mé.

Alt a scríobh Divi Kapoor in 2011:An Eithne Linux Cur i bhFeidhm Píopaí agus FIFOanna" forbhreathnú ar conas a oibríonn píblínte (fós) i Linux. A gealltanas le déanaí i Linux léiríonn sé samhail píblíne idirghníomhaíochta, a sháraíonn a gcumas cumais na gcomhad sealadach; agus léiríonn sé freisin cé chomh fada agus a tháinig píblínte ón "glasáil an-choimeádach" den eithne Unix séú eagrán.

Foinse: will.com

Add a comment