Hackathon SO

Hackathon-ul presupune rezolvarea unui task propus de către echipa de Sisteme de Operare, din materia parcursă în cadrul cursului si laboratoarelor de SO. Challenge-ul este propus spre a fi rezolvat de către o echipă de 2 studenți înscriși la cursul de Sisteme de Operare. Primele 3 cele mai bune implementări vor fi recompensate cu premii. În timpul desfășurării hackathon-ului, echipele vor primi live support din partea echipei de SO.

Obiective

  • Colaborare, lucrul in echipa
  • Dezvoltare cu suport din partea echipei SO
  • Dezvoltare a unei aplicați portabile

Data

Sâmbătă, 7 mai 2022, în intervalul 9:00 - 17:30.

Locație

Hackathonul se desfășoară în mod hibrid. O echipă poate participa fie fizic (la facultate), fie online (pe MS Teams).

  • Fizic (EG405, sala BitdefenderPR706)
  • Online (Microsoft Teams)

Precondiții

  1. La acest hackathon vor putea participa studenții înscriși la cursul de SO în anul universitar 2021-2022;
  2. Participanții vor forma echipe de câte două persoane;
  3. Participanții vor lucra pe propriile sisteme.

Înscriere

Echipele participante se vor putea înscrie la hackathon prin completarea formularului de aici până pe data de 4 mai 2022, ora 14:00. Se vor alege maxim 20 de echipe.

Regulament

  1. Codul versionat trebuie să fie adăugat într-un repository privat folosind platforma GitLab a facultății. Faceți un repo privat în care adăugați asistenții supraveghetori și un ReadMe cu componența echipei.
  2. Codul trebuie să fie portabil (să fie dezvolat pentru platformele Linux și Windows).
  3. Codul trebuie să treacă un set de teste puse la dispoziție de către echipa de SO.

Premii

Fiecare membru al echipelor câștigătoare va fi premiat, în funcție de locul obținut:

  1. Premiul 1: Monitor
  2. Premiul 2: Smartwatch
  3. Premiul 3: Dronă

Echivalare

Toate echipele participante sunt eligibile de echivalarea unui punct din notele pentru temă în cadrul materiei Sisteme de Operare (în funcție de complexitatea implementării și stadiului proiectului dezvoltat în timpul hackathonului).

Anunțare câștigători

Echipele câștigătoare vor fi anunțate marți, 10 mai 2022.

Dezvoltarea aplicației

Dezvoltarea trebuie făcută exclusiv pe mașinile virtuale SO.

Nu rulați testele local (pe calculatoarele voastre sau în mașinile voastre virtuale). Pot să apară diferențe între local și VM-uri, iar, pentru corectare, noi vom considera doar mașinile virtuale de la SO.

In-memory cacher

O soluție de in-memory cacher este, în general, un serviciu care permite stocarea (în memoria RAM care acționează ca un cache) a unor perechi key:value pentru a facilita modul de lucru al altor servicii. Cele mai cunoscute soluții de in-memory cache sunt Memcached și Redis. Principalul avantaj al folosirii unei astfel de aplicații este optimizarea păstrării și prelucrării datelor de care au nevoie serviciile, ușurând, în acest mod, încărcarea serviciilor.

Aceste aplicații sunt folosite, în special, în cadrul unei infrastructuri distribuite. Soluția de in-memory caching are rolul unui server, iar serviciile care o folosesc acționează ca niște clienți. Clienții fac subscribe către soluția de in-memory caching și comunică, de obicei, prin intermediul sockeților pentru a transmite datele care trebuie ținute în cache. Există numeroase companii și produse care folosesc soluții de in-memory caching. Găsiți aici exemple de companii care folosesc Redis și aici companii care folosesc memcached.

În cadrul hackathonului de la SO, ne propunem să implementăm o astfel de soluție de in-memory caching, însă ne vom axa doar pe partea de stocare și prelucrare de loguri. ElasticSearch și rsyslog sunt utilitare dedicate, folosite în industrie, pentru prelucrarea rapidă a liniilor de log și stocarea lor în mod optim.

