Mar a tha pìoban air an cur an gnìomh ann an Unix

Mar a tha pìoban air an cur an gnìomh ann an Unix
Tha an artaigil seo a’ toirt cunntas air buileachadh pìoban ann an kernel Unix. Bha mi car diombach gun robh artaigil o chionn ghoirid leis an tiotal "Ciamar a bhios pìoban ag obair ann an Unix?» thionndaidh a mach chan eil mun structar a-staigh. Fhuair mi fiosrach agus chladhaich mi seann stòran gus am freagairt a lorg.

Cò mu dheidhinn a tha thu a ’bruidhinn?

Is dòcha gur e pìoban “an innleachd as cudromaiche ann an Unix” - feart sònraichte den fheallsanachd bhunaiteach aig Unix a thaobh a bhith a’ cur phrògraman beaga ri chèile, agus an sluagh-ghairm air a bheil sinn eòlach:

$ echo hello | wc -c
6

Tha an gnìomh seo an urra ris a’ ghairm siostam a tha air a sholarachadh le kernel pipe, a tha air a mhìneachadh air na duilleagan sgrìobhainnean pìob(7) и pìob(2):

Bidh pìoban a 'toirt seachad sianal aon-shligheach airson conaltradh eadar-phròiseas. Tha cuir a-steach aig an loidhne-phìoban (crìoch sgrìobhaidh) agus toradh (deireadh leughaidh). Faodar dàta a chaidh a sgrìobhadh gu cuir a-steach na loidhne-phìoban a leughadh aig an toradh.

Tha an loidhne-phìoban air a chruthachadh le bhith a 'gairm pipe(2), a thilleas dà thuairisgeul faidhle: tha aon a’ toirt iomradh air cuir a-steach na loidhne-phìoban, an dàrna fear air an toradh.

Tha an toradh lorg bhon àithne gu h-àrd a’ sealltainn cruthachadh loidhne-phìoban agus sruthadh dàta troimhe bho aon phròiseas gu 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

Bidh am pròiseas pàrant a 'gairm pipe()gus tuairisgeulan faidhle ceangailte. Bidh aon phròiseas pàiste a’ sgrìobhadh gu aon tuairisgeul agus pròiseas eile a’ leughadh an aon dàta bho thuairisgeul eile. Tha an t-slige “ag ath-ainmeachadh” tuairisgeulan 2 agus 3 le dup4 gus stdin agus stdout a mhaidseadh.

Às aonais pìoban, dh'fheumadh an t-slige toradh aon phròiseas a sgrìobhadh gu faidhle agus a phìobadh gu pròiseas eile gus an dàta bhon fhaidhle a leughadh. Mar thoradh air an sin, bhiodh sinn a’ caitheamh barrachd ghoireasan agus àite diosc. Ach, tha pìoban math airson barrachd air dìreach faidhlichean sealach a sheachnadh:

Ma dh'fheuchas pròiseas ri leughadh bho loidhne-phìoban falamh, an uairsin read(2) bacadh gus am bi an dàta ri fhaighinn. Ma tha pròiseas a 'feuchainn ri sgrìobhadh gu loidhne-phìoban slàn, an uairsin write(2) bacadh gus am bi dàta gu leòr air a leughadh bhon loidhne-phìoban gus an sgrìobhadh a chrìochnachadh.

Coltach ris an riatanas POSIX, is e seilbh chudromach a tha seo: sgrìobhadh chun loidhne-phìoban suas gu PIPE_BUF feumaidh bytes (co-dhiù 512) a bhith atamach gus an urrainn do phròiseasan conaltradh le chèile tron ​​​​loidhne-phìoban ann an dòigh nach urrainn do fhaidhlichean àbhaisteach (nach eil a’ toirt seachad geallaidhean mar sin).

Le faidhle cunbhalach, faodaidh pròiseas a thoraidhean gu lèir a sgrìobhadh thuige agus a chuir air adhart gu pròiseas eile. No faodaidh pròiseasan obrachadh ann am modh cruaidh co-shìnte, a’ cleachdadh inneal comharran bhon taobh a-muigh (mar semaphore) gus innse dha chèile mu bhith a’ crìochnachadh sgrìobhadh no leughadh. Sàbhalaidh luchd-giùlain sinn bhon duilgheadas seo gu lèir.

