Чӣ тавр қубурҳо дар Unix амалӣ карда мешаванд

Чӣ тавр қубурҳо дар Unix амалӣ карда мешаванд
Ин мақола татбиқи қубурҳоро дар ядрои Unix тавсиф мекунад. Ман то андозае ноумед шудам, ки мақолаи ба наздикӣ таҳти унвони "Чӣ тавр қубурҳо дар Unix кор мекунанд?"маълум шуд не дар бораи сохтори дохилӣ. Ман кунҷков шудам ва манбаҳои кӯҳнаро кофтам, то ҷавоб ёбам.

Мо дар бораи чӣ гап мезанем?

Pipelines, "эҳтимолан муҳимтарин ихтироъ дар Unix", як хусусияти муайянкунандаи фалсафаи аслии Unix оид ба пайваст кардани барномаҳои хурд ва инчунин аломати шинос дар сатри фармон мебошанд:

$ echo hello | wc -c
6

Ин функсия аз занги системаи ядроӣ вобаста аст pipe, ки дар саҳифаҳои ҳуҷҷатҳо тасвир шудааст қубур (7) и қубур (2):

Қубурҳо як канали якҷонибаро барои иртибот байни равандҳо таъмин мекунанд. Қубур дорои вуруд (охири навиштан) ва баромад (охири хондан) дорад. Маълумотеро, ки ба вуруди қубур навишта шудааст, дар баромад хондан мумкин аст.

Қубур бо истифода аз занг сохта мешавад pipe(2), ки ду тавсифкунандаи файлро бармегардонад: яке ба вуруди лӯла ишора мекунад, дуюмаш ба баромад.

Натиҷаи пайгирӣ аз фармони боло эҷоди қубур ва ҷараёни маълумотро тавассути он аз як раванд ба раванди дигар нишон медиҳад:

$ 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

Раванди волидайн занг мезанад pipe()Барои ба даст овардани дескрипторҳои файли насбшуда. Як раванди кӯдак ба як даста менависад ва раванди дигар ҳамон маълумотро аз дастаки дигар мехонад. Снаряд dup2-ро барои "номи иваз кардани номи" дескрипторҳои 3 ва 4 барои мувофиқат бо stdin ва stdout истифода мебарад.

Бе қубурҳо, ҷабҳа бояд натиҷаи як равандро ба файл нависед ва онро ба раванди дигар гузаронад, то маълумотро аз файл хонед. Дар натиҷа, мо захираҳои бештар ва фазои дискро сарф мекунем. Аммо, қубурҳо на танҳо аз он сабаб хубанд, ки онҳо ба шумо имкон медиҳанд, ки аз истифодаи файлҳои муваққатӣ канорагирӣ кунед:

Агар раванд кӯшиш кунад, ки аз лӯлаи холӣ хонда шавад read(2) то дастрас шудани маълумот баста мешавад. Агар раванд кӯшиш кунад, ки ба лӯлаи пурра нависед, пас write(2) то он даме, ки маълумоти кофӣ аз лӯла барои иҷрои навиштан хонда нашавад, блок хоҳад кард.

Мисли талаботи POSIX, ин як моликияти муҳим аст: навиштан ба қубур то PIPE_BUF байтҳо (ҳадди ақал 512) бояд атомӣ бошанд, то равандҳо бо ҳам тавассути қубур тавре муошират кунанд, ки файлҳои муқаррарӣ (ки ин гуна кафолатҳоро таъмин намекунанд) наметавонанд.

Ҳангоми истифодаи файли муқаррарӣ, раванд метавонад тамоми натиҷаи худро ба он нависад ва онро ба раванди дигар гузаронад. Ё равандҳо метавонанд дар ҳолати хеле мувозӣ кор кунанд, бо истифода аз механизми сигнализатсияи беруна (ба монанди семафор) барои огоҳ кардани ҳамдигар ҳангоми ба итмом расидани навиштан ё хондан. Конвейерхо моро аз хамаи ин душворй начот медиханд.

