Kumaha pipelines dilaksanakeun di Unix

Kumaha pipelines dilaksanakeun di Unix
Artikel ieu ngajelaskeun palaksanaan pipelines dina kernel Unix. Kuring rada kuciwa yén tulisan anyar anu judulna "Kumaha pipelines jalan di Unix?"Tétéla teu ngeunaan struktur internal. Kuring jadi panasaran sarta ngali kana sumber heubeul pikeun manggihan jawaban.

Naon urang ngobrol ngeunaan?

Pipelines, "panginten penemuan anu paling penting dina Unix," mangrupikeun ciri anu ngahartikeun filosofi Unix anu ngahubungkeun program-program leutik babarengan, ogé tanda anu biasa dina garis paréntah:

$ echo hello | wc -c
6

Fungsionalitas ieu gumantung kana panggero sistem anu disayogikeun ku kernel pipe, anu dijelaskeun dina kaca dokuméntasi pipa (7) и pipa (2):

Pipelines nyadiakeun saluran unidirectional pikeun komunikasi interprocess. Pipa boga input (tulis tungtung) jeung kaluaran (baca tungtung). Data anu ditulis kana input pipa tiasa dibaca dina kaluaran.

Pipa didamel nganggo telepon pipe(2), anu mulihkeun dua deskriptor file: hiji ngarujuk kana input pipa, anu kadua pikeun kaluaran.

Kaluaran renik tina paréntah di luhur nunjukkeun nyiptakeun pipa sareng aliran data ngaliwatan éta tina hiji prosés ka anu sanés:

$ 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

Prosés indungna nelepon pipe()pikeun meunangkeun deskriptor file dipasang. Hiji prosés anak nulis ka hiji cecekelan, sarta prosés séjén maca data anu sarua ti cecekelan sejen. Cangkang ngagunakeun dup2 pikeun "ganti ngaran" deskriptor 3 jeung 4 pikeun cocog stdin na stdout.

Tanpa pipa, cangkang kedah nyerat kaluaran hiji prosés kana file sareng ngalirkeun kana prosés anu sanés pikeun maca data tina file. Hasilna, urang bakal miceunan langkung seueur sumber sareng rohangan disk. Nanging, saluran pipa saé sanés ngan ukur ngamungkinkeun anjeun ngahindarkeun panggunaan file samentawis:

Upami prosés nyobian maca tina pipa kosong teras read(2) bakal meungpeuk nepi ka data sadia. Upami prosés nyobian nyerat kana pipa pinuh, teras write(2) bakal meungpeuk dugi cukup data geus dibaca tina pipa pikeun ngalakukeun nulis.

Kawas sarat POSIX, ieu mangrupa sipat penting: nulis kana pipa nepi ka PIPE_BUF bait (sahenteuna 512) kudu atom ambéh prosés bisa komunikasi saling ngaliwatan pipa dina cara nu file biasa (nu teu nyadiakeun jaminan misalna) teu bisa.

Lamun maké file biasa, hiji prosés bisa nulis sakabéh kaluaran na kana eta sarta ngalirkeun kana ka prosés séjén. Atawa prosés bisa beroperasi dina modeu kacida paralel, ngagunakeun hiji mékanisme signalling éksternal (kawas semafor a) pikeun ngabejaan silih lamun nulis atawa maca geus réngsé. Conveyors nyalametkeun urang tina sagala repot ieu.

Naon anu urang pilari?

Kuring bakal ngajelaskeun eta dina istilah basajan ambéh leuwih gampang pikeun anjeun ngabayangkeun kumaha conveyor a tiasa jalan. Anjeun bakal kedah allocate a panyangga sarta sababaraha kaayaan dina mémori. Anjeun peryogi fungsi pikeun nambihan sareng mupus data tina panyangga. Anjeun peryogi sababaraha cara pikeun nelepon fungsi nalika operasi maca sareng nyerat dina deskriptor file. Sareng anjeun peryogi konci pikeun ngalaksanakeun kabiasaan khusus anu dijelaskeun di luhur.

Ayeuna kami siap ngainterogasi kode sumber kernel dina lampu lampu anu terang pikeun ngonfirmasi atanapi ngabantah modél mental kami anu samar. Tapi salawasna jadi disiapkeun keur kaduga.

Dimana urang pilari?

