Programování v Unixu

A) Vývojové nástroje


1. Popište činnost kompilátoru jazyka C a linkeru.
    - preprocesor - cteni maker, vkladani #include, vynechani komentaru
    - kompilator: C -> asm (cc -S), assembler: asm -> obj (cc -c)
    - prekladac: moduly C -> object file: export. identifikatory, nesamostatny
    - ld : spojeni object-files do spustitelne podoby, odkazy na sdilene knihovny/pripojeni statickych knihoven (lze nastavit)
    - dyn. linker: spojeni programu pri spusteni (sekce .dynamic, mmap())
2. Vysvětlete princip utility make a napište příklad jednoduchého makefile pro překlad a slinkování programu v C.
    - rizeni prekladu, linkovani; zavislosti - Makefile: popis zavislosti & potrebnych prikazu; make vzdy po zmene souboru prelozi jen to co na nem zavisi. Targets - hl. cil cinnosti, mozno definovat vic; univ. nastroj
    - targets: files; <tab>commands; #comment; line-begin\<LF> line contd.; definice makro = string; pouziti ${makro}

B) Jádro a C API UNIXu


1. Vysvětlete význam parametrů funkce main() a proměnné environ. Jak se zpracovávají argumenty programu pomocí funkce getopt()?
    - argc, argv -- parametry; nepritomnost mainu zpusobi chybu linkeru; vraci int (0 = uspech), konec = konec programu
    - varianta argc, argv, char * envp - promenne prostredi
    - promenna prostredi predavana: extern char ** environ - null-terminated pole, prvky PROMENNA=hodnota
    - manipulace s environmentem: char * getenv( const char * name ); int putenv( char * string ) <- PROMENNA=hodnota
    - prepinace "-x", "-x hodnota", konci "--" nebo 1. argumentem nezacinajicim "-"
    - int getopt( int argc, char ** argv, const char * opt_format ), extern char * optarg; extern int optind (cislo 1.nezprac.), optopt, opterr (pri chybe vraci '?', ulozi do optopt skutecny parametr a pokud opterr != 0, vypise chybu )
    - dlouhe prepinace: getopt_long(), pole struktur
2. Vysvětlete význam pojmů proces, vlákno (thread), program. Čím se liší proces a vlákno?
    - program: soubor definovaneho formatu, instrukce, data, sluzebni informace -- spustitelny soubor
    - proces: system. objekt, ma svuj kontext & PID -- kod & data v pameti
    - vlakno: system. objekt uvnitr procesu, charakterizovan svym stavem; sdili v 1 procesu adres. prostor (krom stacku, CPU)
    - pamet -> procesy, CPU -> vlakna; vlakna mohou byt implementovana knihovnimi fcemi
3. Vysvětlete pojmy knihovní funkce a systémové volání. Popište konvenci, podle které systémová volání a knihovní funkce oznamují úspěšné ukončení nebo chybu.
    - knihovni fce: user-mode; system. volani: zpracuje argumenty, preda rizeni jadru, upravi vysledek, vrati;
    - z hlediska programatora stejne; man-pages 2 vs. 3; v assembleru mozne volat jadro primo;
    - navr. hodnota: >= 0 OK, -1 chyba / != NULL OK, == NULL chyba. pri chybe nastaven extern int errno; perror( char * prefix ), strerror( int errnum );
    - fce ze stdio: ferror( FILE * stream ); pthread - pri chybe vraci != 0; nekt. fce maji -1 jako platne -> nutne testovat primo errno
4. Co obsahují soubory /etc/passwd a /etc/group? Kdy a jak používá Unix informace z těchto souborů.
    - popis uzivatelu, popis skupin -- uziv:x:1205:106:Jmeno Prijmeni:/home/uziv:/bin/bash -- group:*:106:uziv1,uziv2...
    - jadro nezpracovava, jen login, su. prepinani skupin puv. explicitne newgrp, dnes - SGID
    - krom /etc/passwd -- databaze (passwd, groups, services, protocols) - data ze zdroju (soubory, DNS, NIS, LDAP) - def. v nsswitch.conf