Мо чӣ меҷӯем?

Ман онро бо ибораҳои оддӣ шарҳ медиҳам, то тасаввур кунед, ки конвейер чӣ гуна кор карда метавонад. Ба шумо лозим меояд, ки буфер ва баъзе ҳолатро дар хотира ҷудо кунед. Барои илова ва хориҷ кардани маълумот аз буфер ба шумо функсияҳо лозиманд. Барои даъват кардани функсияҳо дар давоми амалиёти хондан ва навиштан дар дескрипторҳои файл ба шумо якчанд восита лозим мешавад. Ва барои татбиқи рафтори махсуси дар боло тавсифшуда ба шумо қуфлҳо лозим аст.

Ҳоло мо омодаем, ки коди сарчашмаи ядроро дар зери чароғҳои равшан пурсед, то модели рӯҳии норавшани моро тасдиқ ё рад кунем. Аммо ҳамеша ба ногаҳонӣ омода бошед.

Мо ба куҷо менигарем?

Ман намедонам, ки нусхаи китоби машҳури ман дар куҷост "Китоби шерон"бо рамзи сарчашмаи Unix 6, аммо ба шарофати Ҷамъияти мероси Unix шумо метавонед онлайн ҷустуҷӯ кунед рамзи сарчашма ҳатто версияҳои кӯҳнаи Unix.

Сайругашт дар бойгонии TUHS ба тамошои осорхона монанд аст. Мо метавонем ба таърихи муштараки худ назар кунем ва ман ба талошҳои чандинсола барои оҳиста-оҳиста барқарор кардани ҳамаи ин мавод аз наворҳо ва чопҳои кӯҳна эҳтиром дорам. Ва ман аз он пораҳое, ки то ҳол гум шудаанд, ба таври ҷиддӣ огоҳам.

Кунҷковии моро дар бораи таърихи қадимии конвейерҳо қонеъ карда, мо метавонем барои муқоиса ба ядроҳои муосир назар андозем.

Бо роҳи, pipe рақами занги системавӣ 42 дар ҷадвал аст sysent[]. Тасодуф?

Ядроҳои анъанавии Unix (1970–1974)

Ман ҳеҷ осоре наёфтам pipe(2) на дар PDP-7 Unix (январи соли 1970), на дар нашри якуми Unix (ноябри соли 1971), на дар коди ибтидоии нопурра нашри дуюм (июни соли 1972).

Дар ин бора TUHS хабар медиҳад нашри сеюми Unix (феврали соли 1973) версияи аввалини конвейерҳо гардид:

Нашри 1973-юми Unix версияи охирин бо ядрое буд, ки бо забони ассемблер навишта шудааст, аммо версияи аввалини қубурҳо низ буд. Дар давоми соли XNUMX барои такмил додани нашри сеюм кор бурда шуд, ядро ​​дар C аз нав навишта шуд ва ҳамин тавр нашри чоруми Unix пайдо шуд.

Як хонанда скани ҳуҷҷатеро ёфт, ки дар он Даг Макилрой идеяи "пайваст кардани барномаҳо ба мисли шланги боғ"-ро пешниҳод кардааст.

Чӣ тавр қубурҳо дар Unix амалӣ карда мешаванд
Дар китоби Брайан КерниганUnix: Таърих ва ёддоштҳо", дар таърихи пайдоиши конвейерҳо ин ҳуҷҷат низ зикр шудааст: "... он дар офиси ман дар лабораторияи Bell 30 сол дар девор овезон буд." Ин ҷо мусоҳиба бо McIlroy, ва достони дигар аз Кори McIlroy, ки дар соли 2014 навишта шудааст:

Вақте ки Unix баромад, шавқу ҳаваси ман ба корутинҳо маро водор кард, ки аз муаллифи ОС Кен Томпсон хоҳиш кунам, ки маълумоте, ки ба раванд навишта шудааст, на танҳо ба дастгоҳ, балки инчунин ба раванди дигар интиқол дода шавад. Кен тасмим гирифт, ки ин имконпазир аст. Аммо, ҳамчун минималист, ӯ мехост, ки ҳар як функсияи система нақши муҳим бозад. Оё бевосита дар байни равандҳо навиштан дар ҳақиқат бартарии калон нисбат ба навиштан ба файли фосилавӣ аст? Танҳо вақте ки ман пешниҳоди мушаххасеро бо номи ҷолиби "қубур" ва тавсифи синтаксиси мутақобила байни равандҳо пешниҳод кардам, ки Кен ниҳоят хитоб кард: "Ман ин корро мекунам!"

Ва кард. Як шоми тақдирсоз, Кен ядро ​​ва қабатро иваз кард, якчанд барномаҳои стандартиро ислоҳ кард, то онҳо чӣ гуна воридотро қабул кунанд (ки метавонад аз қубур пайдо шавад) ва инчунин номи файлҳоро тағир дод. Рӯзи дигар, қубурҳо дар барномаҳо хеле васеъ истифода мешуданд. Дар охири ҳафта котибон онҳоро барои фиристодани ҳуҷҷатҳо аз коркардкунандагони матн ба чопгар истифода мебурданд. Чанде пас, Кен API ва синтаксиси аслӣ барои печонидани истифодаи қубурҳоро бо конвенсияҳои тозатаре иваз кард, ки аз он вақт инҷониб истифода мешаванд.

Мутаассифона, рамзи сарчашмаи нашри сеюми ядрои Unix гум шудааст. Ва гарчанде ки мо рамзи сарчашмаи ядроро дар C навишта шудаем нашри чорум, ки мохи ноябри соли 1973 бароварда шуда буд, вале он чанд мох пеш аз интишори расмй ба табъ расида буд ва татбики конвейерро дарбар намегирад. Афсӯс, ки рамзи сарчашмаи ин функсияи афсонавии Unix гум мешавад, шояд то абад.

Мо ҳуҷҷатҳои матнӣ барои pipe(2) аз ҳарду нашр, то шумо метавонед бо ҷустуҷӯи ҳуҷҷатҳо оғоз кунед нашри сеюм (барои калимаҳои муайяне, ки зери хаташ «дастӣ», як қатори ҳарфҳои ^H, пас аз зерхат!) Ин прото-pipe(2) бо забони ассемблер навишта шудааст ва танҳо як дескриптори файлро бармегардонад, аммо аллакай вазифаи асосии интизоршударо таъмин мекунад:

Зангҳои системавӣ қубур механизми воридот/барориро эҷод мекунад, ки қубур номида мешавад. Дескриптори файли баргардонидашуда метавонад барои амалиёти хондан ва навиштан истифода шавад. Вақте ки чизе ба лӯла навишта мешавад, то 504 байт маълумот буфер карда мешавад ва пас аз он раванди навиштан боздошта мешавад. Ҳангоми хондан аз қубур маълумоти буферӣ гирифта мешавад.

Дар соли оянда ядро ​​дар C аз нав навишта шуда буд ва қубур (2) дар нашри чорум намуди муосири худро бо прототип ба даст овард "pipe(fildes)»:

Зангҳои системавӣ қубур механизми воридот/барориро эҷод мекунад, ки қубур номида мешавад. Дескрипторҳои файли баргардонида метавонанд дар амалиёти хондан ва навиштан истифода шаванд. Вақте ки чизе ба қубур навишта мешавад, дастае, ки дар r1 баргардонида мешавад (файлҳо[1]) истифода мешавад, ки ба 4096 байт маълумот буфер карда мешавад ва пас аз он раванди навиштан боздошта мешавад. Ҳангоми хондан аз қубур, даста ба r0 баргашт (файлҳо [0]) маълумотро мегирад.