Dè tha sinn a’ sireadh?

Mìnichidh mi air mo chorragan gus a dhèanamh nas fhasa dhut smaoineachadh air mar as urrainn do inneal-giùlain obrachadh. Feumaidh tu bufair agus cuid de staid a riarachadh mar chuimhne. Bidh feum agad air gnìomhan gus dàta a chur ris agus a thoirt air falbh bhon bhufair. Bidh feum agad air goireas airson gnìomhan a ghairm rè obair leughaidh is sgrìobhaidh air tuairisgeulan faidhle. Agus tha feum air glasan gus an giùlan sònraichte a tha air a mhìneachadh gu h-àrd a chur an gnìomh.

Tha sinn a-nis deiseil airson còd stòr an kernel a cheasnachadh fo sholas lampa soilleir gus ar modal inntinn neo-shoilleir a dhearbhadh no a dhearbhadh. Ach bi an-còmhnaidh deiseil airson an fheadhainn ris nach robh dùil.

Càit a bheil sinn a’ coimhead?

Chan eil fios agam càite a bheil an leth-bhreac agam den leabhar ainmeil.Leabhar leòmhainn« le còd stòr Unix 6, ach le taing dha Comann Dualchais Unix faodar a lorg air-loidhne còd stòr eadhon tionndaidhean nas sine de Unix.

Tha siubhal tro thasglannan TUHS coltach ri tadhal air taigh-tasgaidh. Faodaidh sinn coimhead air ar n-eachdraidh cho-roinnte agus tha spèis agam do na bliadhnaichean de dh’ oidhirp gus an stuth seo gu lèir fhaighinn air ais mean air mhean bho sheann chèiseagan agus clò-bhualaidhean. Agus tha mi gu math mothachail air na criomagan sin a tha fhathast a dhìth.

Às deidh dhuinn ar feòrachas a shàsachadh mu sheann eachdraidh nam pìoban, is urrainn dhuinn coimhead air coraichean an latha an-diugh airson coimeas a dhèanamh.

Air an t-slighe, pipe tha àireamh gairm siostam 42 sa chlàr sysent[]. Co-thuiteamas?

Craobhan Unix traidiseanta (1970-1974)

Cha do lorg mi lorg sam bith pipe(2) ni a-steach PDP-7 Unix (Faoilleach 1970), no ann an Unix a ' chiad iris a (Samhain 1971), no ann an còd stòr neo-choileanta dàrna deasachadh (An t-Ògmhios 1972).

Tha TUHS ag agairt sin An treas deasachadh Unix (Gearran 1973) a’ chiad dreach le loidhnichean-phìoban:

B 'e an treas deasachadh de Unix an dreach mu dheireadh le kernel sgrìobhte ann an assembler, ach cuideachd a' chiad dreach le pìoban. Ann an 1973, bha obair a 'dol air adhart gus an treas deasachadh a leasachadh, chaidh an kernel ath-sgrìobhadh ann an C, agus mar sin rugadh an ceathramh deasachadh de Unix.

Lorg aon leughadair scan de sgrìobhainn anns an do mhol Doug McIlroy am beachd “prògraman a cheangal mar hose gàrraidh.”

Mar a tha pìoban air an cur an gnìomh ann an Unix
Anns an leabhar aig Brian KernighanUnix: Eachdraidh agus Cuimhneachan", tha eachdraidh coltas luchd-giùlain cuideachd a 'toirt iomradh air a' phàipear seo: "... bha e crochte air a 'bhalla anns an oifis agam aig Bell Labs airson 30 bliadhna." Seo agallamh le McIlroyagus sgeul eile bho Obair McIlroy, sgrìobhte ann an 2014:

Nuair a nochd Unix, thug mo dhealas airson coroutines orm iarraidh air ùghdar an OS, Ken Thompson, leigeil le dàta a chaidh a sgrìobhadh gu pròiseas air choreigin a dhol chan ann a-mhàin chun inneal, ach cuideachd chun t-slighe a-mach gu pròiseas eile. Cho-dhùin Coinneach gun robh e comasach. Ach, mar minimalist, bha e airson gum biodh àite cudromach aig a h-uile feart siostam. A bheil sgrìobhadh dìreach eadar pròiseasan na bhuannachd mhòr dha-rìribh seach sgrìobhadh gu faidhle eadar-mheadhanach? Agus dìreach nuair a rinn mi moladh sònraichte leis an ainm tarraingeach “pìob-loidhne” agus tuairisgeul air co-chòrdadh eadar-obrachadh phròiseasan, thuirt Ken mu dheireadh: “Nì mi e!”.

Agus rinn. Aon fheasgar uamhasach, dh’ atharraich Ken an kernel agus an t-slige, shuidhich e grunn phrògraman àbhaisteach gus gnàthachadh a dhèanamh air mar a ghabhas iad ri cuir a-steach (a dh’ fhaodadh tighinn bho loidhne-phìoban), agus dh’ atharraich e ainmean faidhle. An ath latha, chaidh pìoban a chleachdadh gu farsaing ann an tagraidhean. Ro dheireadh na seachdain, chleachd na rùnairean iad airson sgrìobhainnean a chuir bho luchd-deasachaidh fhaclan chun chlò-bhualadair. Beagan nas fhaide air adhart, chuir Ken an àite an API agus an co-chòrdadh tùsail airson cleachdadh pìoban a chòmhdach le gnàthasan nas glaine a chaidh a chleachdadh bhon uair sin.

Gu mì-fhortanach, chaidh an còd tùsail airson an treas deasachadh Unix kernel air chall. Agus ged a tha an còd stòr kernel againn sgrìobhte ann an C ceathramh deasachadh, a chaidh a leigeil a-mach san t-Samhain 1973, ach thàinig e a-mach beagan mhìosan ron fhoillseachadh oifigeil agus chan eil buileachadh pìoban ann. Tha e duilich gu bheil an còd stòr airson an fheart uirsgeulach Unix seo air chall, is dòcha gu bràth.

Tha teacsa sgrìobhainnean againn airson pipe(2) bhon dà fhoillseachadh, gus an urrainn dhut tòiseachadh le bhith a’ sgrùdadh nan sgrìobhainnean treas deasachadh (airson cuid de dh’ fhacail, le “manually” air a shoilleireachadh, sreath de litrichean ^H agus fo-sgrìobhadh an dèidh sin!). Tha am proto-pipe(2) air a sgrìobhadh ann an assembler agus a’ tilleadh dìreach aon tuairisgeul faidhle, ach mar-thà a’ toirt seachad am prìomh ghnìomhachd ris a bheil dùil:

Call siostam pìoba a’ cruthachadh inneal I/O ris an canar loidhne-phìoban. Faodar an tuairisgeul faidhle a chaidh a thilleadh a chleachdadh airson obair leughaidh is sgrìobhaidh. Nuair a thèid rudeigin a sgrìobhadh chun loidhne-phìoban, bidh e a ’bufair suas ri 504 bytes de dhàta, às deidh sin tha am pròiseas sgrìobhaidh air a chuir dheth. Nuair a leughas tu bhon loidhne-phìoban, thèid an dàta bufair a thoirt.

Ron ath bhliadhna, bha an kernel air ath-sgrìobhadh ann an C, agus pìob(2) ceathramh iris fhuair e an sealladh ùr-nodha aige leis a’ prototype "pipe(fildes)"

Call siostam pìoba a’ cruthachadh uidheamachd I/O ris an canar loidhne-phìoban. Faodar na tuairisgeulan faidhle a chaidh a thilleadh a chleachdadh ann an obair leughaidh is sgrìobhaidh. Nuair a thèid rudeigin a sgrìobhadh chun loidhne-phìoban, thathas a’ cleachdadh an tuairisgeul air a thilleadh ann an r1 (rep. fildes [1]), air a bhufair suas gu 4096 bytes de dhàta, agus às deidh sin tha am pròiseas sgrìobhaidh air a chuir dheth. Nuair a leughas tu bhon loidhne-phìoban, thill an tuairisgeul gu r0 (rep. fildes[0]) an dàta.