5. Jak se používá identifikace vlastníka a skupiny pro testování přístupových práv? Jaký je rozdíl mezi reálným a efektivním vlastníkem procesu?
    - uzivatel - UID, primary GID, supplementary GIDs;
    - 1. UID_u == 0 - root, 2. UID_u == UID_s - prava vlastnika, 3. PGID_u == GID_s || GID_s ∈ SGIDs_u - clen skupiny; else ostatni
    - rootovske procesy mohou menit identitu uzivatele: login; nehledi se na to jestli skupina ma vic prav nez uziv.;
    - kazdy program: RUID, RGID, EUID, EGID; pokud programu nastavim SetUID bit, je moje EUID rovno UID vlastnika programu (su, passwd), pro kontrolu pristupu se pouziva EUID, EGID, supplementary gids
    - navic uschovane UID - to EUID, se kterym byl proces spusten. EUID lze menit mezi uschovanym UID a RUID (root muze vsechno, pokud zmeni RUID, zmeni se i uschovane UID)
    - fce getuid(), geteuid(), getgid(), getegid(); getgroups(); setuid() - jine chovani pro roota, setgid(), setgroups() - jen pro roota;

C) Systém souborů


1. Jaké objekty jsou v UNIXu přístupné pomocí rozhraní systému souborů? Jaký je rozdíl mezi znakovými a blokovými zařízeními?
    - periferie, pojmenovane roury, sockety, procesy /proc, pamet /dev/mem, pseudosoubory /dev/tty ...; vsechno v 1 stromu
    - spec. soubory (periferie atp.) - znakove: data primo mezi procesem & ovladacem (RS-232); blokove: prochazi cache po blocich (HDD)
    - id zarizeni: 2 cisla: major - cislo ovladace v jadru, minor - cislo souboru v ramci 1 ovladace
    - nekde (FreeBSD) blokova zarizeni nejsou vubec; /dev - puv. rucne, dnes devfs;
2. Popište strukturu svazku typu s5 a jeho vylepšení (ufs).
    - boot block, superblock, i-node area, data area.; jediny superblok, pevne oddelene i-node area;
    - i-node -- typ souboru, prist.prava, UID, GID, atime, mtime, i-node mtime, pocet odkazu, velikost, 10 odkazu na dat. bloky & 3 neprime, max. velikost cca 1GB pri velikosti bloku 512B. Max. 14 zn. jmena souboru.
    - UFS - cleneni na skupiny cylindru, kazda: kopie superbloku, ridici blok skupiny, tabulka i-uzlu, bitmapy volnych i-uzlu & dat., data
    - 4-8K bloky, fragmenty bloku; 255 zn. jmena;
3. Vysvětlete princip navigace ve struktuře adresářů, tj. jak jádro najde podle cesty k souboru příslušné datové bloky. Vysvětlete rozdíl mezi pevnými (hardlink) a symbolickými (symlink) odkazy na soubory.
    - adresar -- soubor s odkazy na i-nodes; "/... " - abs. cesta z korenoveho adresare; jinak rel. cesta; ///a//b == /a/b !
    - hard-link: 2 adresare obsahuji odkaz na stejny i-node; nelze pro adresare, soubor musi existovat, fyzicke smazani az po zniceni posl. odkazu
    - soft-link: normalni (typem odlisny!) soubor, obs. jmeno cil. souboru, jine chovani pri mazani, nemusi odkazovat na existujici soubor, lze pro adresare;
    - puv. - linearni, nove filesystemy - B-Stromy; UFS- zpetne kompatibilni dirhash
4. Jaká jsou přístupová práva k souborům? Co je to propůjčování práv (set UID)?
    - rwx pro uziv., skupinu, ostatni & suid, sgid, sticky (4-2-1); sgid pro adresar - System V nastavi stejnou skupinu jako adresar, sticky - pravo mazat & prejmenovavat maji jen vlastnici (pro adresar - napr. /tmp)
    - nektere filesystemy (AFS,XFS,UFS2) maji lepsi kontrolu - Access Control List
    - SetUID viz B.5
5. Jak procesy přistupují k otevřeným souborům? Jak se liší deskriptor a otevření souboru?
    - kazdy proces ma svou tabulku otevr. souboru; z ni je odkazovano na 1 systemovou tabulku otevr. souboru - v ni je mod otevreni souboru, akt. pozice; z ni je odkazovano do tabulky vnodu v pameti
    - pri open(): alokuje se novy slot v tab. deskriptoru, i v systemove tabulce otevr. souboru; sdileni: dup() - vic deskriptoru sdili 1 slot v sys. tabulce, stejne fork()
    - std. deskriptory: 0 (vstup), 1, 2 (stdout, stderr);
6. Co je to virtual file system? K čemu slouží a jaký je princip jeho fungování?
    - nutnost abstrakce konkretniho file-systemu kvuli prenositelnosti, interoperabilite; VFS/vnode: Sun
    - 1 soubor: struct file (v system. tabulce otevrenych); v nem cast zavisla & nez. na implementaci; kazdy filesystem implementuje danou sadu operaci -- vnode interface.
    - pro filesystem - struct vfs: odkaz na root FS, vfsops - tabulka def. fci, pole odkazu na vfsops pro vsechny podporovane FS, odkaz na mountpoint kde je pripojeny
    - z vnode: odkaz na vfs pod kterym je soubor ulozen, prip. mountpoint;
