2005 HARMADIK JAVÍTÁSON ÁTESETT VERZIÓ, NEM TELJES ÉS TARTALMAZ HIBÁKAT!
„A régi időkben, aki tanult, önmagáért tette. Manapság, aki tanul, más miatt (az elismerésért) teszi.” - Konfuciusz, ókori Kína
1
Hardverismeretek
CPU CISC
RISC
bonyolult utasítások
egyszerű utasítások
több mindenre jó
gyorsabb, jobban párhuzamosítható
kevesebb regiszterrel rendelkezik
több regiszter
változó hosszúságú operandusok használata
egyenlő hosszú operandusok
lassabb CPU bizonyos környezetben bonyolult felépítésű pl. x86-architechtúra (Intel P4, AMD XP)
pl. ARM
x86 regiszterei: ─ ─ ─ ─ ─ ─ ─ ─
általános regiszterek: AX (akkumulátor~), BX (bázis~), CX (számláló~), DX (adat~) szegmensregiszterek: CS (kódszegmens), SS (verem~), DS (adat~), ES(extra~) IP: következő utasítás SP: veremmutató BP: címmutató a veremben SI: mutató az adatforrásra DI: mutató a célra 16 bitesek, kiterjeszthetőek 32 bitessé (EAX, EBX, stb.)
●
Paraméterek a vermen keresztül adódnak át eljáráshíváskor. Pascal esetén az első paraméter kerül először a verembe, C esetén az utolsó -> C-ben van lehetőség eltérő számú paraméterrel történő függvényhívásra (pl. printf). A veremürítést a hívóeljárás végzi (Pascal) vagy a ret utasítás argumentumaként is meg lehet adni a törlendő egységek számát (C). A visszaadott érték ax-be kerül; ha nem fér el, akkor ax:dx-be vagy a 32 bites EAX-be.
● ●
Egy függvény gépi kódú alakja: int f(int a, int b) { return (a+b)*2; }
2
Intel szintakszis
AT&T szintakszis (pl. GNU)
push ebp
pushl %ebp
mov ebp, esp
movl %esp, %ebp
mov eax, [ebp+12]
movl 12(%ebp), %eax
mov edx, [ebp+8]
movl 8(%ebp)
pop ebp
popl %ebp
add eax, edx
addl %edx, %eax
add eax, eax
addl %eax, %eax
ret
ret Ugyanez a függvény optimalizált ARM assemblyben
add r2, r0, r1
@
r2 = r0 + r1
mov r0, r2, asl#1
@
r0 = r2 << 1
mov pc, lr
ARM (RISC) regiszterei: ─ ─ ─ ─ ─
r0, r1, ... r15: 32 bites regiszterek r0: visszatérési érték r13: ≈ sp r14: ≈ lr (rutinok hívásánál használatos, a visszatérési cím nem a verembe kerül, hanem az lr-be) paraméter-átadás során az első négy r0,...,r3-ba kerül, a többi a verembe
Adat- és címbusz CPU és RAM kommunikációjának menete: ha a memóriába kell írni valamit, az adat az adatbuszra kerül, a cím a címbuszra. A RAM ”érzékeli” a bejövő jeleket, s az adatot beírja a megfelelő helyre. Olvasásnál a CPU lesz a címzett, s az adatbuszra került a kért adat. A kommunikáció hardverszinten megszakításokon keresztül történik (interrupt). Assembly nyelven port-ok állnak rendelkezésre. in al, 0xD ; a 0xD port-ról olvasson be adatot out 0xE, ax ; a 0xD port-ra írjon ki adatot Ha a port-szám > 256, a dx regisztert használjuk: in al, dx Van sok olyan architektúra, ahol nincs külön I/O címtartomány, hanem az eszközök is a 3
memóriára vannak rá-mapelve.
4
Hardverkezelési módszerek: ●
Polling: a kérés elküldése után bizonyos időközönként megnézi, megjött-e már a válasz. Ez a módszer ”fárasztó”, lassú, de buta hardverrel nincs más módszer.
●
Interrupt: CPU ”észleli” a megszakítást, az aktuális folyamatot menti, majd megnézi, hogy mi kezeli le a hardver kérését. Ennek átadja a feladatot, s folytatja a saját felfüggesztett feladatát.
Időzítő megszakítása: a DOS 18,2 Hz-enként váltott a task-ok között. A Linux kezdetben 100, manapság 1000 Hz-el vált ki megszakítást (HZ nevű define segítségével szabályozható a kernel-en belül). Ennek a segítségével történik a taszkok közötti váltás (ha lejár az időszelet), és növel egy jiffies nevű globális változót is. Minél magasabb a HZ érték, annál gyorsabb válaszidejű lehet a rendszer, de annál több idő elmegy magára a kapcsolgatásra. DMA (Direct Memory Access): lényege, hogy a CPU terhelése nélkül bonyolítja le az eszközök és a memória közötti kommunikációt. A DMA (közvetlen memória-hozzáférés) módszer esetén a buszra egy DMA-vezérlő egység is csatlakozik. A CPU feladata csak a DMA-vezérlő beprogramozása. Az adatátvitel megkezdésekor a DMA közvetlen kapcsolatot teremt a periféria és a memória között. Miután az adatátvitel megtörtént, a CPU feldolgozhatja a memóriába került adatokat. A DMA vezérlő önállóan hajtja végre az adat-átvitelt (miután a CPU-tól megkapta a paramétereket) Paraméterek: honnan / hova (memóriacím), mennyit (átviendő adatok száma), hogyan (kivitel, behozatal) Előny: Az adatátvitel a CPU-t szinte teljesen tehermentesíti Hátrány: Elég nehéz programozni. A CPU és a DMA vezérlő a memóriabuszra kapcsolódik, mert működésükhöz szüksé-gük van a memóriára. Mivel a két eszköz egyidejűleg nem férhet a memóriához, a hozzáférés lehetőségét meg kell osztani a CPU és a DMA vezérlő között. Ennek megoldási formája a CPU leállítása, a memória időszelet eljárás vagy a cikluslopásos eljárás.
5
A DMA működési módjai: •
A CPU leállítása: A DMA vezérlő kérésére a CPU leáll, és le-kapcsolódik a memóriabuszról a DMA adatátvitel időtartama alatt. A DMA átviteli sebessége ekkor a legnagyobb. Olcsó és viszonylag kis sebességű memória esetén jöhet szóba. Ez csökkenti a CPU feldolgozó képességét (effektív sebességét).
•
Memória időszelet eljárás: A memóriaciklusokat két részre osztják: az egyik a CPU-é, a másik a DMA vezérlőé. Két memóriaciklusban mindig van CPU hoz-záférés, és mindig van DMA hozzáférés, ezért nagy CPU adatfeldolgozási sebesség, és nagy DMA adatátviteli sebesség lehetséges. Megvalósításához igen gyors memória szükséges, ez pedig drága.
•
Ciklus-lopásos eljárás: A CPU és a DMA vezérlő átlapolva használja a buszt. Ha a CPU és a DMA vezérlő azonos időben kér memória hozzáférést, akkor a DMA vezérlőnek elsőbbsége (prioritása) van a CPU-val szem-ben. Ilyenkor a CPU vár, míg a DMA memó-riaciklusa véget ér. Mivel a CPU időnként vár, működése lassul, de nem áll le.
DMA-t általában gyors adatátvitelnél használnak, amikor az adat csak rövid időre áll rendelkezésre. Ilyen pl. a winchesterről érkező adatok fogadása (a mágneses felület nagyon rövid idő alatt halad át az író-olvasó fej alatt), hang digitalizálása (az elhangzás pillanatában kell feldolgozni és a memóriában tárolni). A DMA nem csak a perifériák felől érkező adatok fogadására, hanem fordított irányban is használható (pl. a memóriában tárolt digitalizált hang lejátszása). Egy általános példa a működési elvére: a DMA a 2048-as és a feletti címtartomány egy részét ”birtokolja”, ezáltal több csatorna is rendelkezésre áll (minden eszközhöz külön). A DMA-vezérlő figyeli az adatbuszt. Ha a CPU nem foglalja le, akkor a 2048-as címet kiteszi a címbuszra, ”megszólítja” a hardvert, hogy írhatja az adatbuszra a biteket, ahonnan azok közvetlenül a memóriába kerülnek, megkerülve a CPU-t (erőforrás-megtakarítás, párhuzamosítás). A következő kérés számára már a 2049-es csatorna áll rendelkezésre.
6
HDD: a merevlemezben egy vagy több korong (lemez) helyezkedik el egymás alatt, mindegyikhez tartozik egy író-olvasó fej, mely a tányérhoz tartozó tekercset mozgatja. A tekercsen áram folyik át, ezáltal mágneses mezőt indukálva maga körül (írás). Olvasás során a váltakozó mágneses mező indukál áramot a tekercsen. A lemezen elhelyezkedő koncentrikus köröket sávoknak nevezzük. Az adatok a szektorokban kerülnek tárolásra, melyek blokkokba vannak csoportosítva. Kezdetben limitálták a HDD ezen paramétereit, de a merevlemez gyors tárkapacitás-növekedése hamar kinőtte ezeket a határokat, ezért bevezettek egy új címzési módszert, az LBA-t (Logical Block Addressing). A 0. szektor létfontosságú, ugyanis itt található a partíciós tábla. tekercs
sáv 5
4
fej
szektor
3 2
1
0
7
Flash: nem felejtő memória. Két fő típusa van: NOR
NAND page erase blokk
a memóriaterület mindkét típusnál blokkokra van osztva (8-128 kbyte) NOR: a bájtok egyesével írhatók / olvashatók. Törölni csak egyszerre lehet, ilyenkor minden bit 1 lesz. Íráskor ezek átbillenek 0-ra (fordítva nem működik). Egy blokk kb. 100.000 törlést bír ki. NAND: újabb technológia, olcsóbb, van hibaérzékelő rész. A page (512 byte / 2 kbyte) a legkisebb logikai egység, amit egyszerre írni / olvasni lehet. Minden lap mellett van egy OOB, ami ECC-t (ellenőrző-összeget) és jósági állapotot tárol. Láthatóan a flash kicsit másképp viselkedik, mint egy „hagyományos” (merevlemezszerű) adathordozó. Emiatt rajta „hagyományos” fájlrendszerrel adatot tárolni nem célszerű, mert a legtöbb esetében vannak olyan lemezterületek, amik sokkal gyakrabban használatosak, mint az átlag (super block, FAT, …), és így az az erase blokk sokkal hamarabb elérné a 100.000-es használhatatlansági határt, mint a többiek – jelentősen lerövidítve az egész adathordozó élettartalmát. Az egyik, kifejezetten flashre tervezett Linuxos fájlrendszer a JFFS(2) – Journalling Flash File System. Ez napló alapon működik: minden eseményhez egy bejezés kerül kiírásra a flashre (létrehozódott ez a fájl, beleíródott 100 A karakter, átneveződött, …). A használat folyamán lesznek olyan bejegyzések, amelyek már nem élnek (obsolated). Ha nincs elég szabad hely, a fájlrendszer szemétgyűjtő része elkezdi átpakolni a bejegyzéséket, az obsolated-ek kivételével. A pakolás módszere úgy van kitalálva, hogy az egyes blokkok egyenletesen terhelődjenek. A JFFS2 támogatja a tömörítést is.
8
Linux
process (folyamat): ●
saját munkaterülettel rendelkezik (verem)
●
jellemzői tárolva vannak (pl. User ID, Group ID,...)
●
erőforrást tud birtokolni
thread (szál): ●
kevésbé önálló mint a folyamat (általában)
●
lehetnek osztott részei más szálakkal
erőforrás: ●
RAM: az operációs rendszernek védenie kell a többi folyamattól
●
I/O
●
”minden, amiből kevés van”
API (Application Program Interface): az a felület, amit az operációs rendszer nyújt a programok felé Shell: parancsértelmező Linux memóriakezelése: A memória 4 kbyte-os területekre van osztva (lapozás), ami könnyebb címzést tesz lehetővé, ugyanakkor az ebből következő töredezettséget kezelni kell. Minden egyes folyamat rendelkezésére álló címtartomány a logikai 00-n kezdődik, ami a valóságos fizikai memóriában már más címre mutat, eképp nem történhet meg az, hogy különböző process-ek azonos memóriaterületet sajátként kezeljenek. Virtuális memória használata esetén (swap) a laptáblába be kell jegyezni, hogy az adott tartomány már nem közvetlenül elérhető. Kérés esetén kivétel generálódik, amit az operációs rendszer kezel, majd megtörténik a visszatöltés.
9
Unix alatt minden fájl (még a hardver is fájlokon keresztül érhető el). a /dev/ könyvtárban találhatók meg a hw-elemekre hivatkozó bejegyzések. 2 típus van, b – blokkos eszköz (HDD), c – karakteres (billentyűzet, USB, soros port)). ”brwx.........X,Y
fájlnév” alakúak, ahol az első karakter a típus, utána következnek a
jogosultságok, X a major number, amit azt határozza meg, hogy hanyadik a meghajtó, míg Y a minor number, ami eszközfüggő (lehet pl. partíciószám). A /proc/devices tartalmazza, hogy mely hw-eszközök aktívak + a hozzájuk tartozó major number-t. Írás és olvasás az eszközön hajtódik végre, minden egyéb extra igény az IOCTL-en (I / O Control) keresztül vehető igénybe. [konkrét példa ”brwx...” alakra és a /proc/device-ra]
/bin
mindenki számára nélkülözhetetlen dolgok
/sbin
rendszergazdák számára nélkülözhetetlen dolgok
/usr/local
az általunk felrakott fájlokat tartalmazza
/usr/bin
mindenki számára hasznos dolgok (pl. telnet, ftp)
/usr/sbin
rendszergazdák számára hasznos dolgok
/etc
konfig fájlok
/home/xxx
felhasználói könyvtárak
/tmp
ideiglenes könyvtár
●
A '.'-tal kezdődő fájlnevek ”rejtettek”, az ls parancs nem listázza ki.
●
Jogosultságok: rwx rwx rwx (hármas csoportokban: user, group, mindenki más).
●
Ha az első 'w' helyett lehet 's' áll; akkor a futó program a tulajdonos jogosultságával fog rendelkezni (set uid), pl. passwd. Ha a második 'w' helyett van 's', akkor ez a kitétel a csoportra teljesül.
●
Könyvtárak esetén van egy 't' flag, ami kikapcsolt állapotban azt eredményezi, hogy amennyiben egy könyvtárhoz van törlési jogunk, akkor minden fájlt törölhetünk belőle, még másokét is. Ez komoly problémákhoz vezethet pl. temp fájlok esetén, ezért /temp-hez ezt be szokták állítani.
Átismétlendő parancsok: mv, rm, cd, cp, mkdir, rmdir, cat, man, chown, chgrp, ssh, scp, mknod. Megjegyzés: chown a fájl tulajdonosát változtatja meg, mknod-dal pedig különleges fájlokat lehet létrehozni a következő argumentumokkal: -help, -r (rekurzív), -f (forced).
10
Fontosabb fájlok /etc/passwd: felhasználói adatok havasi:
x:
1000: 1000:
felhasználó jelszó régi helye ●
uid
guid
Havasi:
/home/havasi/
bin/bash
név
home
shell
a jelszavak kezdetben az /etc/shadow könyvtárban voltak a következő formában: 9 mezőt tartalmaz:
login-id
password
lastchg
min
max
warn
inactive
expire
flag
●
login-id: bejelentkező név
●
password: a jelszó 13 karakteren elkódolva. Tratalmazhat extra
●
lastchg: az 1970 január 1. és az utolsó jelszómódosítás között eltelt napok száma.
●
min: a minimális napok száma, ami 2 jelszó változtatás között el kell teljen.
●
max: a maximális napok száma, amíg a jelszó érvényes. (Utána meg kell változtatni.)
●
warn: hány nappal előtte kell értesíteni a user-t, mielőtt a jelszava elavulna
●
inactive: hány nap inaktivitás engedélyezett a usertől.
●
expire: abszolút dátum, amely után a user belépése már nem engedélyezett
●
flag: jelenleg nincs használatban
●
Az alábbi linken megtalálható, hogy néz ki egy szabványos /etc/shadow fájl: http://wks.uts.ohio-state.edu/sysadm_course/html/sysadm-240.html
●
jelszóellenőrzés során nincs dekódolás, hanem a begépeltet kódolja le, majd összehasonlítja a tároltat és a kapottat; a visszafejtés ugyanis közel lehetetlen
/etc/fstab: alapértelmezés szerint mely eszközök legyenek felcsatlakoztatva a rendszerre /dev/hda2/home
ext3
mount mode
dump
mount mode: 11
pass
●
no auto: ne csatlakoztasson automatikusan
●
user: a felhasználó is csatlakoztathatja
dump: 0 esetén kikapcsolva, 1 esetén bekapcsolva pass: induláskor hányadik körben ellenőrizze (0 esetén nem ellenőrzi) /etc/profile: bejelentkezéskor mindig lefut
12
Partíciós tábla: ●
0. sáv, 0. szektor
●
Mindig 4 bejegyzés: hda1, …, hda4; ha kevés, bővíthető (mutató újabb négy bejegyzésre), de a bővített fejből csak kettő bejegyzés használható fel láncszerűen
API: ●
●
kernel-rendszerhívásokon keresztül érhető el, szoftvermegszakítások: ●
DOS: 0x21
●
Linux: 0x80
nem közvetlenül szokás hívni, hanem legtöbbször libc segítségével
13
Bash, Awk, fordítás
Fordító
Interpreter
gyorsabb
hordozható
tömörebb lehet
nyílt
hardver közeli dolgok kezelése
gyorsabban fejleszthető
több ellenőrzés zárttá tehető (eladható csak a bináris kód)
Bash bash: (első sor) # ! /bin/bash (mivel kell értelmezni) # a comment jele, ami nem '#'-kal kezdődik, az parancs if feltétel
;
then 1. ág else 2. ág fi
14
A feltételhez programot vár, aminek a visszatérési értéke számít. Ez a program lehet a test (másnéven [), melyhez megadható -f kapcsolóval fájlnév. Utóbbi esetben akkor ad igaz értéket, ha létezik a fájl. Pl. 3==5 string-ként hasonlít össze (03 ≠ 3), míg 3 -eq 5 számként (03 = 3). Egy konkrét if-es példa, igen egyszerű, a paraméterek számát vizsgálja. : if test $# -lt 1 -o $# -gt 2 then echo 1 exit 1 fi ●
$0 egy változó, a program neve
●
$1,...,$9 a paramétereket jelöli (1,...,9)
●
$# a paraméterek száma
●
$* az összes paraméterre egyszerre hivatkozik
for i in lista do … echo $i done pl. `ls` parancs, minden fájlra vonatkozik a könyvtárban, kiíratás.
case … in lista 1) echo...
;;
…
;;
…
;;
2) … x) esac x) a default ág.
15
ls | wc -l
a pipe-jel két parancsot választ el – ami az első kimenete, az a második bemenete
ls > file
fájlba történő írás
ls 2 > errfile.txt >> list.txt
> fájlba-írás, >> hozzáfűzés
tr
karakterek kicserélése
ls | tr 0-9 a-j
betűkre cseréli a számokat
sed
ed stream-elt változata
cat /etc/passwd | sed -e '1,4 d'
az első négy sort kitörli
| sed -e 's/mit/mire/'
csere
# !/bin/bash for i in 'ls *.$1' do j = `echo $j | tr A-Z a-z` k = `echo $i | sed -e s/$1\$/$2/` mv $i $k done
●
feladat: minden mp3 kiterjesztésű fájl átnevezni ogg-ra, miközben a fájlnévben minden nagybetűt kicsire cserélünk
●
k = ... változódefiníció
●
\$: az első helyettesítésben $ marad, így a sed megkapja, ami neki a sor végét jelenti
16
AWK /reguláris kifejezés/
{…}
●
/reg.kif/ a feltétel, reg.kif. a minta (pattern), {..} a végrehajtandó akció
●
ha a feltétel elmarad, minden sorra vonatkozik
●
ha az akció marad el, akkor a mintának megfelelő sort kinyomtatja.
●
ha a reg. kif. illeszkedik a sorra, a {…}-ben lévő utasítás hajtódik végre
●
pl. /alma/ {print $0}
●
$0 az egész sort kiírja
●
az oszlopokat white space vagy tab választja el alapértelmezésben; ez módosítható az FS (field seperator) változón keresztül vagy a -F parancssori opcióval
●
NF: number of fields, adott sorban hány oszlop van
●
több parancsot ';'-vel kell elválasztani, vagy új sorba kezdeni
●
BEGIN
{az egész előtt végrehajtandó utasítás}
END
{az egész után végrehajtandó utasítás}
●
print $1 ≡ print($1)
●
print($1 ”+” $3 ”=” $1 + $2)
●
futtatás: awk 'program' file vagy: cat file | awk -f x.awk ahol x.awk a kódot tartalmazó fájl, ”file” pedig azt adja meg, mivel dolgozzon
17
AWK példa input: Józsi: 12:25 Árpi: 29:2
Józsi
37 >
megfelelő / nem
…
program: BEGIN {print(”...
”; FS = ”:”} END {print(” ...”)} {print(”
” $1 ”
”) p = $2 + $3 print(p ”
”) if (p>35) {print(”megfelelt”) else {…} print(”
”) }
18
Fordítás ●
gcc elso.c
a.out // ebbe a fájlba történik meg a fordítás alapértelmezésben
vagy: gcc -o elso elso.c [további forrásfájlok] // -o-val megadható a kimenet neve ●
-l: a library-t is beszerkeszti (tipikus lelőhely: /lib, /usr/lib, /usr/include/*.h) pl. -lm: math könyvtár
●
-S: assembly-t generál
●
-c: csak fordít (nincs futtatható .c)
●
-I: include könyvtárak keresési útvonalához ad hozzá (.h)
●
-L: hozzászerkesztendő library-k keresési útvonalához ad hozzá
Makefile: elso: elso.c
elso.h
# (cél): miktől függ (tab jel)
gcc -o elso elso.c
Lépések: mi a cél (target)? Miből indulunk ki (függőségek)? Amiktől függ, azok készen vannak, illetve módosultak? Ha a dependensek még nincsenek kész, megpróbálja a többiből meghatározni azokat. Meglévők esetén felülírás csak abban az esetben történik, ha valamelyik komponens módosítva lett. pl. clean: (tab)
rm -f elso // nem ellenőrzi, hogy clean létrejött-e -> takarításra jó
Egy egyszerű makefile példa a gyakorlati jegyzetből, a GNU rendszerben fejlesztés című kiadványból (Pásztor, Dévényi 2003) http://linux.gyakg.u-szeged.hu/~pasztor/gnue/gnue.pdf CFLAGS = -O2 -Wall INSTALLPREFIX = /usr/local INSTALLDIR = $(INSTALLPREFIX)/bin INSTALL = install -s -p -m 0555 BINARIES = prog1 prog2 SOURCES = prog1.c prog2.c prog.c HEADERS = prog.h #Ez egy egyszerű Makefile 3 db forráskód, # 1 közös headerfájl, # és 2 célprogrammal. all: $(BINARIES) prog1: prog1.o prog.o prog2: prog2.o prog.o prog1.o: prog1.c prog.h prog2.o: prog2.c prog.h
1. printf 1.1. #ifdef DEBUG printf('...') #endif 1.2. #if DEBUG_LEVEL > 5 printf('...') #endif 1.3. #if DL > 2 #define D1(X) printf(X) #else #define D1(X) #endif 1.4. kernel-ben printk printk(KERN_WARNING ”...”), ahol ”...” egy string, amelynek az elején szerepel a kiírás fontossága, pl. ”<1>...”.
21
Szintek: a) KERN_EMERG (itt már igen komoly gondok vannak (fatális)) b) KERN_ALERT c) KERN_CRIT d) KERN_ERR e) KERN_WARNING f) KERN_NOTICE g) KERN_INFO h) KERN_DEBUG (legenyhébb szint)
2. debugger ●
Unix alatt minden GDB-re épül, akár grafikus, akár nem (pl. ddd)
●
gcc -g …
●
gdb hello >help >run >set args // megadására is. >set enviroment >break 15 >break f >delete >clear 2 >clear f >print i >list >step >next >condition x [i>5] >where >backtrace >info stack
●
// debug-paraméter // help, a gdb parancsokról segítség kérése // futtatás indítása a gdb alatt. A futtatás az első bp.-ig történik. // indításkor lehetőségünk van a program argumentumainak // a környezetet is beállíthatjuk. // breakpoint elhelyezése a 15. sorra // breakpoint elhelyezése az f függvényre // breakpointok törlése // a 2. soro lévő breakpoint törlése // a breakpont törlése az f függyvényről. // egyébként minden break parancs kap egy hivatkozási számot // forráskód // ha már volt run parancs, akkor a következő bp-ig ugrik egy sort // be tud ugrani függvénybe is // feltételhez kötött bp. elhelyezése. x: bp száma, i>5 a feltétel, // a feltétel elhagyható, akkor egy szimla feltétel nélküli bp.-t állít be. // hol jársz + a stack tartalma (pl. hívási lista) // kiírja a stack tartalmát. // hatása ugyanaz, mint az előző kettőnek.
A debiggerről egy részletes dokumentációt találhatsz a következő weboldalon: http://sources.redhat.com/gdb/current/onlinedocs/gdb.html
22
Core fájl format ●
A core fileok a processz állapotképét tartalmazák egy esetleges hiba bekövetkezésekor. Az operációs rendszer készíti ezeket a core fájlokat
●
Az aktuális könyvtárban jön létre különböző hibák esetén. Ilyen hibák pl.: memóriacímzési hibák, illegális utasítások, bus hibák, és user álltal generált quit szignál esetén, ami a leggyakoribb ok. A core fájlok a terminált processz memóriaképét tartalmazzák.
●
A gcc parancs -g paramétere: Core fiájlt készít hiba esteén., és hozzáadja a szimbólum táblából származó információkat is. A core fájlok vizsgálata segítségünkre lehet abban, hogy kiderítsük, miért is fagyott le a program.
●
Nézzük az alábbi egyszerű példát:
int a (int *p); int main (void) { int *p = 0; /* null pointer */ return a (p); } int a (int *p) { int y = *p; return y; }
Ebben a programban egy null pointerre való hivatkozás van, erre nem hivatkozhatunk, az op. rendszer nem engedi. Hogy megtaláljuk később az összeomlás okát, fordítsuk a programot -g paraméterezéssel, a következő képpen: $ gcc -Wall -g null.c A -Wall opcióval kikapcsoljuk a warningot, ezután futtatjuk a programot. $ ./a.out Segmentation fault (core dumped) Amikor a core dumped üzenet megjelenik, az operációs rendszer létrehoz egy core fájlt az aktuális könyvtárban, ami annak a memóriaképnek a másolatát tartalmazza, amit a program használt. Operációs rendszerenként eltérhet, hogy mekkora core-fájlokat hoz létre, de a mérete 23
Linuxban állítható az unlimit -c [méret byteban] paranccsal. Az unlimit -c 0 -val kikapcsolhatjuk a core dump-ot, míg az unlimit -c unlimited paranccsal tetszőleges méretig megengedjük a core fájl-ok létrehozását. Általában nincs rá nagyon szükség, ezért ki szoktuk kapcsolni, hogy ne „szemetelje tele” a winchester-t. ●
Core fájl készítése a debuggerrel: $ gdb EXECUTABLE-FILE CORE-FILE Jelen esetben példánkban: $ gdb a.out core A debugger azonnal kinyomtatja a diagnosztikát, és megmutatja a sort, ahol a program összeomlott. $ gdb a.out core Core was generated by './a.out'. Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 #0 0x080483ed in a (p=0x0) at null.c:13 13
int y = *p;
(gdb)
Ahol a (gdb) a debugger promtját jelöli. A debugger print paranccsával kinyomtathatjuk a p pointer értékét: (gdb) backtrace #0 0x080483ed in a (p=0x0) at null.c:13 #1 0x080483d9 in main () at null.c:7
24
Linux kernel
Application 1
Application 2
user space
Application n
0x80 interrupt
System call
kernel space
Kernel subsystem
Device driver
Hardware
2.6.11 verziószám felépítése: major.minor.release A minor számok esetén a páratlan szám instabil verziót jelent. Ha a fejlesztés során elérnek egy stabil szintet, egy új, páros számú verziót adnak ki. Néha egyes dolgokat a korábbi visszamenőleg is beépítenek. Mindent, amit csak lehetséges C-ben programoznak. A más nyelven írt elemek (pl. assembly) külön könyvtárba kerülnek (arch alá): arch /
i386 sparc arm …
header file-ok:
include /
linux arch
25
/ fs / ext2/
file system
/driver
eszközmeghajtók
/mm
memory management
/lib
●
make
menuconfig (konzolos / grafikus és konzolos, saját konfiguráció) oldconfig xconfig (grafikus)
A főkönyvtárban található .config szöveges állomány a kernel konfigurációját tárolja. Pl. CONFIG_FS_EXT2 =
y(es) n(o) m(odul)
m(odul) esetén alapállapotban nincs a memóriában, de betölthető.
kernel-fordítás: ●
make bzImage kernel
-> arch/i386/boot/bzImage fájlba történik
make modules make modules_install
-> /lib/modules/2.6.11/...
init: ●
1-es porton keresztül, mindent felilleszt, kezdetkor indul
●
fork() parancs hatására egy egész számmal tér vissza, a másik 0-val -> kettéosztja (duplázza) a programot. Minden új folyamat az első, központi process-ből származik.
●
copy on write mechanizmus (flag): osztódáskor csak megjelölődik a terület; ha írási művelet is történt, akkor duplázódik
26
process állapota:
running
fut / futásra kész
stopped
nem kap CPU-időt / külső hatásra állt meg
sleep (interruptible)
kaphat megszakításszignált
sleep (uninterruptible)
nem kaphat
zombie
futása befejeződött, de a process valamely gyermeke még fut, ezért a fahierarchia miatt nem felszabadítható
Interrupt A megszakítás a kommunikáció egy lehetősége, de csak a kernel-en belül kezelhető le. A handler függvényt meg kell valósítani. int request_irq (unsigned int irq, irq_return_t(*handler)(int, void*,struct pt_reqs.*),unisgned long irqflags, const char* decname, void dev_id) free_irq(unsigned int, void dev_id) Általában két részből áll a megszakítás-kezelő: a Top_half részben kevés az idő, kevés dolgot lehet csinálni, ráadásul jöhetnek újabb megszakítások, ezért csak fontos dolgok gyors mentésére használatos. A Bottom_half részben több idő áll rendelkezésre (nem zavarsz mást), és több lehetőség is van. A kernel futtat egy eljárást az interrupt kezelésére, így nem veszítünk megszakítás-kérelmet.
27
Az erőforrás-kezelő rémálma: deadlock ●
Deadlock: két process egymásra vár. Feloldása történhet az egyik folyamat kilövésével (kill), illetve az erőforrás-lefoglalás sorrendbeállításával: minden erőforrás rendelkezik egy egyedi numerikus sorszámmal. Az erőforrás-foglalást és -felszabadítást a process ezen sorszámok szerinti sorrendben végzi. Ha szüksége van pl. 2,5,8,10-re, de csak 2,5,10 áll rendelkezésre, akkor 2,5-öt foglalja csak le. Ha már megvan mind a négy, de használat után a 8-as szükségtelenné vált, akkor először ”elengedi” a 10-est is, utána a 8-ast, majd újra lefoglalja a 10-est.
●
Spin_lock: jó, ha az erőforrás csak rövid ideig kell, különben túl sok időt emészt fel az állandó rákérdezés („kész vagy már?”).
●
Szemafor: értesít, ha az általa használt erőforrás felszabadul -> nem köti le a CPU-t, a process alszik („szólj, ha kész vagy!”)
Memóriafoglalás a kernel-ben ●
a = kmalloc(size,mode): kis területfoglalás a kernel területén (<= 128 kbyte, általában 2 valamely hatványa, felfelé kerekítve), így nem swap-pelhető
●
kfree(): felszabadítja a kmalloc()-kal foglalt területet
●
vmalloc(size): ha nem áll rendelkezésre akkora összefüggő memóriaterület, mint amit igényeltek, akkor különálló, 4 kilobyte-os egységekben is le tudja foglalni, majd ezekel össze-map-peli
●
vfree(): felszabadítja a vmalloc()-kal foglalt területet
●
címezhető az általános memória, esetleg az alsó 16 MB (DMA csak ezt éri el) vagy a felső 16 MB (ne foglalja a DMA elől a helyet)
●
printk(
, ”...”): int esetén buffer-be kerül, nem egyből kiírásra -> betelhet ->
információvesztés. Ennek elkerülésére a buffer mérete állítható a .config-fájlban: CONFIG_BUF_LEN.
28
Linux kernel - modulok
felhasználói parancsok: ●
insmod modulnév --> kernel modul ”behúzása”, installálása a rendszerbe (pl. insmod jffs2)
●
a kernel modulok mind .ko kiterjesztésű file-ok (/lib/modules/... alatt)
●
lsmod --> kilistázza a bent lévő modulokat
●
rmmod modulnév --> a modul kiszedése, ha lehet (nincs használatban)
●
modprobe --> az insmod fejlettebb változata (függő modulokat is tud installálni)
Példa: Helloworld modul #include #include MODULE_LICENCE("Dual BSD/GPL"); // licence-kezelés: ha nem GPL, akkor bizonyos funkciók nem elérhetők static int hello_init(void) { printk(KERN_ALERT, "Hello Word!"); return 0; } static void hello_exit(void) { printk(KERN_ALERT, "Good Bye!"); } module_init(hello_init); module_exit(hello_exit);
29
visszahívó függvények: hello_init beregisztrálja magát, majd kilép. Ezek után a kernel vissza tudja hívni a hello_init-et egészen addig, amíg a hello_exit nem törli a regisztrációt.
Exportálás modulokból: EXPORT_SYMBOL_GPL(...) EXPORT_SYMBOL(...) Konvenció az elnevezésre: ●
egyedinev_eljaras (pl. a modul neve)
●
mivel globális, egyedinek kell lennie, hogy ne legyen ütközés
globális változó (current): ●
aktuális processz változója (lényegében a task struktúrájára mutató pointer
●
pl. current->pid (azonosító), current->com (Command Line, amiből hívták)
30
VFS (Virtual File System) Részei: ●
super blokk (ha nincs, nekünk kell előállítani)
●
inode table (mutatók az adatra)
inode bejegyzések (név és tartalom nélkül): ●
idő (létrehozási, módosítás ideje, ...)
●
jogosultságok
●
file hossza
●
file típusa
●
pointer a tartalomra
divert bejegyzések: ●
név
●
inode-szám
Nevek: ●
szuperblokkban --> a gyökérkönyvtár inode száma
●
inode könyvtár: ugyanúgy kezeljük, mintha file lenne, csak az adatterületen speciális bejegyzés lényegében egy lista fájlnevekről és inode-okról -> lehet könyvtár is sok ugrásra van szükség, ezért az ugrások nagy része cache-elve van [nevek feloldásának menete + példa]
31
Symlink és Hardlink Symlink: ●
az adatterületen csak egy string van, mely a hivatkozandó file nevét tartalmazza
●
beépített figyelés , hogy ne lehessen rekurzív hivatkozásokat létrehozni (pl. 11. szintig működik, de ez beállítás kérdése)
●
néha követésüket biztonsági okok miatt le szokták tiltani (pl. web-server)
Hardlink: ●
több könyvtárban szerepel a file, de ugyanaz az inode-szám
●
fizikailag csak 1 file van -> bármelyik helyen módosítjuk, ugyanazt a file-t módosítjuk
●
hivatkozások száma el van tárolva; amíg ez az érték nem 0, addig a file létezik
●
törléskor csökkenti a linkek számát
●
csak azonos partíción lévő file-ok között lehetséges
A szignálok a folyamatok vezérlésére szolgáló utasítások, amelyet a rendszer küld a folyamatoknak, de természetesen a user is küldhet ilyen utasításokat.
●
A legfontosabbak a következők: INT, TERM, SLEEP.
●
Processz kiölése a CTRL+C parancs --> INT szignált küld, illetve a kill parancs --> TERM szignál küldése.
●
Processz leállítása adott ideig sleep parancs.
●
Természetesen küldhetünk más típusú szignálokat is (nem TERM), mint például folyamat felfüggesztése, de ekkor a kill parancsot paraméterezni kell. Pl.: kill STOP pid --> hatása ua., mint a CTRL+Z.
●
A HUP és TERM , INT szignálokat a folyamat felülbírálhatja, és saját szignál kezelő rutin állíthatunk be. Ezeket a szignálokat a folyamat kapja meg, és ő kezelei le, ellentétben a KILL szignállal, amit a kernel kap meg, és a kernel öli meg a folyamatot. Így a lefagyott, végtelen ciklusba esett programok is megölhetők.
●
Szignált csak saját processznek köüldhetünk, kivéve a root-ot, ő minden processznek küldhet.
●
Érdemes még megemlíteni az ALARM szignált, rendszer adott idő múlva küld még egyet. Főleg időzítési célokra használják, a SLEEP is ezen alapszik.
●
Folyamatot megölni : HUP és KILL szignálok (nohup parancs immunissá teszi a HUP-ra)
●
void sighandler() egy prototípus, amit a jel kezelőjének kell megvalósítani
●
signum: melyik szignált kívánom kezelni
●
handler: melyik kezelő dolgozza fel (pointer egy metódusra); van két konstans értéke: SIG_IGN (figyelmen kívül hagyja), illetve SIG_DFL (alapértelmezett kezelő)
33
Signal: szokványos menet megváltoztatása, a process tulajdonosa küldheti 1
: SIGHUP
„légyszi, olvasd újra a config-ot!” / nincs hova írni
9
: SIGKILL
az OS ”kitessékeli” a process-t
7
: SIGBUS
hibás memóriakezelés
15
: SIGTERM
„légyszi, lépj ki!”
18
: SIGCNT
continue
19
: SIGSTOP
kérés az OS felé
●
definiálható szignálkezelő
●
a default általában leállítja a folyamatot
●
kill X: adott számmal rendelkező folyamat kilövése
●
killall: minden adott kiterjesztésű folyamat kilövése
34
BSD
●
BSD-s TCP / IP stack-et használ minden rendszer a Linux és a Solaris kivételével
●
FreeBSD: Elsősorban PC-re optimalizált (Intel, AMD, Alpha/AXP, IA-64, PC-98, UltraSPARC® rendszerekre) op. rendszer
●
NetBDS: minden létező platformra van, elsősorban a hordozhatóságra helyezték a hangsúlyt, több mint 50 különböző platform támogatja. Kód minősége nagyon fontos.
●
OpenBSD: 4.4-es BSD-n alapszik, Unix-szerű op. rendszer
●
a BSD nemcsak kernel, hanem teljes operációs rendszer
●
jól visszakereshetőek a korábbi állapotok (CVS)
●
hibajelentő rendszere működik az Interneten keresztül
●
egyes fejlesztők hozzáférnek közvetlenül a kódhoz (az alrendszerekhez már többszáz főnek van jogosultsága)
Testületek jöttek létre, pl. a core team egy commiter-ek választott testület, 8 főből áll, ők hozzák meg a nagy döntéseket, illetve a támogatások megszerzése a feladatuk. FreeBSD esetén további testületek: Technical Review Board, DCC-management Team, Port Management. A fejlesztés ágakra (branch) van bontva. Minden javaslat először a Current-be kerül, emelett végig elérhető korábbi, stabil ág (stable). A főág (Head) mérföldkövekből áll (major release). Módosításkor elég a forráskód-változásokat letölteni, majd újrafordítani az egészet, és már lehet is tovább dolgozni. A vállalati felhasználás miatt minden release új ágat nyit, és csak hibajavítást végeznek rajta. A cégek csak a biztonságok ágakkal foglalkoznak. ports: felhasználói programok gyűjteménye, számuk jelenleg meghaladja a 12e-t. Nem bináris csomagok, hanem csontvázak, aminek segítségével el lehet végezni a fordítást (manapság már akad néhány bináris formátumban). Telepítést, takarítást, frissítést elvégzik maguktól.
35
Free licence-k Egy forráskód lehet fizetős / ingyenes, nyílt / zárt, és ezek kombinációja. Egy forráskód zárt, a nincs a termékhez mellékelve, nem szabadon hozzáférhető; ellenkező esetben nyílt forráskódról beszélünk. nyílt forráskód: ●
BSD-típusú
●
GNU (Free Software Foundation)
●
egyéb: nagyon sokféle más konstrukció van, hasonlóak a BSD-hez, de a fejlesztők rendelkeznek különleges jogokkal (pl. GPL – General Public Licence, ami vonatkozik a dokumentációra is, illetve az LGPL, ami a könyvtárakra is; utóbbit ma már nem használják)
BSD-típusú: ●
”copy center”: szabad másolás, kevés korlátozás
●
terjeszteni is lehet felhasznált elemmel készített produktumot, de nem lehet azt állítani, hogy a felhasznált elem is saját készítésű
●
természetesen (?) Magyarországon ez így nem érvényes, a felelősség a fejlesztőé (kártékony kód esetén a freeware-mivolta sem véd meg); a felelősségkizáró-nyilatkozat érvénytelen :(
GNU: ●
felhasznált elem módosítása terjeszthető a módosítás mértékétől függetlenül
●
futtatásra, használatra nincs korlátozás
●
forráskóddal szabadon terjeszthető
●
zárt esetben problémás a dolog, szükség van a licence beszerzésére az eredeti szerzőtől
●
Magyarországon szerző csak magánszemély lehet
36
Hálózatok
BNC: ●
más néven vékony ethernet
●
coax-kábel
●
árnyékolt
●
10 Megabit / sec
●
broadcast: mindenki hall mindenkit -> sniffer kihasználhatja (nemcsak a neki címzett csomagokat veszi)
●
tipikusan sín topológia
●
működési elve: ”ráfülel” a kábelre, ad-e valaki; ha igen, véletlenszerű ideig vár; ha szabad a ”vonal”, akkor használatba veszi -> sok ütközés, 30% kihasználtság
UTP (unshielded twisted pair): ●
csavart érpár -> zajcsökkentés (árnyékolás nincs)
●
”kiegyensúlyozott zaj”
●
10 / 100 / 1000 Megabit / sec
●
2 gép között
●
kapcsolatteremtő eszköz: switch / hub
Ethernet-csomag felépítése: fix, beépített, egyedi hardvercím 48 biten, hexa-formátumban (pl. 05:15:64:12:AB:12); ha csak F-ekből áll (FF:FF:...:FF), akkor az alhálózaton lévő összes gép címzett (broadcast)
Célgép Ethernet címe (első 32 bit) Ethernet cél (utolsó 16 bit)
Ethernet forrás (első 16 bit)
Forrásgép Ethernet címe (utolsó 32 bit) Típuskód
-
IP fejléc, TCP fejléc, majd a tényleges adatok … (amíg csak van) Ethernet ellenőrzőösszeg
37
Protokollok IPv4: ●
decimális formátumban szokták megadni (pl. 160.114.37.205)
●
alhálózati maszk: 255.255.255.192 vagy /26 (hány darab egyes van a bináris címben); az előző példára alkalmazva ez a maszk 63 címet tesz lehetővé: 160.114.18.0-160.114.18.63
●
meg kell adni az átjáró (gateway) címét is, ha ”kívülre” akarunk küldeni
●
time to live (csomag élete, mindig csökken) -> nincs végtelen körözés a hálózatban
●
ARP protokoll: IP-címhez leképezi a hozzá tartozó MAC-címet
●
RARP: ua., csak a másik irányba
●
DNS (Domain Name Service): névfeloldás, a névhez tartozó IP-címet adja meg
TCP ●
összeköttetés-mentes, az információ datagramm-ok sorozataként kerül továbbításra
●
megbízható: minden csomag megérkezik, és sorrendtartó
●
stream-alapú
●
a kapcsolat felépítése különböző hálózatok között komplex feladat
●
egy kapcsolatot forrás- és cél-port, valamint a forrás- és a cél-IP azonosít
Forrás-port
Cél-port Sorszám Ráültetett nyugta
TCP fejrész hossza
Fenntartott
UAP RS F RCS S Y I GKHT NN
Ellenőrzőösszeg
Ablak Sürgősségi mutató
tényleges adatok … következő 500 oktet …...
38
IP ●
a TCP által feldolgozott datagramm-ok számára keres megfelelő útvonalat
●
a datagramm-okhoz hozzáteszi a saját fejlécét, hogy megkönnyítse az átjutást közbülső rendszereken, átjárókon
Verzió
IHL
Szolgálattípus
Azonosítás Élettartam
Teljes hosszúság XX
Protokoll
DF
MF
A fejrész ellenőrzőösszege Forráscím Célcím
TCP fejléc, majd a tényleges adatok...
UDP ●
összeköttetés nélküli adatátvitel
●
nem javítja a hibákat
●
valós idejű (pl. Internetes rádió)
●
adatgramma
Példa alapértelmezett port-okra: ●
20 / 21: FTP
●
22: SSH
●
23: TELNET
●
25: SMTP
●
80: HTTP
●
110: POP3
Datagramm-eltolás
39
Socket-ek programozása
●
int socket (int domain, int prtokoll); domain lehet pl. AF_INET, protokoll: sock_STREAM, sock_DGRAM, sock_RAW
●
további eljárások: connect get host by name (lekéri az ip címet DNS segítségével) read write bind: szerver esetén, általában 1024 feletti port-okat használják accept listen (addig vár, amíg nem jön létre kapcsolat, majd fogadja az adatfolyamot)
●
bájtsorrend fixált -> ezért rendszertől függően konvertálni kell (little vs. big endian): a htons() eljárás elvégzi a konverziót
ICMP (Internet Control Message Protocol): ●
Internet (kapcsolat) vezérlésére szolgál, pl. ping a tesztelésre
●
10.x.x.x és 192.168.x.x csak alhálózaton belül használatosak, azon kívül a gateway blokkolja a címtartományba eső címeket
IP-Masquering: küldéskor a forrás IP-címét megjegyzi, majd a csomag fejlécében módosítja a sajátjára. A válasz megérkezésekor visszaállítja a fejlécben a címet az eredeti célra. TCP-alapú, lásd NAT. SMTP (Simple Mail Transfer Protocol): ●
25-ös port-on ”figyel”
●
kódolatlan csatorna (bárki álltal elolvasható)
●
rossz beállítás esetén nem figyeli, honnan jön a levél -> spam
40
Titkosítás Nyilvános kulcsú titkosítási algoritmus (PGP): mindenkinek van egy publikus kulcsa, amihez bárki hozzáfér, illetve egy titkos, privát kulcsa. Ha X küldeni akar Y-nak, akkor titkosítja Y publikus kulcsával, így azt csak Y tudja megnyitni a saját privát kulcsával. Ha X azt (is) szeretné, hogy Y biztos legyen abban, hogy X-től kapta az üzenetet, akkor küldés előtt X a saját privát kulcsával titkosítja az üzenetet, melyet majd Y dekódol X publikus kulcsával (digitális aláírás). Szimmetrikus kódolás: a kódoló és dekódoló kulcs megegyezik, ezért az „őrizni” kell, viszont egyszerűbb és gyorsabb. Egyirányú kódolás: olyan módszer, amit legtöbbször jelszavak tárolásánál szoktak használni. Nem magát a jelszót tárolják, hanem annak egy egyirányú kódolással lekódolt reprezentációját, amiből magát a jelszót visszakapni nagyon nehéz. A jelszó ellenőrzése úgy megy, hogy a begépelt jelszót is elkódolják ugyanezzel az algoritmussal, és a kapott eredményt összehasonlítják a tárolt formával.
41
Biztonsági hibák és azok elleni védekezés
A formátum string sebezhetőség: Lényegében az egész dolog programozói hiányosságra épül. A formátum függvények: A formátum függvények olyan speciális ANSI C függvények, melyeknél paraméterként meg lehet adni egy formátum stringet, amely extra paraméteret ad át a függvénynek. A kulcs a paramérekben rejlik, normális esetben ezen függvényeket kiíratáshoz, hiba üzenetek megjelenítéséhez, és string feldolgozáshoz használjuk Akkor beszélünk sebezhetőségről, ha a felhasználó direktben is képes egy formátum stringet átadni egy ANSI C formátum függvénynek. Íme egy példa a hibás és a jó használatra: Rossz használat: int func(char* message) { printf(message); } Jó használat: int func(char* message) { printf("%s", message); } A fontosabb formázó függvény családok: fprintf (fájl stream-be írás) printf (standard output) sprintf (stringbe írás) snprintf (stringbe írás hossz ellenőrzéssel) vprinf, vfprintf, vsprintf, vsnprintf (ugyanezek változatai csak va_arg struktúrából veszik az adatot)
42
A formázó függvények tulajdonságai: –
Egyszerű C adatszerkezeteket string formátumba konvertálnak.
–
Megengedik speciális formátumok használatát.
–
Feldolgozzák az eredménystringet.
A formázó függvények használatának menete: –
A formátum string vezérli a függvény viselkedését
–
Vezérli, hogy a paramétereket milyen típusban kellene kinyomtatni
–
A paramétereket a verembe menti
–
Érték szerint vagy cím szerint
–
A hívó függvénynek tudni kell hány paramétert rak a verembe, hogy elvégezhesse a stack korrekciót, amikor a formázó függvény visszatér.
Stack kezelés: pl: printf("Szia %d. felhasználó, te most %d. vagy.", i, a) esetén, először mindig maga a formátum string kerül a stack-be, majd ezután a paraméterek érték szerint. Ahol &a paraméter van, ott a változó címe kerül a verembe. A %-jel speicális charakter, nem kerül kinyomtatásra, kivéve a %% esetén, ahol 1 db % -jelet ír ki. %-jel után a kiértékelendő paraméter típusa van megadva. A formátum string sebezhetőség: – – – –
információs csatornák között fellépő probléma, általában akkor lép fel, amikor 2 ilyen csatornát akarunk összeolvasztani eggyé, illetve aktív csatornák közötti versengéskor Általában az egyik csatorna az az adatcsatorna, a másik a vezérlő csatorna szokott lenni Önmaga nem egy biztonsági hiányosság, inkább kihasználhatóvá teszi a már meglévő hibákat. Néhány általánossabb csatornákkal kapcsolatos probléma Szituáció
Adatcsatorna
Vezérlőcsatorna
Biztonsági probléma
Telefonrendszerek
Hang vagy adat
Vezérlő hangok
Elfogott sorok vezérlése
PPP protokoll
Adatátvitel
PPP utasítások
Forgalomterhelés
Verem
Verem adat
Visszatérési cím
Visszatérési cím vezérlése
Memóriabuffer foglalás
Adat foglalás a memóriában
Vezérlő információk
Memória írása
Formátum string
Kimeneti string
Formázó paraméterek Formázó vezérlés
43
függvény
A formátum string sebezhetőség kihasználási módjai: 1) Program összeomlasztása printf("%s%s%s%s%s%s%s%s%s%s%s%s"); de eljátszható a %s helyett %n-el is. Kívül hivatkozunk a procesz stack memóriáján, ami illegális címzéshez vezet, amit a UNIX-os rendszerekben a kernel kezel le, és küld a processznek egy SIGSEGV szignált. Normális esetben ekkor a program terminál, és dump core jön létre. 2) Kémkedés a.) Kiiratni a stack tartalmát printf("%08x.%08x.%08x.%08x.%08x\n"); Ez is működik, mert utasítjuk a formázó függvényt, hogy vegyen a stackből 5 paramétert, és írja ki 8 jegynyi hexa számként. Egy lehetséges kimenet a következő lehet: 40012980.080628c4.bffff7a4.00000005.08059c04 A stack tetejétől kezdve akár a teljes stack tartalma is rekonstuálható. b.) Megnézni a memóriát bármely helyen Használható az előző paraméter, egyetlen gond, hogyan kell el helyeznünk a stackbe az a címet, ahonnan kíváncsiak vagyunk a memóriára. Ez is megoldható, ugyanis a formázó függvény belsejében van egy pointer ami az aktuális formázó paraméter stackben lévő helyzetére mutat. A %08x felhasználásával ez is eltolható. Address = 0x08480110 Address (elkódolva 32 bites stringebe ): "\x10\x01\x48\x08" printf("\x10\x01\x48\x08_%08x.%08.%08.%08.%08.%08.|%s|"); Ez ki fogja írni a memória tartalmát a 0x08480110-ás címtől kezdve, amíg NUL byte-ot nem talál. 3) Korlátlan memória felülírása tetszőleges adatokkal egy írható processz segítségével Ez a módszerek már a buffertúrcsorduláson alapszanak, lényegében az sprint, és snprintf hibáit használják ki, oly módon, hogy saját kódot futtathatunk velük. Az előző módszereken alapulnak.
44
A Buffertúlcsordulásos támadásról: A user input esetén fellépő buffer túlcsordulás az egyik legnagyobb kockázati tényező a mai rendszerekben. Előszóban beszélni kell a memória felépítéséről, ami processzor architektúránkánt eltérhet. Az alábbi példa az x86-os és sparcos rendszrekre jellemző. Az egész támadási típusnak az alapelve, hogy felülírjuk a memóriának egy olyan részét, ami normális esetben nem lehetne felülírni, egy saját inputtal, és rákényszerítjük a processzt., hogy futtassa le ez t a kódot. Minden processznek a kernel kioszt egy memórialapot, amely azután relatív címeket használ, anélkül, hogy tudná pontosan (fizikailag) hol helyezkedik el a RAMban a processz. Memóriafelépítés: A processz memóriája a következő 3 részből áll: 1. Kód szegmens: Assembly utasítások, amelyeket a processzor futtat. Nem lineáris a kód, számos ugró utasítást, és függyvényhívást tartalmaz. Van egy EIP – (instruction pointer) utasítás számláló regiszerünk, ami a soron következő utasítást tartalmazza. 2. Adat szegmens: Hely a változóknak és a dinamikus buffereknek. 3. Verem szegmens: - Paraméterátadáshoz, függvények belső lokális változóira használjuk. A stack alja (kezdete), ált. a virtuális memória legvégén helyezkedik el, és lefelé növekszik. Assembler utasítások: a PUSHL hozzáad a stack tetejéhez, a POPL kivesz egyet a tetjéről, és berakja egy regiszterbe. Hogy közvetlenül elérjük a memóriát, van egy ESP (stack pointer) -nk, ami a stack tetejét mutatja (a legalacsonyabb memória címen). Függvényhívás assembly szinten: Függvény --> egy kód a kódszegmensben, meghívás, fügvény végrehajtása, visszatérés. Paraméterek átadása meghíváskor --> a verembe kerülnek. Példa függvényhívásra assembly szinten: memória cím
kód
0x8054321
pushl $0x0
0x8054322
Call $0x80543a0
0x8054327
ret
0x8054328
leave
... 45
memória cím
kód
0x80543a0
pop $eax
0x80543a1
addl $0x1337, $eax
0x80543a4
ret
Mi történik? : A főprogram meghívja a függvényt a 0 paraméterrel ( function(0) ) A főprogram berakja a stackbe a változót (0) és meghívja a függvényt, a függvény kiszedi a stackből, miután befejezte visszatér a 0x8054327-re. A függvényhívások esetén az EBP mindig letárolódik a stackbe, és visszatöltődik visszatéréskor. Ez azért fontos, mert így a függvények saját stack-et használhatnak (a központiból) és relatív címzést az eléréshez. Hogy néz ki a stack? : •
belső változók, és bufferek
•
lementésre kerül az EBP (32 bites, 4 byte-os)
•
a visszatérési cím, ami újabb 4 byte
•
végén az argumentumok kerülnek átadásra.
Példa egy támadható programra: Tekintsük az alábbi kis függvényt, tegyük belea blah.c fájl-ba: void lame (void) { char small[30]; gets (small); printf("%s\n", small); } main () { lame(); return 0; } Fordítsuk le, és nézzük meg az assembly kódját, hogy megértsük mi is történik assembly szinten. Használjuk a GNU debuggert, hogy megvizsgáljuk az assembly kódot. # cc -ggdb blah.c -o blah # gdb blah (gdb) disas main 0x80484c8
pushl %ebp
0x80484c9
movl %esp, %ebp
0x80484cb
call 0x80484a0
0x80484d0
leave
0x80484d1 (gdb) disas lame
ret
0x80484a0
pushl %ebp
0x80484a1
movl %esp, %ebp
46
0x80484a0
pushl %ebp
Kibővítjük a stack-et 0x20-al vagy 32-vel. A bufferünk csak 30 byte, de a processzor 32 bites szavakat használ. Ezzel lefoglaltuk a char small[30]-at. 0x80484a3
subl $0x20, %esp
Betöltjük a small[30]-ra mutató pointert és meghívjuk a gets függvényt. 0x80484a6
leal 0xffffffe0(%ebp), %eax
0x80484a9
pushl %eax
0x80484aa
call 0x80483ec
0x80484af
addl $0x4, %esp
Betöltjük a small[30]-at, és a "%s\n" string-et, és meghívjuk a printf függvényt. 0x80484b2
leal 0xffffffe0(%ebp), %eax
0x80484b5
pushl %eax
0x80484b6
pushl 0x804852c
0x80484bb
call 0x80483dc
0x80484c0
addl $0x8, %esp
Lekérjük a visszatérési címet a veremből, ami jelen esetben 0x80484dc, és visszatérünk erre a címre. 0x80484c3
leave
0x80484c4
ret
Futtassuk a programunkat: # ./blah xxxxxxxxxxxxxxxxxxxxxxx <- user input xxxxxxxxxxxxxxxxxxxxxxx # ./blah xxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxx Segmentation fault (core dumped) #gdb blah core (gdb) info registers eax:
0x24
ecx:
0x804852f
edx:
0x1
ebx:
0x11a3c8
esp:
0xbffffdb8
36 134513967 1 1156040 -1073742408
ebp: 0x787878 7895160 Az EBP értékéből látszik, hogy több adatot írtunk a stackbe, mint amennyit az input puffer képes kezelni. A 0x78 az 'x' reprezentációja. 32 byte-os a maximális puffer, de mi több adatot 47
írtunk bele, mint amennyit a user lefoglalt, így felülírtuk az EBP-t és a visszatérési címet 'xxxx' -el, és a procesz megpróbálta folytatni a futást a 0x787878-on, ami segmentation faulthoz vezetett. A visszatérési cím megváltoztatása: A következő példában megváltoztatjuk a visszatérési címet úgy, hogy a program az inménti függvényre térjen vissza ismételten. Egyetlen teendőnk az, hogy a 0x80484d0 címet átírjuk 0x80484cb-re. Memóriában : 32 byte buffer + 4 byte EPB + 4 byte RET Példa program: (Egy 4 byte-os visszatérési címet teszünk egy 1 byte-os karakterpufferbe.) main() { int i=0; char buf[44]; for (i=0; i<40; i+=4) *(long *) &buf[i] = 0x80484cb; puts(buf); } # ret ËËËËËËËËËËË, # (ret;cat)|./bash test (user input) ËËËËËËËËËËË,test test (user input) test A programunban a függvény 2x futott le. A visszatérési érték megváltoztatásával megváltoztattuk a program végrehajtási szálát. Mire használható?: –
A veremmutató megváltoztatásával írhatunk olyan shell code-ot, mely a rendszer execve rendszerhívását kihasználva egy bash-t ad nekünk
–
Vagy olyan programot, amely bufferét megtámadván a visszatérési címének módosításával saját kódunkat tölthetjük be, ami alkalmas lehet egy minimális shell futtatására.
48
Védekezési módszerek: –
Használjunk mindig ellenőrzést a user inputra, megfelel-e nemcsak a formai, hanem a méretbeli követelményekre is.
–
Sprintf helyett strn*, sn*, így meg tudjuk adni a méretet.
–
Buffereket mindig dinamikusan foglaljunk
A ciklusokra fokozottan ügyeljünk, nehogy túlcsordulás lépjen fel.
Egyéb támadások: ●
jogosultságok -> user-ből root-jogok (set uid)
●
hálózati szolgáltatás -> rendszergazda eszközei (hibák kihasználása) pl. /tmp/... ideiglenes fájlok létrehozása; elkapjuk létrehozás előtt, a saját fájlunkba ír -> /etc/shadow -> root-jelszót kaphatunk
49
Védekezési módszerek ●
minden input ellenőrzése, minden használt dolog (pl. program, eljárás, …) ellenőrzése, akár használat közben is
●
környezeti változók (pl. PATH, LD_LIBRARY_PATH)
●
buffer overflow-t nem figyelő függvények (strcmp, gets, …) helyett azok biztonságos változatának (pl. strncmp) használata, ahol van + 1 paraméter, hogy maximum mennyi karakterrel dolgozzon (mekkora a puffer mérete)
●
mindig a lehető legkisebb jogosultsági szintet használjuk: szükség esetén a program több részre osztása, más-más jogosultságokkal (több szál)
●
atomic műveletek szétbonthatatansága: ne legyenek megszakíthatóak azok a részek, amelyeknél nem vagyunk felkészülve arra
●
minimális segédinformáció kiadása (zárt protokoll-formátum esetén pl a hálózati forgalomban lévő információk mellé ne írjunk kommenteket...)
●
érzékeny adatok kódolása: nyilvános kulcsú algoritmusok szimmetrikus kulcsú algoritmusok (ua. a kulcs a kódolás / dekódoláshoz) egyirányú kódolás -> az információt csak kódolt formában tároljuk
●
integrity check -> a program ellenőrzi saját magát és az adatokat, amit használ
50
X11
●
a kliens TCP-kapcsolatot nyit a server-rel a default, 6000-es port-on keresztül
●
a program megjelenésének formáját a DISPLAY nevű változó dönti el Display = gepnev: 0. 0, ahol a gépnév az ID, az első szám pedig azon objektumok száma, melyekhez külön egér / billentyűzet tartozik
#include <x11 / xlib.h> // ez egy sematikus program! #include <xutil.h> main () { create() { XopenDisplay(NULL) // az argumentum mindig az alapértelmezett NULL … XcreateSimpleWindow(...); } while (Y) { … delay(); int Xpendig(display); // Van-e várakozó esemény / üzenet? Ha igen, akkor: XnextEven(display, Xevent X); eventloop(); } close(); } A következő linken egy X11 tutorial található, ha bővebben akarsz olvasni az X11-ről. http://xander.ncl.ac.uk/game/
51
Xlib alacsony szin, felette GTK, QT, stb. gtk_init(&argc, argv); windows = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_singal_connect(G_OBJECT(window), ”delete_event”, G_CALLBACK(f), NULL); ahol ”delete event” a kezelendő dolog, ”f” pedig a kezelő gtk_widget_show(windows); // megjelenítés gtk_main(); // a loop-ot ezáltal magától oldja meg wxWindows: platformfüggetlen, elrejti az OS-függő dolgokat
TCL / TK ●
a TCL egy szkriptnyelv, a TK egy library, amin keresztül grafikus dolgokat lehet megjeleníteni
●
minden változó string pl. set x 15 set y ”egy ketto” set z szoveg expr $y kifejezes // y értéket kap (szám, kifejezés)
●
set num […] // kiszámolódik, majd behelyettesítődik string-ként
●
puts $num // kiíratás
●
if {$num > 50} then { puts ”xx” }
●
button .b -text ”Hello World!” -command exit ahol ””-k között a kiírandó szöveg, -command után pedig a végrehajtandó akció
●
pack .b // hívás a WindowManager-nek, hogy tegye ki a gombot
●
proc doit {x}{...} // doit{x} megadható gombakciónak
52
●
[10; 2HVT100
: VT10 esetén (10;2)-re ír
●
TERM változó értéke megmondja, hogy VT100-ban van-e (szinte minden terminálon van)
●
termcap: adott terminálon milyen formában lehet pl. képernyőt törölni
egy képet úgy rajzol ki, hogy csak karaktereket jelenít meg
●
matching-el (render-el): adott ponthalmazra melyik karakter illeszkedik legjobban
●
lehet színes / monokróm
ANSI/VT100 Terminal Control Sok computer termiál és terminál emulator támogatja a szín és kurzorvezérlést a rendszer „escape” szekvenciáján keresztül. A legtöbb terminálspecifikáció az ANSI szín szabványán alapul, beleértve a VT100-at is. A VT100 controll vezérlői: <ESC> : ANSI escape karakter, a kódja : 0x1B A zárójeles tagok egy módosítható decimális értéket jelentenek >> pl.: {ROW} : sorok száma.
53
Eszköz státusz A következő kódokkal a terminál beállításairól kaphatunk információkat, de ez az implementációtól függően eltérhet. Név:
Parancskód:
Query Device Code
<ESC>[c
Kér egy „Report Device Code” választ az eszköztől. Report Device Code
<ESC>[{code}0c
Az eszköz generálja válaszként az előző kérésre. Query Device Status
<ESC>[5n
Kér egy „Report Device Status” választ az eszköztől. Report Device OK
<ESC>[0n
Az eszköz geerálja válaszként az előző kérésre, amennyiben az eszköz hiba nélkül, megfelelően működik. Report Device Failure
<ESC>[3n
Az eszköz geerálja válaszként az előző kérésre, amennyiben az eszköz hibás, nem áll rendelkezésre. Query Cursor Position
<ESC>[6n
Kér egy „Report Cursor Position” választ az eszköztől. Report Cursor Position
<ESC>[{ROW};{COLUMN}R
Az eszköz generálja válaszként az előző kérésre, visszaadja a kurzor pozícióját.
Terminál beállítások A h és l kódok a terminál megjelenítő módjának beállítására használatosak, természetesen ezek is függnek a megvalósítástól. Line Warp (sortörés), az egyik olyan kód a néhány beállítható kódból, amelyet közvetlenül ajánlott használni. Név:
Parancskód:
Reset Device
<ESC>c
Minden terminál beállítást alaphelyzetbe állít. Enable Line Warp
<ESC>[7h
Engedélyezi a Line Warpping-ot. A szöveget új sorba töri, ha hosszabb, mint a megjelenítő felület. Disable Line Warp
<ESC>[7l
Kikapcsolja a Line Warpping-ot.
Fontok 54
Néhány terminál támogat spe ciális fontokat, mint például félkövér, dőlt, stb. Sok változata van ezen kódoknak, terminálnoként eltérhet, a következő 2 kód elég általános. Név:
Parancskód:
Font Set G0
<ESC>(
Alap fontot állít be. Font Set G1
<ESC>)
Változó fontot állít be.
Kurzor vezérlés Név:
Parancskód:
Cursor Home
<ESC>[{ROW};{COLUMN}H
A paraméterekkel megadott bekezdés elejére állítja a kurzorpozíciót. Ha nem adunk meg sor és oszlop paramétereket, pl.: „<ESC>H”, akkor a kurzort a home pozícióba, a képernyő bal felső sarkába helyezi. Cursor Up
<ESC>[{COUNT}A
A paraméter értékének megfelelő sorral mozdítja el a kurzort felfelé. Ha nem adjuk meg, az alapértelmezett érték 1. Cursor Down
<ESC>[{COUNT}B
A paraméter értékének megfelelő sorral mozdítja el a kurzort lefelé. Ha nem adjuk meg, az alapértelmezett érték 1. Cursor Forward
<ESC>[{COUNT}C
A paraméter értékének megfelelő oszloppal mozdítja el a kurzort előre. Ha nem adjuk meg, az alapértelmezett érték 1. Force Cursor Position
<ESC>[{ROW};{COLUMN}f
Hatása megegyezik a Cursor Home utasítással. Save Cursor
<ESC>[s
Elmenti az aktuális kurzorpozíciót. Unsave Cursor
<ESC>[u
Vissztölti az utoljára elmentett kurzorpozíciót, és ennek megfelelően beállítja a kurzor helyzetét. Save Cursor & Attrs
<ESC>7
Ugyanaz, mint a Save Cursor utasítás, csak attribútumokat is elment. Restores Cursor & Attrs
<ESC>8
Ugyanaz, mint a Unsave Cursor utasítás, csak attribútumokat is visszatölt.
Scrolling
55
Név:
Parancskód:
Scroll Screen
<ESC>[r
Bekapcsolja a scrollozást az egész megjelenítőre. Sroll Screen
<ESC>[{start};{end}r
Bekapcsolja a scrollozást a start és az end paraméterekel megadott számú sorok között. Sroll Down
<ESC>D
Továbbgörgeti a képernyőt 1 sorral lefelé. Scroll Up
<ESC>M
Továbbgörgeti a képernyőt 1 sorral felfelé.
Tabulátor vezérlés Név:
Parancskód:
Set Tab
<ESC>H
Beállít egy tabulátort az aktuális kurzorpozícióra. Clear Tab
<ESC>[g
Kitöröl egy tabulátort az aktuális kurzorpozíción. Clear All Tabs
<ESC>[3g
Kitörli az összes tabulátort.
Szöveg törlése Név:
Parancskód:
Erase End of Line
<ESC>[K
Az aktuális kurzorpozícitól a sor végéig a szöveg törlése. Erase Start of Line
<ESC>[1K
Az aktuális kurzorpozícitól a sor elejéig a szöveg törlése. Erase Line
<ESC>[2K
Az aktuális sor törlése. Erase Down
<ESC>[J
Az aktuális sortól a képernyő aljáig lefelé törli a sorokat. Erase Up
<ESC>[1J
Az aktuális sortól a képernyő tetejéig felfelé törli a sorokat. Erase Screen
<ESC>[2J
Törli a teljes képernyőt az aktuális háttérszínnel, és a kurzot a „home” pozícióba állítja.
Nyomtatás Náhány terminál támogatja a helyi nyomtatást. 56
Név:
Parancskód:
Print Screen
<ESC>[i
Az aktuális képernyő nyomtatása. Print Line
<ESC>[1i
Az aktuális sor nyomtatása. Stop Print Log
<ESC>[4i
Log indítás, minden fogadott szöveg a nyomtatóra is elküldésre kerül. Start Print Log
<ESC>[5i
Log laállítá, kikapcsolja a logolást.
Kulcs definiálás Név:
Parancskód:
Set Key Definition
<ESC>[{key;”{string}”p
Hozzárendelhetünk egy stringet a billentyűzet egy billentyűjéhez. A key paraméter tartalmazza a karaktert, amit ASCII decimális értékként kell megadni.
Megjelenítő attribútumainak beállítása Név:
Parancskód:
Set Atrribute Mode
<ESC>[{attr1};...;{attrn}m
Beállíthatjuk a megjelenítő attribútumait. A következő táblázat a megadható attribútumok értékeit tartalmazza. Attribútum értékek ésjelentésük Előtér színek
Háttér színek
0
Reset all
30
Fekete
40
Fekete
1
Bright
31
Piros
41
Piros
2
Dim
32
Zöld
42
Zöld
4
Underscore
33
Sárga
43
Sárga
5
Blink
34
Kék
44
Kék
7
Reverse
35
Magenta
45
Magenta
8
Hidden
36
Cyan
46
Cyan
37
Fehér
47
Fehér
Ncurses Az Ncurses egy könnyen kezelhető programozói felület, illetve függvénykönyvtár Linux alá, amellyel egyszerűen programozhatjuk a terminálunkat. Lehetőséget biztosít például az egérmozgás, ablakkezelés, illetve a kurzormozgatás programozására. A segítségével könnyen 57
lehet konzol módban grafikus felhasználói felületet készíteni menükkel, panelokkal, formokkal. Ha használni szeretnénk az NCurses -t, hivatkoznunk kell rá a forrásban az #include sorral, illetve a linkernek meg kell mondani, hogy szerkessze be a megfelelő függvénykönyvtárakat: gcc -lncurses -o Egy egyszerű hello wor program: #include
int main () { initscr ();/* a curses mod inditasa */ printw ("Hello Vilag !!!");/* kiirjuk a megfelelo szoveget */ refresh ();/* megjelenitjuk a szoveget a "valodi" kepernyon */ endwin ();/* Kilepes a curses modbol*/ return 0; }
Initscr: Inicializálja a terminál curses módját. Általában a képernyőt törli. (fekete képernyő) Alapállapotba hozza a curses módot, memóriát foglal a rendszer-ablakok számára, melyet az stdscr használ, illetve pár szükséges adat-struktúrát. Printw: Hasonló a normál printf utasításhoz, kiír egy adatot az stdscr által használt ablak aktuális (x;y) pozíciójába. Az első kordináta a (0;0), mely a képernyő bal sarkában helyezkedik el. A kiírt adatok nem jelennek meg rögtön a képernyőn, csupán az stdscr által reprezentált adatstrukturába kerülnek, de ez még nem frissíti a látható képernyőt, erre a Refresh () függvény szolgál. Endwin: Curses mód elhagyása, szükséges a kilépés előtt. Felszabadítja a curses számára lefoglalt adatterületeket, strukturákat, a curses alrendszert, és a terminált visszahelyezi normál módba. Fontosabb utasítások : raw(), cbreak(): Letiltják a soronkénti bufferelést, raw esetén az alkalmazás közvetlenül megkapja a a CTRL+Z és CTRL+C vezérlőkaraktereket, és nem generál szignált, míg cbreak esetén értelmezi ezeket a terminálvezérlő, de a szerkesztőkaraktereket (pl. backspace) már az alkalmazásnak kell feldolgoznia. halfdelay(): Hasonló a cbreak-hez, de ha adott ideig nem jön válasz, akkor folytatódik a program futása. Pl.: jelszóbekérés. echo(), noecho(): Echo mód bekapcsolása, kikapcsolása. Az echo () mód használatával a leütött karakterek visszhangoznak a terminálon.
58
keypad(): A keypad () lehetővé teszi, hogy beolvassuk a funkcióbillentyűket (F1, F2, stb), a kurzormozgató gombokat, és egyéb speciális karaktereket. Ablakkezelés: Szinte majdnem minden vezérlő utasításnak van w-vel kezdőd párja, amivel egy saját ablakon dolgozhatunk, paraméterként meg kell adni az ablak nevét, valahogy így: wprintw (win, "Itt vagyok !"); wrefresh (win);
Formázott kiiratás: –
addch () csoport: Egy karaktert lehet vele kiiratni, attributumokkal
–
printw () csoport: Formázott kimenet, hasonlóan a printf () -hez.
–
addstr () csoport: Sztring kinyomtatása Attribútumok beálltása egy adott karakterhez: pl: addch (ch | A_BOLD | A_UNDERLINE); A lehetséges beállítások megtalálhatóak a header fájl-okban. attrset (), attron (), attroff () függvények aaz attribútumok beállításaihoz. mvprintw() mozgatja a kurzort egy adott helyre a képernyőn, és ott megjelenít egy adott szöveget mvaddstr() segítségével kitehető egy sztring a kért ablakba, ez a funkció hasonló, ha minden karakterre egyszer meghívná az addch () alprogramot.
Egy egyszerű példa a print-ek használatára: #include #include <string.h>
/* ncurses.h hozza az stdio.h -t is */
int main () { char mesg []="Ez egy szoveg"; /* az uzenet, amit meg szeretnenk jeleniteni */ int row,col; /* eltaroljuk a sorok es oszlopok szamat a kepernyon */ initscr (); /* go to heaven... indulunk a curses modba */ getmaxyx (stdscr,row,col); /* lekerdezi egy adott ablak sorainak es oszlopainak szamat */ mvprintw (row/2,(col-strlen(mesg))/2,"%s",mesg); /* megjeleniti a szoveget a kepernyo kozepen */ mvprintw (row-2,0,"A kepernyo %d sort es %d oszlopot tartalmaz\n",row,col); printw ("Meretezd at az ablakot (ha tudod), es futtasd ujra a programot !"); refresh (); getch (); endwin (); }
return 0;
Az Ncurses (5.4-es) ingyen letölthető a következő weboldalon, a doksit meg alatta találod: http://www.gnu.org/software/ncurses/ncurses.html http://www.tldp.org/HOWTO/NCURSES-Programming-HOWTO/ [A TARTOLOMJEGYZEKET normálisan megcsinálni!]