Carane pipelines dileksanakake ing Unix

Carane pipelines dileksanakake ing Unix
Artikel iki nerangake implementasine pipelines ing kernel Unix. Aku rada kuciwa amarga ana artikel anyar sing judhulé "Kepiye cara kerja pipa ing Unix?"ternyata ora babagan struktur internal. Aku dadi penasaran lan ndudhuk menyang sumber lawas kanggo nemokake jawaban.

Apa kita ngomong babagan?

Pipelines, "mbokmenawa penemuan paling penting ing Unix," minangka ciri khas filosofi Unix sing ndasari ngubungake program-program cilik, uga minangka tandha akrab ing baris perintah:

$ echo hello | wc -c
6

Fungsi iki gumantung marang panggilan sistem sing disedhiyakake kernel pipe, sing diterangake ing kaca dokumentasi pipa (7) и pipa (2):

Pipeline nyedhiyakake saluran searah kanggo komunikasi antar proses. Pipa kasebut nduweni input (tulis pungkasan) lan output (waca pungkasan). Data sing ditulis ing input pipa bisa diwaca ing output.

Pipa kasebut digawe nggunakake telpon pipe(2), sing ngasilake rong deskriptor file: siji nuduhake input saka pipeline, sing liya kanggo output.

Output jejak saka printah ing ndhuwur nuduhake nggawe pipa lan aliran data saka siji proses menyang liyane:

$ 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

Proses wong tuwa nelpon pipe()kanggo njaluk deskriptor file sing dipasang. Siji proses anak nulis menyang siji gagang, lan proses liyane maca data sing padha saka gagang liyane. Cangkang nggunakake dup2 kanggo "ganti jeneng" deskriptor 3 lan 4 kanggo cocog stdin lan stdout.

Tanpa pipa, cangkang kudu nulis asil siji proses menyang file lan ngirim menyang proses liyane kanggo maca data saka file kasebut. Akibaté, kita bakal mbuwang luwih akeh sumber daya lan ruang disk. Nanging, saluran pipa apik ora mung amarga ngidini sampeyan ngindhari panggunaan file sementara:

Yen proses nyoba maca saka pipa kosong banjur read(2) bakal diblokir nganti data kasedhiya. Yen proses nyoba nulis menyang pipa lengkap, banjur write(2) bakal mblokir nganti cukup data wis diwaca saka pipeline kanggo nindakake nulis.

Kaya syarat POSIX, iki minangka properti penting: nulis menyang pipa nganti PIPE_BUF bita (paling ora 512) kudu atom supaya pangolahan bisa komunikasi karo saben liyane liwat pipeline ing cara sing file biasa (sing ora nyedhiyani jaminan kuwi) ora bisa.

Nalika nggunakake file biasa, sawijining proses bisa nulis kabeh output menyang file kasebut lan ditransfer menyang proses liyane. Utawa pangolahan bisa operate ing mode Highly podo, nggunakake mekanisme signaling external (kaya semafor) kanggo ngabari saben liyane nalika nulis utawa maca wis rampung. Conveyor nylametake kita saka kabeh kerumitan iki.

Apa sing kita goleki?

Aku bakal nerangake ing syarat-syarat wong awam supaya luwih gampang kanggo mbayangno carane conveyor bisa. Sampeyan kudu nyedhiakke buffer lan sawetara negara ing memori. Sampeyan mbutuhake fungsi kanggo nambah lan mbusak data saka buffer. Sampeyan mbutuhake sawetara cara kanggo nelpon fungsi sajrone operasi maca lan nulis ing deskriptor file. Lan sampeyan butuh kunci kanggo ngetrapake prilaku khusus sing kasebut ing ndhuwur.

Saiki kita siyap kanggo interogasi kode sumber kernel ing lampu padhang kanggo konfirmasi utawa mbantah model mental kita sing samar. Nanging tansah disiapake kanggo sing ora dikarepke.

Ing ngendi kita nggoleki?

Aku ora ngerti ngendi salinan buku sing misuwur "Buku singa"Kanthi kode sumber Unix 6, nanging thanks kanggo Unix Heritage Society sampeyan bisa nelusuri online ing kode sumber malah versi lawas saka Unix.

