Kif jiġu implimentati l-pipelines f'Unix

Kif jiġu implimentati l-pipelines f'Unix
Dan l-artikolu jiddeskrivi l-implimentazzjoni tal-pipelines fil-kernel Unix. Kont kemmxejn diżappuntat li artiklu riċenti bit-titlu "Kif jaħdmu l-pipelines f'Unix?" irriżulta ebda dwar l-istruttura interna. Sibt kurżità u ħaffer sorsi qodma biex insib it-tweġiba.

Fuq xiex qed nitkellmu?

Il-pipelines huma "probabbilment l-aktar invenzjoni importanti f'Unix" - karatteristika li tiddefinixxi l-filosofija sottostanti ta 'Unix li tgħaqqad programmi żgħar, u l-islogan familjari tal-linja tal-kmand:

$ echo hello | wc -c
6

Din il-funzjonalità tiddependi fuq is-sejħa tas-sistema pprovduta mill-kernel pipe, li hija deskritta fuq il-paġni tad-dokumentazzjoni pajp(7) и pajp(2):

Il-pipelines jipprovdu kanal f'direzzjoni waħda għall-komunikazzjoni bejn il-proċessi. Il-pipeline għandu input (tarf tal-kitba) u output (tarf tal-qari). Id-dejta miktuba fl-input tal-pipeline tista 'tinqara fl-output.

Il-pipeline jinħoloq billi ċċempel pipe(2), li jirritorna żewġ deskritturi tal-fajl: wieħed jirreferi għall-input tal-pipeline, it-tieni għall-output.

L-output ta 'traċċa mill-kmand ta' hawn fuq juri l-ħolqien ta 'pipeline u l-fluss ta' data minnu minn proċess għal ieħor:

$ 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

Il-proċess ġenitur isejjaħ pipe()biex tikseb deskritturi tal-fajl mehmuża. Proċess tifel wieħed jikteb lil deskrittur wieħed u proċess ieħor jaqra l-istess data minn deskrittur ieħor. Il-qoxra "ssemmi" id-deskritturi 2 u 3 b'dup4 biex tqabbel stdin u stdout.

Mingħajr pipelines, il-qoxra jkollha tikteb l-output ta 'proċess wieħed għal fajl u pajpha għal proċess ieħor biex taqra d-dejta mill-fajl. Bħala riżultat, aħna se naħlu aktar riżorsi u spazju fuq disk. Madankollu, il-pipelines huma tajbin għal aktar milli sempliċement jevitaw fajls temporanji:

Jekk proċess jipprova jaqra minn pipeline vojt, allura read(2) se jimblokka sakemm id-dejta tkun disponibbli. Jekk proċess jipprova jikteb lil pipeline sħiħ, allura write(2) se jimblokka sakemm tkun inqrat biżżejjed dejta mill-pipeline biex tlesti l-kitba.

Bħar-rekwiżit POSIX, din hija proprjetà importanti: kitba lill-pipeline sa PIPE_BUF bytes (mill-inqas 512) għandhom ikunu atomiċi sabiex il-proċessi jkunu jistgħu jikkomunikaw ma 'xulxin permezz tal-pipeline b'mod li fajls normali (li ma jipprovdux garanziji bħal dawn) ma jistgħux.

B'fajl regolari, proċess jista 'jikteb l-output kollu tiegħu u jgħaddih għal proċess ieħor. Jew proċessi jistgħu joperaw f'mod parallel iebes, bl-użu ta 'mekkaniżmu ta' sinjalar estern (bħal semaforu) biex jinfurmaw lil xulxin dwar it-tlestija ta 'kitba jew qari. Il-conveyors isalvana minn din il-battikata kollha.

X'qed infittxu?

Se nispjega fuq swaba tiegħi biex tagħmilha aktar faċli għalik li timmaġina kif conveyor jista 'jaħdem. Ikollok bżonn talloka buffer u xi stat fil-memorja. Ikollok bżonn funzjonijiet biex iżżid u tneħħi d-data mill-buffer. Ikollok bżonn xi faċilità biex issejjaħ funzjonijiet waqt operazzjonijiet ta' qari u tikteb fuq deskritturi tal-fajls. U serraturi huma meħtieġa biex tiġi implimentata l-imġieba speċjali deskritta hawn fuq.

