Ackoli zaneprazdnen matfyzem rozhold jsem se napsat dalsi skolicku. Je trochu netradicni, protoze se netyka primo UNIXU ale zabyva se operacnim systemem plan9. Asi pred pul rokem jses si jeho "demoverzi" (plne funkcni) nainstaloval na svuj pocitac vicemene ze zvedavosti (upoutali me naprosto strasne vyhlizejici screenshoty na jeho homepagi).

Musim rict ze po ponekud zdlouhave a komplikovane instalaci jsem byl velice prekvapen. Ctyrdisketova verze totiz obsahuje okeni prostredi, vyvojove prostredi, kompiler c a alefu, debugger, prohlizec webu, sitove utility a kdovi co jeste. Ze zacatku jsem byl zmaten. Plan9 se casto uvadi jakozto pokracovatel vyvojove rady unixu. Pravda je ze ls chodi. To je ale priblizne vsechno. Rad bych tu sepsal ty nejzajimavejsi napady a zmeny oproti unixu, na ktere jsem narazil.

O co tedy jde?


Panove: Rob Pike, Dave Presotto, Sean Dorward, Bob Flandrena, Ken Thompson, Howard Trickey, Phil, Winterbottom vesele pracovali v bellovych laboratorich na UNIXU. Jednoho dne ale poznali, ze UNIX ma problemy, ktere uz vzhledem ke kompatibilite nelze odstranit. Proto pocatkem roku 1980 zacali premyslet o necem novem a radikalnim.

Od GNU a ostatnich projektu vyvijejici nove operacni systemy se plan9 lisi v jedne zakladni veci. Autori se rozhodli vykaslat na veskere existujici standardy a udelan novy, neskazeny, minimalni ale dobre pouzitelny, rozsiritelny a prenositelny operacni system. A ne nejaky mix vsech dobrych i spatnych napadu za poslednich 30 let. A to se jim opravdu povedlo, podle meho nazoru jde o nejoriginalnejsi a nejzajimavesi OS vubec. Prekopaji ho totiz od zakladu az do takovych detailu jako ze exit nevraci cislo ale string.

Vyvoj na systemu byl zastaven. Je k dispozici demoverze na http://plan9.bell-labs.com. Nebo si muzete zakoupit za 350 dolaru cdrom. Doporucuju nainstalovat alespon tu demoverzi. Sice to nema zadnou budoucnost ale je to UZASNE! Vyvoj pry zacal na systemu brazil, ktery bude jeste lepsi nez plan9, ale uz asi rok o nem neni nic slyset...tak se nechame prekvapit.

Navrh


Zakladni principy systemu jsou: Asi si ted rikate, ze to neni nic noveho na slunci. Ale oproti UNIXU to plan9 dovadi o hodne dal.

Plan9 je distribuovany, tak jedna instalace muze bezet i na mnoha ruznych masinach, kazda zpristupnujici nejake sluzby. Jak autori se brani klasickemu schematu, kde masiny jsou vsechny v klimatizovane mistnosti, pospojovane vykonou siti, pomalejsi site potom tento mozek pripojuji do uradu a domovu stastnych uzivatelu parcujicich na terminalech. U planu 9 vsechny masiny jsou rovnocene (to jsme uz nekdy slyseli:)

Masiny ale se nemusi schodne tvarit. Kazda si muze poupravit name space a dalsi veci, tak jak to uzivatel chce. Proto treba /dev/cons je lokalni konzole atd.

Nad tim vsim bdi 9P protokol. Je to protokol na praci se soubory. Ma vsak i ruzne dalsi rozsireni, na preneseni hiearchie se serveru apod.. Na rozdil od NFS a spol nepracuje s bloky ale byty. Navic se neprenasi jenom soubory ale i zarizeni, okna, /proc atd. Narozdil od unixu, kazdy program muze snadno takove zarizeni zakladat a komunikovat tak s ostatnima procesama. A tak se i deje.

Napriklad okeni system (osum a pul - 8 1/2) je takovy souborovy server. Kazde okno dostane vlastni /mnt/81/2//. Kde soubory jsou veci jako mouse, bitblit, cons atd. Ty se potom procesu mapuji do /dev/mouse, /dev/cons atd. Tak proces muze snadno a jednoduse otevrit /dev/cons a pristupovat na obrazovku.

Radikalni take je, ze filesystem(name space) neni sdilen mezi procesy. Namisto toho kazdy proces si muze vytvorit svuj vlastni a pozmeneny. Proto kazdy muze mit svoje /dev/cons a neni treba pouzivat zadne spinave triky.

Okeni system


Je to prvni aplikace, ktera me nadchla. Je to totiz 4000 radek dlouhy programek, ktery pracuje asi takto: Kazda obrazovka je terminal. Ne vsak klasicky unixovy ale plan9 terminal, ktery umi grafiku (veci jako kresleni kolecek, car, textu apod) v souboru bitblt, klasicky textovy vystup v souboru cons, mys v souboru mouse a klavesnici(unixovy X server). Okeni system pak spociva v malinkem programku 8 1/2, ktery tento terminal rozmnozi do vice stejnych terminalu, v prekryvajicich se oknech(unixovy window manager a xterm v jednom). Proto okeni system muze byt spusten sam v sobe, aplikaci je jedno jestli bezi ve fullscreen nebo zmensenem rezimu apod.

