610 likes | 814 Views
Sistemski klici The Kernel API - System Calls. Sistemski klici. Ap likacije. Sistemske knjižnice (libc). Vmesnik sistemskih klicev. Vhod-izhod. Vezano na procese. Datotečni sistemi. razvrščevalnik. M oduli. Omreženje. Upravljanje s pomnilnikom. Gonilniki naprav.
E N D
Sistemski klici Aplikacije Sistemske knjižnice (libc) Vmesnik sistemskih klicev Vhod-izhod Vezano na procese Datotečni sistemi razvrščevalnik Moduli Omreženje Upravljanje s pomnilnikom Gonilniki naprav Medprocesna komunikacija Arhitekturno odvisna koda Aparaturna oprema
Seznam sistemskih klicev (1) _exit exitgetprioritymlock pipe _llseekfchdirgetresgidmlockall poll _newselectfchmodgetresuidmmapprctl _sysctlfchowngetrlimitmodify_ldtpread accept fcntlgetrusage mount prof access fdatasyncgetsidmprotectptrace acct flock getsocknamempxquery_module adjtimex fork getsockoptmremapquotactl afs_syscallfstatgettimeofdaymsgctl read alarm fstatfsgetuidmsggetreaddir bdflushfsyncgttymsgopreadlink bind ftruncate idle msgrcvreadv break get_kernel_symsinit_modulemsgsnd reboot brkgetcontext intro msyncrecv cacheflushgetdentsioctlmunlockrecvfrom capgetgetdomainnameioctl_listmunlockallrecvmsg capsetgetdtablesizeiopermmunmap rename chdirgetegidioplnanosleeprmdir chmodgeteuidipcnfsservctlsbrk chowngetgid kill nice sched_get_priority_max chrootgetgroupskillpgobsoletesched_get_priority_min clone gethostidlchownoldfstatsched_getparam close gethostname link oldlstatsched_getscheduler connect getitimer listen oldoldunamesched_rr_get_interval creatgetpagesizellseekoldstatsched_setparam create_modulegetpeername lock oldunamesched_setscheduler delete_modulegetpgidlseek open sched_yield dup getpgrplstatoutb select dup2 getpidmkdir pause semctl execvegetppimknod personality semget
Seznam sistemskih klicev (2) semopshmat sync send shmctlsyscalls sendfileshmdtsysctl sendmsgshmgetsysfs sendtoshmopsysinfo setcontext shutdown syslog setdomainnamesigaction time setegidsigaltstack times seteuidsigblock truncate setfsgidsiggetmaskumask setfsuidsigmaskumount setgid signal uname setgroupssigpauseundocumented sethostidsigpendingunimplemented sethostnamesigprocmask unlink setitimersigreturnuselib setpgidsigsetmaskustat setpgrpsigsuspendutime setprioritysigvecutimes setregid socket vfork setresgidsocketcallvhangup setresuidsocketpair vm86 setreuidssetmask wait setrlimit stat wait3 setsidstatfs wait4 setsockoptstimewaitpid settimeofdaystty write setuidswapoffwritev setup swapon sgetmasksymlink
Režim, prostor, kontekst • Režim (Mode): Stanje izvajanja, ki ga omejuje aparaturna oprema • Omejen dostop, privilegirani nukazi • Režim uporabnika (user mode) in režim jedra (kernel mode) • Intel podpira 4 “obroče” zaščite: 0 kernel, 1 unused, 2 unused, 3 user • Prostor (Space): Naslovni prostor jedra in naslovni prostor uporabnika • Zahteva podporo MMU (navidezni pomnilnik) • “userland”: Naslovni prostor nekega procesa; imamo več naslovnih prostorov uporabnikov • dejansko: jedro pogosto preslikamo v naslovni prostor uporabnika • Kontekst: Dejavnost jedra “v imenu...” • procesa: v imenu trenutno tekočega procesa • Sistema: nevezano na trenutni proces (lahko procesa sploh ne bi bilo!) • Primer “interrupt context” • Blokiranje ni dovoljeno!
Režim uporabnika, kontekst procesa Kontekst Userland Proces Sistem User Space Kernel Space User Režim Kernel
Režim jedra, kontekst procesa Kontekst Proces Sistem “trap to kernel” User Režim Kernel Sistemski klici, izjeme User Space Kernel Space
Režim jedra, kontekst sistema Konteks Proces Sistem User Režim Kernel Prekinitve, naloge sistema User Space?? Kernel Space prekinitve
Cena prečkanja “meje user/kernel” • Več kot klic procedure • Manj od preklopa konteksta • cena: • Mehanizem vektorske tabele (vectoring mechanism) • Vzpostavitev sklada jedra • Vrednotenje parametrov • Preslikava jedra v naslovni prostor uporabnika? • Osveževanje dovoljenj za preslikave • Jedro v ločenem naslovnem prostoru? • Ponovno nalaganje preslikav strani • Razveljavljanje medpomnilnika
Prekinitve in izjeme • prekinitve – asinhrona komunikacija med napravami in CPE • primer: zahtevek za storitev, obvestilo o zaključku • Sistem je lahko prekinjen v režimu uporabnika ali v režimu jedra • Prekinitve logično niso vezane na tekoče izvajanje • Izjeme – sinhrono obveščanje o aparaturnik napakah • primer: delitev z nič (AE), nelegalen naslov (MMU) • Izjeme povzroča tekoče izvajanje • Programske prekinitve (pasti, traps) • Sinhrona “simulacija” prekinitve • Omogoča nadzorovan “vstop” v jedro iz uporabnikovega prostora
Potek izvedbe sistemskega klica • Ko proces v uporabniškem režimu sproži sistemski klic, preklopi CPE v režim jedra in začne izvajati neko funkcijo jedra. • Arhitektura 80x86 omogoča izvedbo sistemskega klica Linux na dva načina. • Pri obeh načinih pride do preskoka na funkcijo, ki ji pravimo system call handler.
Rokovalnik sistemskega klica • “system call handler”, ima podobno zgradbo kot drugi rokovalniki izjem. Izvede naslednje operacije: • Shrani vsebino večine registrov na sklad jedra. • Obravnava sistemski klic s klicem ustrezne funkcije, ki ji pravimo system call service routine. • Sledi izstop iz rokovalnika: • Restavracija vsebine registrov, shranjene na skladu jedra • Preklop CPE iz režima jedra v režim uporabnika.
Potek sistemskega klica • Puščice kažejo potek izvajanja med funkcijami. • Izraza "SYSCALL" in "SYSEXIT" pomenita skupino ukazov v zbirnem jeziku, ki preklopijo CPE iz uporabniškega režima v režim jedra in obratno. • Dogovor o poimenovanju: sistemskemu klicu xyz( )običajno ustreza servisna rutina sys_xyz( )..
Blokiranje sistemskih klicev • Sistemski klici lahko blokirajo “v jedru” • “počasni” sistemski klici lahko blokirajo nedefiniran čas • Branja in pisanja cevi, terminalov, omrežnih naprav • Nekateri kliciipc, pause, open inioctls • Diskovni vhod-izhod NI počasen (sčasoma se bo zaključil) • Blokirane počasne klice lahko “prekinemo” s signalom • problem: počasne klice moramo oviti v zanko • BSD je uvedel “automatic restart” počasnih prekinjenih klicev • POSIX ne specificira pomena • Linux • Privzeto ni avtomatskega ponovnega starta • Ponovni start moramo specificirati v rokovalniku sigralov (SA_RESTART)
API in sistemski klici • APIne ustreza nujno določenemu sistemskemu klicu. • API lahko ponuja svoje storitve kar v uporabniškem režimu (primer, matematične funkcije, kjer ni potrebe po sistemskih klicih) • Api lahko izvede več sistemskih klicev. • Še več, različne funkcije API lahko uporabljajo isti sistemski klic, ki mu s svojim ovojem dodajo različno funkcionalnost.
Primer: različni API uporabljajo isti sist. klic • V Linuxu imamo v knjižnici “libc” funkcije malloc( ) , calloc( )infree( ). • Koda v tej knjižnici sledi zahtevkom za alokacijo in dealokacijo in pri tem uporablja za povečevanje ali zmanjševanje kopice isti sistemski klic brk( ).
Povratna vrednost ovojne rutine • Večina ovojnih rutin vrača celoštevilčno vrednost, katere pomen je odvisen od ustreznega sistemskega klica. • Povratna vrednost-1običajno pomeni, da jedro ni uspelo zadostiti zahtevku procesa. • Izpad v “system call handler“ lahko povzročijo • Nepravilni parametri • Pomanjkanje potrebnih virov • Aparaturni problemi itd. • Kodo napake dobimo v spremenljivki errno, ki je definirana v knjižnici libc. Errno kode
Številka sistemskega klica • Ker ima jedro vgrajene različne sistemske klice, mora uporabniški proces posredovati parameter, ki mu pravimo “system call number“, ki identificira sistemski klic. To številko moramo vpisati v register eax. • Kasneje bomo videli, da sistemskemu klicu običajno posredujemo še več parametrov.
Številke nekaterih sistemskih klicev /* * This file contains the system call numbers. */ #define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3 #define __NR_write 4 #define __NR_open 5 #define __NR_close 6 #define __NR_waitpid 7 #define __NR_creat 8 #define __NR_link 9 #define __NR_unlink 10 #define __NR_execve 11 #define __NR_chdir 12 ……… #define __NR_exit_group 252
Povratna vrednost sistemskega klica • Vsi sistemski klici vrnejo celoštevilčno vrednost. • Dogovori za te povratne vrednosti se razlikujejo od povratnih vrednosti ovojnih rutin. • V jedru • Pozitivna vrednost ali 0 pomeni uspešen zaključek sistemskega klica • Negativna vrednost pomeni kodo napake • V slednjem primeru je to negacija kode napake, ki naj bi jo aplikacija dobila v spremenljivki errno. • Vrednost spremenljivke errnone nastavlja jedro, pač pa ustrezna ovojna rutina že po povratku iz sistemskega klica.
System Call Dispatch Table • Vsaki številki sistemskega klica mora ustrezati neka servisna rutina. To asociacijo izvede jedro s pomočjo dispečerske tabele (system call dispatch table), ki je shranjena v polju sys_call_table in ima NR_syscallselementov (289 pri jedru Linux 2.6.11). • n-ti element te tabele vsebuje naslov servisne rutine za sistemski klic n.
Tabela sistemskih klicev Vsak element tabele lahko ima naslov funkcije sys_ni_syscall( ), ki je servisna rutina za neimplementirane sistemske klice. Ta vrne kodo napake -ENOSYS.
Kako sprožimo sistemski klic? • Aplikacije izvedejo sistemski klic na dva načina: • Z izvedbo ukaza (v zbirnem jeziku) int $0x80; V starejših verzijah Linux je bil to edini način preklopa iz uporabniškega režima v režim jedra. • Ukaz intje počasen, saj mora izvesti več preverjanj konsistence in varnosti. • Z izvajanjem ukaza (v zbirnem jeziku) sysenter, ki so jo uvedli pri procesorju Intel Pentium II; ta ukaz je podprt od jedra Linux 2.6 naprej • Ukaz sysenter(opis najdemo v Intelovi dokumentaciji kot "Fast System Call“) nudi hitrejši preklop iz uporabniškega režima v režim jedra.
Kako izstopimo iz sistemskega klica • Jedro lahko izstopi iz sistemskega klica in tako preklopi nazaj v uporabniški režim na dva načina: • Z izvajanjem zbirnega ukaza iret • Z izvajanjem zbirnega ukaza sysexit, ki je bil uveden hkrati z ukazom sysenter.
Kaj dela ukaz sysexit ? • The sysexit assembly language instruction is the companion of sysenter: it allows a fast switch from Kernel Mode to User Mode. When the instruction is executed, the CPU control unit performs the following steps: • Adds 16 to the value in the SYSENTER_CS_MSR register, and loads the result in the cs register. (p.s. : 16=10000b) • Copies the content of the edx register into the eip register. • Adds 24 to the value in the SYSENTER_CS_MSR register, and loads the result in the ss register. (p.s. : 24=11000b) • Copies the content of the ecx register into the esp register • As a result, the CPU switches from Kernel Mode to User Mode and starts executing the instruction whose address is stored in the edx register.
Interrupt Descriptor Table • Sistemska tabela ”Interrupt Descriptor Table (IDT) asociira prekinitveni vektor ali vektor izjem z naslovom ustreznega rokovalnika prekinitev ali izjem. • IDT moramo primerno inicializirati, še preden jedro omogoči prekinitve. • Vsak element te tabele ustreza enemu prekinitvenemu vektorju (ali vektorju izjem) in vsebuje 8-bytni opisnik. Tako potrebujemo za tabelo IDT 256 x 8 = 2048 bytov.
Vektorji prekinitev za procedure “interrupt gate” ali “trap gate “ kaže indirektno na proceduro, ki se bo izvedla v kontekstu trenutno izvajanjega procesa. Z izbiro vrat kažemo na opisnik izvršljivega segmenta bodisi v GDT ali tekoči LDT. Polje “offset” vrat kaže na začetek procedure za obravnavo prekinitve ali izjeme.
Vektor 128 elementa v Interrupt Descriptor Table • Vektor 128(šestnajstiško0x80) ustreza vstopni točki jedra. • Funkcijatrap_init( ), ki jo kličemo med inicializacijo jedra, vzpostavi na naslednji način “Interrupt Descriptor Table entry”, ki ustreza vektorju 128: set_system_gate(0x80, &system_call); Zato, ko v uporabniškem režimu izvajamo ukaz int $0x80, preklopi CPE v režim jedra in začne izvajati ukaze na naslovu system_call .
Kaj dela “system_call ()” • Funkcija system_call( ) najprej shrani na sklad številko sistemskega klica in vse registre CPE, ki bi jih rokovalnik izjeme lahko uporabil, razen eflags, cs, eip, ss, in esp, ki jih je CPE že sama avtomatsko shranila. • Nato funkcija system_call( ) preveri zastaviceTIF_SYSCALL_TRACE inTIF_SYSCALL_AUDIT , ki so shranjene v polju flags strukture thread_info. Te zastavice povedo, ali naj sistemski klic zasleduje razhroščevalnik.
Ponazoritev shranjevanja registrov ss esp eflags cs eip original eax es ds eax ebp edi esi edx ecx ebx Shrani CPE Sklad režima jedra %esp esp esp0 eip thread thread_info
Preverjanje veljavnosti klica • Številka sistemskega klica mora biti manjša od števila elementov tabele system call dispatch table, sicer system call handlerzaključi svoje delo, shrani kodo napake (-ENOSYS) na sklad, kjer je bil register eax in skoči na “resume_userspace”. Proces bo tako nadaljeval v uporabniškem režimu in v registru eax dobil negativno vrednost (napaka): cmpl $NR_syscalls, %eax jbnobadsys movl $(-ENOSYS), 24(%esp) jmpresume_userspace nobadsys:
Povratna koda neveljavnega sistemskega klica -ENOSYS ss esp eflags cs eip original eax es ds eax ebp edi esi edx ecx ebx Shrani CPE -ENOSYS Sklad režima jedra %esp esp esp0 eip thread thread_info
Klic servisne rutine sistemskega klica • Končno izvedemo klic servisne rutine, ki ustreza številki sistemskega klica, pomnjeni v registru eax : call *sys_call_table(0, %eax, 4) • Vsak element dispečerske tabele je dolg 4 byte. Jedro zato najde naslov ustrezne rutine tako, da začetku sistemske tabele sys_call_table prišteje odmik (zmnožek številke sistemskega klica s 4).
Izstop iz sistemskega klica • Ko servisna rutina sistemskega klica zaključi svoje delo, pridobi funkcija system_call( )njeno povratno kodo v registru eaxin jo shrani na sklad na mesto, kjer je bila prej shranjena vrednost eaxv uporabniškem režimu: • movl %eax, 24(%esp) • Tako bo proces aplikacije našel povratno kodo sistemskega klica v registru eax . • Za pravilen zapis povratne vrednosti servisne rutine v register eax poskrbi že prevajalnik C, ki prevede stavek • return n;
Priprava povratne kode sistemskega klica ss esp eflags cs eip original eax es ds eax ebp edi esi edx ecx ebx Shrani CPE Return Code Sklad režima jedra %esp esp esp0 eip thread thread_info
Preverjanje zastavic pred povratkom • Zatem funkcija system_call( )prepreči lokalne prekinitve in preveri zastavice v strukturi thread_infotekoče niti: cli movl 8(%ebp), %ecx testw $0xffff, %cx je restore_all • Če nobena od zastavic ni nastavljena, skočimo na kodo z oznako restore_all. Ta koda • Obnovi vsebine vseh registrov, shranjenih na skladu jedra • Izvede ukaz iretin tako nadaljuje s procesom v uporabniškem režimu.
Zastavice nakazujejo dodatno delo • Če je bila katera od zastavic nastavljena, moramo pred prehodom na uporabniški režim še kaj postoriti:. • Če je nastavljena zastavica TIF_SYSCALL_TRACE, kliče funkcija system_call( )še enkrat funkcijo do_syscall_trace( )in šele nato na oznako resume_userspace. • Če zastavica TIF_SYSCALL_TRACEni nastavljena, skoči funkcija na oznako work_pending. • Koda na oznakah resume_userspacein work_pendingpreveri • Zahtevke po ponaovnem razvrščanju, • virtual-8086 mode • Viseče signale • Koračno delovanje; • In sčasoma skoči na oznakorestore_allter preide na uporabniški režim delovanja procesa
Linuxova tabelaGDT Linux’s GDT Linux’s GDT
Tipi parametrov sistemskega klica • Podobno kot pri navadnih funkcijah tudi sistemski klici pogosto zahtevajo vhodno-izhodne parametre, ki so lahko: • Dejanske vrednosti (na primer števila) • Naslovi spremenljivk v naslovnem prostoru procesa v uporabniškem režimu • Naslovi podatkovnih struktur vključno s kazalci na funkcije v uporabniškem režimu (na primer pri sistemskih klicih za rokovanje s signali).
Nastavitev številke sistemskega klica • Ker predstavljata funkciji system_call( ) insysenter_entry( ) skupni vstopni točki za vse sistemske klice Linux, morata imeti vsaj en parameter: številko sistemskega klica v registru eax • Če na primer aplikacijskiprogram kliče rutino fork( ),postavimo register eaxna vrednost 2 (torej __NR_fork) preden izvedemo ukaz int $0x80ali sysenter. • Register nastavi ovojna rutina, ki je del knjižnicelibc. Zato programerjem ni treba skrbeti za številke sistemskih klicev.
Posredovanje parametrov • Pri navadnih funkcijah v jeziku C običajno posredujemo parametre z zapisom njihovih vrednosti na sklad (bodisi v režimu uporabnika, bodisi v režimu jedra). • Vendar so sistemski klici posebna vrsta funkcij, ki prehaja iz režima uporabnika v režim jedra. Tako ne moremo uporabljati niti sklada uporabniškega programa niti sklada jedra. • Parametre sistemskih klicev moramo zato zapisati v registre CPE. • Jedro skopira parametre, shranjene v registrih CPE na sklad jedra, preden pokliče servisno rutino sistemskega klica. Ta rutina je spet navadna C-jevska funkcija.
Omejitve za parametre sistemskih klicev • Parametre lahko prenašamo preko registrov, če sta izpolnjena dva pogoja: • Dolžina posameznega parametra ne sme presegati dolžine registra (32 bitov). • Število parametrov ne sme biti večje od 6 poleg številke sistemskega klica, ki jo prenašamo preko registra eax. Število registrov arhitekture 80x86 je pač omejeno.
Obsežni parametri • Prvi pogoj je vedno izpolnjen. Če obsežnih parametrov ne moremo shraniti v 32 bitne registre, jih posredujemo po referenci. • Tipičen primer je sistemski klic settimeofday( ) , ki mora prebrati 64-bitno strukturo.
Številni sistemski parametri • Nekateri sistemski klici pa potrebujejo več kot 6 parametrov. • V teh primerih en register kaže na pomnilniško polje v naslovnem prostoru procesa, kjer so shranjene vrednosti parametrov. • Za to programerju ni treba skrbeti. Pravilen način posredovanja parametrov je skrit v ovojni rutini, ki dejansko izvede sistemski klic. X: tabela parametrov uporabi parametre v tabeli X naloži X v register Izvedi sistemski klic Koda Sistemskega klica Uporabniški proces jedro
Preverjanje naslovnih parametrov • Pred uporabo naslovnih parametrov mora jedro preveriti, ali tak parameter naslavlja znotraj naslovnega prostora procesa. • To preverjanje je zamudno • Preverjanje naslovov je pogosto prestavljeno na čim kasnejši čas, ko enota ostranjevanja (paging unit) že preslika linearne naslove v fizične.
Naslavljanje prostora procesa • Servisne rutine sistemskih klicev morajo pogosto brati ali zapisovati podatke v naslovnem prostoru uporabniškega procesa. • Linux ima več makrojev, ki lajšajo tak dostop. • Omenimo dva: get_user( ) in put_user( ). • Prvi zna brati enega ali več zaporednih bytov iz danega naslova, drugi pa tako zaporedje bytov zapisuje na dani naslov.
get_user(x,ptr),put_user(x,ptr) • Vsaka od teh funkcij sprejme dva argumenta. Vrednost x, ki naj bi jo prenesli, ter spremenljivko ptr. Slednja tudi določa, koliko bytov (1, 2 ali 4) prenašamo. • Makro put_user(x,ptr)je podoben prejšnjemu, le da vrednost xzapisuje v naslovni prostor procesa začenši z naslovom ptr.
Funkcije in makroji za dostop do naslovnega prostora procesa