Thathas a’ gabhail ris, aon uair ‘s gu bheil loidhne-phìoban air a mhìneachadh, dà phròiseas eadar-obrachaidh (no barrachd) (air an cruthachadh le fiosan às deidh sin Fork) a’ dol seachad air dàta bhon loidhne-phìoban a’ cleachdadh fiosan leughadh и sgrìobhadh.

Tha co-chòrdadh aig an t-slige airson sreath sreathach de phròiseasan a tha ceangailte tro loidhne-phìoban a mhìneachadh.

Bidh gairmean airson leughadh bho loidhne-phìoban falamh (anns nach eil dàta bufair) aig nach eil ach aon cheann (sgrìobh tuairisgeulan faidhle dùinte) a’ tilleadh “deireadh faidhle”. Thathas a’ seachnadh gairmean sgrìobhte ann an suidheachadh coltach ris.

Na bu tràithe buileachadh loidhne-phìoban glèidhte buntainn chun a’ chòigeamh deasachadh de Unix (Ògmhios 1974), ach tha e cha mhòr co-ionann ris an fhear a nochd san ath fhoillseachadh. Cha deach ach beachdan a chur ris, agus mar sin faodar an còigeamh deasachadh a sheachnadh.

Unix san t-siathamh deasachadh (1975)

A’ tòiseachadh a’ leughadh còd stòr Unix an t-siathamh deasachadh (Cèitean 1975). Gu ìre mhòr taing dha Lions tha e mòran nas fhasa a lorg na stòran nan dreachan nas tràithe:

Airson iomadh bliadhna an leabhar air a Lions an aon sgrìobhainn air an kernel Unix a bha ri fhaighinn taobh a-muigh Bell Labs. Ged a leig cead an t-siathamh deasachadh le tidsearan an còd stòr aca a chleachdadh, cha do chuir cead an t-seachdamh deasachadh a-mach an comas seo, agus mar sin chaidh an leabhar a sgaoileadh ann an lethbhric clò-sgrìobhaidh mì-laghail.

An-diugh faodaidh tu leth-bhreac ath-chlò-bhualaidh den leabhar a cheannach, agus tha an còmhdach a’ sealltainn oileanaich aig an neach-copaidh. Agus taing dha Warren Toomey (a thòisich am pròiseact TUHS), faodaidh tu luchdachadh sìos Stòr an t-siathamh deasachadh pdf. Tha mi airson beachd a thoirt dhut air dè an oidhirp a chaidh a dhèanamh gus am faidhle a chruthachadh:

O chionn còrr air 15 bliadhna, thapaidh mi leth-bhreac den chòd stòr a chaidh a thoirt seachad Lionsoir cha bu toil leam càileachd mo leth-bhreac bho àireamh neo-aithnichte de leth-bhreacan eile. Cha robh TUHS ann fhathast, agus cha robh cothrom agam air na seann stòran. Ach ann an 1988 lorg mi seann teip le 9 slighean aig an robh cùl-taic bho choimpiutair PDP11. Bha e duilich faighinn a-mach an obraich e, ach bha craobh slàn / usr/src/ anns an robh a’ mhòr-chuid de na faidhlichean air an comharrachadh 1979, a bha eadhon an uairsin a’ coimhead aosta. B’ e an seachdamh deasachadh a bh’ ann, no derivative PWB, shaoil ​​​​mi.

Ghabh mi an lorg mar bhunait agus dheasaich mi na stòran le làimh gu staid an t-siathamh deasachadh. Dh'fhuirich pàirt den chòd mar a bha e, dh'fheumadh pàirt a bhith air a dheasachadh beagan, ag atharrachadh an tòcan ùr-nodha += gu seann-fhasanta =+. Bha rudeigin dìreach air a dhubhadh às, agus dh'fheumadh rudeigin a bhith air ath-sgrìobhadh gu tur, ach cha robh cus.

Agus an-diugh is urrainn dhuinn còd tùsail an t-siathamh deasachadh de TUHS a leughadh air-loidhne tasglann, aig an robh làmh aig Dennis Ritchie.