7. Jaký je vztah diskového oddílu a svazku? Jak probíhá vytvoření, kontrola konzistence, připojení a odpojení svazku?
    - svazek = filesystem: na oddilu disku (fyz.=partition/log.=volume-i na vic fyz. discich, pouziti RAID)
    - po startu jadra-pripojeny jen korenovy; ost. pomoci mount, /etc/fstab; pred vypnutim umount; automounter
    - kontrola konzistence: fsck - vicenasobne odkazy na stejny blok, odkazy out-of-range, spatny pocet odkazu, spatna velikost, neplatny format i-node/adresare/superbloku, ztracene bloky; cas. narocne.
    - synchronni zapis metadat (UFS), zurnalovani, soft-updates (sleduje zavislosti mezi ukazateli na disk. struktury, zapisuje na disk takz data jsou vzdy konzistentni), ZFS- copy on write.

File API:
    - int open( char * path, O_RDONLY| O_WRONLY| O_RDWR| O_APPEND| O_CREAT| O_EXCL| O_TRUNC, mode_t mode), creat() = open s O_WRONLY| O_CREAT| O_TRUNC
    - int write( int fd, void * buf, size_t bytes ); read( int fd, void * buf, size_t bytes );
    - int close( int fd ); int mknod( char * path, mode_t mode, dev_t d ); int mkfifo( char * path, mode_t mode ); (pozor na rouru a sit. filesystemy, blokovani (O_NONBLOCK,PIPE_BUF))
    - off_t lseek( int fd, off_t offset, SEEK_SET/SEEK_CUR/SEEK_END ); (i vraci akt. pozici)
    - int truncate( char * path, off_t len );, int ftruncate( int fd, off_t len ); (pro prodluzovani lepsi lseek & write )
    - int dup( int fd ); int dup2( int fd, int target_fd );
    - int fcntl( int fd, F_DUPFD/F_SETFD/F_GETFL/F_GETLK ... , ... ); int ioctl( int fd, ... );
    - int stat( char * path, struct stat * buf ); int fstat( int fd, struct stat * buf );
    - int utime( char * path, const struct utimbuf * times );
    - int access( char * path, R_OK| W_OK| X_OK| F_OK );
    - int chmod( char * path, mode_t mode ); int chown( char * path, uid_t owner, gid_t group );
    - int link( char * path1, char * path2 ); int unlink( char * path ); int rename( char * path1, char * path2 );
    - int symlink( char * dest, char * linkname ); int readlink( char * path, char * buf, size_t len );
    - int mkdir( char * path, mode_t mode ); int rmdir( char * path ); DIR * opendir( char * dirname ); struct dirent * readdir( DIR * d ); int closedir( DIR * d ); ( dirent: d_ino, d_filename )
    - int chdir( char * path ); int fchdir( char * path ); char * getcwd( char * buf, int len );

D) Procesy a roury


1. Popište paměťový prostor procesu v uživatelském režimu a v režimu jádra.
    - uziv. rezim: text(kod programu), data(inicializovane promenne), bss(neinicial.promenne) <-- dohromady: datovy segment procesu; uziv. stack (na konci oblasti, automaticky roste/bez vlaken), u-area - neadresovatelna procesem - informace pro jadro (dalsi jsou ve strukture proc - vzdy v pameti)
    - v rezimu jadra: text jadra, data & bss jadra, struct user pro bezici proces, zasobnik jadra (prazdny pro proces bezici v user-mode)
    - kazdy proces: 3 zakl. pametove segmenty - text, data, stack; dalsi - sdilena pamet (mmap())
2. Nakreslete a popište stavový diagram procesu
                       [ bezi v    ]
                       [ user-mode ]
                           |   ^
                  preruseni|   |  navrat             (zanik)
                           v   |                      ^
                       [ bezi v      ]    exit        |
   (vznik)    preempce/[ kernel-mode ] -------->[ zombie ]
        \            /  ^             \ uspani
         v          v  / prideleni CPU ----\
         [pripraven v]/                     \>[ spi      ]
         [  pameti   ] <--------------------- [ v pameti ]
             |   ^           probuzeni             |
    odlozeni |   |  zavedeni                       |  odlozeni
             v   |                                 v
         [ pripraven ] <--------------------- [ spi na ]
         [  na disku ]        probuzeni       [ disku  ]

    - zombie: kvuli dotazu na navratovou hodnotu (dokud to rodic neudela, nelze zrusit struct proc, ost. ano)
    - dnes: neswapuje se cely proces
