Giunsa gipatuman ang mga pipeline sa Unix

Giunsa gipatuman ang mga pipeline sa Unix
Kini nga artikulo naghulagway sa pagpatuman sa mga pipeline sa Unix kernel. Medyo nasagmuyo ako nga ang usa ka bag-o nga artikulo nga giulohan "Giunsa pagtrabaho ang mga pipeline sa Unix?»nigawas dili mahitungod sa internal nga istruktura. Nakuryuso ko ug nangita sa daan nga mga tinubdan aron makit-an ang tubag.

Unsa man ang atong gihisgutan?

Ang mga linya sa tubo mao ang "tingali ang labing hinungdanon nga imbensyon sa Unix" - usa ka piho nga bahin sa nagpahiping pilosopiya sa Unix sa paghiusa sa gagmay nga mga programa, ug ang pamilyar nga slogan sa command-line:

$ echo hello | wc -c
6

Kini nga pag-andar nagdepende sa tawag sa sistema nga gihatag sa kernel pipe, nga gihulagway sa mga panid sa dokumentasyon tubo(7) и tubo(2):

Ang mga pipeline naghatag ug one-way nga channel para sa inter-process nga komunikasyon. Ang pipeline adunay input (pagsulat sa katapusan) ug usa ka output (pagbasa sa katapusan). Ang datos nga gisulat sa input sa pipeline mabasa sa output.

Ang pipeline gihimo pinaagi sa pagtawag pipe(2), nga nagbalik sa duha ka mga deskriptor sa file: ang usa nagtumong sa input sa pipeline, ang ikaduha sa output.

Ang pagsubay sa output gikan sa sugo sa ibabaw nagpakita sa paghimo sa usa ka pipeline ug ang dagan sa datos pinaagi niini gikan sa usa ka proseso ngadto sa lain:

$ 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

Ang proseso sa ginikanan nagtawag pipe()aron makakuha og gilakip nga mga deskriptor sa file. Ang usa ka proseso sa bata nagsulat sa usa ka deskriptor ug ang lain nga proseso nagbasa sa parehas nga datos gikan sa lain nga deskriptor. Ang kabhang "nag-ngalan" sa mga deskriptor 2 ug 3 nga adunay dup4 aron ipares ang stdin ug stdout.

Kung walay mga pipeline, ang kabhang kinahanglan nga isulat ang output sa usa ka proseso ngadto sa usa ka file ug i-pipe kini ngadto sa laing proseso aron mabasa ang datos gikan sa file. Ingon usa ka sangputanan, mag-usik kami og daghang mga kapanguhaan ug espasyo sa disk. Bisan pa, ang mga pipeline labi pa sa paglikay sa temporaryo nga mga file:

Kung ang usa ka proseso mosulay sa pagbasa gikan sa usa ka walay sulod nga pipeline, nan read(2) mo-block hangtod nga magamit ang datos. Kung ang usa ka proseso mosulay sa pagsulat sa usa ka bug-os nga pipeline, nan write(2) mag-block hangtod nga nabasa ang igo nga datos gikan sa pipeline aron makompleto ang pagsulat.

Sama sa kinahanglanon sa POSIX, kini usa ka hinungdanon nga kabtangan: pagsulat sa pipeline hangtod sa PIPE_BUF bytes (labing menos 512) kinahanglang atomic aron ang mga proseso makakomunikar sa usag usa pinaagi sa pipeline sa paagi nga dili mahimo sa normal nga mga file (nga wala maghatag sa maong mga garantiya).

Uban sa usa ka regular nga file, ang usa ka proseso makasulat sa tanan nga mga output niini ug ipasa kini sa lain nga proseso. O ang mga proseso mahimong molihok sa usa ka hard parallel mode, gamit ang eksternal nga mekanismo sa pagsenyas (sama sa usa ka semaphore) aron sa pagpahibalo sa usag usa mahitungod sa pagkompleto sa usa ka pagsulat o pagbasa. Ang mga conveyor nagluwas kanamo gikan niining tanan nga kahasol.

Unsa ang atong gipangita?