Co-dhiù, aig a’ chiad sealladh, is e prìomh fheart a’ chòd-C ro àm Kernighan agus Ritchie a giorrad. Chan ann tric a bhios e comasach dhomh criomagan de chòd a chuir a-steach gun deasachadh farsaing gus a bhith iomchaidh airson raon taisbeanaidh caran cumhang air an làrach agam.

Aig an toiseach /usr/sys/ken/pipe.c tha beachd mìneachaidh ann (agus tha, tha barrachd ann /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

Chan eil meud bufair air atharrachadh bhon cheathramh deasachadh. Ach an seo chì sinn, às aonais sgrìobhainnean poblach sam bith, gun robh pìoban uaireigin a’ cleachdadh faidhlichean mar stòradh air ais!

A thaobh faidhlichean LARG, tha iad a’ freagairt inode-bratach LARG, a tha air a chleachdadh leis an "algorithm seòlaidh mòr" airson a phròiseasadh blocaichean neo-dhìreach gus taic a thoirt do shiostaman faidhle nas motha. Leis gun tuirt Coinneach gu bheil e nas fheàrr gun a bhith gan cleachdadh, tha mi toilichte am facal aige a ghabhail air a shon.

Seo an fhìor ghairm siostam 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;
}

Tha am beachd a’ toirt cunntas soilleir air na tha a’ tachairt an seo. Ach chan eil e cho furasta an còd a thuigsinn, gu ìre air sgàth mar a “structar neach-cleachdaidh u» agus clàran R0 и R1 thèid paramadairean gairm siostam agus luachan tilleadh seachad.

Feuchaidh sinn le ialloc() àite air an diosg inode (inode), agus le cuideachadh falloc() - stòradh dhà faidhle. Ma thèid a h-uile càil gu math, suidhichidh sinn brataichean gus na faidhlichean sin a chomharrachadh mar dà cheann na loidhne-phìoban, comharraich iad chun an aon inode (a thig an àireamh iomraidh gu 2), agus comharraich an inode mar a chaidh atharrachadh agus a chleachdadh. Thoir an aire gu iarrtasan iput() ann an slighean mearachd gu lùghdachadh an àireamh iomraidh san inode ùr.

pipe() air sgàth R0 и R1 thoir air ais àireamhan tuairisgeul faidhle airson leughadh agus sgrìobhadh. falloc() a 'tilleadh puing gu structar faidhle, ach cuideachd "tilleadh" tro u.u_ar0[R0] agus tuairisgeul faidhle. Is e sin, tha an còd air a stòradh a-steach r tuairisgeul faidhle airson leughadh agus sònraichidh e tuairisgeul airson sgrìobhadh dìreach bho u.u_ar0[R0] às deidh an dàrna gairm falloc().

Didòmhnaich FPIPE, a shuidhich sinn nuair a bhios sinn a 'cruthachadh an loidhne-phìoban, a' cumail smachd air giùlan a 'ghnìomh rdwr() ann an sys2.c, a chanas cleachdaidhean I / O sònraichte:

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

An uairsin an gnìomh readp() в pipe.c a’ leughadh dàta bhon loidhne-phìoban. Ach tha e nas fheàrr am buileachadh a lorg a’ tòiseachadh bho writep(). A-rithist, tha an còd air fàs nas toinnte air sgàth cho sònraichte sa tha an argamaid a’ dol seachad air a’ chùmhnant, ach faodar cuid de mhion-fhiosrachadh fhàgail air falbh.

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

Tha sinn airson bytes a sgrìobhadh gu cuir a-steach na loidhne-phìoban u.u_count. An toiseach feumaidh sinn an inode a ghlasadh (faic gu h-ìosal plock/prele).

An uairsin nì sinn sgrùdadh air an àireamh iomraidh inode. Cho fad 's a bhios dà cheann na loidhne-phìoban fosgailte, bu chòir gum biodh an cuntair 2. Cumaidh sinn air adhart gu aon cheangal (bho rp->f_inode), mar sin ma tha an cuntair nas lugha na 2, bu chòir seo a bhith a’ ciallachadh gu bheil am pròiseas leughaidh air deireadh na loidhne-phìoban a dhùnadh. Ann am faclan eile, tha sinn a 'feuchainn ri sgrìobhadh gu loidhne-phìoban dùinte, rud a tha na mhearachd. Còd na mearachd a ' chiad EPIPE agus comharradh SIGPIPE nochdadh anns an t-siathamh deasachadh de Unix.