Pentru a asigura buna funcționare a unui serviciu, existența logurilor este crucială. Pe baza acestora, putem verifica dacă totul funcționează corespunzător și putem să facem debugging atunci când ceva nu funcționează în modul așteptat. Toate serviciile oferă posibilitatea de logare de date (vezi directorul /var/log în Linux și utilitarul EventViewer în Windows). Pentru a ușura managementul acestor date de logging și a nu încărca serviciul principal pentru operații de stocare, scriere, prelucrare, dorim să implementăm o aplicație care face acest lucru pentru noi.

Astfel, folosind conceptele învățate în cadrul cursurilor și laboratoarelor de Sisteme de Operare (thread-uri, sincronizare, lucru cu fișierele, lucru cu memoria), dorim să implementăm un in-memory cacher, numit liblmc (liblogmemcache), pe modelul oferit de Redis și Memcached. Aplicația noastră va funcționa ca o bibliotecă expusă clienților (clienții au acces la funcțiile ce trebuie apelate) și un proces daemon care reprezintă serverul nostru care acceptă conexiuni de la clienți, salvează logurile în memorie, respectiv în fișiere de log, generează statistici sau oferă înapoi anumite linii de log.

Enunț (LogMemCache)

Realizați o implementare minimală a unui serviciu cross-platform de in-memory cache care reține în memorie (RAM) logurile provenite de la diferite servicii din sistem. Proiectul va funcționa ca o arhitectură client-server, prin care:

  • un client (serviciu de sistem) se conectează la server (serviciul numit lmcd)
  • clientul trimite mai multe comenzi către server:
    • connect - Conectarea clientului la serviciul de in-memory caching
    • subscribe - Același lucru ca opțiunea de connect (deoarece avem disconnect și unsubscribe, subscribe are rolul de a adăuga uniformitate în lista de comenzi);
    • add log - Adăugarea unei linii de log în cache folosind lmcd;
    • flush - Forțarea scrierii log-urilor într-un fișier de log per proces/serviciu conectat. Datele se salvează într-un fișier numit <logfile_path>/<service_name>.log (ex: logs_logmemcache/client1.log).
    • getlogs [t1 [t2]] - Extragerea (din memorie) a log-urilor dintre timestamp-ul t1 și timestamp-ul t2. Dacă t2 nu este specificat, atunci se extrag toate datele începând cu timestamp-ul t1. Dacă nici t1 și nici t2 nu sunt specificate, se extrag toate log-urile din memorie.
    • disconnect - închiderea conexiunii curente.
    • unsubscribe - dealocarea din memorie a datelor despre client și închiderea conexiunii curente.

Precizări/recomandări pentru implementare

  • Înainte de a începe implementarea aplicației, este recomandată recapitularea noțiunilor și conceptelor specifice dobândite până acum în cadrul cursului și laboratoarelor (1-9) de Sisteme de Operare:
    • Fișiere
    • Procese și Threaduri
    • Sincronizare
    • Memorie virtuală
    • Mapare de fișiere în spațiul de adresă al procesului
    • Semnale, întreruperi și timere
  • Dimensiunea unui log ținut în memorie este constantă (LINE_SIZE). Astfel, o pagină de memorie virtuală poate reține PAGE_SIZE / LINE_SIZE log-uri.
  • Pentru lucrul cu fișiere, memorie virtuală, thread-uri, procese, folosiți funcții POSIX pentru Linux, respectiv, funcții Win32 pentru Windows.
  • Aplicația trebuie să fie portabilă (același cod trebuie să compileze și sub Windows și sub Linux, folosind fișierele Makefile furnizate). Veți împărți codul în cod independent de platformă și cod dependent de platformă.
  • Se pot folosi funcții de lucru cu fișiere din libc doar pentru afișarea la stdout/stderr a diverselor mesaje (printf, fprintf etc.) sau formatare de șiruri (sprintf, snprintf etc.). Pentru lucrul cu fișierele de log sau alte fișiere auxiliare, se vor folosi funcțiile POSIX/Win32.
  • Pentru lucru cu memoria se poate folosi malloc doar pentru array-uri, stringuri, structuri interne. Pentru partea de caching, trebuie folosite funcțiile POSIX/Win32 de lucru cu memoria.

