Yadda ake aiwatar da bututun mai a cikin Unix

Yadda ake aiwatar da bututun mai a cikin Unix
Wannan labarin yana bayyana aiwatar da bututun mai a cikin kwaya ta Unix. Na dan ji takaicin labarin kwanan nan mai take "Ta yaya bututun ke aiki a Unix?"ya juya ba game da tsarin ciki. Na yi sha'awar sani kuma na haƙa cikin tsoffin kafofin don samun amsar.

Me muke magana akai?

Bututun, "wataƙila mafi mahimmancin ƙirƙira a cikin Unix," su ne ma'anar ma'anar falsafar Unix ta hanyar haɗa ƙananan shirye-shirye tare, da kuma alamar da aka sani akan layin umarni:

$ echo hello | wc -c
6

Wannan aikin ya dogara da kiran tsarin da aka samar da kwaya pipe, wanda aka kwatanta a kan takardun shaida bututu (7) и bututu (2):

Bututun bututu suna ba da tashoshi marar jagora don sadarwa ta hanyar sadarwa. Bututun yana da shigarwa (ƙarshen rubuta) da fitarwa (ƙarshen karantawa). Ana iya karanta bayanan da aka rubuta zuwa shigar da bututun a wurin fitarwa.

An ƙirƙiri bututun ta amfani da kira pipe(2), wanda ke mayar da bayanan fayiloli guda biyu: ɗaya yana nufin shigar da bututun, na biyu zuwa fitarwa.

Abubuwan da aka samo daga umarnin da ke sama yana nuna ƙirƙirar bututun da kuma kwararar bayanai ta hanyarsa daga wannan tsari zuwa wani:

$ 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

Tsarin iyaye yana kira pipe()don samun ɗora fayiloli masu bayyanawa. Ɗayan tsari na yaro ya rubuta zuwa ga hannu ɗaya, wani tsari kuma yana karanta bayanai iri ɗaya daga wani hannun. Harsashi yana amfani da dup2 don "sake suna" masu siffantawa 3 da 4 don daidaita stdin da stdout.

Ba tare da bututu ba, harsashi zai rubuta sakamakon wani tsari zuwa fayil kuma ya wuce shi zuwa wani tsari don karanta bayanan daga fayil ɗin. A sakamakon haka, za mu ɓata ƙarin albarkatu da sarari diski. Koyaya, bututun mai suna da kyau ba kawai saboda suna ba ku damar guje wa amfani da fayilolin wucin gadi ba:

Idan tsari yana ƙoƙarin karantawa daga bututun da babu komai to read(2) zai toshe har sai bayanan sun samu. Idan tsari yayi ƙoƙarin rubutawa zuwa cikakken bututun, to write(2) zai toshe har sai an karanta isassun bayanai daga bututun don yin rubutun.

Kamar buƙatun POSIX, wannan abu ne mai mahimmanci: rubuta zuwa bututun har zuwa PIPE_BUF bytes (aƙalla 512) dole ne ya zama atomic ta yadda matakai zasu iya sadarwa da juna ta hanyar bututun ta hanyar da fayiloli na yau da kullum (wanda ba ya samar da irin wannan garantin) ba zai iya ba.

Lokacin amfani da fayil na yau da kullun, tsari zai iya rubuta duk abin da yake fitarwa zuwa gare shi kuma ya wuce shi zuwa wani tsari. Ko matakai na iya aiki a cikin yanayin daidaitacce, ta amfani da hanyar siginar waje (kamar semaphore) don sanar da juna lokacin da aka kammala rubutu ko karantawa. Masu jigilar kaya sun cece mu daga duk wannan matsala.

Me muke nema?

Zan yi bayanin shi a cikin sassauƙan kalmomi domin ya sami sauƙi a gare ku don tunanin yadda mai ɗaukar kaya zai iya aiki. Kuna buƙatar keɓance majigi da wasu yanayi a ƙwaƙwalwar ajiya. Kuna buƙatar ayyuka don ƙarawa da cire bayanai daga buffer. Kuna buƙatar wasu hanyoyi don kiran ayyuka yayin karantawa da rubuta ayyukan akan masu siffanta fayil. Kuma kuna buƙatar makullai don aiwatar da halayen musamman da aka bayyana a sama.