Prace z grafikou je az neuveritelne jednoducha (asi tak jako napsat "hello word" v unixu). Mnoho programku pro praci s okny je dokonce implementovano jako skripty.

Pres celkovou eleganci a jednoduchost pouziti je system velice rychly ( podle testu rychlejsi nez X )

Ostatni zajimave file servery


Mezi ostatni nejzajimavejsi file servery patri zejmena adresare jako /n/ftp, /n/http, /proc (neco jako v linuxu), /env (sem se mapuje enviroment), /net (tady je pristup k jednotlivym sitovym protokolum, jde tady otevirat komunikaci atd..)

Navic lze napsat veci jako: import helix /proc, ktery pripoji /proc helixe do lokalniho fs, tak muzete debugovat procesy na helixu apod.

Existuje i obraceny postup. Prikaz cpu, ktery se necha pouzit treba, kdyz chcete lokalne pustit okna, ale vsechny aplikace pobezi na jinem serveru.

Paraelni programovani


I tady bylo vymysleno neco noveho.

Na paraelni programovani je tu novy jazyk alef(popisu pozdejc). Presto ze to jde psat v cecku, pro paraelni programovani je alef rozhodne pohodlnejsi.

Klasicke deleni na procesy a thready je pryc. Plan9 ma jenom jeden typ procesu, ale umoznuje velice presne rict, jake zdroje (jako pamet/file descriptory) se budou sdilet a ktere ne. Navic umoznuje synchronizaci pomoci zamku. Tohle vsechno dela ve dvou systemovych volanich.

Proces vzdnika volanim rforku. Bere bitmasku, ktera rika, co se ma sdilet, kopirovat, vytvorit nove. Zdroje rizene touto maskou jsou nasledujici: Name space, enviroment, file descriptory, segmenty, a notes (neco jako signaly). Jeden bit take urcuje, jestli se ma vytvorit novy soubor. Pokud je vypnuty, zmodifikuje se aktualni proces.

Navic jde samozdrejme sdilet jenom cast pameti.

Alef potom umoznuje mnoho zpusobu synchronizace/komunikace - channels (neco jako pipe), queuing locks, reader/writer lockslocks, a sleep/wakeup. Vsechno tohle je spojeno do jednoho volani rendenzvous s dvema parametry - tag a value. Kdyz je nastaveny tag, proces ceka, dokud druhy proces nenastavi stejny tag, potom se prohodi hodnoty. Toto staci na vsechny synchronizacni fce.

Spin locks jsou implementovany v knihovne architekturove zavisle.

Pomoci threadu se implementuje hodne veci. Napriklad cekani na vstup ze souboru se musi implementovat pomoci vedlejsiho threadu, ktery pocka atd.. Proste zadny select/pool neexistuje. Nejsou zadne potize s cekanim na soubor a semafor zaroven atd.

Name space


Pro to jsou tu hned tri systemova volani: mount, bind a umount.

Mount (jako v unixu) pripoji nejakou hiearchii rizenou fileserverem do adresare. Jako parametr bere file descriptor do 9P protokolu. Bind naopak duplikuje cast existujici name space na jine misto.

Pomoci bind/mount je mozne dostat vice adresaru na jedno misto. Potom vznikne union adresar. Vznikaji tu ale problemy. Mount/bind maji jeste flag, ktery udava, jestli novy adresar se ma pridat na zacatek, dat na konec, nebo prekryt stare adresare. Potom se soubory vyhledavaji od zacatku a bere se prvni nalezeny. Teto konstrukce se pouziva opravdu casto. Napriklad /bin je ve skutecnosti /$cputype$/bin a /rc/bin dohromady apod. Tato konstrukce odstranuje promenou $PATH$. Protoze kazdy uzivatel si to muze proste "hodit do binu"

Dalsi problem je kam ulozit novy soubor v unionech. Normalne vytvareni je zakazano. To se ale muze zmenit flagem. Potom se soubor vytvori v prvnim adresari z povolenym vytvarenim. Tak kazdy uzivatel si muze nabindit svuj bin a vytvareni se bude provadet tam. Stejnym zpusobem se dela napriklad i /tmp.

systemove veci jsou normalne nabindene v /dev ale pri bootu to tam jeste neni. Pak je nutne bindit specialni adresar #. Znak udava typ zarizeni. Takhle se na zacatku nabindi seriove porty:

bind -a '#t' /dev
Flag -a znamena append na konec /dev. Ioctl bylo nahrazeno specialnimi zarizenimi z nazvem *ctl. Napriklad seriovy port se jmenuje eia1 a jeho ridici je eia1ctl. Zapsanim 1200 do eia1ctl se prepne rychlost linky. Zapsanim rawon do kbdctl se prepne do raw modu klavesnice apod. Tohle je samozdrejme mnohem jednodussi na pouziti, necha se ovladat ze scriptiku apod.