Тахмин меравад, ки пас аз муайян кардани қубур, ду (ё зиёда) равандҳои иртиботӣ (бо зангҳои минбаъда ба Голф) тавассути зангҳо маълумотро аз қубур интиқол медиҳад хондан и навиштан.

Дар қабат синтаксис барои муайян кардани массиви хаттии равандҳо, ки тавассути қубур пайваст шудаанд, дорад.

Зангҳо барои хондан аз лӯлаи холӣ (ки дорои маълумоти буферӣ нестанд), ки танҳо як нӯги доранд (ҳамаи тавсифкунандагони файли хаттӣ бастаанд) "охири файл" -ро бармегардонанд. Зангҳо барои навиштан дар вазъияти ба ин монанд сарфи назар карда мешаванд.

Аввалин татбиқи қубур нигоҳ дошта шудааст дахл дорад ба нашри панҷуми Unix (июни соли 1974), аммо он ба он чизе, ки дар нашри навбатӣ пайдо шуда буд, қариб якхела аст. Шарҳҳо нав илова карда шуданд, бинобар ин шумо метавонед нашри панҷумро гузаред.

Нашри шашуми Unix (1975)

Биёед ба хондани коди сарчашмаи Unix оғоз кунем нашри шашум (май соли 1975). Ташаккури зиёд ба шерон пайдо кардани он нисбат ба манбаъҳои версияҳои қаблӣ хеле осонтар аст:

Солхои зиёд китоб шерон ягона ҳуҷҷати ядрои Unix буд, ки берун аз Bell Labs дастрас буд. Ҳарчанд литсензияи нашри шашум ба муаллимон имкон дод, ки рамзи ибтидоии онро истифода баранд, иҷозатномаи нашри ҳафтум ин имконро истисно кард, аз ин рӯ китоб дар шакли нусхаҳои ғайриқонунии чопшуда паҳн карда шуд.

Имрӯз шумо метавонед нусхаи такрории китобро харед, ки дар муқоваи он донишҷӯён дар мошини нусхабардорӣ нишон дода шудаанд. Ва ба шарофати Уоррен Туми (ки лоиҳаи TUHS-ро оғоз кардааст) шумо метавонед зеркашӣ кунед Файли PDF бо рамзи сарчашма барои нашри шашум. Ман мехоҳам ба шумо тасаввурот диҳам, ки барои эҷоди файл чӣ қадар заҳмат сарф шудааст:

Зиёда аз 15 сол пеш, ман нусхаи рамзи сарчашмаро чоп кардам шерон, зеро ба ман сифати нусхаи худ аз шумораи номаълуми нусхаҳои дигар маъқул набуд. TUHS ҳанӯз вуҷуд надошт ва ман ба манбаъҳои кӯҳна дастрасӣ надоштам. Аммо дар соли 1988 ман як лентаи кӯҳнаи 9-ро ёфтам, ки нусхаи эҳтиётии компютери PDP11 дошт. Фаҳмидани он душвор буд, ки оё он кор мекунад, аммо дарахти солим /usr/src/ мавҷуд буд, ки дар он аксари файлҳо бо соли 1979 нишонгузорӣ шуда буданд, ки ҳатто он вақт қадимӣ менамуданд. Ин нашри ҳафтум ё PWB ҳосилшудаи он буд, тавре ки ман боварӣ доштам.

Ман бозёфтро асос гирифта, сарчашмаҳоро ба нашри шашум дастӣ таҳрир кардам. Баъзе аз рамзҳо бетағйир монданд, аммо баъзеҳо бояд каме таҳрир карда шаванд ва аломати муосири += ба =+ кӯҳна иваз карда шаванд. Баъзе чизҳо танҳо нест карда шуданд ва баъзеҳо бояд пурра аз нав навишта шаванд, аммо на аз ҳад зиёд.

Ва имрӯз мо метавонем онлайн дар TUHS рамзи сарчашмаи нашри шашумро аз он хонем бойгонӣ, ки ба он Деннис Ричи даст дошт.

