هي آرٽيڪل بيان ڪري ٿو يونڪس ڪنييل ۾ پائپ لائنن تي عمل درآمد. مون کي ڪجهه مايوس ٿي ويو ته هڪ تازو مضمون عنوان "
اسان ڇا ڳالهائي رهيا آهيون؟
پائپ لائنون، "شايد يونڪس ۾ سڀ کان اهم ايجاد"، ننڍڙي پروگرامن کي هڪٻئي سان ڳنڍڻ جي بنيادي يونڪس فلسفي جي هڪ خاص خصوصيت آهي، انهي سان گڏ ڪمان لائن تي هڪ واقف نشاني:
$ echo hello | wc -c
6
هي ڪارڪردگي ڪنييل مهيا ڪيل سسٽم ڪال تي منحصر آهي pipe
، جيڪو دستاويز جي صفحن تي بيان ڪيو ويو آهي
پائپ لائنون هڪ غير طرفي چينل مهيا ڪن ٿيون، وچولي رابطي لاء. پائپ لائن ۾ هڪ ان پٽ (لکڻ جي آخر) ۽ هڪ آئوٽ (پڙهڻ جي آخر) آهي. پائپ لائن جي ان پٽ تي لکيل ڊيٽا آئوٽ تي پڙهي سگهجي ٿو.
پائپ لائن ٺاهي وئي آهي ڪال استعمال ڪندي
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()
نصب ٿيل فائل بيان ڪندڙ حاصل ڪرڻ لاء. هڪ ٻار جو عمل هڪ هينڊل ڏانهن لکندو آهي، ۽ ٻيو عمل ساڳئي ڊيٽا کي ٻئي هينڊل مان پڙهي ٿو. شيل stdin ۽ stdout کي ملائڻ لاءِ ڊُپ 2 کي ”نام بدلائڻ“ جي وضاحت ڪندڙ 3 ۽ 4 کي استعمال ڪري ٿو.
بغير پائپ جي، شيل کي هڪ عمل جو نتيجو فائل ۾ لکڻو پوندو ۽ فائل مان ڊيٽا پڙهڻ لاء ان کي ٻئي پروسيس ڏانهن منتقل ڪرڻو پوندو. نتيجي طور، اسان وڌيڪ وسيلن ۽ ڊسڪ اسپيس کي ضايع ڪنداسين. بهرحال، پائپ لائنون سٺيون آهن نه صرف ڇو ته اهي توهان کي عارضي فائلن جي استعمال کان بچڻ جي اجازت ڏين ٿيون:
جيڪڏهن هڪ عمل هڪ خالي پائپ لائن مان پڙهڻ جي ڪوشش ڪري رهيو آهي ته پوء
read(2)
بلاڪ ڪيو ويندو جيستائين ڊيٽا دستياب ٿي وڃي. جيڪڏهن هڪ عمل مڪمل پائپ لائن تي لکڻ جي ڪوشش ڪري ٿو، پوءwrite(2)
بلاڪ ڪيو ويندو جيستائين ڪافي ڊيٽا پائپ لائن مان پڙهيل لکت کي انجام ڏيڻ لاءِ.
POSIX جي گهرج وانگر، هي هڪ اهم ملڪيت آهي: پائپ لائن تائين لکڻ PIPE_BUF
بائيٽ (گهٽ ۾ گهٽ 512) ايٽمي هجڻ لازمي آهي ته جيئن پروسيس هڪ ٻئي سان پائپ لائن ذريعي رابطو ڪري سگھن ٿيون جيئن باقاعده فائلون (جيڪي اهڙيون ضمانتون فراهم نه ڪن) نه ٿي سگهن.
جڏهن هڪ باقاعده فائل استعمال ڪندي، هڪ عمل ان جي سڀني پيداوار کي لکي سگهي ٿو ۽ ان کي ٻئي پروسيس تي منتقل ڪري ٿو. يا عمل هڪ انتهائي متوازي موڊ ۾ ڪم ڪري سگهن ٿا، هڪ خارجي سگنلنگ ميکانيزم استعمال ڪندي (جهڙوڪ سيمفور) هڪ ٻئي کي اطلاع ڏيڻ لاءِ جڏهن لکڻ يا پڙهڻ مڪمل ٿي چڪو آهي. Conveyors اسان کي هن تمام پريشاني کان بچائي.
اسان ڇا ڳولي رهيا آهيون؟
مان ان کي آسان اصطلاحن ۾ بيان ڪندس ته جيئن توهان لاءِ اهو تصور ڪرڻ آسان ٿئي ته هڪ ڪنويئر ڪيئن ڪم ڪري سگهي ٿو. توهان کي ميموري ۾ بفر ۽ ڪجهه رياست مختص ڪرڻ جي ضرورت پوندي. توھان کي بفر مان ڊيٽا شامل ڪرڻ ۽ ختم ڪرڻ لاءِ افعال جي ضرورت پوندي. توهان کي فائل بيان ڪندڙن تي پڙهڻ ۽ لکڻ جي عملن دوران افعال کي ڪال ڪرڻ لاءِ ڪجهه ذريعن جي ضرورت پوندي. ۽ مٿي بيان ڪيل خاص رويي کي لاڳو ڪرڻ لاءِ توهان کي تالا جي ضرورت پوندي.
ھاڻي اسان تيار آھيون ڪرنل سورس ڪوڊ جي پڇا ڳاڇا ڪرڻ لاءِ روشن چراغ جي تحت اسان جي مبہم ذهني ماڊل جي تصديق يا غلط ثابت ڪرڻ لاءِ. پر هميشه غير متوقع لاء تيار ٿي.
اسان ڪٿي ڳولي رهيا آهيون؟
خبر ناهي منهنجي مشهور ڪتاب جي ڪاپي ڪٿي آهي“
TUHS آرڪائيوز ذريعي گھمڻ هڪ ميوزيم جو دورو ڪرڻ وانگر آهي. اسان پنهنجي گڏيل تاريخ کي ڏسي سگهون ٿا، ۽ مون کي ڪيترن ئي سالن جي ڪوشش جو احترام آهي ته هي سڄو مواد پراڻن ٽيپ ۽ پرنٽس مان ٿوري دير سان ٻيهر حاصل ڪرڻ لاء. ۽ مان انهن ٽڪرن کان واقف آهيان جيڪي اڃا تائين غائب آهن.
conveyors جي قديم تاريخ بابت اسان جي تجسس کي مطمئن ڪرڻ کان پوء، اسان مقابلي لاء جديد دانا کي ڏسي سگهون ٿا.
رستي ۾، pipe
ٽيبل ۾ سسٽم ڪال نمبر 42 آهي sysent[]
. اتفاق؟
روايتي يونڪس ڪرنل (1970-1974)
مون کي ڪوبه نشان نه مليو pipe(2)
نه ئي اندر
TUHS چوي ٿو
يونڪس 1973rd ايڊيشن آخري ورزن هو جنهن ۾ هڪ ڪرنل اسمبلي ٻولي ۾ لکيل هو، پر پائپ لائنن سان گڏ پهريون نسخو پڻ. XNUMX جي دوران، ٽئين ايڊيشن کي بهتر ڪرڻ لاء ڪم ڪيو ويو، ڪنيل کي C ۾ ٻيهر لکيو ويو، ۽ يونڪس جو چوٿون ايڊيشن ظاهر ٿيو.
هڪ پڙهندڙ کي هڪ دستاويز جو اسڪين مليو جنهن ۾ Doug McIlroy "ڳنڍيندڙ پروگرامن جهڙوڪ باغي نلي" جو خيال پيش ڪيو.
برائن ڪرنيگن جي ڪتاب ۾
جڏهن يونڪس ٻاهر آيو، ڪوروٽين سان منهنجي دلچسپي مون کي او ايس جي ليکڪ، ڪين ٿامپسن کان پڇڻ لاء، هڪ پروسيس تي لکيل ڊيٽا کي نه رڳو ڊوائيس ڏانهن وڃڻ جي اجازت ڏيڻ جي اجازت ڏني، پر ٻئي پروسيس ڏانهن پڻ. ڪين فيصلو ڪيو ته اهو ممڪن هو. بهرحال، هڪ minimalist طور، هن چاهيو ته هر سسٽم جي فنڪشن کي اهم ڪردار ادا ڪرڻ لاء. ڇا سڌو سنئون عملن جي وچ ۾ لکڻ هڪ وچولي فائل تي لکڻ تي هڪ وڏو فائدو آهي؟ اهو صرف تڏهن هو جڏهن مون هڪ خاص تجويز پيش ڪئي جنهن سان دلڪش نالو ”پائپ لائن“ ۽ عملن جي وچ ۾ رابطي لاءِ نحو جي وضاحت ڪئي وئي ته ڪين آخر ۾ چيو: ”مان اهو ڪندس!
۽ ڪيو. هڪ ڀيانڪ شام، ڪين ڪينل ۽ شيل کي تبديل ڪيو، ڪيترن ئي معياري پروگرامن کي طئي ڪيو ته معيار کي ڪيئن انهن ان پٽ کي قبول ڪيو (جيڪو پائپ لائن مان اچي سگهي ٿو)، ۽ فائل جا نالا پڻ تبديل ڪيا. ٻئي ڏينهن، پائپ لائنز ايپليڪيشنن ۾ تمام وڏي پيماني تي استعمال ٿيڻ لڳو. هفتي جي آخر تائين، سيڪريٽري انهن کي استعمال ڪري رهيا هئا لفظ پروسيسرز کان دستاويز موڪلڻ لاء پرنٽر ڏانهن. ٿوري دير کان پوء، Ken کي صاف ڪرڻ واري ڪنوينشن سان پائپ لائنن جي استعمال کي لپائڻ لاء اصل API ۽ نحو کي تبديل ڪيو، جيڪي ڪڏهن کان استعمال ڪيا ويا آهن.
بدقسمتي سان، ٽيون ايڊيشن يونڪس ڪنيل جو سورس ڪوڊ گم ٿي ويو آهي. ۽ جيتوڻيڪ اسان وٽ ڪنيل سورس ڪوڊ لکيل آهي سي
اسان وٽ ٽيڪسٽ دستاويز آهن pipe(2)
ٻنهي رليز مان، تنهنڪري توهان شروع ڪري سگهو ٿا دستاويز ڳولڻ سان pipe(2)
اسيمبلي جي ٻولي ۾ لکيل آهي ۽ صرف هڪ فائل بيان ڪندڙ کي واپس ڪري ٿو، پر اڳ ۾ ئي متوقع بنيادي ڪارڪردگي مهيا ڪري ٿو:
سسٽم ڪال پائپ هڪ ان پٽ/آئوٽ پٽ ميڪنزم ٺاهي ٿو جنهن کي پائپ لائن سڏيو ويندو آهي. واپسي فائل بيان ڪندڙ کي پڙهڻ ۽ لکڻ جي عملن لاء استعمال ڪري سگهجي ٿو. جڏهن پائپ لائن تي ڪجهه لکيو ويندو آهي، ڊيٽا جي 504 بائيٽ تائين بفر ٿي ويندي آهي، جنهن کان پوء لکڻ جي عمل کي معطل ڪيو ويندو آهي. جڏهن پائپ لائن مان پڙهي، بفر ٿيل ڊيٽا ڪڍيو ويندو آهي.
ايندڙ سال تائين ڪرنل کي C ۾ ٻيهر لکيو ويو، ۽ pipe(fildes)
»:
سسٽم ڪال پائپ هڪ ان پٽ/آئوٽ پٽ ميڪنزم ٺاهي ٿو جنهن کي پائپ لائن سڏيو ويندو آهي. واپسي فائل بيان ڪندڙ پڙهڻ ۽ لکڻ جي عملن ۾ استعمال ڪري سگھجن ٿيون. جڏهن پائپ لائن تي ڪجهه لکيو ويندو آهي، r1 (resp. fildes[1]) ۾ موٽڻ وارو هينڊ استعمال ڪيو ويندو آهي، ڊيٽا جي 4096 بائيٽ تائين بفر ڪيو ويندو آهي، جنهن کان پوء لکڻ جي عمل کي معطل ڪيو ويندو آهي. جڏهن پائپ لائن مان پڙهي، هينڊل r0 ڏانهن موٽيو (resp. fildes[0]) ڊيٽا وٺي ٿو.
اهو فرض ڪيو ويو آهي ته هڪ ڀيرو هڪ پائپ لائن جي وضاحت ڪئي وئي آهي، ٻه (يا وڌيڪ) مواصلاتي عمل (بعد ۾ ايندڙ ڪالن ذريعي ٺاهيل) ڪٽڪ) ڪال استعمال ڪندي پائپ لائن مان ڊيٽا منتقل ڪندو پڙهڻ и لکڻ.
شيل ۾ پائپ لائن سان ڳنڍيل عملن جي لڪير واري صف کي بيان ڪرڻ لاء هڪ نحو آهي.
هڪ خالي پائيپ لائين مان پڙهڻ لاءِ ڪالون (جنهن ۾ ڪو به بفر ٿيل ڊيٽا ناهي) جنهن جي صرف هڪ پڇاڙي آهي (سڀني لکڻين جي وضاحت ڪندڙ بند ٿيل آهن) واپسي "فائل جي آخر". ساڳئي صورتحال ۾ لکڻ لاء ڪالون نظر انداز ڪيا ويا آهن.
سڀ کان پهريان
يونڪس جو ڇهين ايڊيشن (1975)
اچو ته يونڪس سورس ڪوڊ پڙهڻ شروع ڪريون
ڪيترن سالن کان ڪتاب شعر بيل ليبز کان ٻاهر موجود يونڪس ڪنيل تي واحد دستاويز هو. جيتوڻيڪ ڇهين ايڊيشن جي لائسنس استادن کي ان جو سورس ڪوڊ استعمال ڪرڻ جي اجازت ڏني، ستين ايڊيشن جي لائسنس ان امڪان کي خارج ڪري ڇڏيو، تنهن ڪري ڪتاب غير قانوني ٽائيپ ٿيل ڪاپين جي صورت ۾ ورهايو ويو.
اڄ توهان ڪتاب جو ٻيهر ڇپائي خريد ڪري سگهو ٿا، جنهن جو احاطو شاگردن کي ڪاپي مشين تي ڏيکاري ٿو. ۽ وارين ٽومي جي مهرباني (جنهن TUHS پروجيڪٽ شروع ڪيو) توهان ڊائون لوڊ ڪري سگهو ٿا
15 سال اڳ کان وڌيڪ، مون ڏنل سورس ڪوڊ جي ڪاپي ٽائيپ ڪئي شعر, ڇاڪاڻ ته مون کي پسند نه ڪيو منهنجي ڪاپي جي معيار کي اڻڄاتل تعداد جي ٻين نسخن مان. TUHS اڃا تائين موجود نه هو ۽ مون کي پراڻي ذريعن تائين رسائي نه هئي. پر 1988 ۾، مون کي هڪ پراڻي 9-ٽريڪ ٽيپ مليو جنهن ۾ PDP11 ڪمپيوٽر مان بيڪ اپ شامل هو. اهو ٻڌائڻ مشڪل هو ته اهو ڪم ڪري رهيو هو، پر اتي هڪ برقرار /usr/src/ وڻ هو جنهن ۾ اڪثر فائلن تي سال 1979 جو ليبل لڳل هو، جيڪو پوءِ به قديم نظر اچي رهيو هو. اهو ستين ايڊيشن يا ان جو نڪتل PWB هو، جيئن مون سمجهيو.
مون ڳولها کي بنياد طور ورتو ۽ دستي طور تي ڇهين ايڊيشن تائين ذريعن کي ايڊٽ ڪيو. ڪجھ ڪوڊ ساڳيا رھيا، پر ڪجھ کي ٿورڙو تبديل ڪرڻو پيو، جديد += ٽوڪن کي پراڻي =+ ۾ تبديل ڪرڻ. ڪجهه شيون صرف ڊهي ويون، ۽ ڪجهه مڪمل طور تي ٻيهر لکڻيون هيون، پر تمام گهڻو نه.
۽ اڄ اسان TUHS تي آن لائن پڙهي سگھون ٿا ڇهين ايڊيشن جو سورس ڪوڊ
رستي جي ذريعي، پهرين نظر ۾، ڪرنيگن ۽ رچي جي دور کان اڳ سي-ڪوڊ جي بنيادي خصوصيت ان جي آهي. اختصار. اهو اڪثر نه آهي ته مان ڪوڊ جا ٽڪرا داخل ڪرڻ جي قابل ٿي سگهان ٿو بغير ڪنهن وسيع ترميم جي منهنجي سائيٽ تي نسبتا تنگ ڊسپلي واري علائقي کي پورو ڪرڻ لاءِ.
شروعات ۾
/*
* 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 فائلن لاء، اهي ملن ٿا
هتي حقيقي سسٽم ڪال آهي 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;
}
تبصرو واضح طور تي بيان ڪري ٿو ته هتي ڇا ٿي رهيو آهي. پر ڪوڊ کي سمجھڻ ايترو آسان ناهي، جزوي طور تي رستو "R0
и R1
سسٽم ڪال پيٽرولر ۽ واپسي جا قدر گذري ويا آهن.
اچو ته ڪوشش ڪريون
pipe()
ذريعي ٿيڻ گهرجي R0
и R1
پڙهڻ ۽ لکڻ لاءِ فائل بيان ڪندڙ نمبر واپس ڪريو. falloc()
ھڪڙي پوائنٽر کي فائل جي جوڙجڪ ڏانھن موٽائي ٿو، پر پڻ "واپسي" ذريعي u.u_ar0[R0]
۽ هڪ فائيل وضاحت ڪندڙ. اهو آهي، ڪوڊ محفوظ ڪري ٿو اندر r
پڙهڻ لاءِ فائل بيان ڪندڙ ۽ سڌو سنئون لکڻ لاءِ فائل بيان ڪندڙ کي تفويض ڪري ٿو u.u_ar0[R0]
ٻئي سڏ کان پوء falloc()
.
پرچم FPIPE
، جيڪو اسان سيٽ ڪيو جڏهن پائپ لائن ٺاهي، فنڪشن جي رويي کي سنڀاليندو آهي
/*
* 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
يونڪس جي ڇهين ايڊيشن ۾ ظاهر ٿيو.
پر جيتوڻيڪ ڪنويئر کليل آهي، اهو مڪمل ٿي سگهي ٿو. انهي حالت ۾، اسان تالا کي ڇڏي ڏيو ۽ ننڊ ۾ وڃون ٿا اميد ته ٻيو عمل پائپ لائن مان پڙهي ۽ ان ۾ ڪافي جاء خالي ڪري ڇڏيندي. جاڳڻ کان پوء، اسان شروعات ڏانهن موٽندا آهيون، ٻيهر تالا بند ڪريو ۽ هڪ نئون رڪارڊنگ چڪر شروع ڪيو.
جيڪڏهن پائپ لائن ۾ ڪافي خالي جاء آهي، ته پوء اسان ان کي استعمال ڪندي ڊيٽا لکندا آهيون i_size1
inode تي (جيڪڏهن پائپ لائن خالي آهي ته اها 0 جي برابر ٿي سگهي ٿي) ڊيٽا جي پڇاڙي کي ظاهر ڪري ٿي جيڪا اڳ ۾ ئي شامل آهي. جيڪڏهن ڪافي رڪارڊنگ جي جڳهه آهي، اسان پائپ لائن ڀري سگهون ٿا i_size1
ڪرڻ PIPESIZ
. ان کان پوء اسان تالا کي ڇڏي ڏيو ۽ ڪنهن به عمل کي جاڳائڻ جي ڪوشش ڪئي جيڪا پائپ لائن مان پڙهڻ جي انتظار ۾ آهي. اسان شروعات ۾ واپس وڃون ٿا ته ڏسون ته ڇا اسان لکي سگھون ٿا جيترو بائيٽ اسان کي ضرورت آھي. جيڪڏهن اهو ناڪام ٿئي ٿو، پوء اسان هڪ نئين رڪارڊنگ چڪر شروع ڪندا آهيون.
عام طور تي پيٽرولر i_mode
inode اجازتن کي ذخيرو ڪرڻ لاء استعمال ڪيو ويندو آهي r
, w
и x
. پر پائپ لائنز جي صورت ۾، اسان اشارو ڪريون ٿا ته ڪجهه عمل بٽ استعمال ڪندي لکڻ يا پڙهڻ جي انتظار ۾ آهي IREAD
и IWRITE
ترتيب سان. اهو عمل پرچم ۽ ڪالن کي سيٽ ڪري ٿو sleep()
، ۽ اميد آهي ته مستقبل ۾ ڪجهه ٻيو عمل سبب ٿيندو wakeup()
.
حقيقي جادو ۾ ٿئي ٿو sleep()
и wakeup()
. انهن تي عمل ڪيو ويو آهي
/*
* 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
انوڊ تي. اسان پوزيشن کي 0 تي ري سيٽ ڪيو ۽ ڪوشش ڪريو ڪنهن به عمل کي جاڳائڻ جي جيڪو پائپ لائن تي لکڻ چاهي ٿو. اسان کي خبر آهي ته جڏهن conveyor مڪمل آهي، writep()
تي سمهي پوندو ip+1
. ۽ هاڻي ته پائپ لائن خالي آهي، اسان ان کي جاڳائي سگھون ٿا ته جيئن ان جي لکڻ واري چڪر کي ٻيهر شروع ڪيو وڃي.
جيڪڏهن توهان وٽ پڙهڻ لاء ڪجهه ناهي، پوء readp()
پرچم قائم ڪري سگهي ٿو IREAD
۽ سمهڻ ip+2
. اسان ڄاڻون ٿا ته هن کي ڇا جاڳندو writep()
، جڏهن اهو پائپ لائن ڏانهن ڪجهه ڊيٽا لکي ٿو.
ڏانهن تبصرو u
"اسان انھن کي عام I/O افعال وانگر علاج ڪري سگھون ٿا جيڪي ھڪڙي فائل، ھڪڙي پوزيشن، ميموري ۾ بفر، ۽ پڙھڻ يا لکڻ لاء بائيٽ جو تعداد شمار ڪن ٿا.
/*
* 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)
، جيڪو تعطل ڏانهن وٺي وڃي ٿو if readp()
اڃا تائين منهنجو بلاڪ نه هٽايو آهي، تنهنڪري ڪنهن به طرح ڪوڊ صحيح ڪم ڪرڻ گهرجي. جيڪڏهن توهان ڏسندا wakeup()
، پوءِ اهو واضح ٿئي ٿو ته اهو صرف سمهڻ واري عمل کي عمل ڪرڻ لاءِ تيار طور نشانو بڻائيندو آهي ، انهي ڪري ته مستقبل ۾ sched()
واقعي ان کي شروع ڪيو. سو readp()
سبب wakeup()
، تالا هٽائي ٿو، سيٽ IREAD
۽ ڪالون sleep(ip+2)
- اهو سڀ ڪجهه اڳ writep()
سائيڪل کي ٻيهر شروع ڪري ٿو.
هي ڇهين ايڊيشن ۾ conveyors جي وضاحت مڪمل ڪري ٿو. سادي ڪوڊ، دور رس نتيجا.
Xv6، ھڪڙو سادو يونڪس جھڙو دانا
ڪنيل ٺاهڻ لاءِ
ڪوڊ هڪ واضح ۽ سوچيل سمجهيل عمل تي مشتمل آهي 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
۾ لاڳو ٿيل هڪ چادر آهي
لينڪس 0.01
لينڪس 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;
}
ساخت جي وصفن کي ڏسڻ کان سواءِ، توهان اهو اندازو لڳائي سگهو ٿا ته انوڊ ريفرنس ڳڻپ ڪيئن استعمال ٿئي ٿي چيڪ ڪرڻ لاءِ ته ڇا لکڻ جي آپريشن جي نتيجي ۾ SIGPIPE
. بائيٽ بائيٽ ڪم ڪرڻ کان علاوه، هي فنڪشن مٿي بيان ڪيل خيالن سان مقابلو ڪرڻ آسان آهي. حتي منطق sleep_on
/wake_up
ايترو اجنبي نه ٿو لڳي.
جديد لينڪس ڪنيلز، فري بي ايس ڊي، نيٽ بي ايس ڊي، اوپن بي ايس ڊي
مون تڪڙو تڪڙو ڪجهه جديد ڪنلن ذريعي ڀڄي ويو. انهن مان ڪو به ڊسڪ تي عمل درآمد نه آهي (عجب جي ڳالهه ناهي). لينڪس ان جو پنهنجو عمل آهي. جيتوڻيڪ ٽي جديد BSD ڪنيلز ڪوڊ جي بنياد تي لاڳو ٿين ٿا جيڪي جان ڊيسن پاران لکيل هئا، ڪيترن سالن کان اهي هڪ ٻئي کان بلڪل مختلف ٿي چڪا آهن.
پڙهڻ fs
/pipe.c
(لينڪس تي) يا sys
/kern
/sys_pipe.c
(*BSD تي)، اهو حقيقي وقف وٺندو آهي. اڄ جو ڪوڊ ڪارڪردگي ۽ سپورٽ جي باري ۾ آهي خاصيتن لاءِ جيئن ته ویکٹر ۽ هم وقت ساز I/O. ۽ ميموري مختص ڪرڻ جا تفصيل، لاڪ ۽ ڪنييل ترتيب سڀ مختلف آهن. اهو نه آهي ته ڪاليجن کي هڪ تعارفي آپريٽنگ سسٽم ڪورس جي ضرورت آهي.
بهرحال، مون کي ڪجهه پراڻن نمونن (جهڙوڪ پيدا ڪرڻ SIGPIPE
۽ واپسي EPIPE
جڏهن هڪ بند پائپ لائن ڏانهن لکندو آهي) انهن سڀني مختلف جديد ڪنلن ۾. مان شايد ڪڏهن به PDP-11 ڪمپيوٽر کي حقيقي زندگي ۾ نه ڏسندس، پر ڪوڊ مان سکڻ لاءِ اڃا گهڻو ڪجهه آهي جيڪو منهنجي ڄمڻ کان سال اڳ لکيو ويو هو.
ديوي ڪپور پاران 2011 ۾ لکيل هڪ مضمون:
جو ذريعو: www.habr.com