Mlaku-mlaku ing arsip TUHS kaya ngunjungi museum. Kita bisa ndeleng sejarah sing dienggo bareng, lan aku ngormati upaya pirang-pirang taun kanggo mbalekake kabeh materi iki kanthi sithik saka kaset lan cetakan lawas. Lan aku ngerti banget babagan pecahan sing isih ilang.

Duwe wareg penasaran kita bab sajarah kuna conveyors, kita bisa ndeleng ing kernels modern kanggo comparison.

Miturut cara, pipe yaiku nomer telpon sistem 42 ing tabel sysent[]. Kebetulan?

Kernel Unix Tradisional (1970–1974)

Aku ora nemu jejak pipe(2) utawa ing PDP-7 Unix (Januari 1970), utawa ing edition pisanan Unix (November 1971), utawa ing kode sumber sing ora lengkap edition kapindho (Juni 1972).

TUHS nyatakake yen edition katelu saka Unix (Februari 1973) dadi versi pisanan karo conveyor:

Unix 1973rd Edition minangka versi pungkasan kanthi kernel sing ditulis ing basa assembly, nanging uga versi pisanan karo pipelines. Sajrone taun XNUMX, karya ditindakake kanggo nambah edisi katelu, kernel ditulis maneh ing C, mula Unix edisi kaping papat muncul.

Siji maca nemokake scan dokumen ing ngendi Doug McIlroy ngusulake gagasan "nyambungake program kaya selang taman."

Carane pipelines dileksanakake ing Unix
Ing buku Brian KernighanUnix: Sejarah lan Memoir", ing sajarah munculé conveyor, dokumen iki uga kasebut: "... digantung ing tembok ing kantorku ing Bell Labs suwene 30 taun." kene wawancara karo McIlroy, lan crita liyane saka Karya McIlroy, ditulis ing 2014:

Nalika Unix metu, pikatan karo coroutines mimpin kula kanggo takon pangarang OS kang, Ken Thompson, kanggo ngidini data ditulis kanggo proses kanggo pindhah ora mung kanggo piranti, nanging uga kanggo output kanggo proses liyane. Ken mutusaké iku bisa. Nanging, minangka minimalis, dheweke pengin saben fungsi sistem duwe peran penting. Apa nulis langsung ing antarane proses pancen dadi kauntungan gedhe tinimbang nulis menyang file penengah? Mung nalika aku nggawe proposal khusus kanthi jeneng "pipa" lan katrangan babagan sintaks interaksi antarane proses, Ken pungkasane ngucap: "Aku bakal nindakake!"

Lan iya. Ing sawijining wayah sore, Ken ngganti kernel lan cangkang, ndandani sawetara program standar kanggo standarisasi carane nampa input (sing bisa teka saka pipa), lan uga ngganti jeneng file. Dina sabanjuré, pipelines wiwit digunakake banget digunakake ing aplikasi. Ing pungkasan minggu, sekretaris digunakake kanggo ngirim dokumen saka pemroses tembung menyang printer. A little mengko, Ken ngganti API asli lan sintaks kanggo mbungkus panggunaan pipelines karo konvènsi resik, kang wis digunakake wiwit.

Sayange, kode sumber kanggo kernel Unix edisi katelu wis ilang. Lan sanajan kita duwe kode sumber kernel sing ditulis ing C edisi kaping papat, dirilis ing November 1973, nanging metu sawetara sasi sadurunge release resmi lan ora ngemot implementasine pipeline. Iku isin sing kode sumber kanggo fungsi Unix legendaris iki ilang, mbok menawa ing salawas-lawase.

Kita duwe dokumentasi teks kanggo pipe(2) saka loro rilis, supaya sampeyan bisa miwiti kanthi nggoleki dokumentasi edition katelu (kanggo tembung tartamtu, digaris "kanthi manual", senar huruf ^H, banjur digaris ngisor!). Iki proto-pipe(2) ditulis ing basa assembly lan mung ngasilake siji deskriptor file, nanging wis nyedhiyakake fungsi inti sing dikarepake:

Panggilan sistem Pipa nggawe mekanisme input / output sing diarani pipa. Deskriptor file sing bali bisa digunakake kanggo operasi maca lan nulis. Nalika ana sing ditulis ing pipeline, nganti 504 bita data buffered, sawise proses nulis dilereni soko tugas. Nalika maca saka pipeline, data buffered dijupuk.

Ing taun sabanjuré kernel wis ditulis maneh ing C, lan pipa (2) ing edisi kaping papat entuk tampilan modern kanthi prototipe "pipe(fildes)"

Panggilan sistem Pipa nggawe mekanisme input / output sing diarani pipa. Deskriptor file sing bali bisa digunakake ing operasi maca lan nulis. Nalika soko ditulis kanggo pipo, nangani bali ing r1 (resp. fildes [1]) digunakake, buffered kanggo 4096 bita data, sawise kang proses nulis dilereni soko tugas. Nalika maca saka pipo, nangani bali menyang r0 (resp. fildes [0]) njupuk data.

Dianggep yen saluran pipa ditetepake, loro (utawa luwih) proses komunikasi (digawe dening telpon sakteruse kanggo Garpu) bakal nransfer data saka pipa nggunakake telpon maca и nulis.

Cangkang nduweni sintaks kanggo nemtokake array linear proses sing disambungake dening pipa.

Telpon kanggo maca saka pipa kosong (ora ana data buffered) sing mung siji mburi (kabeh deskriptor file nulis ditutup) ngasilake "mburi file". Telpon kanggo nulis ing kahanan sing padha ora digatekake.

paling awal implementasine pipeline wadi ditrapake menyang edisi kaping lima Unix (Juni 1974), nanging meh padha karo sing muncul ing rilis sabanjure. Komentar wis ditambahake, supaya sampeyan bisa ngliwati edisi kaping lima.

Unix edisi kaping enem (1975)

Ayo miwiti maca kode sumber Unix edisi kaping enem (Mei 1975). Umumé thanks kanggo singa luwih gampang ditemokake tinimbang sumber versi sadurungé:

Kanggo akèh taun buku singa minangka siji-sijine dokumen ing kernel Unix sing kasedhiya ing njaba Bell Labs. Sanajan lisensi edisi kaping enem ngidini para guru nggunakake kode sumber, lisensi edisi kapitu ora kalebu kemungkinan kasebut, mula buku kasebut disebarake ing bentuk salinan ketik sing ora sah.

Dina iki sampeyan bisa tuku reprint saka buku, tutup kang nuduhake siswa ing mesin salinan. Lan thanks kanggo Warren Toomey (sing miwiti proyek TUHS) sampeyan bisa download File PDF kanthi kode sumber kanggo edisi kaping enem. Aku pengin menehi idea babagan carane nggawe file:

Luwih saka 15 taun kepungkur, aku ngetik salinan kode sumber sing diwenehake singa, amarga aku ora seneng karo kualitas salinan saka salinan liyane sing ora dingerteni. TUHS durung ana lan aku ora duwe akses menyang sumber lawas. Nanging ing taun 1988, aku nemokake tape 9-trek lawas sing ngemot serep saka komputer PDP11. Iku angel kanggo ngerti apa iku bisa, nanging ana wit utuh /usr/src/ kang paling file padha label karo taun 1979, kang malah katon kuna. Iki minangka edisi kaping pitu utawa turunan PWB, kaya sing dakkarepake.

Aku njupuk golek minangka basis lan manual diowahi sumber kanggo edition enem. Sawetara kode tetep padha, nanging sawetara kudu diowahi rada, ngganti token += modern dadi =+ sing wis lawas. Sawetara iku mung dibusak, lan sawetara kudu rampung ditulis maneh, nanging ora kakehan.

Lan dina iki kita bisa maca online ing TUHS kode sumber saka edition enem saka arsip, kang Dennis Ritchie wis tangan.