Воқеан, дар назари аввал, хусусияти асосии коди C пеш аз давраи Керниган ва Ричи он аст. мухтасарй. На он вақт аст, ки ман қодирам бе таҳрири васеъ пораҳои кодро ворид кунам, то ба майдони нисбатан танги намоиш дар сайти худ мувофиқат кунам.

Дар ибтидо /usr/sys/ken/pipe.c шарҳи тавзеҳотӣ вуҷуд дорад (ва ҳа, бештар вуҷуд дорад /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

Андозаи буфер аз нашри чорум тағйир наёфтааст. Аммо дар ин ҷо мо мебинем, ки бидуни ҳуҷҷатҳои ҷамъиятӣ, қубурҳо як вақтҳо файлҳоро ҳамчун захираи эҳтиётӣ истифода мекарданд!

Дар мавриди файлҳои LARG, онҳо мувофиқат мекунанд парчами inode LARG, ки онро "алгоритми бузурги адресатсия" барои коркард истифода мебарад блокҳои ғайримустақим барои дастгирии системаҳои файлии калонтар. Азбаски Кен гуфт, ки беҳтараш онҳоро истифода набарам, ман бо камоли майл сухани ӯро қабул мекунам.

Ин аст занги воқеии система 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;
}

Шарҳ ба таври возеҳ тасвир мекунад, ки дар ин ҷо чӣ рӯй медиҳад. Аммо фаҳмидани рамз он қадар осон нест, қисман аз сабаби роҳ "истифодабарандаи сохтор u» ва кайд мекунад R0 и R1 параметрҳои занги система ва арзишҳои баргардонидан гузаронида мешаванд.

Биёед бо кӯшиш кунем ialloc () ба диск гузоред inode (дастори индекс), ва бо ёрии falloc () - дуро дар хотира ҷойгир кунед файл. Агар ҳамааш хуб бошад, мо парчамҳоро барои муайян кардани ин файлҳо ҳамчун ду канори лӯла гузошта, онҳоро ба ҳамон иноде нишон медиҳем (шумораи истинодашон ба 2 муқаррар карда мешавад) ва инодро ҳамчун тағирёфта ва истифодашаванда қайд мекунем. Ба дархостҳо диққат диҳед ворид () дар роҳҳои хато барои кам кардани шумораи истинод дар inode нав.

pipe() бояд гузарад R0 и R1 рақамҳои тасвири файлро барои хондан ва навиштан баргардонед. falloc() ишоракунак ба сохтори файл бар мегардонад, балки ҳамчунин "бармегардад" тавассути u.u_ar0[R0] ва тавсифкунандаи файл. Ин аст, ки код захира мекунад r тавсифкунандаи файл барои хондан ва барои навиштан бевосита аз он тавсифкунандаи файл таъин мекунад u.u_ar0[R0] пас аз занги дуюм falloc().

Флаг FPIPE, ки мо ҳангоми сохтани қубур муқаррар мекунем, рафтори функсияро назорат мекунад rdwr() дар sys2.cдаъват кардани реҷаҳои мушаххаси вуруд/чор:

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

Сипас, функсия readp() в pipe.c маълумотро аз трубопровод мехонад. Аммо беҳтар аст, ки татбиқи онро аз он пайгирӣ кунед writep(). Боз ҳам, код аз сабаби конвенсияҳои интиқоли далелҳо мураккабтар шудааст, аммо баъзе тафсилотҳоро метавон сарфи назар кард.

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

Мо мехоҳем ба вуруди қубур байт нависед u.u_count. Аввал мо бояд инодро қулф кунем (ба поён нигаред plock/prele).

