# Řešené příklady - kolona, thready, UDP server

<{ForumPost(poster="Jenda_", timestamp=2016-02-02 00:43:15)}>
Ahoj,  
v rámci přípravy jsem si vypracoval pár příkladů podobných minulým zkouškám. Upozorňuji, že jsem UNIXové API nikdy moc nepoužíval, takže tam budou různé věci blbě.  
  
Budete potřebovat nsautil.h a jcc z [blobutils](https://brmlab.cz/user/jenda/blobutils)  
  
Zdroje: [http://mff.devnull.cz/pvu/common/zkousk ... ousek.html](http://mff.devnull.cz/pvu/common/zkousky/priklady_ze_zkousek.html) a toto fórum.  
  
**Příklad: Nasimulujte shellovou kolonu cat /proc/cpuinfo | grep model | tr a-z A-Z | sed -r "s/(A|E|I|O|U)/_/g"**

    #include <nsautil.h>
    
    int main(int argc, char **argv) {
    pid_t child;int filedes[2];int pipeprev = -1;
    pipe(filedes);child = fork();if(child == 0) {dup2(filedes[1], STDOUT_FILENO);close(filedes[1]);execlp("cat", "cat", "/proc/cpuinfo", NULL);}close(filedes[1]);close(STDIN_FILENO);pipeprev = filedes[0];
    pipe(filedes);child = fork();if(child == 0) {dup2(pipeprev, STDIN_FILENO);dup2(filedes[1], STDOUT_FILENO);close(filedes[1]);execlp("grep", "grep", "model", NULL);}close(filedes[1]);close(pipeprev);pipeprev = filedes[0];
    pipe(filedes);child = fork();if(child == 0) {dup2(pipeprev, STDIN_FILENO);dup2(filedes[1], STDOUT_FILENO);close(filedes[1]);execlp("tr", "tr", "a-z", "A-Z", NULL);}close(filedes[1]);close(pipeprev);
    child = fork();if(child == 0) {dup2(filedes[0], STDIN_FILENO);//close(filedes[0]);execlp("sed", "sed", "-r", "s/(A|E|I|O|U)/_/g", NULL);}close(filedes[0]);
    return 0;
    }
    

(jako cvičení by bylo vhodné parametrizovat to příkazy v poli + execvp)  
  
**Příklad: Hlavní vlákno vytvoří počet vláken zadaný jako parametr příkazové řádky. Každé vlákno simuluje činnost pomocí sleep() na náhodnou dobu, 1 až 10 sekund.** Po vykonání činnosti, tj. po ukočení funkce sleep(), vlákno zahlásí hlavnímu vláknu, že skončilo. Hlavní vlákno okamžitě vytvoří další vlákno.  
  
Cílem je tedy udržovat stále stejný počet vláken v běhu. Každé vlákno má navíc svoje pořadové číslo, při vytvoření a zániku vlákna se vypíše na termínál hlášení o této skutečnosti, s použitím pořadového čísla.  
  
Pro synchronizaci použijte mutexy a podmínkové proměnné. Aktivní čekání je nepřípustné. Velký důraz je kladen na korektnost použití synchronizačních primitiv.  
  
Usage: ./pthreads 10

    #include <nsautil.h>
    #include <pthread.h>
    #include <poll.h>
    
    pthread_mutex_t mujmutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t mojecond = PTHREAD_COND_INITIALIZER;
    
    int numt;
    
    int * r;
    
    typedef struct tr {int tid;int toffs;char * message;pthread_t thr;
    } tr;
    
    tr * t;
    
    void * mujthread(void *ptr) {tr * ctx = (tr*)ptr;printf("Thread %i(%i) is running
    ", ctx->toffs, ctx->tid);
    poll(NULL, 0, random() % 10000);
    // for(volatile int i = 0; i<100*1000*1000; i++) {}
    printf("Thread %i(%i) says: %s
    ", ctx->toffs, ctx->tid, ctx->message);
    poll(NULL, 0, random() % 10000);
    printf("Thread %i(%i) is finishing
    ", ctx->toffs, ctx->tid);
    r[ctx->tid] = 0;
    pthread_cond_signal(&mojecond);
    return(NULL);
    }
    
    int main(int argc, char ** argv) {
    numt = atoi(argv[1]);
    r = malloc(sizeof(int) * numt);t = malloc(sizeof(tr) * numt);
    
    int iter = 0;int toffs = 0;while (1) {pthread_mutex_lock(&mujmutex);iter++;for (int i = 0; i<numt; i++) {if (r[i] == 0) {pthread_join(t[i].thr, NULL);pthread_t thr;t[i].message = (char*)malloc(100);sprintf(t[i].message, "This is iter %i", iter);t[i].tid = i;t[i].toffs = toffs;int ret = pthread_create(&thr, NULL, &mujthread, (void*) (t + i));t[i].thr = thr;toffs++;if(ret == 0) {r[i] = 1;} else {printf("Cannot create thread!
    ");}}}pthread_cond_wait(&mojecond, &mujmutex);pthread_mutex_unlock(&mujmutex);}
    exit(EXIT_SUCCESS);
    }
    

**Příklad: datagram server**, IP agnostic. Kvůli bugu v glibc ([https://sourceware.org/bugzilla/show_bug.cgi?id=9981](https://sourceware.org/bugzilla/show_bug.cgi?id=9981)) to není úplně agnostic, je tam ten if(rp->ai_family == AF_INET).  
  
Usage: $ ./server 6666  
$ date | nc -u :: 6666  
Vrátí datum s čísly přepsanými křížky.

    #include <sys/types.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <arpa/inet.h>
    
    #define BUF_SIZE 500
    
    /* Zkopírováno z manpage getaddrinfo z glibc a z https://stackoverflow.com/questions/5956516/getaddrinfo-and-ipv6 */
    
    int main(int argc, char *argv[]) {
    struct addrinfo hints;struct addrinfo *result, *rp;int sfd, s;struct sockaddr_storage peer_addr;socklen_t peer_addr_len;ssize_t nread;char buf[BUF_SIZE];
    if (argc != 2) {fprintf(stderr, "Usage: %s port
    ", argv[0]);exit(EXIT_FAILURE);}
    memset(&hints, 0, sizeof(struct addrinfo));hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */hints.ai_socktype = SOCK_DGRAM;hints.ai_flags = AI_PASSIVE;hints.ai_protocol = 0;          /* Any protocol */hints.ai_canonname = NULL;hints.ai_addr = NULL;hints.ai_next = NULL;
    s = getaddrinfo(NULL, argv[1], &hints, &result);//s = getaddrinfo("localhost", argv[1], &hints, &result);if (s != 0) {fprintf(stderr, "getaddrinfo: %s
    ", gai_strerror(s));exit(EXIT_FAILURE);}
    /* getaddrinfo() returns a list of address structures.   Try each address until we successfully bind(2).   If socket(2) (or bind(2)) fails, we (close the socket   and) try the next address. */
    for (rp = result; rp != NULL; rp = rp->ai_next) {
    if(rp->ai_family == AF_INET6) {char * buf = malloc(INET6_ADDRSTRLEN);struct sockaddr_in* saddr = (struct sockaddr_in*)rp->ai_addr;if(inet_ntop(AF_INET6, &(saddr->sin_addr), buf, INET6_ADDRSTRLEN)) {printf("Trying (v6) %s
    ", buf);} else {printf("ntop err
    ");}} else if (rp->ai_family == AF_INET) {char * buf = malloc(INET_ADDRSTRLEN);struct sockaddr_in* saddr = (struct sockaddr_in*)rp->ai_addr;if(inet_ntop(AF_INET, &(saddr->sin_addr), buf, INET_ADDRSTRLEN)) {printf("Trying (v4) %s
    ", buf);} else {printf("ntop err
    ");}}
    if(rp->ai_family == AF_INET) {continue;}
    sfd = socket(rp->ai_family, rp->ai_socktype,rp->ai_protocol);if (sfd == -1) {continue;}
    if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) {printf(" ...bound OK
    ");break;}
    close(sfd);}
    if (rp == NULL) {               /* No address succeeded */fprintf(stderr, "Could not bind
    ");exit(EXIT_FAILURE);}
    freeaddrinfo(result);           /* No longer needed */
    /* Read datagrams and echo them back to sender */
    for (;;) {peer_addr_len = sizeof(struct sockaddr_storage);nread = recvfrom(sfd, buf, BUF_SIZE, 0,(struct sockaddr *) &peer_addr, &peer_addr_len);if (nread == -1)continue;               /* Ignore failed request */
    char host[NI_MAXHOST], service[NI_MAXSERV];
    s = getnameinfo((struct sockaddr *) &peer_addr,peer_addr_len, host, NI_MAXHOST,service, NI_MAXSERV, NI_NUMERICSERV);if (s == 0)printf("Received %ld bytes from %s:%s
    ",(long) nread, host, service);elsefprintf(stderr, "getnameinfo: %s
    ", gai_strerror(s));
    for(int i = 0; i<nread; i++) {if(buf[i] >= '0' &&buf[i] <= '9') {buf[i] = '#';}}
    if (sendto(sfd, buf, nread, 0,(struct sockaddr *) &peer_addr,peer_addr_len) != nread)fprintf(stderr, "Error sending response
    ");}
    }
    


<{/ForumPost}>

<{ForumPost(poster="Jenda_", timestamp=2016-02-02 01:07:43)}>
Poznámky k otázkám z webu:  
  
> Napište sekvenci příkazů pro vytvoření dynamické knihovny a programu který na ní závisí (ideálně na Linuxu a Solarisu).   
cc -G -o libadd.so add.c (gcc -shared)  
cc -L. -ladd -R. main.c (gcc -Xlinker -R. nebo LD_LIBRARY_PATH=.)  
  
> Proč není dobrý nápad používat proměnnou LD_LIBRARY_PATH pro něco jiného než ladění programů ?  
Obcházení problémů nečistou metodou. A protože se dědí.  
  
> Vysvětlete význam parametrů funkce main() a proměnné environ.  
main může mít 3. parametr a to bude stejně jako extern char** environ  
  
> Jak se zpracovávají argumenty programu pomocí funkce getopt() ?  
getopt(int argc, char * argv\[], char* optstring)  
 + extern int optind  
  
proces má kontext, vlákna jsou uvnitř procesu, program je spustitelný soubor na disku, např. ve formátu elf  
paměť se přiděluje procesům  
čas/procesor se přiděluje vláknům (pokud to není library-level threading)  
  
syscall: vrací signed: -1 chyba, >=0 OK, vrací pointer: NULL chyba; nastavuje se errno  
když čekám -1, musím nastavit errno na 0 a pak se kouknout  
pthreads vracé 0 OK, >0 fail  
  
/bin/ping má ruid=jenda, euid=root  
  
testují se v tomto pořadí práva roota, vlastníka a skupiny, takže "---rwxrwx jenda jenda" nepřečtu, ale můžu chmodnout a pak to přečtu.  
  
svazek = instance FS  
  
block devices se bufferují  
  
files: special, regular (hardlink), dir, fifo, socket, symlink  
  
s5: boot block, superblock, inody, data - taky ve stromech, omezená jména a velikosti souborů, sloooow  
  
ffs/ufs: cylgrupy (ne že by to v době LBA pomohlo) s kopiemi superbloku, bitmapy, jména 255, ufs2: dirhash cache  
  
obrázek str. 80 - tree traversal  
  
přístup k souborům: vždy open(), nebo creat(); vı́ce deskriptorů může sdı́let jedno otevřenı́ souboru (mód čtenı́/zápis, ukazatel pozice)  
creat - bacha na umask  
  
> Popište paměťový prostor procesu v uživatelském režimu a v režimu jádra.  
text - data - bss - - stack - oblast user (nepřístupná) (ne vždy v tomto pořadí)  
kernel text - data a bss jádra, uvnitř struktura user, pod tím stack jádra  
  
> Nakreslete a popište stavový diagram procesu.   
obrázek 123  
  
> Popište základy mechanismu plánování procesů. Jaké jsou prioritní třídy ?   
preemptivní (interrupty timerem)  
timeshared ("normální"), ladí se velikost kvanta, system, realtime - definovaná časová kvanta  
  
FIXME: signal handling u vláken  
  
> Jaká pravidla existují pro psaní signal handlerů ? Jakým způsobem se vyrovnat s omezeními při jejich implementaci ?  
reentrantní; globální přepínač  
  
155: alarm signál  
  
> Jak je možné vytvořit globální proměnnou privátní pro jedno vlákno ?  
pthread_key_create vyrobí pointr, který si každé vlákno může nastavit jinam.  
  
> Které atributy jsou společné pro proces, které jsou privátní pro každé vlákno ?  
Soukromé: IP, stack, thread ID, plánovacı́ priorita a politika, errno, klíče  
  
> Jak fungují destruktory klíčovaných hodnot a zásobník úklidových handlerů ?  
Klíčové hodnotě prostě předám funkci.  
pthread_cleanup_push a pop  
  
> Co se stane když jedno vlákno zavolá fork() ? Jaký problém při tom může vzniknout a jak ho lze řešit ?  
Neuvolněná paměť, navždy zamčené mutexy a deadlocky. Je dobré v prefork handleru vše zamknout (tedy počkat, až to ostatní vlákna uvolní) a v postfork odemknout.  
  
Signály: masknout a jedno vlákno se věnuje jen signálům přes sigwait.  
  
> Uveďte nástroje pro synchronizaci vláken.  
Mutexy, podmínkové proměnné, RW zámky  
  
> Popište způsob použití podmínkových proměnných, důvody proč se používají právě daným způsobem a co by se mohlo stát kdyby tomu bylo jinak.  
Zamknu mutex, otestuji neplatnost podmínky, pthread_cond_wait. Jinak bych se mohl zaseknout - probudím se opravdu jen při změně, není tam fronta jako u signálů.  
Probuzení nemusí nutně znamenat, že podmínka platí.  
typicky je dobré volat wait ve while(!podminka)  
  
231: použití  
  
> Popište zamykání souborů pomocí fcntl().  
dokonce se dá zamykat rozsah  
advisory a mandatory (ne vždy je implementován) locking  
při zavření se všechny moje zámky uvolní - a to i když to třeba otevřela a zavřela subrutina  
  
> Vysvětlete zamykání pomocí lock souborů.  
open(rdwr | creat | excl), vrátí -1 při souběhu  
unlock: unlink  
  
> Popište semafory, implementované v UNIX System V IPC. Jakými příkazy lze zobrazit jejich výpis a stav ?  
pracuje atomicky nad polem! semaforů  
semctl(semnum), GETVAL, GETNCNT (kolik drží procesů)  
použití: semop, operace snížit, zvýšit, čekat na 0, a operace jsou v sembuf, kde je vždycky pro který semafor jakou operaci  
jo a než s tím vůbec můžu začít pracovat, tak si samozřejmě musím pořídit IPC klíč přes ftok()  
  
> Popište činnost serveru a klienta (posloupnost systémových volání) pro spojované síťové služby.  
server: socket, bind, listen, accept, read/write, close  
klient: socket, connect, read/write, close; bind pokud by chtěl extra odchozí port (může vyžadovat vyšší oprávnění)  
  
nespojované: socket, bind, sendto/recvfrom  
  
> Co je to soket? Jaké jsou varianty adresace soketů (v rámci jednoho stroje a po síti) ?  
AF_UNIX, AF_INET6; UNIX má filename  
  
> Jaké funkce slouží pro převod mezi číselnými a symbolickými jmény protokolů, portů a IP adres?  
pton, ntop (network, presentable format)  
getprotobyname, getservbyname, pozor, proto je IP protokol, serv je HTTP/SSH/IMAP  
  
> Popište standardní funkce pro převod jména na adresu a naopak a způsob jejich použití pro klient a server.  
getaddrinfo, getnameinfo  
bývaly gethostbyname a gethostbyaddr  
  
> Jak lze čekat na příchod dat z několika deskriptorů souborů zároveň?  
select - čtecí, zapisovací, chybové  
poll - čeká na událost a nastaví pro fd události ("lze číst", "lze zapisovat"…)
<{/ForumPost}>