Vsechny bezne sluzby (jako telnet,ftp apod) jsou udelany pomoci souboru, proto 9P je jediny protokol pouzity planem 9. 9P je neblokovy a pouziva ruzne stavy. Je implementovan pomoci remote procedure call. Muze ziskavat identifikatory souboru (fids - file identifiers). A vsechny operace se provadeji pomoci fidu. Cely protokol se sklada ze 17 zprav:authenticate users, navigate fids around a file system hierarchy, copy fids, perform I/O, change file attributes, and create and delete files.

Cache jsou implementovany na serveru. U clienta popisuji jak snadne to je. Ale cachuji se pouze spustitelne programy. V aktualni verzi to proste neni.

Sit


Sit sidli v adresari /net. Stejne jako u seriovych portu, vsechno se dela zapisovanim textovych zprav do ridicich devici. Data se ziskavaji pomoci cteni/zapisu souboru. Proto jde opet vsechno udelat scriptem. priklad:

d-r-xr-xr-x I 0 bootes bootes 0 Feb 23 20:20 0

d-r-xr-xr-x I 0 bootes bootes 0 Feb 23 20:20 1

--rw-rw-rw- I 0 bootes bootes 0 Feb 23 20:20 clone

% ls -lp /net/tcp/0

--rw-rw---- I 0 rob    bootes 0 Feb 23 20:20 ctl

--rw-rw---- I 0 rob    bootes 0 Feb 23 20:20 data

--rw-rw---- I 0 rob    bootes 0 Feb 23 20:20 listen

--r--r--r-- I 0 bootes bootes 0 Feb 23 20:20 local

--r--r--r-- I 0 bootes bootes 0 Feb 23 20:20 remote

--r--r--r-- I 0 bootes bootes 0 Feb 23 20:20 status

%
/dev/tcp obsahuje clonovaci soubor a adresare pro otevrena spojeni. Otevrenim clonu vytvorite nove spojeni. Jeho cislo se pak precte ze clonu. Potom v ocislovanem adresari je vsechno, co je potreba. Sam sem si pomoci prikazu cat otevrel ftp sesion. Telnet se otevre se pomoci:connect 135.104.9.52!23 ktere poslete do ctl souboru. Potom zapisete announce 23 ktere rika ze chcete cist.

Sit ma na svedomi cs demon, ktery pouziva system podobny streamum.

IL protokol


IL protokol je novy protokol cosi mezi tcp a udp. Pouziva datagramovy system ale podporuje reliable zpravy. Zpravy muzou byt prehazovany apod.

Autentifikace


Autentifikaci si zarizuje server po dotazu clienta vyvolanym uzivatelem. A urcuje se podle uzivatele, ne masiny. Kazdy uzivatel ma prirazeny klic, ktery se vsak nikdy neprenasi v dekodovatelne podobe, aby ho nikdo nemohl zneuzit. Klice se koduji pomoci DES. Terminal napred uzivateli priradi klic pomoci jeho hesla. Potom se domluvi z autentification serverem, ktery ma prehled o vsech klicich v jeho panstvi. Po zaslani klicu z kazde strany autentification server odpovi pomoci ticketu, ktere obsahuji novy komunikacni klic. Pomoci neho se potom koduje.

Navic tu existuje neco podobneho sticky bitum v unixu..(prebirani cizich prav)

Specialni uzivatele


Zadni takovi tu nejsou. Existuje uzivatel adm, ktery ma sice prava zpravovat system, ale nemuze cist cizi adresare apod.

Na druhou stranu je tu uzivatel none, ktery nemuze skoro nic a je obdobou anonymnich ftp serveru.

Pristupy k souborum


To je podpbny jako v unixu - prava vlastnika, skupiny a ostatnich. Jenom skupiny jsou jine. Je to vpodstate normalni uzivatel, jenom ma pridany seznam uzivatelu ve skupine.

Zarizeni


V adresari /dev najdete opravdu nevidane veci jako date, msec, mouse pod. Zjistil jsem ze to je opravdu prakticke.

ACME


Acme je prvni z aplikaci, ktere bych tu rad popsal. Je to vyvojove prostredi. Ale ne tak ledajake. Nic podobneho jsem v zivote nevidel. Je naprosto male a jednoduche ale je velice pohodlne. Napsal jsem v tom plan9 verzi XaoSe takze vim o cem mluvim.

Po spusteni se objevi stroha obrazovka. Nahore je lista s par prikazy (cut, paste, new apod). Vpravo je sloupec z vypisem adresare. Uzasne je ze cele se to ridi mysi ale nejsou tu zadne menu, nic na vas nevyskakuje, nic nepada, zadne dialogy, miliony piktogramu apod... Prvni tlacitko oznacuje nebo umistuje cursor, prostredni vyhledava v textu nebo otevira soubor, na ktery zmacknete a prave spousti. Vystup programu se zobrazuje do okna. Genialni je ze to vam uplne staci. Kdyz chcete kompilovat, macknete pravym tlacitkem do horniho "menu" a pripisete si prikaz mk(obdoba make) a potom pravym tlacitkem mk spustie. Kompilace se nastartuje a vypise chybu. Macknete prostrednim tlacitkem na chybu a otevre se okno se zdrojakem a kurzor (jak textovy tak misojdni) sam skoci na chybu. Chybu opravite, macknutim prostredniho tlacitka na Put ulozite a zase kompilovat.

