Jsem asi hnidopich, ale na zacatku melo byt tucnym pismem uvedeno, ze se jedna o Linux na x86. Mozna je to profesionalni deformace, jelikoz pracuji i s jinymi architekturami. Kazdopadne formulace "Pro systémová volání se v Linuxu používá přerušení int 0x80." je bez uvedeni architektury nesmysl.
Dal:
> Když program zapíše na nealokované místo, bude
> proces ukončen (SIGKILL kernelu).
Tesne vedle - kernel ho sejme signalem SIGSEGV
> Linux však podporuje mnoho formátů spustitelných
> souborů, my se zaměříme třeba na formát ELF -
> executable and linkable format.
Jsem rad, ze padla volba "třeba na formát ELF" - on je totiz nejdulezitejsi a prakticky na Linuxu nejvic pouzivany :-)
> Adresa 0x08048000 [... sem se zavede program]
Kde se tohle cislo vzalo? Muze byt jine?
- Ano muze byt jine a je urceno linkerem pri sestavovani programu. A kde se ho kernel dozvi? V hlavicce ELFu.
> Program obvykle bývá nahrán do 128 MB adresového
> prostoru.
Zase vedle - ten vas konkretni program bude zacinat na adrese 0x08048000, cili tesne _nad_ 128 MB
No nic, necham prudeni a jdu na obed :-))
Nechtel byste neco zajimaveho zase napsat vy sam ? Nepresne extrakty man & HOWTO a jeste monokulturni x86-linux centrismus :-) mi nijak zajimavy neprijde... Myslim ze hodnotne informace hodne publikovani jsou ty s vysokou (autorovou) pridanou hodnotou z praxe a multiplatformnim nadhledem (nebo jeste lepe i mimo linux-only kernel).
> > Když program zapíše na nealokované místo, bude
> > proces ukončen (SIGKILL kernelu).
> Tesne vedle - kernel ho sejme signalem SIGSEGV
Tesne vedle ;) kernel posle procesu signal SIGSEGV, tj. zavola se handler tohoto signalu. To ze default handler proces zabije je jina vec :) Muzete si napsat handler svuj a osetrit to jak chcete, muzete dokonce zpusobit dalsi SIGSEGV v samotnem handleru a volat ho tak rekurzivne treba 1000x ;)
Co je zajimavejsi, v linuxu muzete uvnitr handleru SIGSEGV zjistit adresu na ktere doslo k chybe, zavolat na ni mmap, vratit se a pokracovat v behu programu ... otazkou je k cemu je to dobre, protoze obvykle pokud program spacha SIGSEGV, je to zpusobeno chybou kterou zevnitr tezko opravite.
Vyjimkou je User-Mode Linux a podobne aplikace, ktere diky teto vlastosti mohou provadet vlastni strankovani.
Tady nejde o navratovou adresu, tj adresu chybne instrukce (mimochodem, myslim ze by to bylo tezke, protoze se jedna o zasobnik KERNELU). Tady jde o adresu mista v pameti, jehoz cteni/zapis vyvolalo chybu. Tj. pokud sigsegv nastalo diky radku c=a[i], zjistite adresu a+i a muzete na tuto adresu primapovat pamet, takze radek c=a[i] muze probehnout bez chyby.
delate, jako by to byla vysada linuxu :-) snad kazdy operacni system umoznuje nejakym zpusobem reseni vyjimek. at uz je to SEH a VEH pod nt nebo signal handlery pod *nixama a je snad logicke, ze u nekterych vyjimek muzeme (treba po zmene atributu stranky a pripadne alokaci pameti - takto jsou treba reseny samoexpandujici stacky - guard pages) pokracovat v cinnosti
Ty guard pages --- to mi pripomina, jak jsem portoval gcc na OS/2. Guard pages jsou strasna prasarna, ktera komplikuje kompilaci kodu --- pokud funkce obsahuje lokalni promenne delsi nez jedna stranka, tak hrozi, ze sahne o dve stranky pod posledne namapovanou stranku zasobniku a cely program spadne. Proto se do ramu musi na kazdou stranku napsat. Totez plati pro alloca --- je treba se dotknout tech alokovanych mist v sestupnem poradi.
Na Linuxu to potreba neni, ten vzdycky alokuje a namapuje pamet pri page faultu nad esp.
>Ty guard pages --- to mi pripomina, jak jsem portoval gcc na OS/2.
>Guard pages jsou strasna prasarna, ktera komplikuje kompilaci kodu
>--- pokud funkce obsahuje lokalni promenne delsi nez jedna stranka,
>tak hrozi, ze sahne o dve stranky pod posledne namapovanou stranku
>zasobniku a cely program spadne. Proto se do ramu musi na kazdou
>stranku napsat. Totez plati pro alloca --- je treba se dotknout
>tech alokovanych mist v sestupnem poradi.
ano. kazdopadne toto je zalezitost prekladace a vetsinou se resi nejakymi
pomocnymi fcemi v prologu (treba check_esp), ktere manuelne donuti
stack expandovat. nerikam, ze je to koser, nicmene -
mit vice jak 4 kila na zasobniku, to uz o necem svedci. nevim, mozna jsem
prilis ovlivnena kernel modem, ale tam je stack maximalne 12 kilo velky a
kdyz jste nekde hluboko v i/o stacku, nemuzete si se zasobnikem dovolit
moc. ale od toho tu je preci bss sekce (pridat static).
mit tolika mista lokalne ve fci spise (IMHO) ukazuje, ze by mela byt
rozdelena na vice podfci, kde onen problem odpada ...
>Na Linuxu to potreba neni, ten vzdycky alokuje a namapuje pamet pri
>page faultu nad esp.
pamet prideli kdekoliv, nebo pouze v rozsahu zasobniku? rozsah zasobniku
je nekde zapsan? treba v pe filu je polozka commit/reserve co se tyce
zasobniku.
Jisteze neni zrovna kulturni alokovat na zasobniku pole o velikosti 4k --- jenomze userspace programy museji fungovat vzdy, kdyz splnuji specifikaci, ne jen tehdy, kdyz jsou napsany kulturne. Proto je treba pri pouziti guard pages po kazdem volani alloca zjistit velikost a prohrabnout alokovane pole smerem dolu. Stejne tak to kompilator musi pri alokaci lokalnich promennych zjistovat, zda nepresahnou 4k a pripadne se zasobniku dotknout jeste drive nez je zacne pouzivat.
K tomu > 4k na zasobniku - kernel je v tomto pripade trosku extrem, pomerne kompaktni "program", u ktereho lze uhlidat, ze se cely kod k zasobniku bude chovat slusne a lze tedy rict, ze celkova velikost stacku bude 8k (nebo 12k, nevim, jestli se to posledni dobou nezmenilo).
Nicmene user-programy obsahuji tuny knihoven, o kterych nikdo nic nevi, a proto je by default omezena velikost stacku na 2MB (aspon v pthread-programech na starsich kernelech). Tedy neni duvod, proc si nevytvorit na stacku neco ala char path[MAX_PATH], na coz se zrovna v linux-kernelu pouziva dynamicka alokace get_page...
Samozrejme nejake slusne pravidla jsou vhodne (psat takhle rekursivni funkce atd.), nicmene povazovat to predem za prasarnu mi prijde prehnane.
No jo no jo, dyt ja vim ;-)
V sighandlerech se vubec daji delat zajimave veci. Namatkou:
1) Backtrace - jakmile mi program nekde zhuci, tak v handleru segfaultu muzu vypsat backtrace, abych se dozvedel, kde k tomu doslo. Bohuzel s nastupem novych formatu ladicich informaci to prestava byt trivialni zalezitost.
2) Osetrovani chyb - napr. mam pointer a nevim jestli je platny (inu stane se :-). Pomoci setjmp(3) si ulozim aktualni stav procesu, zkusim sahnout na adresu v tom pointeru a kdyz to nahodou nevyjde a ocitnu se v sighalndleru, muzu se pomoci longjmp(3) vratit a podruhy uz to nezkouset.