Ach eadhon ma tha an inneal-giùlain fosgailte, faodaidh e a bhith làn. Anns a 'chùis seo, bidh sinn a' leigeil a-mach a 'ghlas agus a' dol a chadal an dòchas gun leugh pròiseas eile bhon loidhne-phìoban agus gun saor sinn àite gu leòr ann. Nuair a dhùisgeas sinn, tillidh sinn chun toiseach, croch a’ ghlas a-rithist agus tòisichidh sinn cearcall sgrìobhaidh ùr.

Ma tha àite gu leòr an-asgaidh san loidhne-phìoban, bidh sinn a’ sgrìobhadh dàta thuige a’ cleachdadh sgrìobh (). Paramadair i_size1 faodaidh an inode'a (le loidhne-phìoban falamh a bhith co-ionann ri 0) puingean gu deireadh an dàta a tha ann mu thràth. Ma tha àite gu leòr ann airson sgrìobhadh, is urrainn dhuinn an loidhne-phìoban a lìonadh bho i_size1 gu PIPESIZ. An uairsin bidh sinn a 'leigeil a-mach a' ghlas agus a 'feuchainn ri pròiseas sam bith a tha a' feitheamh ri leughadh bhon loidhne-phìoban a dhùsgadh. Bidh sinn a’ dol air ais chun toiseach feuch an deach againn air na h-uimhir de bytes a sgrìobhadh ’s a bha a dhìth oirnn. Mura h-eil, an uairsin tòisichidh sinn cearcall clàraidh ùr.

Mar as trice paramadair i_mode inode air a chleachdadh gus ceadan a stòradh r, w и x. Ach a thaobh loidhnichean-phìoban, bidh sinn a’ comharrachadh gu bheil pròiseas air choireigin a’ feitheamh ri sgrìobhadh no leughadh a’ cleachdadh pìosan IREAD и IWRITE fa leth. Bidh am pròiseas a 'suidheachadh a' bhratach agus na gairmean sleep(), agus thathar an dùil gun tig pròiseas air choireigin eile san àm ri teachd wakeup().

Bidh an fhìor dhraoidheachd a’ tachairt ann an sleep() и wakeup(). Tha iad air an cur an gnìomh ann an slp.c, stòr a’ bheachd ainmeil "Chan eilear an dùil gun tuig thu seo". Gu fortanach, chan fheum sinn an còd a thuigsinn, dìreach thoir sùil air cuid de na beachdan:

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

Am pròiseas a tha a 'gairm sleep() airson sianal sònraichte, faodar a dhùsgadh nas fhaide air adhart le pròiseas eile, a dh’ ainmicheas wakeup() airson an aon sianal. writep() и readp() co-òrdanachadh an gnìomhan tro na gairmean càraideach sin. thoir an aire sin pipe.c prìomhachas an-còmhnaidh PPIPE nuair a chaidh a ghairm sleep(), mar sin uile sleep() faodar stad a chuir air le comharra.

A-nis tha a h-uile dad againn airson an gnìomh a thuigsinn 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);
}

Is dòcha gum bi e nas fhasa dhut an gnìomh seo a leughadh bho bhonn gu mullach. Mar as trice bidh am meur "leugh is tilleadh" air a chleachdadh nuair a tha beagan dàta san loidhne-phìoban. Anns a 'chùis seo, bidh sinn a' cleachdadh leugh() leugh na h-uimhir de dhàta ’s a tha ri fhaighinn a’ tòiseachadh bhon fhear a th’ ann an-dràsta f_offset leugh, agus an uairsin ùraich luach an chothromachadh co-fhreagarrach.