Uzasne je ze k tomu vsemu bylo treba pouze funkce na editovani textu, spousteni externich nebo asi peti internich (jako cut, paste, put) prikazu. Vyhledavani funguje i na specialni adresy napriklad soubor:radka. Coz je (cirou nahodou :) format vypisovany kompilerem pri chybach(nebo grepu apod) - proto to doskocilo do souboru. Nebo napriklad file:/regexp/, regexp atd. Takze macknutim na ahoj na obrazovce vyhledate dalsi vyskyt ahoj apod (opakovani).

Stejnym genialnim zpusobem je vytvoreno i okno s adresarem - je to vypis ls. Provedeni adresare je opet zavolani ls takze se vam okno prepise novym adresarem. Macknuti prostredniho tady nevyhledava ale otevira soubor(to se pozna podle nazvu okna (pokud to je neco jako /bin/ je to adresar - tedy otevirat a pokud to je /usr/none/aa.txt je to soubor - tedy vyhledavat.)

Prostredi me neprestava fascinovat svoji jednoduchosti a chytrosti. Navic umi poustet externi prikazy, a mam mimochodem takto udelany prikazy pro substituci, apod. Take dela adresarovou strukturu pro komunikaci s clienty(neco jako 8 1/2) tak funguje treba mail program, ktery vypise inbox a prostredni tlacitko odchyti a otevre postu, potom tam pripise tlacitko send, ktere je jednoduchy mailer. Podobne je implementovan i debuger, ktery umi zobrazovat promene jen macknutim na ne apod.

Impozantni je kdyz napisete napriklad Cut a povedete ho a ono ze souboru zmizi protoze to je interni prikaz, ktery smaze oznacenou cast-cut :) Ma samozdrejme i undo.

Nadhrene je take to, ze po vyhledani se cursor premisti na vyhledanou pozici. Takze opetovnym bouchnutim do mysi se necha vyhledat dalsi a mate vse pripraveno na modifikaci textu.

Oblibil jsem si take system oken. Neni tu zadne presouvani, ikonizovani stosovani a ruzne neprakticke schovavani oken. Misto toho jsou tu sloupce oken(vetsinou dva, jedev velky a druhy maly) V malem soupci je vetsinou vypis adresare, chyby apod, velky se potom pouziva pro editaci. Kazdy sloupec obsahuje okna, ty jsou pres celou tlousku sloupce a vetsinou jen jedno je videt a ostatni sjou "nastosovane" nahore, kde macknutim na ne si je hodite dopredu. Genialni na tom je, ze kdyz okno chcete videt, tak na nej bouchnete(protoze lista (tag s nazvem a prikazy) je vzdycky videt), kdyz ho chcete maly tak ho hodite kamkoliv do pravyho sloupce a naopak, nemusi si clovek lamat hlavu s presnym umistenim a velikosti, proste vetsinou se to obejde jednim asi dvoucentimetrovym dragem coz je zcela neobvykle.

Kazde okno obsahuje tag, coz je lista s nazvem a sadou internich prikazu (jako u hlavniho tagu) kde je put pro ulozeni, undo, cut, paste, I look (pro vyhledavani) a libovolny prikaz co si pridelate. Externi prikazy se potom pousti ve stejnem adresari. Pokud chcete ulozit soubor pod novym jmenem, bouchnete na jmeno v tagu a prepisete ho. Tak se napriklad necha i ulozit vystup z programu apod.

Na mys ma podivnou heruistiku. Takze to treba pozna ceckarsky include a soubory v <> hleda i v include cestach. Navic pomoci pragma umi najit zdrojaky dane knihovny

Jako staremu vi-ckarovi mi moc vadi ignorance klavesnicovych prikazu - dokonce ani sipky nefungujou a musite posouvat cursor mysi, coz me neuveritelne irituje. Na druhou stranu je to ale tak jednoduchy a geniali koncept, ze mu to odpoustim. Poprve jsem se s mysi citil dobre..A kdyz se mi zachce prikazu, je tu editor sam.

Toto prostredi se pry vyskytuje i pro unix na plan9 fans ftp site (dostupny z te homapagi) pod krycim nazvem wily. Nevim jak tam vypada ale doporucuju ho otestovat - protoze tohle se neda popsat, to se musi zazit :)

Acid


Acid je debuger. Ale ne tak obycejny debuger, protoze to vlastne debuger neni. Je to interpretr jazyka acid s rozsirenim pro debugovani. (to sem to zamotal co?)