3. Popište základy mechanismu plánování procesů. Jaké jsou prioritní třídy?
    - preemptivni: CPU odebran po uplynuti cas. kvanta; fronty podle priorit, CPU vzdy 1. pripravenemu z fronty s nejvyssi prioritou
    - jadro - tradicne nepreemptivni; dnes uz ne (real-time systemy);
    - tridy: system (60-99) - pevna, real-time (100-159) - pevna, dane cas. kvantum, time-shared (0-59) - promenna - uziv. nastavena & menena systemem - vyvazovani (narocne procesy: nizsi priorita, vyssi kvantum)
4. Popište mechanismus mapování souborů do paměti. Popište, jak lze za běhu programu získat přístup k proměnným a funkcím definovaným v nějaké dynamické sdílené knihovně.
    - namapuje cast souboru do pameti, lze sdilet (MAP_SHARED, MAP_ANONYMOUS/MAP_ANON - nestd.), nutna synchronizace
    - jina moznost - tmpfs, memory disk (nestd.)
    - int mmap( void * addr, size_t len, PROT_READ| PROT_WRITE| PROT_EXEC/PROT_NONE, MAP_PRIVATE/MAP_SHARED| MAP_FIXED, int fd, int off );
    - int msync( void * addr, size_t len, MS_ASYNC/MS_SYNC/MS_INVALIDATE ); - ulozeni do souboru zarucene az po zavolani tohoto, ale ost. procesy co maji soubor namapovany, vidi zmeny hned;
    - int munmap( void * addr, size_t len );
    - int mprotect( void * addr, size_t len, PROT_READ| ... );
    - dyn. knihovny - lze otevrit & volat v nich fce -> lze implementovat napr. plug-iny; musi byt ve spravnem formatu (gcc -shared)
    - void * dlopen( char * file, RTLD_NOW/RTLD_LAZY| RTLD_GLOBAL/RTLD_LOCAL ); - vraci "handle"/NULL, konstanty -- pro relokace = vyreseni odkazu (tj. zpristupneni symbolu), global == muzou byt pouzity pri relokacich v ost. knihovnach, dostupne pomoci dlopen( NULL, RTLD_GLOBAL )
    - void * dlsym( void * handle, char * symbol_name ); int dlclose( void * handle ); char * dlerror() - text. popis chyby pri praci s knihovnami

Process API:
    - int setpriority( int which, id_t who, int nice ); int nice( int incr );
    - pid_t getpid(), pid_t getpgrp() - id skupiny procesu, pid_t getppid() - rodic, pid_t getsid( pid_t member ) - vedouci session pro member
    - int fork(): -1 - chyba, 0 - dite, pid - rodic; pri vice vlaknech se kopiruje jen aktualni, copy-on-write
    - int execl( char * path, char * arg0, ... (char *) NULL ) - cesta, argumenty, arg0 by mel obs. jmeno programu; dedi environment, pri uspechu se nikdy nevrati
    - int execv( char * path, char ** argv ), execle, execve - s environmentem, execlp, execvp - pouziva se $PATH
    - void exit( int return_val ); pid_t wait( int * status ); <- pocka na lib. syna & vrati jeho pid, ulozi status, ktery jde testovat
    - int waitpid( pid_t pid, int * status, int opts ); <- ceka na konkretni proces
    - int pipe( int fd[2] ); 0-cteni, 1-zapis -- deskriptor predam jedine pres fork
    - mmap(), dlopen() viz D.4

E) Signály


1. Co jsou to signály? Jak může být pro proces vygenerován signál? Jaký je rozdíl mezi posláním signálu procesu a vláknu?
    - informace pro proces o urc. udalosti; pomoci preruseni; chyb. udalosti (SIGSEGV), asynchronni udalosti (SIGALM,SIGHUP,SIGINT)
    - asynchronni -- vyvola rovnou handler, nenese jinou informaci nez cislo signalu, po navratu z handleru proces pokracuje kde prestal
    - hist. pro nasilne zabiti procesu
    - int kill( pid_t pid, int sig ); (pid == 0 - vsem ve skupine, == -1 vsem krom systemovym, < -1 ve skupine -pid), pro sig == 0 jen test prava signal poslat (shodne EUID & podobne kejkle, ruzne na ruznych systemech);
    - vlakna: signaly primo : pthread_kill(), obsluha pro vsechny stejna, ale muzou mit svou masku (pthread_sigmask()); signal pro proces osetri prave 1 vlakno, ktere ho nema zablokovany; lze vyhradit 1 vlakno pro synchronni prijem signalu (sigwait()), v ost. zablokovat (bloknout v hlavnim threadu, ost. masku dedi).