Yanzu a shirye muke don yin tambayoyi ga lambar tushe ta kernel a ƙarƙashin hasken fitila mai haske don tabbatarwa ko karyata tsarin tunanin mu mara kyau. Amma a koyaushe a shirya don abin da ba zato ba tsammani.

Ina muke nema?

Ban san inda kwafin shahararren littafina yake ba "Littafin zaki"tare da lambar tushe Unix 6, amma godiya ga Ƙungiyar Unix Heritage Society za ku iya bincika kan layi a lambar tushe har ma da tsofaffin nau'ikan Unix.

Yawo ta cikin rumbun adana kayan tarihi na TUHS kamar ziyartar gidan kayan gargajiya ne. Za mu iya duba tarihin mu da aka raba, kuma ina mutunta shekaru masu yawa na ƙoƙari na dawo da duk wannan abu kadan kadan daga tsoffin kaset da kwafi. Kuma ina sane da waɗancan guntun da har yanzu ba a rasa ba.

Bayan mun gamsu da sha'awarmu game da tsohon tarihin isar da sako, za mu iya duba kernels na zamani don kwatantawa.

Af, pipe shine tsarin kiran lamba 42 a cikin tebur sysent[]. Daidaito?

Kwayoyin Unix na Gargajiya (1970-1974)

Ban sami wata alama ba pipe(2) kuma ba a ciki PDP-7 Unix (Janairu 1970), ba a bugu na farko na Unix (Nuwamba 1971), kuma ba a cikin lambar tushe da ba ta cika ba bugu na biyu (Yuni 1972).

TUHS ya bayyana cewa bugu na uku na Unix (Fabrairu 1973) ya zama sigar farko tare da masu jigilar kaya:

Unix 1973rd Edition shine sigar ƙarshe tare da kernel da aka rubuta cikin yaren taro, amma kuma sigar farko tare da bututun mai. A lokacin XNUMX, an gudanar da aikin don inganta bugu na uku, an sake rubuta kwaya a cikin C, don haka bugu na huɗu na Unix ya bayyana.

Wani mai karatu ya sami hoton takardar da Doug McIlroy ya ba da shawarar "haɗin shirye-shirye kamar tiyon lambu."

Yadda ake aiwatar da bututun mai a cikin Unix
A cikin littafin Brian KernighanUnix: Tarihi da Memoir", a cikin tarihin bayyanar masu jigilar kaya, an kuma ambaci wannan takarda: "... an rataye shi a bango a ofishina a Bell Labs na shekaru 30." nan hira da McIlroy, da wani labari daga Aikin McIlroy, wanda aka rubuta a cikin 2014:

Lokacin da Unix ya fito, sha'awar da nake yi da coroutines ya sa na tambayi marubucin OS, Ken Thompson, don ba da damar bayanan da aka rubuta zuwa tsari don zuwa ba kawai ga na'urar ba, har ma don fitarwa zuwa wani tsari. Ken ya yanke shawarar zai yiwu. Duk da haka, a matsayin ɗan ƙarami, yana son kowane tsarin aiki ya taka muhimmiyar rawa. Shin rubutu kai tsaye tsakanin matakai da gaske yana da babban fa'ida akan rubutawa zuwa matsakaicin fayil? Sai kawai lokacin da na ba da takamaiman shawara tare da suna "bututu" mai ban sha'awa da bayanin ma'anar ma'amala tsakanin hanyoyin da Ken a ƙarshe ya ce: "Zan yi!"