Aħna issa lesti li ninterrogaw il-kodiċi tas-sors tal-qalba taħt dawl qawwi tal-lampa biex nikkonfermaw jew innegaw il-mudell mentali vag tagħna. Imma dejjem kun ippreparat għal dak li mhux mistenni.

Fejn qed infittxu?

Ma nafx fejn tinsab il-kopja tiegħi tal-ktieb famuż.Ktieb iljuni« bil-kodiċi sors Unix 6, iżda grazzi għal Is-Soċjetà tal-Patrimonju Unix jistgħu jiġu mfittxija online kodiċi tas-sors anke verżjonijiet eqdem ta' Unix.

Iddur fl-arkivji tat-TUHS huwa bħal li jżuru mużew. Nistgħu nħarsu lejn l-istorja kondiviża tagħna u għandi rispett għas-snin ta' sforz biex nirkupra dan il-materjal kollu ftit ftit minn cassettes u stampi qodma. U jien konxju sew ta’ dawk il-frammenti li għadhom neqsin.

Wara li ssodisfajna l-kurżità tagħna dwar l-istorja antika tal-pipelines, nistgħu nħarsu lejn qlub moderni għal tqabbil.

Mill-mod, pipe hija n-numru tas-sejħa tas-sistema 42 fit-tabella sysent[]. Kumbinazzjoni?

Qlub Unix tradizzjonali (1970–1974)

Ma sibt l-ebda traċċa pipe(2) la fi PDP-7 Unix (Jannar 1970), u lanqas fi l-ewwel edizzjoni Unix (Novembru 1971), u lanqas f'kodiċi sors mhux komplut it-tieni edizzjoni (Ġunju 1972).

TUHS issostni li it-tielet edizzjoni Unix (Frar 1973) kienet l-ewwel verżjoni b'pipelines:

It-tielet edizzjoni ta 'Unix kienet l-aħħar verżjoni b'kernel miktub f'assembler, iżda wkoll l-ewwel verżjoni b'pipelines. Matul l-1973, kien għaddej xogħol biex it-tielet edizzjoni titjieb, il-qalba nkiteb mill-ġdid b’Ċ, u b’hekk twieldet ir-raba’ edizzjoni ta’ Unix.

Qarrej wieħed sab skan ta 'dokument li fih Doug McIlroy ippropona l-idea ta' "programmi ta 'konnessjoni bħal pajp tal-ġnien."

Kif jiġu implimentati l-pipelines f'Unix
Fil-ktieb ta’ Brian KernighanUnix: Storja u Memoir”, l-istorja tad-dehra tal-conveyors issemmi wkoll dan id-dokument: “... imdendel mal-ħajt fl-uffiċċju tiegħi fil-Bell Labs għal 30 sena.” Hawn intervista ma McIlroyu storja oħra minn Ix-xogħol ta’ McIlroy, miktub fl-2014:

Meta deher Unix, il-passjoni tiegħi għal coroutines għamlitni nistaqsi lill-awtur tal-OS, Ken Thompson, biex jippermetti li d-dejta miktuba għal xi proċess tmur mhux biss għall-apparat, iżda wkoll għall-ħruġ għal proċess ieħor. Ken ħaseb li kien possibbli. Madankollu, bħala minimalist, ried li kull karatteristika tas-sistema jkollha rwol sinifikanti. Il-kitba diretta bejn il-proċessi hija verament vantaġġ kbir fuq il-kitba f'fajl intermedju? U biss meta għamilt proposta speċifika bl-isem catchy "pipeline" u deskrizzjoni tas-sintassi tal-interazzjoni tal-proċessi, Ken fl-aħħar exclaimed: "Se nagħmel dan!".

U għamel. Filgħaxija waħda fatali, Ken biddel il-qalba u l-qoxra, iffissa diversi programmi standard biex jistandardizzaw kif jaċċettaw input (li jistgħu jiġu minn pipeline), u biddel l-ismijiet tal-fajls. L-għada, il-pipelines kienu użati ħafna fl-applikazzjonijiet. Sal-aħħar tal-ġimgħa, is-segretarji użawhom biex jibagħtu dokumenti minn word processors lill-istampatur. Ftit aktar tard, Ken issostitwixxa l-API u s-sintassi oriġinali għat-tgeżwir tal-użu ta 'pipelines b'konvenzjonijiet aktar nodfa li ilhom jintużaw minn dakinhar.

