دا مقاله د یونیکس کرنل کې د پایپ لاین پلي کول تشریح کوي. زه یو څه مایوسه وم چې یوه وروستۍ مقاله چې عنوان یې دی "
موږ د څه په اړه خبرې کوو؟
پایپ لاینونه، "شاید په یونیکس کې ترټولو مهم اختراع" د وړو برنامو سره د یوځای کولو لپاره د یونکس د اصلي فلسفې یوه مشخصه ځانګړتیا ده، په بیله بیا د کمانډ لاین کې یو پیژندل شوی نښه:
$ 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 تشریح کونکي "نوم" بدلولو لپاره dup4 کاروي.
د پایپونو پرته، شیل باید د یوې پروسې پایله په فایل کې ولیکي او د فایل څخه ډاټا لوستلو لپاره بلې پروسې ته انتقال کړي. د پایلې په توګه، موږ به ډیرې سرچینې او د ډیسک ځای ضایع کړو. په هرصورت، پایپ لاینونه نه یوازې ښه دي ځکه چې دوی تاسو ته اجازه درکوي د لنډمهاله فایلونو کارولو څخه مخنیوی وکړئ:
که یوه پروسه د خالي پایپ لاین څخه د لوستلو هڅه کوي نو بیا
read(2)
تر هغه پورې چې ډاټا شتون ولري بلاک شي. که یوه پروسه هڅه کوي بشپړ پایپ لاین ته ولیکي، نوwrite(2)
تر هغه وخته پورې به بلاک شي چې د لیکلو ترسره کولو لپاره د پایپ لاین څخه کافي معلومات لوستل شوي نه وي.
د POSIX اړتیا په څیر، دا یو مهم ملکیت دی: پایپ لاین ته لیکل PIPE_BUF
بایټس (لږترلږه 512) باید اټومي وي ترڅو پروسې د پایپ لاین له لارې یو له بل سره په داسې طریقه اړیکه ونیسي چې منظم فایلونه (کوم چې دا ډول تضمین نه وړاندې کوي) نشي کولی.
کله چې یو منظم فایل کاروئ، یوه پروسه کولی شي خپل ټول محصول ورته ولیکي او بل پروسې ته یې انتقال کړي. یا پروسې کولی شي په خورا موازي حالت کې کار وکړي ، د بهرني سیګنال میکانیزم په کارولو سره (لکه سیمفور) یو بل ته خبر ورکوي کله چې لیکل یا لوستل بشپړ شي. لیږدونکي موږ له دې ټولو ستونزو څخه وژغوري.
موږ د څه په لټه کې یو؟
زه به دا په ساده شرایطو کې تشریح کړم نو دا ستاسو لپاره اسانه ده چې تصور وکړئ چې یو لیږدونکی څنګه کار کولی شي. تاسو به اړتیا ولرئ په حافظه کې بفر او یو څه حالت تخصیص کړئ. تاسو به د بفر څخه ډیټا اضافه کولو او لرې کولو لپاره دندو ته اړتیا ولرئ. تاسو به د فایل تشریح کونکو کې د لوستلو او لیکلو عملیاتو په جریان کې فنکشن ته زنګ وهلو لپاره ځینې وسیلو ته اړتیا ولرئ. او تاسو به د پورته ذکر شوي ځانګړي چلند پلي کولو لپاره تالاشۍ ته اړتیا ولرئ.
اوس موږ چمتو یو چې د روښانه څراغ لاندې د کرنل سرچینې کوډ څخه تحقیق وکړو ترڅو زموږ مبهم ذهني ماډل تایید یا رد کړي. مګر تل د غیر متوقع لپاره چمتو اوسئ.
موږ چیرته ګورو؟
زه نه پوهیږم چې زما د مشهور کتاب کاپي چیرته ده "
د TUHS آرشیفونو کې ګرځیدل د میوزیم لیدلو په څیر دي. موږ کولی شو خپل ګډ تاریخ ته وګورو، او زه د ډیرو کلونو هڅو ته درناوی لرم چې دا ټول مواد د زړو ټیپونو او چاپونو څخه یو څه بیرته ترلاسه کړي. او زه د هغه ټوټو څخه په کلکه خبر یم چې لاهم ورک دي.
د لیږدونکو د لرغوني تاریخ په اړه زموږ د تجسس پوره کولو سره، موږ کولی شو د پرتله کولو لپاره عصري دانا ته وګورو.
په لاره کې، pipe
په جدول کې د سیسټم زنګ نمبر 42 دی sysent[]
. تصادف؟
دودیز یونیکس دانه (1970-1974)
ما هیڅ نښې ونه موندلې pipe(2)
نه په کې
TUHS وايي
د یونیکس دریمه نسخه وروستۍ نسخه وه چې د اسمبلۍ په ژبه لیکل شوې کرنل سره ، مګر د پایپ لاینونو سره لومړۍ نسخه هم وه. د 1973 په جریان کې، د دریمې نسخې د ښه کولو لپاره کار ترسره شو، کرنل په C کې بیا لیکل شوی و، او په دې توګه د یونکس څلورم نسخه ښکاره شوه.
یو لوستونکي د یو سند سکین وموند چې په کې ډګ مکیلروي د "د باغ نلی په څیر د برنامو سره وصل" نظر وړاندیز کړی.
د براین کیرنیګن په کتاب کې
کله چې یونیکس راووت ، د کورټینونو سره زما علاقه د دې لامل شوه چې د OS لیکوال ، کین تامپسن څخه وپوښتم ، ترڅو یوې پروسې ته لیکل شوي ډیټا اجازه ورکړي چې نه یوازې وسیلې ته لاړ شي ، بلکه بلې پروسې ته د تولید لپاره هم. کین پریکړه وکړه چې دا ممکنه وه. په هرصورت، د لږترلږه په توګه، هغه غوښتل چې د هر سیسټم فعالیت یو مهم رول ولوبوي. ایا مستقیم د پروسو ترمینځ لیکل واقعیا د مینځنۍ فایل لیکلو په پرتله لویه ګټه ده؟ دا یوازې هغه وخت و چې ما د زړه راښکونکي نوم "پائپ لاین" سره یو ځانګړی وړاندیز وکړ او د پروسو ترمینځ د متقابل عمل لپاره د ترکیب توضیحات چې کین په پای کې وویل: "زه به یې وکړم!"
او وکړل. یو بدمرغه ماښام، کین کرنل او شیل بدل کړ، ډیری معیاري پروګرامونه یې تنظیم کړل ترڅو معیاري کړي چې څنګه دوی ان پټ (کوم چې د پایپ لاین څخه راځي)، او د فایل نومونه هم بدل کړي. بله ورځ، پایپ لاینونه په غوښتنلیکونو کې په پراخه کچه کارول پیل شول. د اونۍ په پای کې، سکرترانو دوی د کلمې پروسس کونکو څخه پرنټر ته د اسنادو لیږلو لپاره کارول. یو څه وروسته ، کین د کلینر کنوانسیونونو سره د پایپ لاینونو کارولو لپاس کولو لپاره اصلي API او ترکیب بدل کړ ، کوم چې له هغه وخت راهیسې کارول کیږي.
له بده مرغه، د دریمې نسخې یونیکس کرنل لپاره د سرچینې کوډ ورک شوی. او که څه هم موږ د کرنل سرچینې کوډ لرو چې په C کې لیکل شوی
موږ لپاره متن اسناد لرو 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 کې د شپږمې نسخې سرچینې کوډ آنلاین ولولئ
د لارې په توګه، په لومړي نظر کې، د کارنیغان او ریچي دورې دمخه د C-code اصلي ځانګړتیا دا ده. لنډیز. دا اکثرا نه وي چې زه د دې توان لرم چې پرته له پراخ ترمیم پرته د کوډ ټوټې داخل کړم ترڅو زما په سایټ کې د نسبتا تنګ نندارې ساحې فټ کړي.
په پیل کې
/*
* 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
د بفر اندازه د څلورم نسخه راهیسې نه ده بدله شوې. مګر دلته موږ ګورو، پرته له کوم عامه اسنادو، دا پایپ لاین یو ځل د بیک اپ ذخیره کولو په توګه فایلونه کارولي!
لکه څنګه چې د لویو فایلونو لپاره، دوی ورته دي
دلته د ریښتیني سیسټم غږ دی 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
په inode کې. موږ موقعیت 0 ته بیا تنظیم کوو او هڅه کوو هر هغه پروسه راپورته کړو چې غواړي پایپ لاین ته ولیکي. موږ پوهیږو کله چې لیږدونکی ډک وي، 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)
، کوم چې د ځنډ لامل کیږي که readp()
تر اوسه زما بلاک نه دی لیرې کړی، نو په یو څه ډول کوډ باید په سمه توګه کار وکړي. که تاسو وګورئ wakeup()
، بیا دا روښانه کیږي چې دا یوازې د خوب کولو پروسه د اجرا کولو لپاره چمتو په توګه په نښه کوي ، ترڅو په راتلونکي کې sched()
واقعیا یې پیل کړ. نو readp()
لاملونه wakeup()
، قفل لرې کوي، سیټ کوي IREAD
او زنګونه sleep(ip+2)
- دا ټول مخکې writep()
دوره بیا پیلوي.
دا په شپږمه نسخه کې د لیږدونکو توضیحات بشپړوي. ساده کوډ، لیرې پایلې.
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 سرچینې کوډ موندل کیدی شي. دا به لارښوونه وي چې په هغه کې د پایپ لاینونو پلي کولو مطالعه وکړي fs
/pipe.c
. دا د پایپ لاین د نمایندګۍ لپاره انوډ کاروي، مګر پایپ لاین پخپله په عصري C کې لیکل شوی. که تاسو د XNUMXth نسخه کوډ له لارې کار کړی وي، تاسو به دلته کومه ستونزه ونه لرئ. دا هغه څه دي چې فعالیت ورته ښکاري 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
دومره اجنبی نه ښکاری
عصري لینکس کرنل، FreeBSD، NetBSD، OpenBSD
زه په چټکۍ سره د ځینو عصري کرنلونو له لارې ورغلم. هیڅ یو یې نور د ډیسک تطبیق نلري (د حیرانتیا خبره نه ده). لینکس خپل تطبیق لري. که څه هم درې عصري BSD دانا د کوډ پراساس پلي کیږي چې د جان ډیسن لخوا لیکل شوي ، د کلونو په اوږدو کې دوی له یو بل څخه ډیر توپیر لري.
لوستل fs
/pipe.c
(په لینکس کې) یا sys
/kern
/sys_pipe.c
(په *BSD کې) ، دا ریښتیني وقف غواړي. د نن ورځې کوډ د ځانګړتیاوو لپاره د فعالیت او ملاتړ په اړه دی لکه ویکتور او غیر متناسب I/O. او د حافظې تخصیص ، تالاشۍ او د کرنل تشکیلاتو توضیحات ټول خورا توپیر لري. دا هغه څه ندي چې کالجونه د ابتدايي عملیاتي سیسټم کورس لپاره اړتیا لري.
په هرصورت، زه د ځینو پخوانیو نمونو کیندلو سره علاقه لرم (لکه تولید کول SIGPIPE
او بیرته راګرځي EPIPE
کله چې تړل شوي پایپ لاین ته لیکل کیږي) په دې ټولو مختلف عصري کرنلونو کې. زه به شاید هیڅکله په ریښتیني ژوند کې د PDP-11 کمپیوټر ونه لیدم ، مګر لاهم د کوډ څخه د زده کولو لپاره ډیر څه شتون لري چې زما له زیږیدو څخه کلونه دمخه لیکل شوی و.
یوه مقاله د دیوی کپور لخوا په 2011 کې لیکل شوې:
سرچینه: www.habr.com