Miturut cara, sepisanan, fitur utama kode C sadurunge periode Kernighan lan Ritchie yaiku singkatan. Ora asring aku bisa nglebokake potongan kode tanpa panyuntingan ekstensif kanggo pas karo area tampilan sing relatif sempit ing situsku.

Wiwitane /usr/sys/ken/pipe.c ana komentar panjelasan (lan ya, ana liyane /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 buffer ora diganti wiwit edisi kaping papat. Nanging ing kene kita weruh, tanpa dokumentasi umum, manawa saluran pipa nate nggunakake file minangka panyimpenan serep!

Kanggo file LARG, padha cocog karo gendéra inode LARG, sing digunakake dening "algoritma alamat gedhe" kanggo proses pamblokiran ora langsung kanggo ndhukung sistem file sing luwih gedhe. Wiwit Ken ngandika iku luwih apik kanggo ora kanggo nggunakake, Aku bakal seneng njupuk tembung kanggo iku.

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

Komentar kasebut kanthi jelas nggambarake apa sing kedadeyan ing kene. Nanging ngerti kode kasebut ora gampang, sebagian amarga cara "pangguna struktur u»lan ndaftar R0 и R1 paramèter telpon sistem lan nilai bali liwati.

Ayo nyoba karo ialloc() sijine ing disk inode (tangan indeks), lan kanthi bantuan falloc() - Panggonan loro ing memori ngajukake. Yen kabeh dadi apik, kita bakal nyetel gendera kanggo ngenali file kasebut minangka rong ujung pipa, arahake menyang inode sing padha (sing nomer referensi bakal disetel dadi 2), lan tandhani inode minangka diowahi lan digunakake. Pay manungsa waé kanggo panjalukan kanggo iput() ing dalan kesalahan kanggo nyuda count referensi ing inode anyar.

pipe() kudu liwat R0 и R1 bali nomer deskriptor file kanggo maca lan nulis. falloc() ngasilake pointer menyang struktur file, nanging uga "bali" liwat u.u_ar0[R0] lan deskriptor file. Sing, kode nyimpen ing r file descriptor kanggo maca lan nemtokake file descriptor kanggo nulis langsung saka u.u_ar0[R0] sawise telpon kapindho falloc().

Bendera FPIPE, sing disetel nalika nggawe pipa, ngontrol prilaku fungsi kasebut rdwr () ing sys2.cnelpon rutin I/O spesifik:

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

Banjur fungsi readp() в pipe.c maca data saka pipa. Nanging luwih apik kanggo nglacak implementasine wiwit saka writep(). Maneh, kode kasebut dadi luwih rumit amarga konvensi kanggo ngliwati argumen, nanging sawetara rincian bisa diilangi.

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

Kita pengin nulis bita menyang input pipeline u.u_count. Pisanan kita kudu ngunci inode (ndeleng ngisor plock/prele).

Banjur kita mriksa counter referensi inode. Anggere loro ujung pipa tetep mbukak, counter kudu padha karo 2. Kita terus siji link (saka rp->f_inode), dadi yen counter kurang saka 2, iku kudu ateges sing proses maca wis nutup mburi pipo. Ing tembung liya, kita nyoba nulis menyang pipa sing ditutup, lan iki minangka kesalahan. Kode kesalahan pisanan EPIPE lan sinyal SIGPIPE muncul ing edisi kaping enem Unix.

Nanging sanajan conveyor mbukak, bisa uga kebak. Ing kasus iki, kita ngeculake kunci lan turu kanthi pangarep-arep yen proses liyane bakal maca saka pipa lan mbebasake papan sing cukup. Sawise tangi, kita bali menyang wiwitan, nutup kunci maneh lan miwiti siklus rekaman anyar.

Yen ana cukup ruang kosong ing pipa, banjur kita nulis data kasebut kanthi nggunakake nulisi()... Parameter i_size1 inode (yen pipeline kosong, bisa padha karo 0) nuduhake mburi data sing wis ana. Yen ana papan rekaman cukup, kita bisa ngisi pipa saka i_size1 kanggo PIPESIZ. Banjur kita ngeculake kunci lan nyoba tangi proses apa wae sing nunggu maca saka pipa. Kita bali menyang wiwitan kanggo ndeleng apa kita bisa nulis bait kaya sing dibutuhake. Yen gagal, banjur kita miwiti siklus rekaman anyar.

Biasane parameter i_mode inode digunakake kanggo nyimpen ijin r, w и x. Nanging ing cilik saka pipelines, kita sinyal sing sawetara proses nunggu nulis utawa maca nggunakake bit IREAD и IWRITE mungguh. Proses nyetel gendera lan telpon sleep(), lan samesthine sawetara proses liyane ing mangsa bakal nimbulaké wakeup().

Piandel nyata kedadeyan ing sleep() и wakeup(). Padha dileksanakake ing slp.c, sumber misuwur "Sampeyan ora samesthine kanggo ngerti iki" komentar. Untunge, kita ora kudu ngerti kode kasebut, deleng sawetara komentar:

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

Proses sing nyebabake sleep() kanggo saluran tartamtu, bisa mengko bakal woken munggah dening proses liyane, kang bakal nimbulaké wakeup() kanggo saluran sing padha. writep() и readp() koordinasi tumindak liwat telpon dipasangake kuwi. cathetan sing pipe.c tansah menehi prioritas PPIPE nalika diarani sleep(), ngono wae sleep() bisa diselani dening sinyal.

Saiki kita duwe kabeh kanggo mangerteni fungsi kasebut 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);
}