Akong ipasabut sa akong mga tudlo aron mas sayon ​​​​alang kanimo ang paghanduraw kung unsa ang mahimo sa usa ka conveyor. Kinahanglan nimo nga maggahin og buffer ug pipila ka estado sa memorya. Kinahanglan nimo ang mga function aron madugang ug makuha ang data gikan sa buffer. Kinahanglan nimo ang pipila ka pasilidad sa pagtawag sa mga function sa panahon sa pagbasa ug pagsulat nga mga operasyon sa mga deskriptor sa file. Ug gikinahanglan ang mga kandado aron mapatuman ang espesyal nga kinaiya nga gihulagway sa ibabaw.

Andam na kami karon sa pagsukitsukit sa source code sa kernel ubos sa hayag nga lamplight aron kumpirmahon o mapanghimakak ang among dili klaro nga mental model. Apan pag-andam kanunay sa wala damha.

Asa ta mangita?

Wala ko kahibalo kung asa nahimutang ang akong kopya sa sikat nga libro.Libro sa mga leon«uban ang Unix 6 source code, apan salamat sa Ang Unix Heritage Society pwede pangitaon online source code bisan ang mga daan nga bersyon sa Unix.

Ang pagsuroy-suroy sa mga archive sa TUHS sama sa pagbisita sa usa ka museyo. Mahimo natong tan-awon ang among gipaambit nga kasaysayan ug ako adunay pagtahod sa mga tuig nga paningkamot aron mabawi ang tanan niini nga materyal sa hinay-hinay gikan sa daan nga mga cassette ug mga printout. Ug nahibal-an gyud nako ang mga tipik nga wala pa.

Natagbaw ang among pagkamausisaon bahin sa karaan nga kasaysayan sa mga pipeline, mahimo naton tan-awon ang mga modernong cores alang sa pagtandi.

Hinuon, pipe mao ang system call number 42 sa lamesa sysent[]. Sulagma?

Tradisyonal nga Unix kernels (1970–1974)

Wala koy nakit-an nga bakas pipe(2) ni sa PDP-7 Unix (Enero 1970), ni sa unang edisyon Unix (Nobyembre 1971), ni sa dili kompleto nga source code ikaduhang edisyon (Hunyo 1972).

Giangkon kini sa TUHS ikatulo nga edisyon Unix (Pebrero 1973) mao ang unang bersyon nga adunay mga pipeline:

Ang ikatulo nga edisyon sa Unix mao ang katapusan nga bersyon nga adunay usa ka kernel nga gisulat sa assembler, apan usab ang una nga bersyon nga adunay mga pipeline. Sa panahon sa 1973, gisugdan ang pagtrabaho aron mapaayo ang ikatulo nga edisyon, ang kernel gisulat pag-usab sa C, ug sa ingon natawo ang ikaupat nga edisyon sa Unix.

Usa ka magbabasa nakit-an ang usa ka pag-scan sa usa ka dokumento diin gisugyot ni Doug McIlroy ang ideya sa "pagkonekta sa mga programa sama sa usa ka hose sa tanaman."

Giunsa gipatuman ang mga pipeline sa Unix
Sa libro ni Brian KernighanUnix: Usa ka Kasaysayan ug Usa ka Memoir", ang kasaysayan sa pagpakita sa mga conveyor naghisgot usab niini nga dokumento: "... kini gibitay sa bungbong sa akong opisina sa Bell Labs sulod sa 30 ka tuig." Dinhi pakighinabi ni McIlroyug laing istorya gikan sa Ang trabaho ni McIlroy, gisulat kaniadtong 2014:

Sa dihang mitungha ang Unix, ang akong gugma sa mga coroutine nakapahimo kanako nga mangutana sa tagsulat sa OS, si Ken Thompson, nga tugotan ang mga datos nga gisulat sa pipila ka proseso nga moadto dili lamang sa device, kondili ngadto usab sa exit ngadto sa laing proseso. Naghunahuna si Ken nga posible. Bisan pa, ingon usa ka minimalist, gusto niya nga ang matag bahin sa sistema adunay hinungdanon nga papel. Ang direkta ba nga pagsulat tali sa mga proseso usa ka dako nga bentaha kaysa pagsulat sa usa ka intermediate file? Ug sa diha nga naghimo ako usa ka piho nga sugyot nga adunay madanihon nga ngalan nga "pipeline" ug usa ka paghulagway sa syntax sa interaksyon sa mga proseso, si Ken sa katapusan mipatugbaw: "Buhaton ko kini!".