Kuma yayi. Wata maraice mai ban tsoro, Ken ya canza kwaya da harsashi, ya gyara daidaitattun shirye-shirye don daidaita yadda suka karɓi shigarwar (wanda zai iya fitowa daga bututun mai), sannan ya canza sunayen fayil. Kashegari, an fara amfani da bututun mai sosai a aikace. A ƙarshen mako, sakatarorin suna amfani da su don aika da takardu daga na'urorin sarrafa kalmomi zuwa na'urar bugawa. Ba da daɗewa ba, Ken ya maye gurbin API na asali da kuma daidaitawa don nannade amfani da bututun mai tare da tarurruka masu tsabta, waɗanda aka yi amfani da su tun daga lokacin.

Abin takaici, lambar tushe don bugu na uku na Unix kernel ya ɓace. Kuma kodayake muna da lambar tushen kernel da aka rubuta a cikin C bugu na hudu, wanda aka saki a watan Nuwamba 1973, amma ya fito watanni da yawa kafin a sake shi a hukumance kuma bai ƙunshi aiwatar da bututun mai ba. Abin kunya ne cewa lambar tushe na wannan aikin Unix na almara ya ɓace, watakila har abada.

Muna da takaddun rubutu don pipe(2) daga duka abubuwan da aka fitar, don haka zaku iya farawa ta hanyar bincika takaddun bugu na uku (ga wasu kalmomi, da aka ja layi a “da hannu”, jigon ma’anar ^H, wanda ke biyo baya!). Wannan proto-pipe(2) an rubuta shi cikin yaren taro kuma yana dawo da bayanin fayil ɗaya kawai, amma ya riga ya samar da ainihin aikin da ake tsammani:

Kiran tsarin bututu yana haifar da hanyar shigarwa/fitarwa da ake kira pipeline. Ana iya amfani da siffanta fayil ɗin da aka dawo don ayyukan karantawa da rubutawa. Lokacin da aka rubuta wani abu zuwa bututun, ana adana bayanai har zuwa 504 bytes, bayan haka an dakatar da aikin rubutun. Lokacin karantawa daga bututun, ana ɗaukar bayanan buffered.

A shekara mai zuwa an sake rubuta kernel a C, kuma bututu(2) a bugu na hudu ya samu kamannin sa na zamani tare da samfurin"pipe(fildes)»:

Kiran tsarin bututu yana haifar da hanyar shigarwa/fitarwa da ake kira pipeline. Ana iya amfani da masu siffanta fayil ɗin da aka dawo a cikin ayyukan karantawa da rubutawa. Lokacin da aka rubuta wani abu zuwa bututun, ana amfani da abin da aka dawo da shi a cikin r1 (resp. fildes [1]), an ajiye shi zuwa 4096 bytes na bayanai, bayan haka an dakatar da aikin rubutun. Lokacin karantawa daga bututun, hannun ya koma r0 (resp. fildes[0]) yana ɗaukar bayanan.

Ana ɗauka cewa da zarar an bayyana bututun, hanyoyin sadarwa guda biyu (ko fiye) (wanda aka ƙirƙira ta hanyar kira na gaba zuwa ga. Cokali mai yatsa) zai canja wurin bayanai daga bututun ta amfani da kira karanta и rubuta.

Harsashi yana da ma'anar ma'anar tsarin tafiyar da layi na layi wanda aka haɗa ta bututun.

Kiran da za a karanta daga bututun da ba komai (wanda ya ƙunshi bayanan da ba a ɓoye ba) wanda ke da ƙarshensa ɗaya kawai (duk masu siffanta fayilolin rubutu an rufe su) suna dawo da "ƙarshen fayil". Kira don rubutawa a cikin irin wannan yanayin ba a yi watsi da su ba.

Farko aiwatar da bututun da aka kiyaye amfani zuwa bugu na biyar na Unix (Yuni 1974), amma kusan yayi kama da wanda ya bayyana a cikin sakin gaba. An ƙara sharhi, don haka za ku iya tsallake bugu na biyar.

Buga na shida na Unix (1975)

Bari mu fara karanta lambar tushe ta Unix bugu na shida (Mayu 1975). Godiya sosai ga Lions yana da sauƙin samu fiye da tushen sigar farko:

Shekaru da yawa littafin Lions ita ce kawai daftarin aiki akan kernel Unix da ake samu a wajen Bell Labs. Ko da yake lasisin bugu na shida ya ƙyale malamai su yi amfani da lambar tushe, lasisin bugu na bakwai ya ware wannan yuwuwar, don haka an rarraba littafin a cikin nau'in kwafi da aka rubuta ba bisa ƙa'ida ba.

A yau za ku iya siyan sake buga littafin, wanda murfinsa ya nuna ɗalibai a injin kwafi. Kuma godiya ga Warren Toomey (wanda ya fara aikin TUHS) zaka iya saukewa Fayil na PDF tare da lambar tushe don bugu na shida. Ina so in ba ku ra'ayi na irin ƙoƙarin da aka yi wajen ƙirƙirar fayil ɗin:

Fiye da shekaru 15 da suka wuce, na buga kwafin lambar tushe da aka bayar a ciki Lions, saboda ba na son ingancin kwafin nawa daga adadin wasu kwafin da ba a san adadinsu ba. TUHS bai wanzu ba tukuna kuma ban sami damar zuwa tsoffin hanyoyin ba. Amma a cikin 1988, na sami wani tsohon kaset mai lamba 9 wanda ke ɗauke da maajiyar kwamfuta ta PDP11. Yana da wuya a gane ko yana aiki, amma akwai bishiyar /usr/src/ maras kyau wanda aka yiwa yawancin fayilolin lakabi da shekara ta 1979, wanda har ma ya yi kama da daɗaɗɗen. Shi ne bugu na bakwai ko PWB na asali, kamar yadda na yi imani.

Na ɗauki abin nemo a matsayin tushe kuma da hannu na gyara tushen zuwa bugu na shida. Wasu daga cikin lambar sun kasance iri ɗaya, amma wasu dole ne a gyara su kaɗan, suna canza alamar zamani += zuwa tsohon =+. Wasu abubuwa an goge su kawai, wasu kuma dole ne a sake rubuta su gaba ɗaya, amma ba su yi yawa ba.

Kuma a yau za mu iya karanta kan layi akan TUHS lambar tushe na bugu na shida daga Archive, wanda Dennis Ritchie ke da hannu.

Af, a kallon farko, babban fasalin C-code kafin lokacin Kernighan da Ritchie shine. takaice. Ba sau da yawa ba ne zan iya saka ɓangarorin lamba ba tare da ɗimbin gyare-gyare ba don dacewa da ƙunƙun wurin nuni akan rukunin yanar gizona.

A farkon /usr/sys/ken/pipe.c akwai sharhin bayani (kuma eh, akwai ƙari /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

Girman buffer bai canza ba tun bugu na huɗu. Amma a nan mun ga, ba tare da wani takardun jama'a ba, cewa bututun da aka taɓa amfani da fayiloli azaman ajiyar ajiya!

Dangane da fayilolin LARG, sun dace da inode flag LARG, wanda "manyan adireshi algorithm" ke amfani dashi don aiwatarwa tubalan kai tsaye don tallafawa manyan fayilolin fayiloli. Tun da Ken ya ce yana da kyau kada in yi amfani da su, zan yi farin ciki da maganarsa.

Anan shine ainihin kiran tsarin 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;
}

Sharhin ya bayyana a fili abin da ke faruwa a nan. Amma fahimtar lambar ba ta da sauƙi, wani ɓangare saboda hanyar "struct user u» da rajista R0 и R1 ana wuce sigogin kiran tsarin da ƙimar dawowa.

Bari mu gwada da ialloc() saka faifai inode (hannun hannu), kuma tare da taimakon falloc() - sanya biyu a ƙwaƙwalwar ajiya fayil. Idan komai ya yi kyau, za mu sanya tutoci don tantance waɗannan fayiloli a matsayin ƙarshen bututun, mu nuna su zuwa inode ɗaya (wanda ƙididdigarsa za a saita zuwa 2), sa'annan mu yiwa inode alama kamar yadda aka gyara kuma ana amfani da su. Kula da buƙatun zuwa iput() a cikin hanyoyin kuskure don rage ƙididdiga a cikin sabon inode.