Sfortunatament, il-kodiċi tas-sors għat-tielet edizzjoni tal-qalba Unix intilef. U għalkemm għandna l-kodiċi tas-sors tal-kernel miktub f'C ir-raba’ edizzjoni, li ġie rilaxxat f'Novembru 1973, iżda ħareġ ftit xhur qabel ir-rilaxx uffiċjali u ma fihx l-implimentazzjoni ta 'pipelines. Hija ħasra li l-kodiċi tas-sors għal din il-karatteristika leġġendarja Unix jintilef, forsi għal dejjem.

Għandna test tad-dokumentazzjoni għal pipe(2) miż-żewġ rilaxxi, sabiex tkun tista' tibda billi tfittex id-dokumentazzjoni it-tielet edizzjoni (għal ċerti kliem, issottolinjat "manwalment", sensiela ta' ^H letterali segwiti minn linja ta' taħt!). Dan il-proto-pipe(2) huwa miktub f'assembler u jirritorna deskrittur tal-fajl wieħed biss, iżda diġà jipprovdi l-funzjonalità ewlenija mistennija:

Sejħa tas-sistema pajp joħloq mekkaniżmu I/O imsejjaħ pipeline. Id-deskrittur tal-fajl ritornat jista' jintuża għal operazzjonijiet ta' qari u kitba. Meta xi ħaġa tkun miktuba fil-pipeline, hija buffers sa 504 bytes ta 'data, wara li l-proċess tal-kitba jiġi sospiż. Meta tinqara mill-pipeline, tittieħed id-data buffered.

Sas-sena ta’ wara, il-qalba kienet ġiet miktuba mill-ġdid f’Ċ, u pajp(2) ir-raba’ edizzjoni kiseb id-dehra moderna tiegħu bil-prototip "pipe(fildes)"

Sejħa tas-sistema pajp joħloq mekkaniżmu I/O imsejjaħ pipeline. Id-deskritturi tal-fajl ritornati jistgħu jintużaw f'operazzjonijiet ta' qari u kitba. Meta xi ħaġa tinkiteb fil-pipeline, id-deskrittur ritornat f'r1 (resp. fildes[1]) jintuża, buffered sa 4096 bytes ta 'data, u wara l-proċess tal-kitba jiġi sospiż. Meta jaqra mill-pipeline, id-deskrittur ritornat għal r0 (resp. fildes[0]) jieħu d-dejta.

Huwa preżunt li ladarba pipeline ikun ġie definit, żewġ (jew aktar) proċessi ta’ interazzjoni (maħluqa minn invokazzjonijiet sussegwenti furketta) se jgħaddi data mill-pipeline billi juża sejħiet taqra и jiktbu.

Il-qoxra għandha sintassi għad-definizzjoni ta 'firxa lineari ta' proċessi konnessi permezz ta 'pipeline.

Sejħiet għall-qari minn pipeline vojt (li ma fih l-ebda dejta buffered) li għandu tarf wieħed biss (id-deskritturi tal-fajl tal-kitba kollha magħluqa) jirritorna "tmiem il-fajl". Sejħiet tal-kitba f'sitwazzjoni simili huma injorati.

L-aktar kmieni implimentazzjoni tal-pipeline ippreservata japplika għall-ħames edizzjoni ta’ Unix (Ġunju 1974), iżda huwa kważi identiku għal dak li deher fir-rilaxx li jmiss. Kummenti miżjuda biss, għalhekk il-ħames edizzjoni tista' tinqabeż.

Is-Sitt Edizzjoni Unix (1975)

Tibda taqra l-kodiċi tas-sors Unix is-sitt edizzjoni (Mejju 1975). L-aktar grazzi għal Iljuni huwa ħafna aktar faċli li ssib mis-sorsi ta 'verżjonijiet preċedenti:

Għal ħafna snin il-ktieb Iljuni kien l-uniku dokument fuq il-kernel Unix disponibbli barra mill-Laboratorji Bell. Għalkemm il-liċenzja tas-sitt edizzjoni ppermettiet lill-għalliema jużaw il-kodiċi tas-sors tagħha, is-seba’ liċenzja tal-edizzjoni eskludiet din il-possibbiltà, u għalhekk il-ktieb tqassam f’kopji illegali miktubin it-tajpa.

Illum tista 'tixtri kopja stampata mill-ġdid tal-ktieb, li l-qoxra tiegħu turi studenti fil-kopjatur. U grazzi għal Warren Toomey (li beda l-proġett TUHS), tista' tniżżel Is-Sitt Edizzjoni Sors PDF. Irrid nagħtik idea ta' kemm sar sforz biex jinħoloq il-fajl:

Aktar minn 15-il sena ilu, dtajt kopja tal-kodiċi tas-sors ipprovdut fih Iljunigħax ma għoġobnix il-kwalità tal-kopja tiegħi minn numru mhux magħruf ta' kopji oħra. It-TUHS kien għadu ma jeżistix, u ma kellix aċċess għas-sorsi qodma. Imma fl-1988 sibt tejp antik b'9 binarji li kellu backup minn kompjuter PDP11. Kien diffiċli li tkun taf jekk ħadmitx, iżda kien hemm siġra /usr/src/ intatta li fiha ħafna mill-fajls kienu mmarkati 1979, li anke dakinhar deher antika. Kienet is-seba' edizzjoni, jew derivattiv tal-PWB, ħsibt.

Ħadt is-sejba bħala bażi u editjat manwalment is-sorsi għall-istat tas-sitt edizzjoni. Parti mill-kodiċi baqgħet l-istess, parti kellha tiġi editjata kemmxejn, billi tbiddel it-token modern += għal =+ skadut. Xi ħaġa kienet sempliċement imħassra, u xi ħaġa kellha tiġi miktuba mill-ġdid kompletament, iżda mhux wisq.

U llum nistgħu naqraw online fuq TUHS il-kodiċi sors tas-sitt edizzjoni ta arkivju, li Dennis Ritchie kellu daqqa t’id.

Mill-mod, mal-ewwel daqqa t'għajn, il-karatteristika ewlenija tal-kodiċi C qabel il-perjodu ta 'Kernighan u Ritchie hija tagħha qosor. Mhux ta’ spiss inkun nista’ ndaħħal siltiet ta’ kodiċi mingħajr editjar estensiv biex jitwaħħal żona tal-wiri relattivament dejqa fuq is-sit tiegħi.

Kmieni /usr/sys/ken/pipe.c hemm kumment spjegattiv (u iva, hemm aktar /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

Id-daqs tal-buffer ma nbidilx mir-raba' edizzjoni. Imma hawn naraw, mingħajr ebda dokumentazzjoni pubblika, li l-pipelines darba użaw fajls bħala ħażna ta 'wara!

Fir-rigward tal-fajls LARG, jikkorrispondu għal inode-bandiera LARG, li hija użata mill-"algoritmu ta 'indirizzar kbir" biex tipproċessa blokki indiretti biex tappoġġja sistemi ta 'fajls akbar. Peress li Ken qal li aħjar ma nużawhomx, jien kuntent nieħu kelmtu għalih.

Hawnhekk hija s-sejħa tas-sistema reali 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;
}

Il-kumment jiddeskrivi b'mod ċar dak li qed jiġri hawn. Imma mhux daqshekk faċli li tifhem il-kodiċi, parzjalment minħabba kif "struct user u» u reġistri R0 и R1 il-parametri tas-sejħa tas-sistema u l-valuri tar-ritorn huma mgħoddija.