Sampeyan bisa uga luwih gampang maca fungsi iki saka ngisor menyang ndhuwur. Cabang "maca lan bali" biasane digunakake nalika ana sawetara data ing pipa. Ing kasus iki, kita nggunakake maca () kita maca minangka akeh data sing kasedhiya wiwit saka saiki f_offset maca, banjur nganyari Nilai saka offset cocog.

Ing maca sakteruse, pipa bakal kosong yen offset maca wis tekan i_size1 ing inode. Kita ngreset posisi kanggo 0 lan nyoba kanggo tangi proses apa wae sing pengin nulis menyang pipeline. Kita ngerti yen conveyor kebak, writep() bakal turu ing ip+1. Lan saiki pipeline kosong, kita bisa tangi kanggo nerusake siklus nulis.

Yen sampeyan ora duwe apa-apa kanggo maca, banjur readp() bisa nyetel gendera IREAD lan turu ing ip+2. Kita ngerti apa sing bakal tangi writep(), nalika nulis sawetara data menyang pipa.

Komentar ing readi() lan writei() bakal mbantu sampeyan ngerti yen tinimbang ngliwati parameter liwat "u"Kita bisa nambani kaya fungsi I / O normal sing njupuk file, posisi, buffer ing memori, lan ngetung jumlah bita kanggo maca utawa nulis.

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

Minangka kanggo pamblokiran "konservatif", banjur readp() и writep() mblokir inode nganti rampung karya utawa nampa asil (yaiku, nelpon wakeup). plock() и prele() bisa mung: nggunakake pesawat beda saka telpon sleep и wakeup ngidini kita tangi proses apa wae sing mbutuhake kunci sing nembe dirilis:

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

Wiwitane aku ora ngerti sebabe readp() ora njalari prele(ip) sadurunge telpon wakeup(ip+1). Ingkang kapisan inggih punika writep() nimbulaké ing siklus sawijining, iki plock(ip), kang ndadékaké kanggo deadlock yen readp() durung ngilangi pemblokiranku, mula kode kasebut kudu bisa digunakake kanthi bener. Yen sampeyan ndeleng wakeup(), banjur dadi cetha yen mung nandhani proses turu minangka siap kanggo kaleksanan, supaya ing mangsa ngarep. sched() tenan dibukak. Dadi readp() nimbulaké wakeup(), mbusak kunci, mranata IREAD lan telpon sleep(ip+2)- kabeh iki sadurunge writep() nerusake siklus.

Iki ngrampungake deskripsi conveyor ing edisi kaping enem. Kode prasaja, akibat sing adoh.