pipe() dole ta hanyar R0 и R1 mayar da lambobin bayanin fayil don karatu da rubutu. falloc() yana mayar da mai nuni zuwa tsarin fayil, amma kuma "ya dawo" ta hanyar u.u_ar0[R0] da kuma bayanin fayil. Wato lambar tana ajiyewa r bayanin fayil don karantawa kuma sanya mai siffanta fayil don rubutawa kai tsaye daga u.u_ar0[R0] bayan kira na biyu falloc().

Flag FPIPE, wanda muka saita lokacin ƙirƙirar bututun, yana sarrafa halayen aikin rdwr () a cikin sys2.ckiran takamaiman ayyukan 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);
    }
        /* … */
}

Sai aikin readp() в pipe.c karanta bayanai daga bututun. Amma yana da kyau a gano aiwatar da farawa daga writep(). Bugu da ƙari, lambar ta zama mafi rikitarwa saboda ƙa'idodin gardama masu wucewa, amma ana iya barin wasu cikakkun bayanai.

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

Muna son rubuta bytes zuwa shigarwar bututun u.u_count. Da farko muna buƙatar kulle inode (duba ƙasa plock/prele).

Sa'an nan kuma mu duba inode reference counter. Muddin duka ƙarshen bututun ya kasance a buɗe, ƙididdiga ya kamata ya zama daidai da 2. Muna riƙe hanyar haɗi ɗaya (daga rp->f_inode), don haka idan ma'aunin bai wuce 2 ba, dole ne ya nuna cewa tsarin karatun ya rufe ƙarshen bututun. A wasu kalmomi, muna ƙoƙarin rubutawa zuwa bututun da ke rufe, kuma wannan kuskure ne. Lambar kuskure ta farko EPIPE da sigina SIGPIPE ya bayyana a cikin bugu na shida na Unix.

Amma ko da na'urar a bude take, yana iya cika. A wannan yanayin, mun saki makullin kuma mu kwanta barci da fatan cewa wani tsari zai karanta daga bututun kuma ya ba da isasshen sarari a ciki. Bayan mun farka, mun koma farkon, sake rataya kulle kuma mu fara sabon zagayowar rikodi.

Idan akwai isasshen sarari kyauta a cikin bututun, to muna rubuta bayanai zuwa gare shi ta amfani da shi rubuta (). Siga i_size1 a inode (idan bututun babu kowa zai iya zama daidai da 0) yana nuna ƙarshen bayanan da ya riga ya ƙunshi. Idan akwai isasshen wurin rikodi, za mu iya cika bututun daga i_size1 to PIPESIZ. Sa'an nan kuma mu saki makullin kuma muyi ƙoƙari mu farka duk wani tsari da ke jiran karantawa daga bututun. Mu koma farkon mu ga ko za mu iya rubuta yawan bytes kamar yadda muke bukata. Idan ta kasa, to za mu fara sabon zagayowar rikodi.

Yawancin lokaci siga i_mode ana amfani da inode don adana izini r, w и x. Amma game da bututun, muna nuna alamar cewa wani tsari yana jiran rubutu ko karanta ta amfani da bits IREAD и IWRITE bi da bi. Tsarin yana saita tuta da kira sleep(), kuma ana sa ran cewa wani tsari a nan gaba zai haifar wakeup().

Ainihin sihiri yana faruwa a ciki sleep() и wakeup(). Ana aiwatar da su a cikin slp.c, tushen sanannen "Ba a sa ran ku fahimci wannan" sharhi. Abin farin ciki, ba lallai ne mu fahimci lambar ba, kawai duba wasu sharhi:

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