Ug gibuhat. Usa ka makalilisang nga gabii, giusab ni Ken ang kernel ug shell, giayo ang daghang mga standard nga programa aron ma-standardize kung giunsa nila pagdawat ang input (nga mahimo’g gikan sa usa ka pipeline), ug giusab ang mga filename. Pagkasunod adlaw, ang mga pipeline kaylap nga gigamit sa mga aplikasyon. Sa kataposan sa semana, gigamit sila sa mga sekretaryo sa pagpadalag mga dokumento gikan sa mga tigproseso sa pulong ngadto sa tig-imprenta. Sa wala madugay, gipulihan ni Ken ang orihinal nga API ug syntax alang sa pagputos sa paggamit sa mga pipeline nga adunay mas limpyo nga mga kombensiyon nga gigamit sukad.

Ikasubo, ang source code alang sa ikatulo nga edisyon nga Unix kernel nawala. Ug bisan kung kami adunay kernel source code nga gisulat sa C ikaupat nga edisyon, nga gipagawas sa Nobyembre 1973, apan kini migawas pipila ka bulan sa wala pa ang opisyal nga pagpagawas ug wala maglangkob sa pagpatuman sa mga pipeline. Makaluluoy nga nawala ang source code alang niining maalamat nga bahin sa Unix, tingali hangtod sa hangtod.

Naa miy documentation text para pipe(2) gikan sa duha ka pagpagawas, aron makasugod ka pinaagi sa pagpangita sa dokumentasyon ikatulo nga edisyon (alang sa pipila ka mga pulong, gibadlisan nga "manual", usa ka hilo sa ^H literal nga gisundan ug underscore!). Kini nga proto-pipe(2) gisulat sa assembler ug nagbalik lamang sa usa ka file descriptor, apan naghatag na sa gipaabot nga core functionality:

Sistema nga tawag tubo naghimo ug mekanismo sa I/O nga gitawag ug pipeline. Ang gibalik nga deskriptor sa file mahimong magamit alang sa pagbasa ug pagsulat nga mga operasyon. Kung adunay gisulat sa pipeline, kini nag-buffer hangtod sa 504 bytes nga datos, pagkahuman gisuspinde ang proseso sa pagsulat. Kung nagbasa gikan sa pipeline, gikuha ang buffered data.

Pagkasunod tuig, ang kernel nasulat na usab sa C, ug tubo(2) ikaupat nga edisyon nakuha ang modernong hitsura niini gamit ang prototype "pipe(fildes)»:

Sistema nga tawag tubo naghimo ug mekanismo sa I/O nga gitawag ug pipeline. Ang gibalik nga mga deskriptor sa file mahimong magamit sa pagbasa ug pagsulat nga mga operasyon. Kung adunay gisulat sa pipeline, gigamit ang descriptor nga gibalik sa r1 (resp. fildes [1]), gi-buffer hangtod sa 4096 bytes nga datos, pagkahuman gisuspinde ang proseso sa pagsulat. Kung nagbasa gikan sa pipeline, ang deskriptor mibalik sa r0 (resp. fildes [0]) nagkuha sa datos.

Gituohan nga sa higayon nga ang usa ka pipeline gihubit, duha (o daghan pa) nga mga proseso sa interaksyon (gibuhat pinaagi sa sunod nga mga pag-ampo. Tinidor) ipasa ang datos gikan sa pipeline gamit ang mga tawag basaha и isulat.

Ang kabhang adunay usa ka syntax alang sa pagtino sa usa ka linear array sa mga proseso nga konektado pinaagi sa usa ka pipeline.

Ang mga tawag sa pagbasa gikan sa usa ka walay sulod nga pipeline (walay buffered data) nga adunay usa lamang ka tumoy (tanan isulat ang mga deskriptor sa file nga sirado) ibalik ang "katapusan sa file". Ang pagsulat sa mga tawag sa susamang sitwasyon wala tagda.

pinakauna gipreserbar nga pagpatuman sa pipeline nagtumong ngadto sa ikalimang edisyon sa Unix (Hunyo 1974), apan kini halos susama sa usa nga mitungha sa sunod nga pagpagawas. Gidugang lamang nga mga komento, aron ang ikalimang edisyon mahimong laktawan.

Unix Ika-unom nga Edisyon (1975)