Structura proiectului

Proiectul conține două elemente principale:

  • liblmc - O bibliotecă prin care se expun către posibilii clienți funcțiile suportate de către serviciu: conectare, subscribe, adăugare de log-uri, forțare a scrierii datelor pe disk. Pentru a folosi aplicația de in-memory caching, un client trebuie să fie folosească biblioteca liblmc și să apeleze funcțiile expuse. Un exemplu în pseudocod de implementare a clientului ar putea fi următorul:
    	struct lmc client;
    	lmc_connect(&client);
    	lmc_add_log(client, “add this line”);
    	<...>
    	lmc_disconnect(client);
  • lmcd - Un serviciu care primește comenzi de la clienți și implementează logica de in-memory cacher.

Proiectul necesită implementarea unei părți comune (în general, lucrul cu libc, operații uzuale), cât și a unei părți dependente de sistemul de operare (unde sunt implementate apeluri ce țin de standardul POSIX pe Linux, respectiv, Win32 pe Windows). Componentele dependente de sistemul de operare trebuie să fie reduse la minim.

Pentru funcționalitatea de bază a proiectului (vezi mai jos), vi se furnizează implementarea bibliotecii, fișierele Makefile necesare și un schelet de cod pentru server de la care puteți pleca. De asemenea, aveți și câteva exemple de fișiere client care pot fi folosite pentru testare. În scheletul de cod furnizat, sunt deja realizate operațiile de lucru cu sockeți, precum și funcții wrapper peste send/recv pe care le puteți folosi în comunicare. Pentru funcționalitatea extinsă (bonusuri) pe baza căreia se poate face departajarea între echipele câștigătoare, puteți aduce modificări atât în server, cât și în bibliotecă.

Structura de fișiere

Structura de fișiere prezentată mai jos conține următoarele componente:

  • include - director în care se află toate headerele folosite în proiect
    • include/lmc.h - header care expune funcțiile de bibliotecă; headerul care trebuie inclus de fiecare client pentru a putea folosi biblioteca lmc;
    • include/server.h - header care expune funcțiile dependente de arhitectură folosite de către server
    • include/utils.h - header care expune funcțiile utile/ datele comune folosite de către bibliotecă și server.
  • liblmc - director care contine implementarea pentru bibliotecă.
    • liblmc/lmc.c - fișier sursă cu implementarea funcțiilor de bibliotecă
    • liblmc/{lin,win}/lmc_os.c - fișiere sursă cu apelurile funcțiilor specifice sistemului de operare (POSIX, Win32) folosite de către bibliotecă
    • liblmc/lmc.def - funcțiile expuse de bilbiotecă. Fișierul este necesar la compilarea bibliotecii liblmc.dll pe Windows (nu mai este nevoie să folosim declspec(dllexport) și declspec(import) pentru folosirea bibliotecii partajate).
  • Makefile.{lin,win} - fișiere Makefile pentru Linux, respectiv, pentru Windows. În Linux, compilarea se face folosind utilitarul make din bash. În Windows, compilarea se face folosind utilitarul nmake din PowerShell. Atenție! Pe Linux, biblioteca se numește liblmc.so și executabilul lmcd. Pe Windows, biblioteca se numește liblmc.dll și executabilul se numbește lmcd.exe.
  • server - directorul care conține implementarea serverului.
    • server/server.c - funcționalitatea serverului
    • server/{lin,win}/server_os.c - fișiere sursă cu apelurile funcțiilor specifice sistemului de operare (POSIX, Win32) folosite de către server
  • utils.c - implementarea diverselor funcții utile care pot fi folosite de către server și bibliotecă.
