Ovaj članak opisuje implementaciju cjevovoda u Unix kernelu. Donekle me razočarao nedavni članak pod naslovom "
O čemu pričamo?
Cjevovodi, “vjerojatno najvažniji izum u Unixu,” definirajuća su karakteristika temeljne filozofije Unixa povezivanja malih programa, kao i poznati znak na naredbenom retku:
$ echo hello | wc -c
6
Ova funkcionalnost ovisi o sistemskom pozivu koji pruža kernel pipe
, što je opisano na stranicama dokumentacije
Cjevovodi pružaju jednosmjerni kanal za međuprocesnu komunikaciju. Cjevovod ima ulaz (kraj pisanja) i izlaz (kraj čitanja). Podaci upisani na ulaz cjevovoda mogu se očitati na izlazu.
Cjevovod se stvara pomoću poziva
pipe(2)
, koji vraća dva deskriptora datoteke: jedan se odnosi na ulaz cjevovoda, drugi na izlaz.
Izlaz praćenja iz gornje naredbe pokazuje stvaranje cjevovoda i protok podataka kroz njega od jednog procesa do drugog:
$ 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
Roditeljski proces poziva pipe()
da dobijete montirane deskriptore datoteka. Jedan podređeni proces piše u jedan handle, a drugi proces čita iste podatke iz drugog handle-a. Ljuska koristi dup2 za "preimenovanje" deskriptora 3 i 4 da odgovaraju stdin i stdout.
Bez cijevi, ljuska bi morala zapisati rezultat jednog procesa u datoteku i proslijediti ga drugom procesu da pročita podatke iz datoteke. Kao rezultat toga, trošili bismo više resursa i prostora na disku. Međutim, cjevovodi su dobri ne samo zato što vam omogućuju izbjegavanje upotrebe privremenih datoteka:
Ako proces pokušava čitati iz praznog cjevovoda tada
read(2)
blokirat će se dok podaci ne postanu dostupni. Ako proces pokuša pisati u puni cjevovod, tadawrite(2)
blokirat će se dok se iz cjevovoda ne pročita dovoljno podataka za izvođenje pisanja.
Kao i POSIX zahtjev, ovo je važno svojstvo: pisanje u cjevovod do PIPE_BUF
bajtovi (najmanje 512) moraju biti atomski kako bi procesi mogli međusobno komunicirati kroz cjevovod na način na koji obične datoteke (koje ne daju takva jamstva) ne mogu.
Kada koristite običnu datoteku, proces može zapisati sav svoj izlaz u nju i proslijediti ga drugom procesu. Ili procesi mogu raditi u vrlo paralelnom načinu, koristeći vanjski signalni mehanizam (poput semafora) da obavijeste jedni druge kada je pisanje ili čitanje završeno. Transportne trake spašavaju nas od svih ovih gnjavaža.
Što tražimo?
Objasnit ću to jednostavnim riječima kako biste lakše zamislili kako pokretna traka može funkcionirati. Morat ćete dodijeliti međuspremnik i neko stanje u memoriji. Trebat će vam funkcije za dodavanje i uklanjanje podataka iz međuspremnika. Trebat će vam neka sredstva za pozivanje funkcija tijekom operacija čitanja i pisanja na deskriptorima datoteka. Trebat će vam i brave za implementaciju gore opisanog posebnog ponašanja.
Sada smo spremni ispitati izvorni kod kernela pod jakim svjetlom lampe kako bismo potvrdili ili opovrgli naš nejasan mentalni model. Ali uvijek budite spremni na neočekivano.
Gdje gledamo?
Ne znam gdje je moj primjerak poznate knjige"
Lutanje arhivama TUHS-a je poput posjeta muzeju. Možemo pogledati našu zajedničku povijest i poštujem dugogodišnje napore da se sav ovaj materijal malo po malo povrati sa starih vrpci i otisaka. I jako sam svjestan tih fragmenata koji još uvijek nedostaju.
Nakon što smo zadovoljili svoju znatiželju o drevnoj povijesti transportera, možemo pogledati moderne kernele za usporedbu.
Usput, pipe
je sistemski poziv broj 42 u tablici sysent[]
. Koincidencija?
Tradicionalne Unix jezgre (1970. – 1974.)
Nisam našao nikakve tragove pipe(2)
niti u
TUHS navodi da
Unix 1973rd Edition je bila posljednja verzija s kernelom napisanim u asemblerskom jeziku, ali i prva verzija s cjevovodima. Tijekom XNUMX. radilo se na poboljšanju trećeg izdanja, kernel je prepisan u C-u, pa se pojavilo četvrto izdanje Unixa.
Jedan je čitatelj pronašao skenirani dokument u kojem je Doug McIlroy predložio ideju "povezivanje programa poput vrtnog crijeva".
U knjizi Briana Kernighana
Kada se Unix pojavio, moja fascinacija korutinama navela me da zamolim autora OS-a, Kena Thompsona, da dopusti da podaci koji su upisani u proces idu ne samo na uređaj, već i na izlaz u drugi proces. Ken je zaključio da je to moguće. Međutim, kao minimalist, želio je da svaka funkcija sustava igra značajnu ulogu. Je li izravno pisanje između procesa doista velika prednost u odnosu na pisanje u međudatoteku? Tek kad sam dao konkretan prijedlog upečatljivog naziva "cjevovod" i opisom sintakse za interakciju između procesa, Ken je konačno uzviknuo: "Učinit ću to!"
I učinio. Jedne kobne večeri Ken je promijenio kernel i ljusku, popravio nekoliko standardnih programa kako bi standardizirao način na koji prihvaćaju unos (koji je mogao doći iz cjevovoda), a također je promijenio i nazive datoteka. Sljedeći dan, cjevovodi su se počeli vrlo široko koristiti u aplikacijama. Do kraja tjedna tajnice su ih koristile za slanje dokumenata iz programa za obradu teksta na pisač. Nešto kasnije, Ken je zamijenio izvorni API i sintaksu za omatanje korištenja cjevovoda čistijim konvencijama, koje se od tada koriste.
Nažalost, izvorni kod trećeg izdanja Unix kernela je izgubljen. I iako imamo izvorni kod kernela napisan u C
Imamo tekstualnu dokumentaciju za pipe(2)
iz oba izdanja, tako da možete početi pretraživanjem dokumentacije pipe(2)
napisan je u asemblerskom jeziku i vraća samo jedan deskriptor datoteke, ali već pruža očekivanu temeljnu funkcionalnost:
Poziv sustava cijev stvara ulazno/izlazni mehanizam koji se naziva cjevovod. Vraćeni deskriptor datoteke može se koristiti za operacije čitanja i pisanja. Kada se nešto zapiše u cjevovod, do 504 bajta podataka se sprema u međuspremnik, nakon čega se proces pisanja obustavlja. Prilikom čitanja iz cjevovoda, podaci u međuspremniku se oduzimaju.
Do sljedeće godine kernel je prepisan u C-u, i pipe(fildes)
'
Poziv sustava cijev stvara ulazno/izlazni mehanizam koji se naziva cjevovod. Vraćeni deskriptori datoteka mogu se koristiti u operacijama čitanja i pisanja. Kada se nešto upiše u cjevovod, koristi se oznaka vraćena u r1 (odnosno fildes[1]), sprema se u međuspremnik na 4096 bajtova podataka, nakon čega se proces pisanja obustavlja. Prilikom čitanja iz cjevovoda, regulator vraćen na r0 (odnosno fildes[0]) preuzima podatke.
Pretpostavlja se da nakon što je cjevovod definiran, dva (ili više) komunikacijska procesa (stvorena naknadnim pozivima na viljuška) prenosit će podatke iz cjevovoda pomoću poziva čitati и pisati.
Ljuska ima sintaksu za definiranje linearnog niza procesa povezanih cjevovodom.
Pozivi za čitanje iz praznog cjevovoda (koji ne sadrži podatke u međuspremniku) koji ima samo jedan kraj (svi deskriptori datoteke za pisanje su zatvoreni) vraćaju "kraj datoteke". Pozivi na pisanje u sličnoj situaciji se ignoriraju.
Najranije
Šesto izdanje Unixa (1975.)
Počnimo čitati izvorni kod Unixa
Već dugi niz godina knjiga Znamenitosti bio je jedini dokument o Unix kernelu dostupan izvan Bell Labsa. Iako je licenca za šesto izdanje dopuštala nastavnicima korištenje izvornog koda, licenca za sedmo izdanje je isključivala tu mogućnost, pa je knjiga distribuirana u obliku ilegalnih tipkanih kopija.
Danas možete kupiti reprint knjige na čijoj naslovnici su prikazani studenti za fotokopirnim strojem. A zahvaljujući Warrenu Toomeyu (koji je pokrenuo projekt TUHS) možete preuzeti
Prije više od 15 godina upisao sam kopiju navedenog izvornog koda Znamenitosti, jer mi se nije svidjela kvaliteta moje kopije od nepoznatog broja drugih kopija. TUHS još nije postojao i nisam imao pristup starim izvorima. Ali 1988. pronašao sam staru vrpcu s 9 zapisa koja je sadržavala sigurnosnu kopiju s računala PDP11. Bilo je teško reći radi li, ali postojalo je netaknuto stablo /usr/src/ u kojem je većina datoteka bila označena godinom 1979., koja je čak i tada izgledala prastaro. Bilo je to sedmo izdanje ili njegov derivat PWB, kako sam vjerovao.
Nalaz sam uzeo kao osnovu i ručno uredio izvore do šestog izdanja. Dio koda ostao je isti, no neki su morali biti malo uređeni, mijenjajući moderni += token u zastarjeli =+. Neke stvari su jednostavno izbrisane, a neke su se morale potpuno prepisati, ali ne previše.
I danas možemo čitati online na TUHS-u izvorni kod šestog izdanja iz
Inače, na prvi pogled, glavna značajka C-koda prije razdoblja Kernighana i Ritchieja je njegova kratkoća. Ne događa se često da mogu umetnuti dijelove koda bez opsežnog uređivanja kako bi odgovarao relativno uskom području prikaza na mojoj web stranici.
Rano
/*
* 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
Veličina međuspremnika nije se promijenila od četvrtog izdanja. Ali ovdje vidimo, bez ikakve javne dokumentacije, da su cjevovodi nekada koristili datoteke kao sigurnosnu pohranu!
Što se tiče VELIKIH datoteka, one odgovaraju
Ovo je pravi sistemski poziv pipe
:
/*
* The sys-pipe entry.
* Allocate an inode on the root device.
* Allocate 2 file structures.
* Put it all together with flags.
*/
pipe()
{
register *ip, *rf, *wf;
int r;
ip = ialloc(rootdev);
if(ip == NULL)
return;
rf = falloc();
if(rf == NULL) {
iput(ip);
return;
}
r = u.u_ar0[R0];
wf = falloc();
if(wf == NULL) {
rf->f_count = 0;
u.u_ofile[r] = NULL;
iput(ip);
return;
}
u.u_ar0[R1] = u.u_ar0[R0]; /* wf's fd */
u.u_ar0[R0] = r; /* rf's fd */
wf->f_flag = FWRITE|FPIPE;
wf->f_inode = ip;
rf->f_flag = FREAD|FPIPE;
rf->f_inode = ip;
ip->i_count = 2;
ip->i_flag = IACC|IUPD;
ip->i_mode = IALLOC;
}
Komentar jasno opisuje što se ovdje događa. Ali razumijevanje koda nije tako lako, dijelom zbog načina na koji "R0
и R1
prosljeđuju se parametri sistemskog poziva i povratne vrijednosti.
Pokušajmo sa
pipe()
mora kroz R0
и R1
vratiti brojeve deskriptora datoteke za čitanje i pisanje. falloc()
vraća pokazivač na strukturu datoteke, ali također "vraća" putem u.u_ar0[R0]
i deskriptor datoteke. Odnosno, kod se sprema r
deskriptor datoteke za čitanje i dodjeljuje deskriptor datoteke za izravno pisanje u.u_ar0[R0]
nakon drugog poziva falloc()
.
zastava FPIPE
, koji postavljamo prilikom stvaranja cjevovoda, kontrolira ponašanje funkcije
/*
* 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);
}
/* … */
}
Zatim funkcija readp()
в pipe.c
čita podatke iz cjevovoda. Ali bolje je pratiti implementaciju počevši od writep()
. Opet, kod je postao složeniji zbog konvencija prosljeđivanja argumenata, ali se neki detalji mogu izostaviti.
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;
}
Želimo upisati bajtove u ulaz cjevovoda u.u_count
. Prvo moramo zaključati inode (pogledajte dolje plock
/prele
).
Zatim provjeravamo brojač referenci inoda. Sve dok su oba kraja cjevovoda otvorena, brojač bi trebao biti jednak 2. Držimo jednu vezu (od rp->f_inode
), pa ako je brojač manji od 2, to mora značiti da je proces čitanja zatvorio svoj kraj cjevovoda. Drugim riječima, pokušavamo pisati u zatvoreni cjevovod, a to je pogreška. Kod prve pogreške EPIPE
i signal SIGPIPE
pojavio se u šestom izdanju Unixa.
Ali čak i ako je pokretna traka otvorena, može biti puna. U tom slučaju otključavamo bravu i idemo spavati u nadi da će neki drugi proces očitati iz cjevovoda i osloboditi dovoljno mjesta u njemu. Nakon buđenja vraćamo se na početak, ponovno vješamo bravu i započinjemo novi ciklus snimanja.
Ako u cjevovodu ima dovoljno slobodnog prostora, upisujemo podatke u njega pomoću i_size1
at inode (ako je cjevovod prazan može biti jednak 0) označava kraj podataka koje već sadrži. Ako ima dovoljno prostora za snimanje, možemo napuniti cjevovod iz i_size1
na PIPESIZ
. Zatim oslobađamo bravu i pokušavamo probuditi bilo koji proces koji čeka na čitanje iz cjevovoda. Vraćamo se na početak da vidimo jesmo li uspjeli napisati onoliko bajtova koliko nam je potrebno. Ako ne uspije, započinjemo novi ciklus snimanja.
Obično je parametar i_mode
inode se koristi za pohranjivanje dopuštenja r
, w
и x
. Ali u slučaju cjevovoda signaliziramo da neki proces čeka na pisanje ili čitanje pomoću bitova IREAD
и IWRITE
odnosno. Proces postavlja zastavu i poziva sleep()
, a očekuje se da će neki drugi proces u budućnosti izazvati wakeup()
.
Prava magija se događa u sleep()
и wakeup()
. Implementiraju se u
/*
* 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) /* … */
Proces koji uzrokuje sleep()
za određeni kanal, kasnije se može probuditi drugim procesom, što će uzrokovati wakeup()
za isti kanal. writep()
и readp()
koordiniraju svoje radnje putem takvih uparenih poziva. imajte na umu da pipe.c
uvijek daje prednost PPIPE
kad se prozove sleep()
, znaci to je to sleep()
može biti prekinut signalom.
Sada imamo sve za razumijevanje funkcije 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);
}
Možda će vam biti lakše čitati ovu funkciju odozdo prema gore. Grana "čitaj i vraćaj" obično se koristi kada postoje neki podaci u cjevovodu. U ovom slučaju koristimo f_offset
očitanje, a zatim ažurirajte vrijednost odgovarajućeg pomaka.
Pri sljedećim čitanjima, cjevovod će biti prazan ako je dosegnut pomak čitanja i_size1
na inodu. Ponovno postavljamo poziciju na 0 i pokušavamo probuditi svaki proces koji želi pisati u cjevovod. Znamo da kada je pokretna traka puna, writep()
zaspat će na ip+1
. I sada kada je cjevovod prazan, možemo ga probuditi da nastavi svoj ciklus pisanja.
Ako nemate što čitati, onda readp()
može postaviti zastavu IREAD
i zaspati dalje ip+2
. Znamo što će ga probuditi writep()
, kada zapisuje neke podatke u cjevovod.
Komentari na u
"Možemo ih tretirati kao normalne I/O funkcije koje uzimaju datoteku, poziciju, međuspremnik u memoriji i broje broj bajtova za čitanje ili pisanje.
/*
* 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;
/* … */
Što se tiče “konzervativnog” blokiranja, dakle readp()
и writep()
blokiraju inode dok ne završe svoj posao ili ne prime rezultat (tj. poziv wakeup
). plock()
и prele()
raditi jednostavno: koristeći drugačiji skup poziva sleep
и wakeup
dopustite nam da probudimo svaki proces koji treba zaključavanje koje smo upravo otpustili:
/*
* 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);
}
}
Isprva nisam mogao shvatiti zašto readp()
ne uzrokuje prele(ip)
prije poziva wakeup(ip+1)
. Prva stvar je writep()
uzrokuje u svom ciklusu, ovo plock(ip)
, što dovodi do zastoja ako readp()
još nisam uklonio moju blokadu, tako da kod nekako mora raditi ispravno. Ako pogledate wakeup()
, tada postaje jasno da samo označava proces spavanja kao spreman za izvršenje, tako da u budućnosti sched()
stvarno pokrenuo. Tako readp()
uzroci wakeup()
, skida bravu, postavlja IREAD
i poziva sleep(ip+2)
- sve ovo prije writep()
nastavlja ciklus.
Ovim je završen opis pokretnih traka u šestom izdanju. Jednostavan kod, dalekosežne posljedice.
Xv6, jednostavan kernel sličan Unixu
Za stvaranje kernela
Kod sadrži jasnu i promišljenu implementaciju 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()
postavlja stanje ostatka implementacije, što uključuje funkcije piperead()
, pipewrite()
и pipeclose()
. Stvarni sistemski poziv sys_pipe
je omot implementiran u
Linux 0.01
Može se pronaći izvorni kod Linuxa 0.01. Bit će poučno proučiti implementaciju cjevovoda u njegovoj fs
/pipe.c
. Ovo koristi inode za predstavljanje cjevovoda, ali sam cjevovod je napisan u modernom C-u. Ako ste se probili kroz kod šestog izdanja, ovdje nećete imati problema. Ovako izgleda funkcija 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;
}
Čak i bez gledanja definicija strukture, možete shvatiti kako se broj referenci inoda koristi za provjeru rezultira li operacija pisanja SIGPIPE
. Osim rada bajt po bajt, ovu je funkciju lako usporediti s gore opisanim idejama. Čak i logika sleep_on
/wake_up
ne izgleda tako strano.
Moderni Linux kerneli, FreeBSD, NetBSD, OpenBSD
Brzo sam prošao kroz neke moderne kernele. Nitko od njih više nema implementaciju diska (ne iznenađuje). Linux ima vlastitu implementaciju. Iako tri moderne BSD kernele sadrže implementacije temeljene na kodu koji je napisao John Dyson, tijekom godina postale su previše različite jedna od druge.
Čitati fs
/pipe.c
(na Linuxu) ili sys
/kern
/sys_pipe.c
(na *BSD), potrebna je prava predanost. Današnji kod govori o performansama i podršci za značajke kao što su vektorski i asinkroni I/O. A detalji dodjele memorije, zaključavanja i konfiguracije jezgre uvelike se razlikuju. Ovo nije ono što fakulteti trebaju za uvodni tečaj operativnih sustava.
U svakom slučaju, zanimalo me iskopavanje nekih starih obrazaca (poput generiranja SIGPIPE
i vratiti EPIPE
kada pišete u zatvoreni cjevovod) u svim tim različitim modernim kernelima. Vjerojatno nikad neću vidjeti PDP-11 računalo u stvarnom životu, ali još uvijek ima mnogo toga za naučiti iz koda koji je napisan godinama prije mog rođenja.
Članak koji je napisala Divi Kapoor 2011.:
Izvor: www.habr.com