Сипас, мо ҳисобкунаки истинодро тафтиш мекунем. То он даме, ки ҳарду канори қубур кушода боқӣ мемонад, ҳисобкунак бояд ба 2 баробар бошад. Мо як звеноро (аз rp->f_inode), бинобар ин, агар ҳисобкунак аз 2 камтар бошад, ин бояд маънои онро дорад, ки раванди хониш охири қубурро бастааст. Ба ибораи дигар, мо кӯшиш мекунем, ки ба лӯлаи пӯшида нависем ва ин хато аст. Рамзи хатогии бори аввал EPIPE ва сигнал SIGPIPE дар нашри шашуми Unix пайдо шуд.

Аммо конвейер кушода бошад хам, вай пур шуда метавонад. Дар ин ҳолат, мо қулфро раҳо мекунем ва ба хоб меравем, ки раванди дигар аз лӯла хонда мешавад ва дар он фазои кофӣ холӣ мекунад. Пас аз бедор шудан, мо ба ибтидо бармегардем, боз қулфро овезон мекунем ва давраи нави сабтро оғоз мекунем.

Агар дар қубур фазои кофӣ мавҷуд бошад, мо бо истифода аз он маълумот менависем нависед (). Параметр i_size1 inode (агар қубур холӣ бошад, он метавонад ба 0 баробар бошад) охири маълумотеро, ки аллакай дар он мавҷуд аст, нишон медиҳад. Агар фазои сабти кофӣ мавҷуд бошад, мо метавонем қубурро аз он пур кунем i_size1 ба PIPESIZ. Сипас, мо қулфро озод мекунем ва кӯшиш мекунем, ки ҳама гуна равандеро, ки интизори хондан аз қубур аст, бедор созем. Мо ба ибтидо бармегардем, то бубинем, ки оё тавонистем ба қадри зарурӣ байт нависем. Агар он ноком шавад, мо як давраи нави сабтро оғоз мекунем.

Одатан параметр i_mode inode барои нигоҳ доштани иҷозатҳо истифода мешавад r, w и x. Аммо дар мавриди қубурҳо, мо сигнал медиҳем, ки ягон раванд бо истифода аз битҳо навиштан ё хонданро интизор аст IREAD и IWRITE мутаносибан. Раванд парчам ва зангҳоро муқаррар мекунад sleep(), ва дар назар аст, ки баъзе равандҳои дигар дар оянда боиси wakeup().

Ҷодуи воқеӣ дар он рӯй медиҳад sleep() и wakeup(). Онҳо дар амал татбиқ карда мешаванд slp.c, манбаи шарҳи машҳури "Интизор нест, ки шумо инро фаҳмед". Хушбахтона, мо набояд кодро фаҳмем, танҳо ба баъзе шарҳҳо нигаред:

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

Раванде, ки боиси он мегардад sleep() барои як канали махсус, метавонад дертар аз тарафи раванди дигар бедор, ки боиси wakeup() барои ҳамон канал. writep() и readp() амалиёти худро тавассути чунин зангҳои ҷуфтшуда ҳамоҳанг созанд. дар назар гиред, ки pipe.c хамеша афзалият медихад PPIPE вақте ки даъват sleep(), ҳамин тавр sleep() метавонад бо сигнал қатъ карда шавад.

Ҳоло мо барои фаҳмидани функсия ҳама чизро дорем 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);
}

Шояд шумо хондани ин функсияро аз поён то боло осонтар ёбед. Филиали "хондан ва баргардонидан" одатан вақте истифода мешавад, ки дар қубур маълумот мавҷуд аст. Дар ин ҳолат мо истифода мебарем тайёр() мо аз маълумоти ҳозира ба қадри кофӣ маълумотро мехонем f_offset хондан ва сипас арзиши ҷуброни мувофиқро навсозӣ кунед.

Ҳангоми хондани минбаъда, лӯла холӣ хоҳад буд, агар офсети хониш ба даст оварда шавад i_size1 дар inode. Мо мавқеъро ба 0 барқарор мекунем ва кӯшиш мекунем, ки ҳама гуна равандеро, ки ба лӯла навиштан мехоҳад, бедор созем. Мо медонем, ки вакте ки конвейер пур мешавад. writep() ба хоб меравад ip+1. Ва акнун, ки лӯла холӣ аст, мо метавонем онро бедор кунем, то давраи навиштани худро дубора оғоз кунем.