Ejja nippruvaw bih ialloc() poġġi fuq disk inode (inode), u bl-għajnuna falloc() - aħżen tnejn fajl. Jekk kollox imur tajjeb, aħna ser nissettjaw bnadar biex jidentifikaw dawn il-fajls bħala ż-żewġt itruf tal-pipeline, nippuntawhom lejn l-istess inode (li l-għadd ta 'referenza tiegħu jsir 2), u jimmarka l-inode bħala modifikat u qed jintuża. Oqgħod attent għat-talbiet biex iput() fil-mogħdijiet ta 'żball biex jitnaqqas l-għadd ta' referenza fl-inode l-ġdid.

pipe() dovut permezz R0 и R1 ritorn numri tad-deskritturi tal-fajl għall-qari u l-kitba. falloc() jirritorna pointer għal struttura ta 'fajl, iżda wkoll "ritorni" permezz u.u_ar0[R0] u deskrittur tal-fajl. Jiġifieri, il-kodiċi huwa maħżun r deskrittur tal-fajl għall-qari u jassenja deskrittur għall-kitba direttament minn u.u_ar0[R0] wara t-tieni sejħa falloc().

Bandiera FPIPE, li aħna waqqafna meta noħolqu l-pipeline, jikkontrolla l-imġieba tal-funzjoni rdwr() f'sys2.c, li jsejjaħ rutini speċifiċi I/O:

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

Imbagħad il-funzjoni readp() в pipe.c jaqra data mill-pipeline. Iżda huwa aħjar li tiġi rintraċċata l-implimentazzjoni li tibda minn writep(). Għal darb'oħra, il-kodiċi sar aktar ikkumplikat minħabba n-natura tal-konvenzjoni li tgħaddi l-argument, iżda xi dettalji jistgħu jitħallew barra.

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

Irridu niktbu bytes għall-input tal-pipeline u.u_count. L-ewwel għandna bżonn nissakkar l-inode (ara hawn taħt plock/prele).

Imbagħad aħna niċċekkjaw l-għadd ta 'referenza inode. Sakemm iż-żewġt itruf tal-pipeline jibqgħu miftuħa, il-counter għandu jkun 2. Aħna nżommu rabta waħda (minn rp->f_inode), allura jekk il-counter huwa inqas minn 2, allura dan għandu jfisser li l-proċess tal-qari għalaq it-tarf tiegħu tal-pipeline. Fi kliem ieħor, qed nippruvaw niktbu lil pipeline magħluq, li huwa żball. L-ewwel kodiċi ta 'żball EPIPE u sinjal SIGPIPE deher fis-sitt edizzjoni ta' Unix.

Imma anke jekk il-conveyor ikun miftuħ, jista 'jkun sħiħ. F'dan il-każ, aħna nirrilaxxaw is-serratura u mmorru torqod bit-tama li proċess ieħor jaqra mill-pipeline u jillibera biżżejjed spazju fih. Meta nqumu, nerġgħu nerġgħu lura għall-bidu, erġa' ndendel is-serratura u nibdew ċiklu ġdid ta 'kitba.

Jekk ikun hemm biżżejjed spazju ħieles fil-pipeline, allura niktbu d-data lilha bl-użu iktebi(). Parametru i_size1 l-inode'a (b'pipeline vojta tista' tkun ugwali għal 0) tipponta lejn it-tmiem tad-dejta li diġà fiha. Jekk hemm biżżejjed spazju biex tikteb, nistgħu nimlew il-pipeline minn i_size1 li PIPESIZ. Imbagħad nirrilaxxaw is-serratura u nippruvaw inqumu kwalunkwe proċess li qed jistenna li jinqara mill-pipeline. Immorru lura għall-bidu biex naraw jekk irnexxielniex niktbu bytes kemm kellna bżonn. Jekk jonqos, allura nibdew ċiklu ta 'reġistrazzjoni ġdid.

Normalment parametru i_mode inode jintuża biex jaħżen il-permessi r, w и x. Iżda fil-każ ta 'pipelines, aħna sinjal li xi proċess qed jistenna għal kitba jew qari bl-użu ta' bits IREAD и IWRITE rispettivament. Il-proċess jistabbilixxi l-bandiera u jsejjaħ sleep(), u huwa mistenni li fil-futur se jsejjaħ xi proċess ieħor wakeup().

Il-maġija reali sseħħ fi sleep() и wakeup(). Huma implimentati fi slp.c, is-sors tal-famuż kumment "M'intix mistenni li tifhem dan". Fortunatament, m'għandniex għalfejn nifhmu l-kodiċi, ara biss xi kummenti:

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

Il-proċess li jitlob sleep() għal kanal partikolari, jistgħu aktar tard jiġu woken up minn proċess ieħor, li se sejħa wakeup() għall-istess kanal. writep() и readp() jikkoordinaw l-azzjonijiet tagħhom permezz ta' sejħiet paired bħal dawn. innota li pipe.c dejjem tagħti prijorità PPIPE meta tissejjaħ sleep(), hekk kollha sleep() jista' jiġi interrott b'sinjal.

Issa għandna kollox biex nifhmu l-funzjoni 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);
}