Abdi henteu terang dimana salinan buku anu kasohor "Buku maung"Kalayan kode sumber Unix 6, tapi hatur nuhun kana The Unix Warisan Society Anjeun tiasa milarian online di kode sumber malah versi heubeul tina Unix.

Ngumbara ngaliwatan arsip TUHS téh kawas ngadatangan museum. Urang bisa nempo sajarah urang dibagikeun, sarta kuring hormat keur sababaraha taun usaha cageur kabeh bahan ieu bit ku bit tina kaset heubeul na prints. Sareng kuring sadar pisan kana fragmen anu masih leungit.

Saatos nyugemakeun rasa panasaran urang ngeunaan sajarah kuno conveyors, urang tiasa ningali kernels modern pikeun ngabandingkeun.

Ku jalan kitu, pipe nyaeta sistem panggero angka 42 dina tabél sysent[]. Kabeneran?

Kernel Unix Tradisional (1970–1974)

Abdi henteu mendakan jejak pipe(2) henteu di PDP-7 Unix (Januari 1970), atanapi di édisi munggaran Unix (Nopémber 1971), atawa dina kode sumber teu lengkep édisi kadua (Juni 1972).

TUHS nyatakeun yén édisi katilu Unix (Pébruari 1973) janten versi munggaran kalayan conveyors:

Unix 1973rd Edition éta versi panungtungan kalawan kernel ditulis dina basa assembly, tapi oge versi munggaran kalayan pipelines. Salila taun XNUMX, gawé dilumangsungkeun pikeun ngaronjatkeun édisi katilu, kernel ieu ditulis ulang dina C, sahingga édisi kaopat Unix mucunghul.

Hiji pamaca mendakan scan dokumen dimana Doug McIlroy ngusulkeun ide "ngahubungkeun program sapertos selang kebon."

Kumaha pipelines dilaksanakeun di Unix
Dina buku Brian KernighanUnix: A Sajarah sarta Memoir a", dina sajarah mecenghulna conveyors, dokumén ieu ogé disebutkeun: "... eta ngagantung dina témbok di kantor kuring di Bell Labs pikeun 30 taun." Ieuh wawancara jeung McIlroy, jeung carita sejen ti Karya McIlroy, ditulis dina 2014:

Nalika Unix kaluar, karesep kuring sareng coroutines nyababkeun kuring naroskeun ka panulis OS, Ken Thompson, pikeun ngantepkeun data anu diserat kana prosés henteu ngan ukur ka alat, tapi ogé kaluaran kana prosés anu sanés. Ken mutuskeun éta mungkin. Nanging, salaku minimalis, anjeunna hoyong unggal fungsi sistem maénkeun peran anu penting. Nyaeta nulis langsung antara prosés bener kaunggulan badag leuwih nulis kana file panengah? Ngan nalika kuring nyieun proposal husus kalawan ngaran catchy "pipeline" sarta pedaran sintaksis pikeun interaksi antara prosés nu Ken tungtungna exclaimed: "Kuring gé ngalakukeun eta!"

Jeung tuh. Hiji malem fateful, Ken robah kernel jeung cangkang, dibereskeun sababaraha program standar pikeun standarisasi kumaha aranjeunna nampi input (anu bisa datangna tina pipa a), sarta ogé robah ngaran file. Poé saterusna, pipelines mimiti dipaké pisan lega dina aplikasi. Nepi ka tungtun taun saminggu, sekretaris ngagunakeun éta pikeun ngirim dokumén ti pangolah kecap ka printer. A saeutik engké, Ken ngaganti API aslina tur sintaksis pikeun wrapping pamakéan pipelines kalawan Konvénsi cleaner, nu geus dipaké kantos saprak.

Hanjakal, kodeu sumber pikeun kernel Unix édisi katilu geus leungit. Sareng sanaos urang gaduh kode sumber kernel anu ditulis dina C édisi kaopat, dirilis dina bulan Nopémber 1973, tapi kaluar sababaraha bulan saméméh release resmi sarta teu ngandung palaksanaan pipa. Ieu éra yén kode sumber pikeun fungsi Unix legendaris ieu leungit, meureun salawasna.

Simkuring gaduh dokuméntasi téks pikeun pipe(2) ti duanana release, jadi Anjeun bisa ngamimitian ku néangan dokuméntasi édisi katilu (pikeun kecap-kecap nu tangtu, digariskeun "sacara manual", string of literals ^H, dituturkeun ku underscore!). Ieu proto-pipe(2) ditulis dina basa assembly sarta mulih ngan hiji deskriptor file, tapi geus nyadiakeun fungsionalitas inti ekspektasi:

Telepon sistem pipah nyiptakeun mékanisme input / output disebut pipa. Deskriptor file anu dipulangkeun tiasa dianggo pikeun operasi maca sareng nyerat. Nalika aya anu diserat dina saluran pipa, dugi ka 504 bait data disanggakeun, saatos éta prosés nyerat ditunda. Nalika maca tina pipa, data buffered dicandak.

Ku taun saterusna kernel geus ditulis ulang dina C, jeung pipe (2) dina édisi kaopat kaala penampilan modern na kalawan prototipe "pipe(fildes)»:

Telepon sistem pipah nyiptakeun mékanisme input / output disebut pipa. Deskriptor file anu dipulangkeun tiasa dianggo dina operasi maca sareng nyerat. Lamun hal ieu ditulis kana pipa nu, cecekelan balik di r1 (resp. fildes [1]) dipaké, buffered ka 4096 bait data, nu satutasna prosés nulis ditunda. Nalika maca tina pipa, cecekelan balik ka r0 (resp. fildes [0]) nyokot data.

Hal ieu dianggap yén sakali pipa didefinisikeun, dua (atawa leuwih) prosés komunikasi (dijieun ku panggero saterusna pikeun garpuh) bakal nransper data tina pipa nganggo telepon maca и nulis.

Cangkang boga sintaksis pikeun nangtukeun susunan linier prosés disambungkeun ku pipa.

Nelepon maca tina hiji pipa kosong (ngandung euweuh data buffered) nu boga ngan hiji tungtung (sadayana descriptors file tulisan ditutup) balik "tungtung file". Telepon nulis dina kaayaan sarupa teu dipaliré.

Pangpangna palaksanaan pipa dilestarikan manglaku ka edisi kalima Unix (Juni 1974), tapi ampir sarua jeung nu mucunghul dina release salajengna. Koméntar karék ditambahkeun, jadi Anjeun bisa ngaliwatan édisi kalima.

Édisi kagenep Unix (1975)

Hayu urang mimitian maca kode sumber Unix édisi kagenep (Méi 1975). Sakitu legana hatur nuhun kana singa leuwih gampang pikeun manggihan ti sumber tina versi saméméhna:

Mangtaun-taun buku singa éta hiji-hijina dokumén dina kernel Unix sadia di luar Bell Labs. Sanajan lisénsi édisi kagenep diwenangkeun guru ngagunakeun kode sumber na, lisénsi édisi katujuh ngaluarkeun kamungkinan ieu, jadi buku ieu disebarkeun dina bentuk salinan ilegal typewritten.

Dinten anjeun tiasa mésér reprint tina buku, panutup nu nembongkeun siswa dina mesin salinan. Sareng hatur nuhun ka Warren Toomey (anu ngamimitian proyék TUHS) anjeun tiasa unduh File PDF sareng kode sumber pikeun édisi kagenep. Abdi hoyong masihan anjeun ide ngeunaan sabaraha usaha pikeun nyiptakeun file:

Langkung ti 15 sababaraha taun ka pengker, kuring ngetik salinan kode sumber anu dipasihkeun singa, sabab kuring henteu resep kualitas salinan kuring tina jumlah salinan anu teu dipikanyaho. TUHS henteu acan aya sareng kuring henteu ngagaduhan aksés kana sumber anu lami. Tapi dina 1988, kuring manggihan hiji pita heubeul 9-lagu nu ngandung cadangan ti komputer PDP11. Hésé pikeun ngabéjaan yén éta jalan, tapi aya tangkal /usr/src/ anu gembleng dimana seueur file anu dilabélan taun 1979, anu sanaos katingali kuno. Ieu édisi katujuh atawa turunan na PWB, sakumaha kuring yakin.

Kuring nyokot manggihan salaku dadasar sarta sacara manual diédit sumber ka édisi kagenep. Sababaraha kode tetep sarua, tapi sababaraha kudu rada diédit, ngarobah modern += token ka tinggaleun jaman =+. Sababaraha hal anu saukur dihapus, sarta sababaraha kudu sagemblengna ditulis ulang, tapi ulah teuing.

Sareng dinten ayeuna urang tiasa maca online dina TUHS kode sumber édisi kagenep tina arsip, nu Dennis Ritchie kungsi leungeun.