2. Jaké jsou možnosti ošetření signálů? Jak se nastavuje ošetření signálů (handlery, blokování) pro proces a pro vlákna?
    - default akce: exit / core dump / ignore / stop / continue (SIGKILL & SIGSTOP -- vzdy default akce)
    - int sigaction( int sig, struct sigaction * a, struct sigaction * old ), sigaction: void (*sa_handler) (int) == SIG_DFL, SIG_IGN, sigset_t sa_mask (ktere krome sig se maji blokovat), int sa_flags (SA_RESETHAND, SA_RESTART, SA_NODEFER); pro a == NULL se jen zjisti nastaveni
    - blokovani- zadrzi az do odvolani: int sigprocmask( SIG_BLOCK| SIG_UNBLOCK| SIG_SETMASK, const sigset_t * set, sigset_t * old_set ); -- pro sigset_t existuji fce sigaddset(), sigdelset(), sigemptyset(), sigismember(); NENI to to same jako ignorovani
    - int sigpending( sigset_t * set ); - vrati cekajici signaly;
    - int pause( void ); - ceka na lib. signal, int sigsuspend( const sigset_t * sigmask ); - navic zablokuje na dobu cekani nejake signaly, int sigwait( const sigset * s, int * sig ); - ceka na prichod signalu z dane mnoziny, nevola handler (ale tohle neni definovano v norme)

F) Vlákna


1. Co je to vlákno, jak se liší od procesu? Které atributy jsou společné pro proces, které jsou privátní pro každé vlákno? Jak je možné vytvořit globální proměnnou privátní pro jedno vlákno?
    - vlakno = 1 linie vypoctu; vic v ramci 1 procesu; vlastni kontext (stack, CPU), sdileny pametovy prostor
    - vlastni : citac instrukci, thread_id (pthread_t pthread_self() ), planovaci priorita/politika, errno
    - int pthread_key_create( pthread_key_t * key, void (*destructor)(void *)) - destruktory pro vsechny neNULLove klice pri ukonceni vlakna musi nastavit hodnotu na NULL jinak se volaji furt dokola !!!
    - int phtread_key_delete( pthread_key_t key ) - zrusi klic, nemeni asociovana data
    - int phtread_setspecific( pthread_key_t key, void * value ), void * pthread_getspecific( pthread_key_t key )
2. Popište postup při vytvoření a zrušení vlákna. Jak fungují destruktory klíčovaných hodnot a zásobník úklidových handlerů? fork() a vlákna.
    - int pthread_create( pthread_t * thread, pthread_attr_t * attr, void *(*startfn)( void *), void * arg ) -- bacha na vytvareni ve for-cyklu - parametry by mely mit jinou adresu
    - void pthread_exit( void * value ) ukonci volajici vlakno; int pthread_join( pthread_t thread, void ** value ) - pocka na ukonceni vlakna, dostane jeho navr. hodnotu;
    - int pthread_detach( pthread_t thread ) - automaticke uvolneni pameti po ukonceni, nelze na nej cekat joinem (volat pokud nemam v umyslu join, jinak zbydou data pro navr. hodnotu -- zombie vlakno)
    - int pthread_cancel( pthread_t thread ) - pozada o zniceni threadu, zavisi na nastaveni: int phtread_setcancelstate(), int pthread_setcanceltype(), int pthread_testcancel()
    - zasobnik uklid. handleru, ktere se volaji pri ukonceni vlakna pthread_exit()/pthread_cancel(); volaji se v opacnem poradi vlozeni
    - pthread_cleanup_push( void (*routine)( void *), void * arg ), pthread_cleanup_pop( int execute ) - vyjme posledni, provede kdyz execute != 0
    - fork() -- novy proces ma presnou kopii volajiciho vlakna, vc. stavu mutexu a jinych zdroju; nema ostatni vlakna, pokud ostatni vlakna mela naalokovanou pamet, zustane ztracena, stejne tak zamceny mutex --> nema moc smysl, krome exec().
