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.
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.
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/
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.
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 )
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.
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.
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 #
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 ma na svedomi cs demon, ktery pouziva system podobny streamum.
Navic tu existuje neco podobneho sticky bitum v unixu..(prebirani cizich prav)
Na druhou stranu je tu uzivatel none, ktery nemuze skoro nic a je obdobou
anonymnich ftp serveru.
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 :)
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:
Takto napriklad vypada program disasemblujici main:
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.
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:
Zajimave je pojate zpracovani parametru pomoci ARGBEGIN, ARGEND a ARGF:
Pro kompatibilitu tu taky (ale jen v plne verzi) existuje pcc, ktere je
schodnejsi z POSIX standardem. (Timto kompilerem byl uspesne preporten treba
quake)
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:
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:
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:
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:
Alef ma take zabudovaneho cosi jako assert - check. Funguje podobne, date
podminku a chybovou hlasku a v pripade chyby vypise neco jako:
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:
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:
Pro jednoduche smycky je to iteractni operator ::. Ten funguje jako jedoduchy
for. Napsany doprostred statementu ho nekolikrat opakuje:
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.
Pipe ( | ) byla take rozsirena. Je mozne pouzivat ne jen linearni spojeni ale
take stromove:
Syntaxe je take trochu oceckovana:
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.
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..)
Paraelni programovani
I tady bylo vymysleno neco noveho.
Name space
Pro to jsou tu hned tri systemova volani: mount, bind a umount.
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.
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.
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.
Specialni uzivatele
Zadni takovi tu nejsou. Existuje uzivatel adm, ktery ma sice prava zpravovat
system, ale nemuze cist cizi adresare apod.
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.
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?)
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.
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.
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.
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.
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.
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.
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
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?)).
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.
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.
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.
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.
rescue {
close(fd);
continue;
}
if(read(fd, buf, n) <= 0)
raise;
Navic je mozne rescure bloky pojmenovavat/vyvolavat podle jmena.
par {
statement 1;
statement 2;
...
statement n;
}
Statementy se potom provedou paraelne.
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.
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.
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).
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.