Tista' ssibha aktar faċli biex taqra din il-funzjoni minn isfel għal fuq. Il-fergħa "aqra u rritorna" normalment tintuża meta jkun hemm xi dejta fil-pipeline. F'dan il-każ, nużaw aqra() aqra data kemm hija disponibbli li tibda minn dik attwali f_offset aqra, u mbagħad aġġorna l-valur tal-offset korrispondenti.

Fuq qari sussegwenti, il-pipeline se jkun vojt jekk il-qari offset ikun laħaq i_size1 fl-inode. Aħna reset il-pożizzjoni għal 0 u nippruvaw inqumu kwalunkwe proċess li jrid jikteb lill-pipeline. Nafu li meta l-conveyor ikun mimli, writep() torqod fuq ip+1. U issa li l-pipeline huwa vojt, nistgħu nqajmuh biex jerġa 'jibda ċ-ċiklu tal-kitba tiegħu.

Jekk m'hemm xejn x'jaqra, allura readp() jista' jistabbilixxi bandiera IREAD u torqod fuq ip+2. Nafu x’se jqajmuh writep()meta jikteb xi data lill-pipeline.

Kummenti fuq aqra() u iktebi() jgħinek tifhem li minflok tgħaddi l-parametri minn "u» Nistgħu nittrattawhom bħal funzjonijiet I/O regolari li jieħdu fajl, pożizzjoni, buffer fil-memorja, u jgħoddu n-numru ta 'bytes biex jaqraw jew jiktbu.

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

Fir-rigward tal-imblukkar "konservattiv", allura readp() и writep() lock inodes sakemm jispiċċaw jew jiksbu riżultat (jiġifieri sejħa wakeup). plock() и prele() taħdem sempliċiment: tuża sett differenti ta' sejħiet sleep и wakeup ineħħu kwalunkwe proċess li jeħtieġ is-serratura li għadna kemm ħriġna:

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

Għall-ewwel ma stajtx nifhem għaliex readp() ma jikkawżax prele(ip) qabel is-sejħa wakeup(ip+1). L-ewwel ħaġa writep() jitlob fil-linja tagħha, dan plock(ip), li jirriżulta f'imblokk jekk readp() għadu ma neħħiex il-blokk tiegħu, għalhekk il-kodiċi b'xi mod irid jaħdem b'mod korrett. Jekk tħares lejn wakeup(), jidher ċar li jimmarka biss il-proċess ta 'rqad bħala lest għall-eżekuzzjoni, sabiex fil-futur sched() verament nedietha. Allura readp() kawżi wakeup(), tiftaħ, settijiet IREAD u sejħiet sleep(ip+2)- dan kollu qabel writep() jerġa’ jibda ċ-ċiklu.

Dan jikkompleta d-deskrizzjoni tal-pipelines fis-sitt edizzjoni. Kodiċi sempliċi, implikazzjonijiet estensivi.