3. Uveďte nástroje pro synchronizaci vláken.
    - pthread_once( pthread_once_t * once_control, void (*init_routine)(void)) - init_routine provede jenom prvni vlakno, ktere tuhle fci zavola. - napr. v dyn. knihovnach potrebne
    - mutex: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER / int pthread_mutex_init( pthread_mutex_t * mx, pthread_mutexattr_t * attr ) (pro dynamicka data); int pthread_mutex_destroy( pthread_mutex_t * mx ); int pthread_mutex_lock( pthread_mutex_t * mx ), int pthread_mutex_unlock( pthread_mutex_t * mx );
    - podminkove promenne: "wait & signal", slouzi k predani informaci o sdilenych datech, k nimz se pristupuje pres mutex; kazda podm. promenna musi byt asociovana s 1 mutexem (ale mutex i s vic podm.prom.); <-- pomoci nich lze vyrabet semafory, bariery etc.
    - pro testy lib. podminky nad sdilenymi daty & uspani se, kdyz neni splnena; pri zmene stavu explicite oznameni.
    - int pthread_cond_init( pthread_cond_t * cond, pthread_condattr_t * attr ); int pthread_cond_destroy( pthread_cond_t * cond ); int pthread_cond_wait( pthread_cond_t * cond, pthread_mutex_t * mx ); int pthread_cond_timedwait( pthread_cond_t * cond, pthread_mutex_t * mx, struct timespec * tm ); int pthread_cond_signal( pthread_cond_t * cond ); int pthread_cond_broadcast( pthread_cond_t * cond );
    - read-write zamky - obdoba zamykani souboru pomoci fcntl()
    - int phtread_rwlock_init( pthread_rwlock_t * l, pthread_rwlockattr_t * attr ); pthread_rwlock_destroy(); pthread_rwlock_rdlock(), pthread_rwlock_tryrdlock();, pthread_rwlock_wrlock(), pthread_rwlock_trywrlock(); pthread_rwlock_unlock();
    - bariera - SUSv3, vytvoritelna pomoci podm. promennych & mutexu -- pthread_cond_wait() & pthread_cond_broadcast()

G) Synchronizace a zamykání


1. Vysvětlete vznik konfliktu při přístupu ke sdíleným datům a jeho řešení pomocí zamykání. Jak může vzniknout deadlock? Popište zamykání souborů pomocí fcntl().
    - neatomicke operace & preplanovani; <- pokud 1 proces provadi modifikaci dat, musi je uvest do konzistentniho stavu, nez s nimi pracuje druhy.
    - napr. problem log-souboru: zapis musi byt atomicky, aby se nepomichaly casti zaznamu; pri zapisu se nesmi cist;
    - zamky pro cteni & zapis (bud lib. mnoho cteni nebo 1 zapis); lepsi pasivni cekani, nedrzet zamky dlouho
    - teorie - alg. vzajemneho vylouceni (Dekker, Peterson -- rel. slozite alg.)
    - fcntl() - F_GETLK, F_SETLK, F_SETLKW (wait); advisory (kooperace procesu, pouziva se casteji)/mandatory; struct flock : type, whence(SET,CUR,END), start, len, pid.
    - deadlock: 2 zdroje - 2 procesy chteji pristup k obema, zamykaji v opacnem poradi; fcntl() kontroluje E_DEADLK (ale lepsi nespolehat)
2. Vysvětlete zamykání pomocí lock souborů.
    - pro kazdy sdileny zdroj je dohodnute jmeno souboru, lock: cekani ve smycce na moznost zamceni == moznost vytvoreni souboru (open s O_EXCL), unlock: unlink() na soubor
    - error pokud proces neco zabije kdyz ma zamek; reseni -- otestovat jestli proces bezi, zrusit zamek & znova vytvorit (ale ERROR - tuhle akci nelze synchronizovat)
    - test moznosti vytvoreni souboru - aktivni cekani; reseni -- zamek == roura, pri cekani na zamek se procesy uspi tim ze z ni zkusi cist.

H) IPC


1. Popište semafory, implementované v UNIX System V IPC.
    - semafor: cele nezap. cislo (volna kapacita) & fronta procesu; operace init(), p() - zkus dekrementovat, v() - inkrementovat
    - nikdy nad 1 semaforem, vzdy pole;
    - int semget( key_t key, int n_sems, int semflag ); - vrati id pole semaforu, asociovanych s klicem key/IPC_PRIVATE, flag: R/W/EXCL/CREAT
    - int semctl( int semid, int semnum, int cmd, (arg semun ) ); - ridici fce, prikazy GETVAL, SETVAL, GETPID, GETNCNT, GETZCNT, IPC_RMID, IPC_SET
    - int semop( int semid, struct sembuf * ops, size_t len ); - zobecnene operace p,v; sembuf: sem_num, sem_op - p <0, v >0, cekani na 0 == 0, sem_flg - IPC_NOWAIT, SEM_UNDO -- cele atomicke (prevence deadlocku)
    - prvni proces vytvori, ost. se pripoji; nutne dohodnout se na spolecnem klici: key_t ftok( char * program, int id );