Агар шумо чизе барои хондан надошта бошед, пас readp() байракча гузошта метавонад IREAD ва хоб равад ip+2. Мо медонем, ки чӣ ӯро аз хоб бедор мекунад writep(), вақте ки он баъзе маълумотро ба қубур менависад.

Шарҳҳо ба readi() ва writei() ба шумо дар фаҳмидани он кӯмак мекунад, ки ба ҷои интиқоли параметрҳо тавассути "u"Мо метавонем онҳоро ҳамчун функсияҳои муқаррарии воридот/чор, ки файл, мавқеъ, буферро дар хотира мегиранд ва шумораи байтҳоро барои хондан ё навиштан ҳисоб мекунанд, муносибат кунем.

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

Дар хусуси блоки «консервативй» бошад, пас readp() и writep() то ба охир расидани кор ё гирифтани натиҷа (яъне занг wakeup). plock() и prele() оддӣ кор кунед: бо истифода аз маҷмӯи гуногуни зангҳо sleep и wakeup ба мо иҷозат диҳед, ки ҳама гуна равандеро бедор созем, ки қулфи навро баровардаамон лозим аст:

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

Дар аввал ман фаҳмида наметавонистам, ки чаро readp() сабабгор нест prele(ip) пеш аз занг wakeup(ip+1). Аввалин чизест writep() боиси дар давраи худ мегардад, ин plock(ip), ки ба бунбаст оварда мерасонад, агар readp() то ҳол блоки маро хориҷ накардаам, аз ин рӯ код бояд дуруст кор кунад. Агар назар кунед wakeup(), он гоҳ маълум мешавад, ки он танҳо раванди хобро ҳамчун омодаи иҷро нишон медиҳад, то дар оянда sched() дар ҳақиқат онро оғоз кард. Пас readp() сабабҳо wakeup(), қуфлро хориҷ мекунад, муқаррар мекунад IREAD ва занг мезанад sleep(ip+2)— хамаи ин пештар writep() давраро аз нав давом медихад.

Ин тавсифи конвейерҳоро дар нашри шашум ба анҷом мерасонад. Рамзи оддӣ, оқибатҳои дурдаст.

Нашри ҳафтуми Unix (январи соли 1979) нашри нави асосӣ (баъди чор сол) буд, ки бисёр замимаҳои нав ва хусусиятҳои ядроиро ҷорӣ кард. Он инчунин дар робита ба истифодаи рехтагарии типӣ, иттифоқҳо ва нишондиҳандаҳои чопӣ ба сохторҳо тағироти назаррас ба амал омад. Аммо рамзи конвейер амалан бетағйир монд. Мо метавонем ин нашрро гузаред.

Xv6, ядрои оддии ба Unix монанд

Барои сохтани ядро Xv6 аз нашри шашуми Unix таъсир кардааст, аммо он дар C муосир навишта шудааст, то дар протсессори x86 кор кунад. Рамзро хондан осон ва фаҳмо аст. Илова бар ин, бар хилофи сарчашмаҳои Unix бо TUHS, шумо метавонед онро тартиб диҳед, тағир диҳед ва дар чизи дигаре ба ҷуз PDP 11/70 иҷро кунед. Аз ин рӯ, ин ядро ​​дар донишгоҳҳо ҳамчун маводи таълимӣ оид ба системаҳои оператсионӣ васеъ истифода мешавад. Сарчашмаҳо дар Github мебошанд.