Ku jalan kitu, dina glance kahiji, fitur utama C-kode saméméh periode Kernighan na Ritchie nyaeta na. singgetan. Teu sering abdi tiasa nyelapkeun potongan kode tanpa éditan éksténsif pikeun nyocogkeun ka daérah tampilan anu kawilang sempit dina situs abdi.

Dina awal /usr/sys/ken/pipe.c aya komentar panjelasan (sareng enya, aya deui /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

Ukuran panyangga teu robah saprak édisi kaopat. Tapi di dieu urang ningali, tanpa aya dokuméntasi umum, yén pipelines sakali dipaké file salaku gudang cadangan!

Sedengkeun pikeun file LARG, aranjeunna pakait sareng inode bandéra BESAR, nu dipaké ku "algoritma alamat badag" pikeun ngolah blok teu langsung pikeun ngadukung sistem file anu langkung ageung. Kusabab Ken ceuk eta leuwih hade teu make aranjeunna, Kuring gé happily nyandak kecap-Na pikeun eta.

Di dieu nyaeta panggero sistem nyata 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;
}

Koméntar jelas ngajelaskeun naon anu lumangsung di dieu. Tapi ngartos kode éta henteu gampang, sabagéan kusabab jalanna "pamaké struct u»jeung ngadaptar R0 и R1 Parameter panggero sistem sareng nilai mulang disalurkeun.

Hayu urang coba kalawan ialloc() nempatkeun dina disk inode (cecekelan indéks), sareng kalayan bantosan falloc() - nempatkeun dua dina mémori file. Upami sadayana leres, kami bakal nyetél bandéra pikeun ngaidentipikasi file ieu salaku dua tungtung pipa, arahkeun ka inode anu sami (anu cacah rujukanna bakal disetél ka 2), sareng cirian inode parantos dirobih sareng dianggo. Nengetan requests ka iput() dina jalur kasalahan pikeun ngurangan jumlah rujukan dina inode anyar.

pipe() kudu ngaliwatan R0 и R1 mulangkeun angka deskriptor file pikeun maca jeung nulis. falloc() mulih pointer kana struktur file, tapi ogé "mulih" via u.u_ar0[R0] sareng deskriptor file. Hartina, kodeu disimpen dina r file descriptor pikeun bacaan sarta nangtukeun file descriptor pikeun nulis langsung ti u.u_ar0[R0] sanggeus panggero kadua falloc().

bandera FPIPE, anu kami setel nalika nyiptakeun pipa, ngatur paripolah fungsina rdwr () dina sys2.cnelepon rutin I / O husus:

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

Lajeng fungsi readp() в pipe.c maca data tina pipa. Tapi éta hadé pikeun ngalacak palaksanaan mimitian ti writep(). Sakali deui, kodeu parantos janten langkung rumit kusabab konvénsi ngaliwat argumen, tapi sababaraha detil tiasa dileungitkeun.

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

Kami hoyong nyerat bait kana input pipa u.u_count. Mimiti urang kedah ngonci inode (tingali di handap plock/prele).

Teras we pariksa counter rujukan inode. Salami duanana tungtung pipa tetep kabuka, counter kudu sarua jeung 2. Urang tahan hiji link (tina rp->f_inode), janten upami counter kirang ti 2, éta kedah hartosna yén prosés maca parantos ditutup tungtung pipa na. Kalayan kecap séjén, urang nyobian nulis ka pipa ditutup, sarta ieu kasalahan. Kode kasalahan munggaran EPIPE jeung sinyal SIGPIPE muncul dina édisi kagenep Unix.

Tapi sanajan conveyor dibuka, bisa jadi pinuh. Dina hal ieu, urang ngaleupaskeun konci na buka saré dina harepan yén prosés sejen bakal maca tina pipa jeung ngosongkeun cukup spasi dina eta. Saatos hudang, urang uih deui ka awal, ngagantung konci deui sareng ngamimitian siklus rekaman énggal.

Upami aya cukup rohangan bébas dina pipa, teras urang nyerat data nganggo éta nyeratna (). Parameter i_size1 inode (lamun pipa kosong bisa sarua jeung 0) nunjukkeun tungtung data nu geus ngandung. Lamun aya cukup spasi rekaman, urang bisa ngeusian pipa ti i_size1 ka PIPESIZ. Teras we ngaleupaskeun konci sareng nyobian hudang prosés anu ngantosan dibaca tina pipa. Urang balik deui ka awal ningali lamun urang bisa nulis saloba bait sakumaha urang diperlukeun. Upami gagal, teras urang ngamimitian siklus ngarékam énggal.