I) Sítě


1. Popište činnost serveru a klienta (posloupnost systémových volání) pro spojované síťové služby.
    - server: fd = socket(), bind(fd), listen(fd), fd1 = accept(fd), read(fd1)/write(fd1), close( fd1 )
    - klient: fd = socket(), connect(fd), write(fd)/read(fd), close( fd ) - nemusi volat bind(), pokud nechce privilegovany port
2. Popište činnost serveru a klienta (posloupnost systémových volání) pro nespojované síťové služby.
    - server: fd = socket(), bind(fd), recvfrom(fd), sendto(fd) .... close( fd )
    - klient: fd = socket(), sendto(fd), recvfrom(fd), close( fd ) - taky muze a nemusi volat bind(), i pro UDP lze volat connect() a posilat pomoci send() a recv()
3. Co je to soket? Jaké jsou varianty adresace soketů (v rámci jednoho stroje a po síti)? Jaké funkce (uveďte aspoň některé) slouží pro převod mezi číselnými a symbolickými jmény protokolů, portů a IP adres? Proč se používají funkce pro konverzi mezi lokálním a síťovým pořadím bajtů?
    - "1 konec obousmerneho kanalu mezi 2 procesy lok./vzdalene"
    - vytvoreni int socket( AF_UNIX (lok.) / AF_INET (IPv4), SOCK_STREAM / SOCK_DGRAM, protocol ) - protocol - 0 default pro dany type, nebo platne cislo (6=TCP, 17=UDP) -- jinak fce getprotobyname( char * name ) (vraci struct protoent->p_proto - cislo)
    - svazani spojeni s adresou int bind( int socket, struct sockaddr * addr, socklen_t addrlen ); (pretypovani adresy pro AF_INET/UNIX), pro AF_INET - in_addr == INADDR_ANY, in_port_t == port, sa_family_t == AF_INET.
    - DNS - gethostbyname( char * name ) - vraci struct hostent->h_addr; gethostbyaddr( char * addr, int addrlen, int domain )
    - cisla portu pro sluzby: getservbyname( char * name, char * proto ) (struct servent->s_port )
    - fce ntohs(), ntohl(), htons(), htonl() -- kvuli endianum
4. Popište varianty sekvenční a paralelní obsluhy klientů TCP serveru.
    - sekvencni: akceptuje spojeni, read/write, close, opakuje
    - paralelni obsluha: server akceptuje spojeni, fork() & waitpid( WNOHANG ), syn se ukonci po obsluze spojeni
    - vytvori nekolik syn. procesu, kazdy vola accept() a sekvencne obsluhuje klienty. mohou spolu komunikovat, hl. proces muze vytvorit dalsi (Apache)
    - klient nezavisi na zpusobu prace serveru
5. Jak lze čekat na příchod dat z několika deskriptorů souborů zároveň? Jak byste tuto funkci použili při implementaci síťového serveru, který obsluhuje několik klientů zároveň jedním procesem bez použití vláken?
    - pouziti select() & read() (slo by i sleep() & read( O_NONBLOCK ) )
    - int select( int nfds, fd_set * readfds, fd_set * writefds, fd_set * errfds, struct timeval * timeout ); - nfds = rozsah testovani (0 - (nfds-1)), vraci mnoziny pripravenych; pri volani predavame ty ktere chceme testovat, select() jenom nuluje nepripravene (ty na kterych by se read/write blokovaly)
    - pro vic socketu/portu lze volat select() na ne, a pak accept() na ty ktere jsou pripravene ke cteni
    - na 1 portu: select() na pripravenost klientu k odesilani / prijmu -> lze pracovat s vic najednou i akceptovat prichozi spojeni
    - errorfds - zavisle na pripadu (TCP=prichod urgentnich dat) -- neni chyba.
    - int poll( struct pollfd fds[], nfds_t nfds, int timeout ) - obdobne, fds -- fd, events (testovane), revents (nastaly): POLLIN| POLLOUT...