Jak to v praxi vypada? Cely debuger obsahuje interpretr acidu a jednoduchou architekturove zavislou cast a potom asi 600 radek knihoven v jazyku acid.

Jazyk acid ma na uzivatelske urovni promene schodne s promenymi v programu. Umi pracovat se integery, floaty, stringy a listy. Cele debugovani se provadi pomoci nekolika promenych a funkci. Jazyk umi klasicke veci, jako aritmetiku, cykly, podminky a vsechno hodne podobne cecku, aby so ceckari nemuseli prenaucovat. Nasledujici program:

acid: i = 0; loop 1,5 do print(i=i+1)
Je smycka a vypise nasledujici cisla:
0x00000001
0x00000002
0x00000003
0x00000004
0x00000005
To jeste neni moc zajimave. Promene se vsak mohou vyuzivat i z programu. Normalni promene obsahuji adresy (se symboltable) jejich hodnoty jde cist pomoci * (vypise hodnotu ukazatele) a @ (ta to vypise z binarka, ne pameti) Acid nic nevi o jejich typu a tak je clovek musi napred otypovat pomoci:
x = fmt(x, 'D')
Prevede x do desitkove formy. Fmt se da nahradit i operatorem \.
acid: print(x, fmt(x, 'X'), x\X) // print x in decimal & hex
10 0x0000000a 0x0000000a
Ma nekolik zajimavych formatu. X jsou cisla(hexa), D jsou desitkova cisla, s je string, a je adresa (nebude se zobrazovat jako adresa, ale vypise se jmeno ze symbol table), i je instrukce (zdisasembluje se) apod.

Takto napriklad vypada program disasemblujici main:


acid: p=main\i                     // p=addr(main), type INST
acid: loop 1,5 do print(p\X, @p++) // disassemble 5 instr's
0x0000222e LEA  0xffffe948(A7),A7
0x00002232 MOVL s+0x4(A7),A2
0x00002236 PEA  0x2f($0)
0x0000223a MOVL A2,-(A7)
0x0000223c BSR  utfrrune
acid:
Je tu pristup i k registrum jako PC apod. Take lze debugovat zdrojaky pomoci nekolika fci:

acid: pcfile(main)              // file containing main
main.c
acid: pcline(main)              // line # of main in source
11
acid: file(pcfile(main))[pcline(main)]  // print that line
main(int argc, char *argv[])
acid: src(*PC)                  // print statements nearby
 9
 10 void