Unix edisi kaping pitu (Januari 1979) minangka rilis utama anyar (patang taun sabanjure) sing ngenalake akeh aplikasi anyar lan fitur kernel. Uga ngalami owah-owahan sing signifikan ing sambungan karo nggunakake jinis casting, serikat pekerja lan diketik penunjuk kanggo struktur. Nanging kode conveyor praktis ora owah. Kita bisa ngliwati edisi iki.

Xv6, kernel prasaja kaya Unix

Kanggo nggawe kernel Xv6 dipengaruhi dening edition enem Unix, nanging ditulis ing C modern kanggo mbukak ing prosesor x86. Kode kasebut gampang diwaca lan dingerteni. Kajaba iku, ora kaya sumber Unix karo TUHS, sampeyan bisa ngumpulake, ngowahi, lan mbukak ing liyane saka PDP 11/70. Mulane, kernel iki akeh digunakake ing universitas minangka materi pendidikan ing sistem operasi. Sumber ana ing Github.

Kode kasebut ngemot implementasine sing jelas lan wicaksana pipa.c, didhukung dening buffer ing memori tinimbang inode ing disk. Ing kene aku mung menehi definisi "pipa struktural" lan fungsi kasebut 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() nyetel negara liyane saka implementasine, kang kalebu fungsi piperead(), pipewrite() и pipeclose(). Telpon sistem nyata sys_pipe punika wrapper dipun ginakaken ing sysfile.c. Aku nyaranake maca kabeh kode. Kerumitan ing tingkat kode sumber saka edition enem, nanging luwih gampang lan luwih nyenengake kanggo maca.

Linux 0.01

Kode sumber Linux 0.01 bisa ditemokake. Iku bakal instructive sinau implementasine saka pipelines ing na fs/pipe.c. Iki nggunakake inode kanggo makili pipeline, nanging pipeline dhewe ditulis ing modern C. Yen sampeyan wis makarya liwat kode 6. edition, sampeyan ora bakal duwe alangan ing kene. Iki minangka fungsi sing katon 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 ndeleng definisi struktur, sampeyan bisa ngerteni carane count referensi inode digunakake kanggo mriksa apa operasi nulis ngasilake. SIGPIPE. Saliyane nggarap byte-by-byte, fungsi iki gampang dibandhingake karo ide sing wis kasebut ing ndhuwur. Malah logika sleep_on/wake_up ora katon asing banget.

Kernel Linux modern, FreeBSD, NetBSD, OpenBSD

Aku cepet mlayu liwat sawetara kernels modern. Ora ana sing duwe implementasi disk maneh (ora kaget). Linux duwe implementasine dhewe. Sanajan telung kernel BSD modern ngemot implementasi adhedhasar kode sing ditulis dening John Dyson, sajrone pirang-pirang taun, dheweke dadi beda banget.

Kanggo maca fs/pipe.c (ing Linux) utawa sys/kern/sys_pipe.c (ing *BSD), butuh pengabdian sing nyata. Kode dina iki babagan kinerja lan dhukungan kanggo fitur kayata vektor lan I/O asinkron. Lan rincian alokasi memori, kunci lan konfigurasi kernel kabeh beda-beda. Iki dudu sing dibutuhake perguruan tinggi kanggo kursus sistem operasi pambuka.

Oalah, aku kasengsem nggali sawetara pola lawas (kayata ngasilake SIGPIPE lan bali EPIPE nalika nulis menyang pipa tertutup) ing kabeh kernel modern sing beda-beda iki. Aku mbokmenawa ora bakal weruh komputer PDP-11 ing urip nyata, nanging isih akeh sing kudu disinaoni saka kode sing ditulis taun sadurunge aku lair.

Artikel sing ditulis dening Divi Kapoor ing 2011:Implementasi Linux Kernel Pipa lan FIFO" nyedhiyakake ringkesan babagan cara pipa (isih) bisa digunakake ing Linux. A komitmen anyar ing Linux nggambarake model interaksi pipa, sing kapabilitas ngluwihi file sauntara; lan uga nuduhake carane adoh pipelines wis teka saka "pengunci banget konservatif" saka kernel Unix edition enem.

Source: www.habr.com

Add a comment