Pagsugod sa pagbasa sa Unix source code ikaunom nga edisyon (Mayo 1975). Daghang salamat sa Mga leon mas sayon ​​nga pangitaon kay sa mga tinubdan sa naunang mga bersyon:

Sulod sa daghang mga tuig ang libro Mga leon mao lamang ang dokumento sa Unix kernel nga anaa sa gawas sa Bell Labs. Bisan tuod ang lisensya sa ikaunom nga edisyon nagtugot sa mga magtutudlo sa paggamit sa source code niini, ang ikapitong edisyon nga lisensya wala maglakip niini nga posibilidad, mao nga ang libro giapod-apod sa ilegal nga mga kopya nga gi-type.

Karon mahimo ka makapalit usa ka kopya sa libro nga gipatik pag-usab, nga ang hapin niini naghulagway sa mga estudyante sa copier. Ug salamat sa Warren Toomey (nga nagsugod sa proyekto sa TUHS), mahimo nimong i-download Ikaunom nga Edisyon Tinubdan PDF. Gusto ko nga hatagan ka usa ka ideya kung unsa ka daghang paningkamot ang gihimo sa paghimo sa file:

Kapin sa 15 ka tuig ang milabay, nag-type ko og kopya sa source code nga gihatag Mga leontungod kay dili ko ganahan sa kalidad sa akong kopya gikan sa wala mailhi nga gidaghanon sa ubang mga kopya. Wala pa ang TUHS, ug wala akoy access sa daan nga mga tinubdan. Apan niadtong 1988 nakakita kog daan nga teyp nga adunay 9 ka tracks nga dunay backup gikan sa PDP11 nga kompyuter. Lisud mahibal-an kung kini nagtrabaho, apan adunay usa ka buo nga /usr/src/ nga kahoy diin kadaghanan sa mga file gimarkahan nga 1979, nga bisan kaniadto tan-awon nga karaan. Ikapito kadto nga edisyon, o usa ka PWB derivative, sa akong hunahuna.

Gikuha nako ang pagpangita isip basehan ug gi-edit sa mano-mano ang mga tinubdan ngadto sa estado sa ikaunom nga edisyon. Ang bahin sa kodigo nagpabilin nga pareho, ang bahin kinahanglan nga usbon gamay, usbon ang modernong token += ngadto sa karaan nga =+. Adunay yano nga gitangtang, ug adunay kinahanglan nga hingpit nga isulat pag-usab, apan dili sobra.

Ug karon atong mabasa online sa TUHS ang source code sa ikaunom nga edisyon sa archive, diin si Dennis Ritchie adunay kamot.

Pinaagi sa dalan, sa unang pagtan-aw, ang nag-unang bahin sa C-code sa wala pa ang panahon sa Kernighan ug Ritchie mao ang kamubo. Dili kasagaran nga ako makahimo sa pagsal-ot sa mga snippet sa code nga walay halapad nga pag-edit aron mohaum sa medyo pig-ot nga display nga lugar sa akong site.

Sa sinugdanan /usr/sys/ken/pipe.c adunay usa ka pagpatin-aw nga komento (ug oo, adunay daghan pa /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

Ang gidak-on sa buffer wala mausab sukad sa ikaupat nga edisyon. Apan dinhi atong makita, nga walay bisan unsa nga publikong dokumentasyon, nga ang mga pipeline kaniadto migamit sa mga file isip fallback storage!

Sama sa alang sa LARG nga mga file, sila katumbas sa inode-flag LARG, nga gigamit sa "dako nga addressing algorithm" sa pagproseso dili direkta nga mga bloke sa pagsuporta sa mas dagkong mga sistema sa file. Kay niingon si Ken nga mas maayo nga dili na lang sila gamiton, nalipay ko sa pagdawat sa iyang pulong.

Ania ang tinuod nga tawag sa sistema 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;
}

Ang komento tin-aw nga naghulagway kung unsa ang nahitabo dinhi. Apan dili sayon ​​​​sabton ang kodigo, bahin tungod sa kung giunsa "struct user u»ug mga rehistro R0 и R1 gipasa ang mga parameter sa tawag sa sistema ug mga kantidad sa pagbalik.