Рамз татбиқи возеҳ ва оқилона дорад қубур.c, аз ҷониби буфер дар хотира ба ҷои inode дар диск дастгирӣ карда мешавад. Дар ин ҷо ман танҳо таърифи "қубури сохторӣ" ва функсияро пешниҳод мекунам 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() ҳолати боқимондаи татбиқро муқаррар мекунад, ки функсияҳоро дар бар мегирад piperead(), pipewrite() и pipeclose(). Зангҳои воқеии система sys_pipe бастабандӣ аст, ки дар амалӣ карда мешавад sysfile.c. Ман тавсия медиҳам, ки тамоми коди ӯро хонед. Мушкилот дар сатҳи рамзи сарчашмаи нашри шашум аст, аммо хондан хеле осонтар ва лаззатбахштар аст.

Linux 0.01

Рамзи ибтидоии Linux 0.01 -ро ёфтан мумкин аст. Омухтани рафти ичрои трубопроводхо дар у fs/pipe.c. Ин барои муаррифии лӯла инодро истифода мебарад, аммо худи лӯла бо забони муосири C навишта шудааст. Агар шумо бо рамзи нашри 6-ум кор карда бошед, дар ин ҷо ҳеҷ мушкиле надоред. Чунин аст, ки функсия ба назар мерасад 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;
}

Ҳатто бидуни нигоҳ доштани таърифҳои сохтор, шумо метавонед бифаҳмед, ки чӣ гуна ҳисобкунии истинодҳои inode барои санҷидани он, ки оё амалиёти навиштан натиҷа медиҳад? SIGPIPE. Илова ба байт ба байт кор кардан, ин функсияро бо идеяҳои дар боло тавсифшуда муқоиса кардан осон аст. Ҳатто мантиқ sleep_on/wake_up он кадар бегона ба назар намерасад.

Ядроҳои муосири Linux, FreeBSD, NetBSD, OpenBSD

Ман зуд тавассути баъзе ядроҳои муосир давида будам. Ҳеҷ яке аз онҳо дигар татбиқи диск надоранд (ҳайратовар нест). Linux татбиқи худро дорад. Гарчанде ки се ядрои муосири BSD дорои амалияҳое мебошанд, ки дар асоси коди аз ҷониби Ҷон Дайсон навишташуда мавҷуданд, дар тӯли солҳо онҳо аз ҳамдигар хеле фарқ мекунанд.

Хондан fs/pipe.c (дар Linux) ё sys/kern/sys_pipe.c (дар *BSD), ин фидокории воқеиро талаб мекунад. Рамзи имрӯза дар бораи фаъолият ва дастгирии хусусиятҳо ба монанди векторӣ ва асинхронӣ I/O мебошад. Ва тафсилоти тақсимоти хотира, қулфҳо ва конфигуратсияи ядро ​​​​ҳамаашон хеле фарқ мекунанд. Ин чизе нест, ки коллеҷҳо барои курси муқаддимавии системаҳои оператсионӣ лозиманд.

Ба ҳар ҳол, ман ба кофтани баъзе намунаҳои кӯҳна (ба монанди тавлиди SIGPIPE ва бозгашт EPIPE ҳангоми навиштан ба қубури пӯшида) дар ҳамаи ин ядроҳои гуногуни муосир. Эҳтимол ман ҳеҷ гоҳ компютери PDP-11-ро дар ҳаёти воқеӣ набинам, аммо аз коде, ки солҳои пеш аз таваллуди ман навишта шуда буд, омӯхтан лозим аст.

Мақоле, ки Диви Капур дар соли 2011 навиштааст:Амалисозии ядрои Linux қубурҳо ва FIFOs" шарҳи мухтасарро дар бораи чӣ гуна кор кардани қубурҳо (ҳоло) дар Linux пешниҳод мекунад. А ӯҳдадории охирин дар Linux модели лӯлаи ҳамкории мутақобиларо нишон медиҳад, ки қобилиятҳояшон аз имконоти файлҳои муваққатӣ зиёдтар аст; ва инчунин нишон медиҳад, ки қубурҳо аз "қуфлкунии хеле консервативии" ядрои нашри шашуми Unix то чӣ андоза дуранд.

Манбаъ: will.com

Илова Эзоҳ