Biasana parameter i_mode inode dipaké pikeun nyimpen idin r, w и x. Tapi dina kasus pipelines, urang sinyal yén sababaraha prosés ngantosan hiji nulis atawa maca ngagunakeun bit IREAD и IWRITE masing-masing. Prosésna nyetél bandéra sareng nelepon sleep(), sarta eta diperkirakeun yén sababaraha prosés séjén dina mangsa nu bakal datang bakal ngabalukarkeun wakeup().

The magic nyata lumangsung dina sleep() и wakeup(). Aranjeunna dilaksanakeun di slp.c, Sumber tina kawentar "Anjeun teu diperkirakeun ngartos ieu" komentar. Kabeneran, urang henteu kedah ngartos kodeu, tingali sababaraha koméntar:

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

Prosés anu ngabalukarkeun sleep() pikeun saluran tinangtu, bisa engké jadi woken up ku prosés sejen, nu bakal ngabalukarkeun wakeup() pikeun saluran anu sarua. writep() и readp() koordinat lampah maranéhanana ngaliwatan telepon dipasangkeun misalna. Catet éta pipe.c salawasna méré prioritas PPIPE lamun disebut sleep(), tah kitu sleep() bisa diganggu ku sinyal.

Ayeuna urang gaduh sadayana ngartos fungsina 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);
}

Anjeun tiasa mendakan langkung gampang maca fungsi ieu ti handap ka luhur. Cabang "baca sareng uih deui" biasana dianggo nalika aya sababaraha data dina jalur pipa. Dina hal ieu, urang ngagunakeun maca () urang baca saloba data sakumaha sadia mimitian ti nu ayeuna f_offset bacaan, lajeng ngapdet nilai offset pakait.

Dina bacaan saterusna, pipa bakal kosong lamun maca offset geus ngahontal i_size1 di inode. Urang ngareset posisi ka 0 sarta nyoba hudang prosés nu mana wae nu hayang nulis kana pipa nu. Urang terang yén nalika conveyor pinuh, writep() bakal saré dina ip+1. Sareng ayeuna yén pipana kosong, urang tiasa hudang pikeun neruskeun siklus nyeratna.

Lamun teu boga nanaon maca, teras readp() bisa nyetél bandéra IREAD tur saré dina ip+2. Urang terang naon anu bakal ngahudangkeun anjeunna writep(), nalika nyerat sababaraha data kana pipa.

Koméntar kana readi() jeung writei() bakal ngabantosan anjeun ngartos yén tinimbang ngalangkungan parameter via "u"Urang tiasa ngubaran aranjeunna sapertos fungsi I / O normal anu nyandak file, posisi, panyangga dina mémori, sareng ngitung jumlah bait anu dibaca atanapi ditulis.

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

Sedengkeun pikeun blocking "konservatif", lajeng readp() и writep() meungpeuk inode nepi ka rengse pagawean maranéhanana atawa narima hasil (nyaéta, nelepon wakeup). plock() и prele() dianggo saukur: ngagunakeun set béda tina panggero sleep и wakeup Ngidinan urang hudang prosés naon waé anu peryogi konci anu nembé dileupaskeun:

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

Mimitina kuring teu ngarti naha readp() henteu ngabalukarkeun prele(ip) saméméh nelepon wakeup(ip+1). Hal kahiji nyaéta writep() ngabalukarkeun dina siklus na, ieu plock(ip), nu ngabalukarkeun deadlock lamun readp() henteu acan ngaleungitkeun blok kuring, janten kumaha waé kodeu kedah dianggo leres. Lamun nempo wakeup(), teras janten écés yén éta ngan ukur nandaan prosés saré siap dieksekusi, ku kituna dina mangsa nu bakal datang. sched() bener ngaluncurkeunana. Janten readp() nyababkeun wakeup(), ngaluarkeun konci, susunan IREAD jeung nelepon sleep(ip+2)- sadayana ieu sateuacan writep() neruskeun siklus.

Ieu ngalengkepan pedaran conveyors dina édisi kagenep. Kode basajan, konsékuansi jauh-ngahontal.