>11 main(int argc, char *argv[])
 12 {
 13 int a
Listu se pouziva napriklad k praci se seznamem breakpointu. Prilozena knihovna potom dela veci jako step apod. Cele interface komunikujici s acme ja napsano takto.

Tento pristup ma nekolik vyhod(mimo prenositelnosti) take naprilad to, ze muzete vyvinout nejake ty utilitky pro ladeni vaseho programu. Ja jsem si udelal naprilad fci vld, ktera otestovala, jestli jsou hlavni promene XaoS OK. Potom jsem mohl program tracovat tak dlouho, dokud vld a acid udelal vse za me. Naucit se s tim mi trvalo pul hodiny.

C


I tady doslo ke zmenam. Bez specialnich switchu c compiler bere pouze striktni ansi standard (tedy void main(void);), ma zjednoduseny preprocesor a ma kompletne predelanou strukturu knihoven.

Narozdil od klasickeho pojeti, preprocesor je integrovan primo do kompileru. Zrusili podporu #if (grrr), a ##. Autori tvrdi ze vyndali if, protoze kompilkuje preprocesor, neni dulezita a je casto zneuzivana. V tomto ohledu bych se s nima hadal. Pro zaryte #ifare je to externi /bin/cpp, kterym se to muze predkousat. Tvrdi, ze kompiler vynechava nepristupny kod a tak je lepsi pouzit klasicky if a ne #if. Co ale mimo funkci?....

Includy jsou rozdeleny do machine(/i386/include) dependent a undependent(/sys/include). Machine dependent jsou jenom tri-jeden popisuje registry, druhy typy a treti dela makra na promeny pocet parametru.

Vsechny bezne knohovny jsou sklepnuty do jednoho includu libc.h. To je celkem sikopvny, uz to cloveka nebuzeruje, ze nezna memcpy a hned zase exit ci to nebo ono. I ostatni includy odpovidaji knihovnam. To opravdu praci znacne zjednodusuje.

Main uz nevraci int, protoze jak sem rikal vsechno vraci string/NULL kdyz je vse spravne. To se dela pomoci fce exits. A abychom nezakrneli printf zmyzelo a standardne se pouziva trochu prekopane print. Printf je take k dizpozici ale musi se priinkludit klasicke stdio.h. A je tu jenom pro uplnost.

Pripony .o byly zruseny. Misto toho se pouziva pripona podle architektury. Na pc .8. To zjednodusuje kompilaci pro vice architektur. Na praci s nima jsou i stejne pojmenovane nastroje. Kompiler je 8c, linker(tady se mu rika loader) 8l apod. Navic vysledny soubor se implicitne jmenuje 8.out.

To ze linker neni linker ale loader ma svuj vyznam. Objekty totiz nejsou objekty. Kompiler dela preprocesorizaci, parseni, alokaci registru, generaci kodu, a vystupuje do jakesi verze assembleru. Zatimco loader vybira instrukce, dela sheduling, branch folding a uklada vysledny spustitelny kod. Proto loader muze prohazovat data apod. Vysledkem je az neuveritelne rychly, celkem unosne (proti gcc mizerne, proti borlandum skvele) optimizujici, velmi stabilni kompiler.

Stare stdio bylo nahrazeno novou knihovnou bio (buffered input/output). Bio je mnohem mensi a autri tvrdi ze rychlejsi, umi pracovat bufferovane na bloky, radky a nebo nebufferovane. Neni tu standardni buffer pri input/output. Program v biu vypada asi takto:


obuf  bin;
Biobuf  bout;

main(void)
{
        int c;

        Binit(&bin, 0, OREAD);
        Binit(&bout, 1, OWRITE);

        while((c=Bgetc(&bin)) != Beof)
                Bputc(&bout, c);
        exits(0);
}

Text v planu 9 uz neni ASCII. Ale pouziva se 16 bitovy unicode zpetne kompatibilni s ascii. To je jeden z hlavnich duvodu pro zruseni stdia. Take stringove operace jake strchr byly zmeneny na unicodove (utfrune). Pro ukladani/zapis se pouziva Bgetrune/Bputrune. Pro print je tu novy %C a %S pro runy. Nejvetsi problem je z promenyma. naoriklad:

char *cp = "abc ";

Uz nedela string, ale runy. V tomto pripade to bude fungovat, protoze compiler radeji pouzije kratke osmibitove runy. Napriklad:

Rune *rp = L"abc ";

Tohle uz vytvori plnohodnotne 16ctibitove runy.

Zajimave je pojate zpracovani parametru pomoci ARGBEGIN, ARGEND a ARGF:


void
main(int argc, char *argv[])
{
        char *defmnt;
        int p[2];
        int mfd[2];
        int stdio = 0;

        defmnt = "/tmp";
        ARGBEGIN{
        case 'i':
                defmnt = 0;
                stdio = 1;
                mfd[0] = 0;
                mfd[1] = 1;
                break;
        case 's':
                defmnt = 0;
                break;
        case 'm':
                defmnt = ARGF();
                break;
        default:
                usage();
        }ARGEND

Je tu nekolik rozsireni do ansi-C. Napriklad:

r = (Rectangle){add(p, q), (Point){x, y+3}};

Pro nastavovani struktury. Take struktury lze delat anonymne:

struct blb {
  int a;
  union {
   int b;
   double c;
  }
}

Vznikne hybridni typ, kde blb.a se chova jako struktura ale blb.b je jako v unionu. Pokud uz mate nejakou strukturu vytvorenou proste ji pripisete bez jmena a funguje to take. Navic vznikne neco jako dedeni-adresa na blba se necha uzit vsude, kde chteji adresu na tu druhou strukturu. Take jde na anonymni strukturu pristupovat pres jeji typove jmeno:

struct
{

        int     type;

        Point;

} p;

Existuje potom p.Point. Tyto rozsireni jsou velice podobne jazyku alef. Navic jsou tu i dalsi rozsireni pri inicializaci. Tyhle jsou ale IMO nejzajimavejsi.

Pro kompatibilitu tu taky (ale jen v plne verzi) existuje pcc, ktere je schodnejsi z POSIX standardem. (Timto kompilerem byl uspesne preporten treba quake)

Alef


Alef je novy programovaci jazyk podobny cecku, ve kterem je napsana velka cast planu9. Je opravdu hodne podobny cecku(spise se jedna o rozsirene c s par nekomparibilitama) proto jen kratce popisu hlavni rozdily. Kompiler alefu se jmenuje 8al. Je dostupny i na nektere jine platformy (treba SGI). Jeho sila spociva v distribuovanych programech.

Kazdy program v alefu se sklada z nekolika tasku, ktere spolu komunikuji pomoci channelu. V planu 9 a i jinych implementacich je pamet sdilena ale nemusi to byt pravidlo. Novy task vznika nasledujicim volanim:


proc func(1,2);

Tato radka vytvori novy task a spusti v nem funkci func s parametry 1 a 2. Channel v alefu je normalni promena. Jeho deklarace vypada takto:

chan(int) c;

tyto nadeklaruje chanel pro prenaseni integeru. Pred pouzitim je nutne jej take inicializovat:

alloc c;

a ted uz muzem komunikovat. (alloc funguje podobne jako new i pro jine typy) K odesilani zprav se pouziva operatior <-= :

c<-= 1+1;

k prijimani se pouziva operator <-. Napriklad:

i = <-c;

A takhle vypada jednoduchy paraelni program v alefu: #include void void receive(chan(byte*) c) { byte *s; s = <-c; print("%s\n", s); terminate(nil); } void main(void) { chan(byte*) c; alloc c; proc receive(c); c <-= "hello world"; print("done\n"); terminate(nil); } Alef ma take konstrukci nahrazujici unixacky select/pool, ktera umoznuje cekat na vice channelu zaroven:

alt {
case <-c0:      /* receive & discard input on channel c0 */
        break;
case r = <-c1:  /* receive input on channel c1 into r*/
        break;
case c2 <-= 1:  /* send 1 on c2 when there is a receiver*/
        break;
}

Channely standardne pracuji synchrone. Je mozne ale vytvorit i bufferovane asynchroni:

        chan(int)[100] kbd;

Na channely funguje take unarni oprator ?, pomoci ktereho jde zjistit jestli jde data prijimat (if (?ch)) nebo odesilat(if (ch?)).

Nevim do jake miry toto pojeti channelu je dobry napad. Trochu mi to pripomina pascal. Ale je rozhodne zajimave.

Jako alternativu k paraelnim threadum alef podporuje tasky. Ty nebezi zaroven ale chovaji se asi jako miltitasking ve windows. Pokud jeden pracuje ostatni stoji az zacne neco zapisovat/cist z channelu. Pro jejich vytvareni je tu primitivo task, ktere funguje stejne jako proc. Timto zpusobem je napriklad zaintegrovan debugger to kompileru c. U tasku je take garantovano to, ze budou sdilet pamet. Nasledujici program reaguje na mys nebo klavesnici:


void
kbdtask(chan(int) kbdc)
{
        int r;

        for(;;) {
                r = <-kbdc;
                /* process keyboard input */
        }
}

void
mousetask(chan(Mevent) mc)
{
        Mevent m;

        for(;;) {
                m = <-mc;
                /* process mouse input */
        }
}

main(void)
{
        chan(int)[100] kbd;
        chan(int) term;
        chan(Mevent) mouse;

        alloc kbd, mouse, term;
        proc kbdproc(kbd, term), mouseproc(mouse, term);
        task kbdtask(kbd), mousetask(mouse);


        <-term;         /* main thread blocks here */
        postnote(PNPROC, mousepid, "kill"); /*toto je obdoba signalu*/
        postnote(PNPROC, kbdpid, "kill");
        exits(nil);
}
Vlastni cteni je implementovano v kbdproc a mouseproc. Ty cekaji na vstup a posilaji ho do chanelu. Tasky jsou tu vpodstate pouzity misto alt.

BTW doufam ze jste si vsimli, ze misto cisel signalu jsou tu libovolne stringy, coz dela ze starych signalu moc zajimavy komunikacni prostredek.

Dalsi vec, ktera byla zmenena jsou typy. Misto struct je tu aggr. Ten udela rovnou i typedef, takze se situace trochu zjednodusuje. Navic zde zacaly rozsireni, ktere jsem popsal v casti o C. Dalsi novy typ je tuple. Je to jakasi nepojmenovana struktura. Pouziva se casto v kombinace se channely. Deklarace vypada takto:


tuple(int, byte*, int) t;

Vytvori tuple jmenem t skladajici se ze dvou intu a jednoho bytu. Prace s tuplem pak vypada nasledovne:

        tuple(int, int, byte*) t;
        byte i;

        t = (0, (int) i, "abc");
        t = (tuple (int, int, byte*)) (0, i, "abc"); /*udela to same*/
	(nil, a, b) = t; /*nil znamena tuto hodnotu nikam neukladat*/

Pomoci tuple se nechaji naplnovat i struktury apod. Nepojmenovany tuple se se necha pouzit k nastavovani struktur(agregatu) treba nasledujici:

        Circle c;
        Point p;
        int rad;

        (rad, p) = c;

Ulozi do promene c kruh o polomeru rad a stredu p, v pripade ze Circle je struktura vytvorena z integeru rad a anonymni struktury Point. Funguje to priblizne jako {} konstrukce v cecku. V teto situaci ma opravdu hodne vyuziti. Nikdy by me nenapadlo jak takovy podivny typ muze byt uzitecny. Take je mozne pomoci tuple prohazovat promene:

        (b,a) = (a,b);

Jiny priklad vyuziti je:

Point
midpoint(Point p1, Point p2)
{
        return ((p1.x+p2.x)/2,(p1.y+p2.y)/2);
}

void
main(void)
{
        Point p;

        p = midpoint((1,1), (3,1));
}

Jine sikovne vyuziti je v pripade, kde z funkce potrebujete vratit dve nebo vice promenych. Nemusite kvuli tomu vytvaret typ, nebo pouzivat ukazatele. Proto napriklad funkce alefu na extrahovani cisla ze stringu vraci nejen cislo ale i navratovou hodnotu urcujici chybu a pozici, kde skoncila, coz je rozhodne sikovne.

Posledni datovy typ adt. Je to abstraktni datovy typ, ktery potesi C++ckare, protoze to je vpodstate trida. Spojuje data a metody, data jsou privatni, pokud nevyexportujete a metody naopak. Syntax ma podobnou jako aggr/union. Napriklad:


adt Mouse {
                Mevent;
        extern  chan(Mevent)    ch;
                chan(int)       term;
                int             fd;
                int             pid;
                Mouse*          init(byte*, chan(int));
                void            close(*Mouse);
        intern  void            mproc(*Mouse);
};
Metody muzou prebirat ukazatel na instanci jako prvni parametr. Dedicnost uz byla vpodstate popsana v casti o anonymnich strukturach. Tak dojde k dedeni metod. Zdedene metody samozdrejme obdrzi ukazatel na svuj typ a ne na cely adt. Metody se potom definuji jako funkce [jmeno typu].[jmeno metody]. Je mozne volat metody i bez instance pomoci .[jmeno typu].[jmeno metody]. Misto ukazatelu na instanci se potom preda nil.

Alef ma take zabudovaneho cosi jako assert - check. Funguje podobne, date podminku a chybovou hlasku a v pripade chyby vypise neco jako:


t.l:7:main() write error, errstr(file not open)

(errstr je nahrada za errno). Default akci lze predelat pomoci alefcheck. Kontrolu lze take vypnout. Podle meho nazoru ceckarcky assert je prinejmensim stejne dobry prostredek a tak to povazuju za vbytecnost.

Dalsi rozsireni rescure/raise konstrukce. Pomoci rescure muzete urcit kus kodu, ktery se ma provedst po zavolani raise. Bylo pri tom mysleno na osetreni chyb. Kdyz je treba v pripade chyby neco uzavrit, uvolnit apod, jednoduse to date do rescure a potom v pripade chyby zavolate raise. Napriklad:


    rescue {
        close(fd);
        continue;
    }

    if(read(fd, buf, n) <= 0)
        raise;

Navic je mozne rescure bloky pojmenovavat/vyvolavat podle jmena.

Jine rozsireni je ze continue/break bere volitebne i cislo, ktere znamena kolik smycek se ma ukoncit..

Pro paraelni programovani je tu take par. Jeho syntax je:


        par {
                statement 1;
                statement 2;
                   ...
                statement n;
        }

Statementy se potom provedou paraelne.

Pro jednoduche smycky je to iteractni operator ::. Ten funguje jako jedoduchy for. Napsany doprostred statementu ho nekolikrat opakuje:


        int i, a[10];
        a[i=0::10] = i;
        print("%d ", a[0::10]);

Program vypisuje cisla od 0 do deseti. Trosku komplikovanejsi priklad je nasobeni matic:

        typedef float Matrix[4][4];
        void mul(Matrix r, Matrix a, Matrix b){
                int i, j, k;

                r[0::4][0::4] = 0;
                r[i=0::4][j=0::4] += a[i][k=0::4]*b[k][j];
        }

V paraelnim programovani jdou take dulezite zamky. K tomu tu je abstrakti natovy typ QLock. Zamykani se provadi pomoci QLock.lock a odemykani pomoci Qlock.unlock. Pro zamykani casti kodu je sikovne pouzit !{...} coz funguje jako ceckarsky blok ale mate zaruceno ze nikdo jiny se v nem nenaleza.

Posledni rozsireni je ... . Je to konstrukce pro promeny pocet parametru. Na parametry se potom pristupuje nasledujicim zpusobem: ((int*)...)[i]; Je mozne je take predat dalsi funcki s promenym poctem parametru.

Jinak alef bere ... jako ukazatel.

rc


Rc je shell (aby se to nepletlo :). Je velmi podobny born shellu z drobnymi rozsirenimi. Hlavni rozdil je, ze promene nejsou stringy, ale listy. Tedy zmizely problemy z oddelovaci. Existuje operator $#, ktery vrati pocet prvku v listu. Navic ma operator ^, kery slouzi ke spojeni stringu, nebo listu.

Pipe ( | ) byla take rozsirena. Je mozne pouzivat ne jen linearni spojeni ale take stromove:


        cmp <{old} <{new}

Pusti dva prikazy old a new a jejich vytup presmeruje do rour a ty da prikazu cmp jako parametry (roury jsou samozdrejme soubory takze preda neco jako /dev/fd/6 a /dev/fd/7).

Syntaxe je take trochu oceckovana:


for(i in *.c) if(cpp $i >/tmp/$i) vc /tmp/$i

for(i){

    if(test -f /tmp/$i) echo $i already in /tmp

    if not cp $i /tmp

}

Rc ma take nektere zabudovane prikazy jako echo, rfork, exit, cd, shift apod.

mk


Mk je obdoba unixackeho make. Popisuju ho tu jenom jako ukazku ze i tady jde neco zlepsit. A tak jen nekolik vyhod:

Instalace


Doufam ze jsem vas uz presvedcil a zacinate downloadit diskety. Chci popsat par problemu, ktere se mi prihodily.
Doufam ze Vas plan9 zaujal. Tento dokument uz dopisuju ve wilim a musim rict ze docela pekne funguje. Sve mile vi asi neopustim ale ze vsech gui, ktere jsem kdy videl je willy rozhodne to nejjednodusi, nejsnadneji ovladatelne a nejpouzitelnejsi.