.
|--- include
|   |--- lmc.h
|   |--- server.h
|   |--- utils.h
|--- lmc
|   |--- lin
|        |--- lmc_os.c
|   |--- lmc.c
|   |--- lmc.def
|   |---  win
|        |---  lmc_os.c
|--- Makefile.lin
|--- Makefile.win
|--- server
|   |--- lin
|         |--- server_os.c
|   |---  server.c
|   |--- win
|         |--- server_os.c
|--- utils.c

Funcționalitatea de bază a aplicației

Pentru funcționalitatea de bază a aplicației, trebuie implementate următoarele:

  • Comenzi:
    • connect - logica de conectare a unui client, păstrarea numelui clientului, precum și inițializarea structurilor interne.
    • add - adăugarea unei linii de log primite de la un client la cache-ul specific al clientului. O linie de log trimisă de la client către server are dimensiunea maximă LINE_SIZE și conține timestamp-ul (de la client) concatenat cu linia de log. Timestamp-ul are un format predefinit (TIME_FORMAT) și o lungime fixă (TIME_SIZE).
    • flush - scrierea logurilor pe disc. Datele se salvează într-un fișier numit <logfile_path>/<service_name>.log (ex: logs_lmc/client1.log).
    • stats - obținerea unor statistici despre numărul de pagini și memoria utilizată de un client. Formatul statisticilor este dat de către șirul STATS_FORMAT.
    • getlogs - obținerea tuturor log-urilor unui client (funcționalitatea de bază nu tratează cazul în care apar timestamp-uri. Serverul trimite către client un mesaj cu numărul de loguri, urmând ca să trimită, pe rând, fiecare log.
    • unsubscribe - eliberarea datelor despre un client
    • disconnect - închiderea sesiunii curente a unui client.
  • Paralelizarea clienților - interacțiunea cu clienții trebuie realizată în paralel. Rămâne la latitudinea voastră dacă folosiți procese sau fire de execuție.
  • Sincronizarea accesului la datele comune.

Funcționalități extra ale aplicației

Pentru departajare, se pot implementa următoarele funcționalități extra (puteți alege orice funcționalitate doriți voi din listă sau propune funcționalități noi, cu cât mai multe/de impact, cu atât mai bine):

  • închidere controlată și corectă a serverului - interceptarea unui semnal (generat de Ctrl-C) - închiderea tuturor conexiunilor active, dealocarea datelor etc.
  • flush automat la fiecare FLUSH_TIME minute. Pentru aceasta, trebuie să înregistrați câte un timer care generează o întrerupere la FLUSH_TIME minute și un handler de tratare a întreruperii care face flush pe disk a datelor fiecărui client.
  • fișier de configurare a server-ului - la inițializare, serverul citește un fișier de configurare, numit “lmc.config” din care extrage diverse informații: FLUSH_TIME, LINE_SIZE, directorul default de scriere a logurilor, numărul default de clienți, număr maxim de loguri ce pot fi stocate per proces etc. Atenție! Dacă sunt modificați anumiți parametrii care sunt folosiți de către bibliotecă (precum LINE_SIZE), trebuie anunțat și clientul de modificări.
  • folosire operații de multiplexare a conexiunilor pentru a schimba modul în care serviciul acceptă conexiuni.
  • diverse optimizări aduse codului din server: implementarea unei liste înlănțuite/hashmap pentru a reține clienții.
  • optimizare de spațiu: permiterea liniilor de log de lungimi diferite.
  • adăugarea opțiunii [t1 [t2]] în cazul comenzii getlogs.
  • adăugarea unui mecanism de autentificare: o implementare minimală de nume client și parolă pe care un client trebuie să le trimită la connect. Complexitatea rămâne la latitudinea voastră: date păstrate în clar/date criptate/utilizare baze de date etc.
  • logging la nivel de server: fișier cu log-uri despre clienții conectați/deconectați/unsubscribed; extragere de statistici despre server (număr clienți, memorie ocupată în total, număr total de log-uri, număr de log-uri și memorie per client etc.) la un interval dat (pentru aceasta, se va folosi un alt timer; la callback, se extrag datele și se pun în log).
  • posibilitate de snapshot la un anumit interval a datelor serverului (pentru asigurarea restore-ului în cazul în care crapă procesul). Un exemplu de astfel de funcționalitate este oferit de Redis, care pentru a face snapshot, folosește apelul fork() pentru a face snapshot memoriei procesului server.
  • Implementarea bibliotecii într-o manieră REST API pentru a putea fi folosită și de către alți clienți.
  • Dacă la prima conectare a unui serviciu (client) există un fișier vechi de log: <logfile_path>/<service_name>.log (ex: logs_lmc/client1.log), acesta se redenumește (variante posibile: se adaugă un timestamp sau un număr de ordine la fișierul vechi).

Pentru fiecare funcționalitate extra, notați într-un fișier README (sumar) cum ați implementat și testat funcționalitatea respectivă Deoarece este posibil ca să rămâneți fără timp la dispoziție pentru funcționalitățile extra, se acceptă ca acestea să fie implementate doar pentru o singură platformă, însă acest aspect se va reflecta în punctajul final.

Checker

Pentru simplificarea testării, aveți la dispoziție un checker care reprezintă o colecție de clienți (teste) pe care să îi folosiți atunci când implementați codul server-ului.

Pornirea server-ului pentru testare, cât și verificarea informațiilor afișate de server trebuie realizate de voi.

Nu uitați să porniți serverul (lmcd / lmcd.exe) înainte să rulați checkerul.

Deoarece testele (în general) nu realizează golirea datelor păstrate de server, este probabil ca dacă porniți același test de mai multe ori fără să opriți serverul să vedeți date adăugate de rulările anterioare ale clientului (ale unui test). Din acest motiv, recomandarea este să opriți serverul și să ștergeți directorul de log-uri înainte de rularea unui test.

În directorul cu checker-ul aveți sursa checker.c și două fișiere Makefile (Makefile.lin și Makefile.win).Pentru compilarea și folosirea checker-ului, puteți folosi următoarele comenzi (presupunând că realizați dezvoltarea în directorul ../skel relativ la directorul în care se află checker-ul):

user@Linux hackathon/checker$ make -f Makefile.lin SRCDIR=../skel
user@Linux hackathon/checker$ ./checker [TAG|N]
user@Windows hackathon/checker$ nmake /f Makefile.win "SRCDIR=..\skel"
user@Windows hackathon/checker$ .\checker.exe [TAG|N]

Pentru selectarea unui test, argumentul transmis poate avea una dintre următoarele valori:

  • TAG - tag-ul unui test, din lista de teste din checker.c. De exemplu basic-1 va porni primul test, op-3 va porni al șaptelea test.
  • N - indexul (one-based) al unui test din lista de teste din checker.c. De exemplu 1 va porni primul test, 7 va porni al șaptelea test.

În lipsa unui argument, se consideră în mod implicit că este folosit tag-ul “all” care va porni toate testele pe rând.

Departajare concurs

Pentru departajarea în cadrul concursului, vor fi luate în considerare:

  • funcționalitățile de bază, inclusiv portabilitatea soluției
  • Implementarea de extra funcționalități (fiecare extra funcționalitate va aduce puncte în plus echipei, în funcție de complexitate și stadiul implementării).
  • folosirea git
  • extensibilitatea codului
  • coding style-ul

Submisie

Pentru a submite implementarea spre a fi analizată pentru concurs, trebuie să încărcați o arhivă cu soluția (toată structura de fișiere pe care ați folosit-o, fișierele Makefile și testele adiționale) aici. În arhivă trebuie să adăugați un fișier README cu componența echipei și cu funcționalitățile extra aduse implementării, precum și modul de testare a acestora.

În fișierul README trebuie adăugat link către repository-ul privat în care ați lucrat.

so/teme/hackathon.txt · Last modified: 2022/05/06 22:35 by maria.mihailescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0