Tsarin da ke haifar da shi sleep() don wata tashar ta musamman, daga baya za a iya farka da wani tsari, wanda zai haifar wakeup() ga wannan tashar. writep() и readp() daidaita ayyukansu ta irin waɗannan kira guda biyu. lura cewa pipe.c koyaushe yana ba da fifiko PPIPE lokacin da ake kira sleep(), to haka ne sleep() ana iya katse shi da sigina.

Yanzu muna da komai don fahimtar aikin 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);
}

Kuna iya samun sauƙin karanta wannan aikin daga ƙasa zuwa sama. Yawanci ana amfani da reshen "karanta da dawowa" lokacin da akwai wasu bayanai a cikin bututun. A wannan yanayin, muna amfani karatu() mun karanta adadin bayanai kamar yadda ake samu tun daga na yanzu f_offset karanta, sa'an nan kuma sabunta darajar daidaitattun daidaitattun.

A kan karatun na gaba, bututun zai zama fanko idan an gama karantawa i_size1 da inode. Mun sake saita matsayi zuwa 0 kuma muyi ƙoƙarin farka duk wani tsari da ke son rubutawa zuwa bututun. Mun san cewa idan na'urar ta cika. writep() zai yi barci a kan ip+1. Kuma yanzu da bututun ya zama fanko, za mu iya tada shi don ci gaba da zagayowar rubutunsa.

Idan ba ku da abin karantawa, to readp() iya kafa tuta IREAD kuma yayi barci akan ip+2. Mun san abin da zai tashe shi writep(), lokacin da ya rubuta wasu bayanai zuwa bututun.

Sharhi akan readi () dan rubuta () zai taimake ka ka fahimci cewa maimakon wuce sigogi ta hanyar "u"Zamu iya bi da su kamar ayyukan I/O na yau da kullun waɗanda ke ɗaukar fayil, matsayi, ma'auni a ƙwaƙwalwar ajiya, da ƙidaya adadin bytes don karantawa ko rubutawa.

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

Amma ga "masu ra'ayin mazan jiya" tarewa, to readp() и writep() toshe inode har sai sun gama aikinsu ko sun sami sakamako (wato kira wakeup). plock() и prele() aiki a sauƙaƙe: ta amfani da saitin kira daban-daban sleep и wakeup ba mu damar tada duk wani tsari da ke buƙatar kulle da muka saki yanzu:

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

Da farko na kasa gane dalili readp() baya haddasawa prele(ip) kafin kiran wakeup(ip+1). Abu na farko shine writep() yana haifar da zagayowar sa, wannan plock(ip), wanda ke kaiwa ga mutuwa idan readp() Har yanzu ba su cire shinge na ba, don haka ko ta yaya lambar ta yi aiki daidai. Idan ka duba wakeup(), to, ya bayyana a fili cewa kawai alamar tsarin barci yana shirye don aiwatarwa, don haka a nan gaba sched() da gaske kaddamar da shi. Don haka readp() haddasawa wakeup(), yana cire kulle, saita IREAD da kira sleep(ip+2)- duk wannan kafin writep() sake sake zagayowar.

Wannan ya kammala bayanin masu jigilar kaya a bugu na shida. Sauƙaƙan lamba, sakamako mai nisa.

Buga na bakwai na Unix (Janairu 1979) sabon babban saki ne (shekaru hudu bayan haka) wanda ya gabatar da sabbin aikace-aikace da fasalolin kwaya. Hakanan ya sami sauye-sauye masu mahimmanci dangane da amfani da nau'in simintin gyare-gyare, ƙungiyoyin ƙungiyoyi da masu nuni zuwa ga tsari. Duk da haka na'ura code a zahiri ba canzawa. Za mu iya tsallake wannan fitowar.

Xv6, kwaya mai sauƙi kamar Unix