Air leughaidhean às deidh sin, bidh an loidhne-phìoban falamh ma tha an tomhas leughaidh air ruighinn i_size1 aig an inode. Bidh sinn ag ath-shuidheachadh an t-suidheachaidh gu 0 agus a’ feuchainn ri pròiseas sam bith a dhùsgadh a tha airson sgrìobhadh chun loidhne-phìoban. Tha fios againn nuair a bhios an inneal-giùlain làn, writep() tuiteam na chadal ip+1. Agus a-nis gu bheil an loidhne-phìoban falamh, is urrainn dhuinn a dhùsgadh gus a chearcall sgrìobhaidh ath-thòiseachadh.

Mura h-eil dad ri leughadh, an uairsin readp() urrainn bratach a shuidheachadh IREAD agus tuit 'na chadal ip+2. Tha fios againn dè a dhùisgeas e writep()nuair a sgrìobhas e beagan dàta chun loidhne-phìoban.

Beachdan air leugh () agus sgrìobh () cuidichidh e thu le bhith a’ tuigsinn sin an àite a bhith a’ dol seachad air paramadairean tro"u» is urrainn dhuinn an làimhseachadh mar ghnìomhan I/O cunbhalach a bhios a’ gabhail faidhle, suidheachadh, bufair mar chuimhneachan, agus a’ cunntadh àireamh nam bytes ri leughadh no sgrìobhadh.

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

A thaobh bacadh "glèidhidh", mar sin readp() и writep() glas inodes gus an crìochnaich iad no gus am faigh iad toradh (ie call wakeup). plock() и prele() obraich gu sìmplidh: a’ cleachdadh seata gairmean eadar-dhealaichte sleep и wakeup leig leinn pròiseas sam bith a dhùsgadh a dh’ fheumas a’ ghlas a tha sinn dìreach air a leigeil ma sgaoil:

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

An toiseach cha robh mi a 'tuigsinn carson readp() chan eil ag adhbhrachadh prele(ip) ron ghairm wakeup(ip+1). A' chiad rud writep() glaodh na lùib, seo plock(ip), a thig gu crìch ma tha readp() chan eil e air a bhloc a thoirt air falbh fhathast, agus mar sin feumaidh an còd obrachadh ceart dòigh air choireigin. Ma sheallas tu air wakeup(), bidh e soilleir gu bheil e a-mhàin a 'comharrachadh a' phròiseas cadail mar deiseil airson a chur gu bàs, mar sin san àm ri teachd sched() dha-rìribh a chuir air bhog e. Mar sin readp() adhbharan wakeup(), fuasgladh, suidhich IREAD agus gairmean sleep(ip+2)- seo uile roimhe writep() ath-thòisich an cearcall.

Tha seo a’ cur crìoch air an tuairisgeul air pìoban san t-siathamh deasachadh. Còd sìmplidh, buaidhean farsaing.

An t-seachdamh deasachadh Unix (Faoilleach 1979) na fhoillseachadh mòr ùr (ceithir bliadhna às deidh sin) a thug a-steach mòran thagraidhean ùra agus feartan kernel. Tha e cuideachd air a dhol tro atharrachaidhean mòra co-cheangailte ri cleachdadh seòrsa tilgeadh, aonaidhean agus comharran clò-bhuailte air structaran. Ge-tà còd pìoban cha mhòr nach do dh'atharraich. Faodaidh sinn an deasachadh seo a sheachnadh.

Xv6, kernel sìmplidh coltach ri Unix

Gus niuclas a chruthachadh Xv6 fo bhuaidh an t-siathamh deasachadh de Unix, ach sgrìobhte ann an C ùr-nodha gus ruith air pròiseasairean x86. Tha an còd furasta a leughadh agus furasta a thuigsinn. Cuideachd, eu-coltach ri stòran Unix le TUHS, faodaidh tu a chuir ri chèile, atharrachadh, agus a ruith air rudeigin a bharrachd air PDP 11/70. Mar sin, tha an cridhe seo air a chleachdadh gu farsaing ann an oilthighean mar stuth teagaisg air siostaman obrachaidh. Stòran tha air Github.