Atong sulayan uban ialloc() ibutang sa disk inode (inode), ug uban sa tabang falloc() - tindahan duha file. Kung maayo ang tanan, magbutang kami og mga bandila aron mailhan kini nga mga file ingon nga duha ka tumoy sa pipeline, itudlo kini sa parehas nga inode (kansang ihap sa reference mahimong 2), ug markahan ang inode nga giusab ug gigamit. Hatagi'g pagtagad ang mga hangyo sa iput() sa sayop nga mga dalan sa pagkunhod sa reference count sa bag-ong inode.

pipe() tungod pinaagi sa R0 и R1 ibalik ang mga numero sa deskriptor sa file alang sa pagbasa ug pagsulat. falloc() nagbalik sa usa ka pointer sa usa ka istruktura sa file, apan usab "mibalik" pinaagi sa u.u_ar0[R0] ug usa ka file descriptor. Kana mao, ang code gitipigan sa r file descriptor para sa pagbasa ug assign sa usa ka descriptor alang sa pagsulat direkta gikan sa u.u_ar0[R0] pagkahuman sa ikaduhang tawag falloc().

Flag FPIPE, nga among gibutang sa paghimo sa pipeline, nagkontrol sa kinaiya sa function rdwr() sa sys2.c, nga nagtawag sa piho nga I / O nga rutina:

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

Unya ang function readp() в pipe.c nagbasa sa datos gikan sa pipeline. Apan mas maayo nga masubay ang pagpatuman sugod sa writep(). Sa makausa pa, ang kodigo nahimong mas komplikado tungod sa kinaiya sa argumento nga nagpasa sa kombensiyon, apan ang pipila ka mga detalye mahimong matangtang.

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

Gusto namon nga isulat ang mga byte sa input sa pipeline u.u_count. Una kinahanglan natong i-lock ang inode (tan-awa sa ubos plock/prele).

Dayon among susihon ang ihap sa reference sa inode. Hangtud nga ang duha ka tumoy sa pipeline magpabilin nga bukas, ang counter kinahanglan nga 2. Nagkupot kami sa usa ka link (gikan sa rp->f_inode), busa kung ang counter dili mubu sa 2, nan kini nagpasabut nga ang proseso sa pagbasa nagsira sa katapusan sa pipeline. Sa laing pagkasulti, naningkamot kami sa pagsulat sa usa ka sirado nga pipeline, nga usa ka sayup. Unang error code EPIPE ug signal SIGPIPE nagpakita sa ikaunom nga edisyon sa Unix.

Apan bisan kung bukas ang conveyor, mahimo kini puno. Sa kini nga kaso, gibuhian namo ang kandado ug matulog sa paglaum nga ang laing proseso magbasa gikan sa pipeline ug magpagawas og igo nga luna niini. Inigmata nato, mobalik kita sa sinugdanan, ibitay pag-usab ang kandado ug magsugod og bag-ong siklo sa pagsulat.

Kung adunay igo nga libre nga wanang sa pipeline, nan isulat namon ang datos niini gamit pagsulati(). Parameter i_size1 ang inode'a (nga adunay walay sulod nga pipeline mahimong katumbas sa 0) nagpunting sa katapusan sa datos nga anaa na niini. Kung adunay igong luna nga isulat, mahimo natong pun-on ang pipeline gikan sa i_size1 sa PIPESIZ. Dayon gibuhian namo ang kandado ug gisulayan nga pukawon ang bisan unsang proseso nga naghulat nga basahon gikan sa pipeline. Mobalik kami sa sinugdanan aron tan-awon kung nakahimo ba kami sa pagsulat sa daghang mga byte nga among gikinahanglan. Kung dili, nan magsugod kami usa ka bag-ong siklo sa pagrekord.

Kasagaran nga parameter i_mode inode gigamit sa pagtipig sa mga permiso r, w и x. Apan sa kaso sa mga pipeline, kami nagsenyas nga ang pipila ka proseso naghulat alang sa pagsulat o pagbasa gamit ang mga piraso IREAD и IWRITE matag usa. Ang proseso nagtakda sa bandila ug mga tawag sleep(), ug gilauman nga sa umaabot adunay laing proseso nga motawag wakeup().

Ang tinuod nga salamangka mahitabo sa sleep() и wakeup(). Gipatuman sila sa slp.c, ang tinubdan sa sikat nga "Dili ka gidahom nga masabtan kini" nga komento. Maayo na lang, dili kinahanglan nga masabtan ang code, tan-awa lang ang pipila ka mga komento:

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