Socket API:
    - socket(), bind() viz I.3
    - int listen( int socket, int max_waiting_requests );
    - int accept( int socket, struct sockaddr * address, socklen_t * addr_len ) - vraci novy deskriptor, v address - vraci adresu vzdal. soketu, v addr_len skutecnou delku adresy (pro AF_INET -- sockaddr_in )
    - int connect( int socket, struct sockaddr * address, socklent_t addr_len ) - socketu je prirazena nepouzita adresa, pokud nebylo volano bind, navaze spojeni, pokud se nepovede, je nutne soket zavrit;
    - ssize_t sendto( int socket, void * msg, size_t len, int flags, struct sockaddr * addr, socklen_t addr_len );
    - ssize_t recvfrom( int socket, void * buf, size_t len, int flags, struct sockaddr * addr, socklen_t addr_len ); -- flagy: MSG_PEEK
    - zprava je "neprectena", MSG_00B - jen urgentni, MSG_WAITALL - ceka dokud nenacte len bajtu
    - int shutdown( int socket, SHUT_RD / SHUT_WR / SHUT_RDWR );
    - int close( int socket ); preneseni zbylych dat - SO_LINGER
    - getprotobyname(), gethostbyname(), gethostbyaddr(), getservbyname() viz I.3
    - ntohs() a spol. viz I.3

J) Administrace


1. Jak probíhá start operačního systému UNIX? Co jsou to úrovně běhu systému? Jak se spouští startovací skripty na různých systémech?
    - zavisle na architekture, OS, boot manageru
    - PC: nacteni 1. bloku z bootdisku & vyhledani partition; nacteni 1. bloku zvoleneho oddilu & skok na jeho 1. instrukci, nacteni loaderu, potom jadra (nebo slozitejsiho loaderu ktery nacte jadro); vicestupnovy start - volba jadra, moduly, PnP
    - levely -- postupne nacitani, ruzne moznosti prace; System V: 0-halt, 1-root mode, 2-user mode, 3-user/network mode, 4-alt.user mode, 5-halt/firmware, 6-reboot, S-single user mode.
    - jadro - pripoji root filesystem, spusti program init - ten ridi vse dal, v user mode; nacteni /etc/inittab nebo /etc/rc (init x = prechod do urovne x, akt. uroven - who -r, runlevel), format /etc/inittab: id:urovne:akce:proces -- respawn, wait, once, off
    - /etc/rcX pro runlevel X -- /etc/rcX.d/ & skripty (symlinky do /etc/init.d)
    - Solaris - ServiceManagementFacility - sluzby startovany paralelne, automaticky restartovany v pripade potreby, maji sve logy
3. Jak probíhá přihlášení uživatele do systému?
    - init spousti na kazdem terminalu getty (fork() & exec()) - to vypise vyzvu k loginu, nacte jmeno uzivatele & spusti login
    - login precte heslo, nastavi GID, UID, spusti shell; po odhlaseni uzivatele spusti init znovu getty
    - nekdy misto getty - ttymon (pro vsechny terminaly); roli getty pri vzdal. prihlaseni prebira telnetd nebo sshd.
    - shell - na zac. spousti init-skripty : /etc/profile, /home/user/profile; do X - jinak - xdm
4. Jak se používá démon cron?
    - provadeni schedulovanych akci, podle souboru crontab (pro kazdeho uzivatele), jeho zobrazeni - crontab -l; vytvari se crontab < file
    - format: minuta hodina den_v_mesici mesic den_v_tydnu /usr/bin/program ; -- lze *, vic moznosti odd. carkou, */5 -- kazdych 5 (neceho)
    - jednorazove spousteni: prikaz at
    - nekde se ridi i /etc/crontab -- globalni pro cely system
5. Jak funguje síťový server inetd?
    - pro start serveru sit. sluzeb (bud v inittab, nebo spousti inetd) -- ceka na portech definovanych v inetd.conf a kdyz se chce nekdo pripojit, navaze spojeni, spusti prisl. server s presmerovanymi stdin/out/err do socketu pro komunikaci
    - format inetd.conf: sluzba(jmeno dle /etc/services) socket(stream/dgram) protokol(tcp/udp) cekani(wait/nowait - pro paralelni streamove spojeni, pro udp nema nowait smysl) uzivatel(uid se kterym pobezi) server(cesta/"internal") argumenty.
    - (napr. "ftp stream tcp nowait root /usr/etc/ftpd ftpd -l")
    - pri moc castem restartovani 1 serveru inetd tuto konkretni sluzbu zablokuje.
    - casto se nevyplati (napr. pouze pro 1 sluzbu -- bezp. riziko)
6. Jaké úkoly má správce Unixu?
    - instalace, konfigurace; sprava uctu, pridelovani disk. prostoru (kvoty); udrzba filesystemu - fsck, zalohovani;
    - sledovani zatizeni systemu, provoznich hlaseni (syslogd); konfigurace periodicky provadenych akci (cron)
    - sprava sit. sluzeb (inetd, sendmail, ftpd, httpd), zajisteni bezpecnosti systemu
    - root - muze vsechno -- prilis moc - reseni: sudo, RoleBasedAccessControl (Solaris)