Don ƙirƙirar kwaya Xv6 An rinjayi bugu na shida na Unix, amma an rubuta shi a cikin C na zamani don aiki akan na'urori masu sarrafa x86. Lambar yana da sauƙin karantawa kuma ana iya fahimta. Bugu da ƙari, ba kamar tushen Unix tare da TUHS ba, kuna iya haɗa shi, gyara shi, da gudanar da shi akan wani abu banda PDP 11/70. Don haka, ana amfani da wannan kwaya sosai a cikin jami'o'i azaman kayan ilimi akan tsarin aiki. Sources suna kan Github.

Lambar ya ƙunshi aiwatarwa bayyananne da tunani bututu.c, mai goyan bayan buffer a ƙwaƙwalwar ajiya maimakon inode akan faifai. Anan na samar da ma'anar "bututun tsarin" kawai da aikin 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() saita yanayin sauran aiwatarwa, wanda ya haɗa da ayyuka piperead(), pipewrite() и pipeclose(). Kiran tsarin gaskiya sys_pipe nade ne da aka aiwatar a ciki sysfile.c. Ina ba da shawarar karanta lambar sa gaba ɗaya. Matsakaicin yana a matakin lambar tushe na bugu na shida, amma ya fi sauƙi kuma ya fi jin daɗin karantawa.

Linux 0.01

Ana iya samun lambar tushe na Linux 0.01. Zai zama darasi don nazarin aiwatar da bututun mai a cikin nasa fs/pipe.c. Wannan yana amfani da inode don wakiltar bututun, amma bututun kansa an rubuta shi a cikin zamani C. Idan kun yi aiki ta hanyar lambar bugun 6, ba za ku sami matsala a nan ba. Wannan shine yadda aikin yayi kama 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;
}

Ba tare da duba ma'anar tsarin ba, za ku iya gano yadda ake amfani da ƙididdigar inode don bincika ko aikin rubuta ya haifar da SIGPIPE. Baya ga aiki ta byte-byte, wannan aikin yana da sauƙin kwatanta tare da ra'ayoyin da aka bayyana a sama. Ko da hankali sleep_on/wake_up bai yi kama da baƙo ba.

Kwayoyin Linux na zamani, FreeBSD, NetBSD, OpenBSD

Na yi sauri na bi ta wasu kwayayen zamani. Babu ɗayansu da ke da aiwatar da faifai kuma (ba abin mamaki ba ne). Linux yana da nasa aiwatarwa. Ko da yake kwayayen BSD uku na zamani sun ƙunshi aiwatarwa bisa lambar da John Dyson ya rubuta, tsawon shekaru sun bambanta da juna.

Don karantawa fs/pipe.c (a Linux) ko sys/kern/sys_pipe.c (akan *BSD), yana buƙatar sadaukarwa ta gaske. Lambar yau tana game da aiki da goyan baya ga fasali kamar vector da I/O asynchronous. Kuma cikakkun bayanai na rarraba ƙwaƙwalwar ajiya, makullai da tsarin kwaya duk sun bambanta sosai. Wannan ba shine abin da kwalejoji ke buƙata don kwas ɗin tsarin aiki na gabatarwa ba.

Duk da haka dai, na yi sha'awar tono wasu tsofaffin alamu (kamar ƙirƙira SIGPIPE da dawowa EPIPE lokacin rubutawa zuwa bututun da aka rufe) a cikin duk waɗannan kernels na zamani daban-daban. Wataƙila ba zan taɓa ganin kwamfutar PDP-11 a rayuwa ta ainihi ba, amma har yanzu akwai abubuwa da yawa da zan koya daga lambar da aka rubuta shekaru da yawa kafin a haife ni.

Labarin da Divi Kapoor ya rubuta a 2011:Aiwatar da Linux Kernel na Pipes da FIFOs"yana ba da bayyani na yadda bututun (har yanzu) ke aiki a cikin Linux. A ƙaddamar da kwanan nan a cikin Linux yana kwatanta tsarin hulɗar bututun mai, wanda ƙarfinsa ya wuce na fayilolin wucin gadi; ya kuma nuna yadda bututun mai suka zo daga "kulle masu ra'ayin mazan jiya" na bugu na shida na Unix kernel.

source: www.habr.com

Add a comment