Ang proseso nga nagtawag sleep() alang sa usa ka partikular nga channel, mahimong sa ulahi pukawon sa lain nga proseso, nga motawag wakeup() para sa parehas nga channel. writep() и readp() pag-coordinate sa ilang mga aksyon pinaagi sa maong gipares nga mga tawag. timan-i nga pipe.c unahon kanunay PPIPE sa dihang gitawag sleep(), so tanan sleep() mahimong mabalda sa usa ka signal.

Karon naa na kami sa tanan aron masabtan ang function 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);
}

Mahimong mas sayon ​​nimo nga basahon kini nga function gikan sa ubos ngadto sa ibabaw. Ang sanga nga "basaha ug ibalik" kasagarang gigamit kung adunay pipila ka datos sa pipeline. Sa kini nga kaso, gigamit namon basaha() basaha ang daghang datos nga magamit sugod sa karon f_offset basaha, ug dayon i-update ang bili sa katugbang nga offset.

Sa sunod nga mga pagbasa, ang pipeline mahimong walay sulod kung ang read offset naabot na i_size1 sa inode. Gi-reset namon ang posisyon sa 0 ug gisulayan nga pukawon ang bisan unsang proseso nga gusto isulat sa pipeline. Nahibal-an namon nga kung puno na ang conveyor, writep() matulog sa ip+1. Ug karon nga walay sulod ang pipeline, mahimo natong pukawon kini aron ipadayon ang siklo sa pagsulat niini.

Kung walay mabasa, nan readp() makabutang ug bandera IREAD ug matulog sa ip+2. Nahibal-an namon kung unsa ang makapukaw kaniya writep()sa diha nga kini nagsulat sa pipila ka mga data ngadto sa pipeline.

Mga komento sa basaha() ug isulati() makatabang kanimo nga masabtan nga imbis nga ipasa ang mga parameter pinaagi sa "u»Mahimo nato silang trataron sama sa regular nga I/O functions nga nagkuha ug file, posisyon, buffer sa memorya, ug ihap ang gidaghanon sa bytes nga basahon o isulat.

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

Sama sa "konserbatibo" nga pag-block, unya readp() и writep() i-lock ang mga inode hangtod mahuman o makakuha usa ka resulta (ie tawag wakeup). plock() и prele() yano nga pagtrabaho: gamit ang lainlaing hugpong sa mga tawag sleep и wakeup tugoti kami nga pukawon ang bisan unsang proseso nga nanginahanglan sa kandado nga among gipagawas:

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

Sa una wala ko kasabot ngano readp() dili hinungdan prele(ip) sa wala pa ang tawag wakeup(ip+1). Ang unang butang writep() nagtawag sa iyang loop, kini plock(ip), nga moresulta sa deadlock kon readp() wala pa matangtang ang block niini, mao nga ang code kinahanglan nga molihok sa husto. Kung tan-awon nimo wakeup(), nahimong tin-aw nga kini nagtimaan lamang sa proseso sa pagkatulog ingon nga andam na alang sa pagpatay, aron sa umaabot sched() gilusad gyud ni. Busa readp() hinungdan wakeup(), nag-abli, nagtakda IREAD ug mga tawag sleep(ip+2)- kining tanan kaniadto writep() gi-restart ang cycle.

Nakompleto niini ang paghulagway sa mga pipeline sa ikaunom nga edisyon. Yano nga code, layo nga mga implikasyon.

Ikapito nga Edisyon Unix (Enero 1979) usa ka bag-ong mayor nga pagpagawas (upat ka tuig ang milabay) nga nagpaila sa daghang bag-ong mga aplikasyon ug mga bahin sa kernel. Nakaagi usab kini og mahinungdanong mga kausaban kalabot sa paggamit sa type casting, unions ug typed pointers sa mga istruktura. Hinuon code sa mga pipeline halos wala mausab. Mahimo natong laktawan kini nga edisyon.

Xv6, usa ka yano nga kernel nga sama sa Unix

Sa paghimo sa usa ka nucleus Xv6 naimpluwensyahan sa ikaunom nga edisyon sa Unix, apan gisulat sa modernong C aron modagan sa x86 nga mga processor. Ang code dali basahon ug masabtan. Usab, dili sama sa mga tinubdan sa Unix nga adunay TUHS, mahimo nimo kini i-compile, usbon kini, ug ipadagan kini sa usa ka butang gawas sa PDP 11/70. Busa, kini nga kinauyokan kaylap nga gigamit sa mga unibersidad ingon usa ka materyal sa pagtudlo sa mga operating system. Mga tinubdan naa sa Github.