Édisi katujuh Unix (Januari 1979) mangrupikeun rilis utama énggal (opat taun saatosna) anu ngenalkeun seueur aplikasi anyar sareng fitur kernel. Ogé underwent parobahan signifikan dina sambungan kalawan pamakéan tipe casting, union sarta diketik pointers kana struktur. Sanajan kitu kode conveyor praktis unchanged. Urang tiasa ngalangkungan édisi ieu.

Xv6, kernel basajan kawas Unix

Pikeun nyieun kernel Xv6 dipangaruhan ku édisi kagenep Unix, tapi ieu ditulis dina C modern pikeun ngajalankeun on prosesor x86. Kodeu gampang dibaca sareng kaharti. Tambih Deui, teu sapertos sumber Unix sareng TUHS, anjeun tiasa nyusun éta, ngarobih, sareng ngajalankeunana dina hal sanés ti PDP 11/70. Ku alatan éta, kernel ieu loba dipaké di paguron luhur salaku bahan atikan dina sistem operasi. Sumber aya dina Github.

Kode ngandung palaksanaan jelas tur wijaksana pipah.c, Dirojong ku panyangga dina mémori tinimbang inode dina disk. Di dieu kuring ngan ukur masihan definisi "pipa struktural" sareng fungsina 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() nangtukeun kaayaan sesa palaksanaan, nu ngawengku fungsi piperead(), pipewrite() и pipeclose(). Telepon sistem sabenerna sys_pipe nyaeta wrapper dilaksanakeun dina sysfile.c. Abdi nyarankeun maca sakabéh kode na. Pajeulitna aya dina tingkat kode sumber édisi kagenep, tapi éta langkung gampang sareng langkung pikaresepeun pikeun dibaca.

Linux Ubuntu 0.01

Kode sumber Linux 0.01 tiasa dipendakan. Ieu bakal instructive mun diajar palaksanaan pipelines di na fs/pipe.c. Ieu ngagunakeun inode pikeun ngagambarkeun pipa, tapi pipa sorangan ditulis dina C modern. Ieu naon fungsi kasampak kawas 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;
}

Tanpa ningali definisi struktur, anjeun tiasa terang kumaha jumlah rujukan inode dianggo pikeun mariksa naha operasi nulis ngahasilkeun SIGPIPE. Salian dianggo bait-demi-bait, fungsi ieu gampang dibandingkeun jeung gagasan ditétélakeun di luhur. Komo logika sleep_on/wake_up teu kasampak jadi alien.

Kernel Linux modern, FreeBSD, NetBSD, OpenBSD

Kuring gancang lumpat ngaliwatan sababaraha kernels modern. Euweuh sahijina boga palaksanaan disk deui (teu heran). Linux boga palaksanaan sorangan. Sanajan tilu kernels BSD modern ngandung palaksanaan dumasar kana kode anu ditulis ku John Dyson, leuwih taun aranjeunna geus jadi béda teuing ti unggal lianna.

Pikeun maca fs/pipe.c (dina Linux) atawa sys/kern/sys_pipe.c (dina *BSD), butuh dedikasi anu nyata. Kodeu dinten ieu ngeunaan kinerja sareng dukungan pikeun fitur sapertos vektor sareng I/O asinkron. Sareng detil alokasi mémori, konci sareng konfigurasi kernel sadayana béda-béda pisan. Ieu sanés anu diperyogikeun ku akademi pikeun kursus sistem operasi perkenalan.

Atoh, kuring resep ngagali sababaraha pola kuno (sapertos ngahasilkeun SIGPIPE jeung mulang EPIPE nalika nyerat ka pipa katutup) dina sadaya kernel modern anu béda ieu. Kuring sigana moal pernah ningali komputer PDP-11 dina kahirupan nyata, tapi masih seueur anu diajar tina kode anu ditulis sababaraha taun sateuacan kuring lahir.

Artikel anu ditulis ku Divi Kapoor taun 2011:Palaksanaan Kernel Linux Pipa sareng FIFO"Nyadiakeun tinjauan kumaha pipelines (masih) dianggo dina Linux. A komitmen panganyarna dina Linux illustrates model pipa interaksi, anu kamampuhan ngaleuwihan pamadegan file samentara; sarta ogé nembongkeun sabaraha jauh pipelines asalna tina "ngonci pisan konservatif" tina kernel Unix édisi kagenep.

sumber: www.habr.com

Tambahkeun komentar