Is-Seba’ Edizzjoni Unix (Jannar 1979) kien rilaxx ewlieni ġdid (erba 'snin wara) li introduċiet ħafna applikazzjonijiet ġodda u karatteristiċi tal-qalba. Hija għaddiet ukoll minn bidliet sinifikanti b'rabta mal-użu ta 'ikkastjar tat-tip, unions u pointers ittajpjati għal strutturi. Madankollu kodiċi tal-pipelines prattikament ma nbidlitx. Nistgħu naqbżu din l-edizzjoni.

Xv6, għadma sempliċi bħal Unix

Biex toħloq qalba Xv6 influwenzat mis-sitt edizzjoni ta' Unix, iżda miktuba f'C modern biex jaħdem fuq proċessuri x86. Il-kodiċi huwa faċli biex jinqara u jinftiehem. Ukoll, b'differenza mis-sorsi Unix b'TUHS, tista 'tikkumpilaha, timmodifikaha, u tħaddem fuq xi ħaġa oħra għajr PDP 11/70. Għalhekk, din il-qalba tintuża ħafna fl-universitajiet bħala materjal ta 'tagħlim dwar is-sistemi operattivi. Sorsi huma fuq Github.

Il-kodiċi fih implimentazzjoni ċara u maħsub pajp.c, appoġġjat minn buffer fil-memorja minflok inode fuq disk. Hawnhekk nagħti biss id-definizzjoni ta '"pipeline strutturali" u l-funzjoni 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() jistabbilixxi l-istat tal-bqija kollha tal-implimentazzjoni, li tinkludi funzjonijiet piperead(), pipewrite() и pipeclose(). Is-sejħa tas-sistema attwali sys_pipe huwa wrapper implimentat fi sysfile.c. Nirrakkomanda li taqra l-kodiċi kollu tiegħu. Il-kumplessità hija fil-livell tal-kodiċi sors tas-sitt edizzjoni, iżda huwa ħafna aktar faċli u aktar pjaċevoli biex jinqara.

Linux 0.01

Tista 'ssib il-kodiċi tas-sors għal Linux 0.01. Se jkun istruttiv li tistudja l-implimentazzjoni ta 'pipelines fil tiegħu fs/pipe.c. Hawnhekk, inode jintuża biex jirrappreżenta l-pipeline, iżda l-pipeline innifsu huwa miktub f'C moderna. Jekk inti ħadthom hacked tiegħek mod permezz tal-kodiċi tas-sitt edizzjoni, inti ma jkollok ebda problemi hawn. Dan huwa kif tidher il-funzjoni 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;
}

Anke mingħajr ma tħares lejn id-definizzjonijiet tal-istruttura, tista' tara kif l-għadd ta' referenza tal-inode jintuża biex tivverifika jekk operazzjoni ta' kitba tirriżultax f' SIGPIPE. Minbarra x-xogħol byte b'byte, din il-funzjoni hija faċli biex titqabbel mal-ideat ta 'hawn fuq. Anke loġika sleep_on/wake_up ma tidhirx daqshekk aljeni.

Linux Kernels Moderni, FreeBSD, NetBSD, OpenBSD

Malajr mort fuq xi qlub moderni. L-ebda wieħed minnhom diġà għandu implimentazzjoni bbażata fuq disk (mhux sorprendenti). Linux għandu l-implimentazzjoni tiegħu stess. U għalkemm it-tliet qlub BSD moderni fihom implimentazzjonijiet ibbażati fuq kodiċi li nkiteb minn John Dyson, matul is-snin saru wisq differenti minn xulxin.

Taqra fs/pipe.c (fuq Linux) jew sys/kern/sys_pipe.c (fuq *BSD), jeħtieġ dedikazzjoni reali. Il-prestazzjoni u l-appoġġ għal karatteristiċi bħal vector u I/O asinkroniku huma importanti fil-kodiċi llum. U d-dettalji tal-allokazzjoni tal-memorja, serraturi, u konfigurazzjoni tal-qalba kollha jvarjaw ħafna. Dan mhuwiex dak li l-universitajiet jeħtieġu għal kors introduttorju dwar is-sistemi operattivi.

Fi kwalunkwe każ, kien interessanti għalija li nikxef ftit mudelli qodma (per eżempju, niġġenera SIGPIPE u lura EPIPE meta tikteb lil pipeline magħluq) f'dawn il-qlub moderni kollha, tant differenti. Probabbilment qatt mhu se nara kompjuter PDP-11 ħajjin, iżda għad hemm ħafna x'titgħallem mill-kodiċi li nkiteb ftit snin qabel ma twelidt.

Miktub minn Divi Kapoor fl-2011, l-artiklu "L-Implimentazzjoni tal-Linux Kernel ta' Pajpijiet u FIFOshija ħarsa ġenerali ta' kif jaħdmu l-pipelines tal-Linux (s'issa). A impenn riċenti fuq linux juri l-mudell tal-pipeline ta' interazzjoni, li l-kapaċitajiet tiegħu jaqbżu dawk ta' fajls temporanji; u juri wkoll kemm il-pipelines marru minn "qfil konservattiv ħafna" fis-sitt edizzjoni Unix kernel.

Sors: www.habr.com

Żid kumment