Ang code naglangkob sa usa ka tin-aw ug mahunahunaon nga pagpatuman tubo.c, gipaluyohan sa usa ka buffer sa memorya imbes sa usa ka inode sa disk. Dinhi gihatag ra nako ang kahulugan sa "structural pipeline" ug ang function 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() nagtakda sa kahimtang sa tanan nga nahabilin nga pagpatuman, nga naglakip sa mga gimbuhaton piperead(), pipewrite() и pipeclose(). Ang aktwal nga tawag sa sistema sys_pipe usa ka wrapper nga gipatuman sa sysfile.c. Girekomenda nako ang pagbasa sa tanan niyang code. Ang pagkakomplikado anaa sa lebel sa source code sa ikaunom nga edisyon, apan mas sayon ​​ug mas nindot basahon.

Linux 0.01

Makita nimo ang source code para sa Linux 0.01. Kini mahimong matulon-anon sa pagtuon sa pagpatuman sa mga pipelines sa iyang fs/pipe.c. Dinhi, usa ka inode ang gigamit sa pagrepresentar sa pipeline, apan ang pipeline mismo gisulat sa modernong C. Kung imong gi-hack ang imong agianan sa ikaunom nga edisyon nga code, wala ka'y ​​problema dinhi. Kini ang hitsura sa function 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;
}

Bisan kung wala’y pagtan-aw sa mga kahulugan sa istruktura, mahimo nimong mahibal-an kung giunsa ang pag-ihap sa reference sa inode gigamit aron masusi kung ang usa ka operasyon sa pagsulat moresulta sa SIGPIPE. Dugang pa sa byte-by-byte nga trabaho, kini nga function sayon ​​nga itandi sa mga ideya sa ibabaw. Bisan lohika sleep_on/wake_up murag dili alien.

Modernong Linux Kernels, FreeBSD, NetBSD, OpenBSD

Dali nakong gisusi ang pipila ka modernong mga lugas. Walay usa kanila nga adunay usa ka disk-based nga pagpatuman (dili ikatingala). Ang Linux adunay kaugalingong pagpatuman. Ug bisan kung ang tulo ka modernong BSD kernels adunay mga implementasyon base sa code nga gisulat ni John Dyson, sa paglabay sa mga tuig sila nahimong lahi kaayo sa usag usa.

Sa pagbasa fs/pipe.c (sa Linux) o sys/kern/sys_pipe.c (sa *BSD), nagkinahanglan kini og tinuod nga dedikasyon. Ang performance ug suporta alang sa mga feature sama sa vector ug asynchronous I/O importante sa code karon. Ug ang mga detalye sa alokasyon sa memorya, mga kandado, ug pag-configure sa kernel tanan magkalainlain kaayo. Dili kini ang gikinahanglan sa mga unibersidad alang sa pasiuna nga kurso sa mga operating system.

Sa bisan unsa nga kaso, kini makapaikag alang kanako sa pagkalot sa pipila ka daan nga mga sumbanan (pananglitan, pagmugna SIGPIPE ug balik EPIPE kung nagsulat sa usa ka sirado nga pipeline) sa tanan niini, lahi kaayo, modernong mga kernel. Tingali dili gyud ko makakita og PDP-11 nga kompyuter nga buhi, apan daghan pa ang makat-unan gikan sa code nga gisulat pipila ka tuig sa wala pa ako matawo.

Gisulat ni Divi Kapoor sa 2011, ang artikulo nga "Ang Linux Kernel Implementation sa Pipes ug FIFOsmao ang usa ka kinatibuk-ang ideya kung giunsa ang mga pipeline sa Linux (sa pagkakaron) nagtrabaho. A bag-o nga commit sa linux nag-ilustrar sa pipeline nga modelo sa interaksyon, kansang mga kapabilidad labaw sa temporaryo nga mga file; ug gipakita usab kung unsa ka layo ang mga pipeline gikan sa "konserbatibo kaayo nga pag-lock" sa ikaunom nga edisyon sa Unix kernel.

Source: www.habr.com

Idugang sa usa ka comment