Tha buileachadh soilleir agus smaoineachail anns a’ chòd pìob.c, le taic bho bufair mar chuimhneachan an àite inode air diosc. An seo chan eil mi a’ toirt seachad ach am mìneachadh air “loidhne-phìoban structarail” agus an gnìomh 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() a 'suidheachadh staid a' chòrr den bhuileachadh, a tha a 'gabhail a-steach gnìomhan piperead(), pipewrite() и pipeclose(). Dìreach gairm an t-siostam sys_pipe tha e na phasgan air a chuir an gnìomh sysfile.c. Tha mi a’ moladh a chòd gu lèir a leughadh. Tha an iom-fhillteachd aig ìre còd stòr an t-siathamh deasachadh, ach tha e tòrr nas fhasa agus nas tlachdmhoire a leughadh.

Linux 0.01

Gheibh thu an còd tùsail airson Linux 0.01. Bidh e feumail sgrùdadh a dhèanamh air buileachadh nan loidhnichean-phìoban na fs/pipe.c. An seo, thathas a' cleachdadh inode airson an loidhne-phìoban a riochdachadh, ach tha an loidhne-phìoban fhèin sgrìobhte ann an C an latha an-diugh. Ma tha thu air do shlighe tron ​​​​chòd siathamh deasachadh, cha bhith duilgheadas sam bith agad an seo. Seo mar a tha an gnìomh coltach 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;
}

Fiù ‘s gun a bhith a’ coimhead air na mìneachaidhean structarail, faodaidh tu obrachadh a-mach mar a tha an cunntas iomraidh inode air a chleachdadh gus faighinn a-mach a bheil gnìomhachd sgrìobhaidh a’ leantainn SIGPIPE. A bharrachd air obair byte-by-byte, tha an gnìomh seo furasta a choimeas ris na beachdan gu h-àrd. Fiù 's loidsig sleep_on/wake_up chan eil e a’ coimhead cho coimheach.

Kernels Linux ùr-nodha, FreeBSD, NetBSD, OpenBSD

Chaidh mi gu sgiobalta thairis air cuid de kernels an latha an-diugh. Chan eil buileachadh stèidhichte air diosc aig gin dhiubh mu thràth (chan eil e na iongnadh). Tha a bhuileachadh fhèin aig Linux. Agus ged a tha buileachadh anns na trì kernels BSD ùr-nodha stèidhichte air còd a chaidh a sgrìobhadh le John Dyson, thar nam bliadhnaichean tha iad air fàs ro eadar-dhealaichte bho chèile.

A leughadh fs/pipe.c (air Linux) no sys/kern/sys_pipe.c (air * BSD), feumaidh e fìor dhealas. Tha coileanadh agus taic airson feartan leithid vectar agus I/O asyncronach cudromach ann an còd an-diugh. Agus tha mion-fhiosrachadh mu riarachadh cuimhne, glasan, agus rèiteachadh kernel uile ag atharrachadh gu mòr. Chan e seo a dh’ fheumas oilthighean airson cùrsa tòiseachaidh air siostaman obrachaidh.

Ann an suidheachadh sam bith, bha e inntinneach dhomh beagan seann phàtranan a lorg (mar eisimpleir, gineadh SIGPIPE agus tilleadh EPIPE nuair a bhios tu a’ sgrìobhadh gu loidhne-phìoban dùinte) anns na kernels sin uile, cho eadar-dhealaichte, ùr-nodha. Is dòcha nach fhaic mi coimpiutair PDP-11 beò gu bràth, ach tha tòrr ri ionnsachadh fhathast bhon chòd a chaidh a sgrìobhadh beagan bhliadhnaichean mus do rugadh mi.

Air a sgrìobhadh le Divi Kapoor ann an 2011, tha an artaigil "Cur an gnìomh Linux Kernel de phìoban agus FIFOnna shealladh farsaing air mar a tha pìoban Linux (gu ruige seo) ag obair. A gealltanas o chionn ghoirid air linux a’ nochdadh a’ mhodail eadar-obrachaidh loidhne-phìoban, aig a bheil comasan nas àirde na comas faidhlichean sealach; agus tha e cuideachd a 'sealltainn dè cho fada' sa tha pìoban air a dhol bho "glasadh glè ghlèidhidh" anns an t-siathamh deasachadh Unix kernel.

Source: www.habr.com

Cuir beachd ann