Documente Academic
Documente Profesional
Documente Cultură
Lucrarea 5
LUCRAREA 5
VIRTUALIZARE.
Virtualizarea este tehnologia ce permite crearea unei abstractizări a resurselor(hardware sau software) și
expunerea acestor resurse către alte resurse (sisteme de operare, aplicații). În contextul sistemelor de operare
virtualizarea presupune existența unui nivel ce poate expune resursele hardware disponibile către unui sau mai
multe sisteme de operare. Astfel prin intermediul unei mașini virtuale (Virtual Machine VM) se pot rula mai multe
sisteme de operare virtuale pe aceeași mașină fizică (host). Virtualizarea vine astfel cu o serie de avantaje precum:
- Scalabilitate: resursele hardware se pot aloca sistemelor de operare virtuale în funcție de cerințele de
performanță, sau mașinile virtuale se pot reloca pe un alt hardware
- Siguranță: se pot crea rapid copii de siguranță, restaura copiile de siguranță, mașinile virtuale pot migra
rapid pe alte sisteme hardware în caz de defecțiune
- Administrare facilă: prin intermediul aplicațiilor de management se pot administra cu ușurință mașini
virtuale multiple într-un mod centralizat, inclusiv se pot realiza rapid operații (re)alocare de resurse în
funcție de cerințe, realiza copii de siguranță, migra mașini virtuale pe alte sisteme, clona mașini virtuale,
etc
1. INSTALAREA HYPERV
Pentru realizarea acestei lucrări vom utiliza sistemul de operare Windows 10 și caracteristica acestuia pentru
gestionarea mașinilor virtuale HyperV.
HyperV reprezintă soluția Microsoft de virtualizare. Acesta a fost disponibilă începând cu sistemul de operare
Windows Server 2008 și Windows 8. HyperV este un hipervizor (hypervizor) ce poate rula/virtualiza sisteme de
operare pe platforme x86 și x64. Acesta rulează pe sisteme Windows (desktop sau server) dar poate gestiona
mașini virtuale eterogene [3]: sisteme Windows (sisteme desktop XP, 7, Vista, 8, 10 sau sisteme server 2003,
2008, 2012, 2016, 2019), sisteme Linux (Red Hat, Ubuntu, Debian, CentOS, FreeBSD, SUSE, Oracle Linux).
Notă – se va avea în vedere faptul că atât kit-ul de instalare al sistemului de operare Windows 10 (fișierul ISO)
cât și mașina virtuală aferentă unei instanțe de Windows 10 (fișierul VHD) ocupă spațiu semnificativ pe disk.
Astfel înainte de parcurgerea pașilor din lucrare să vă asigurați că aveți spațiu disponibil pe disk (minim 20GB).
Pagina 1
Laborator Sisteme de Operare. Lucrarea 5
- activare ->
Fig. 1. Verificarea suportului pentru virtualizare
Dacă nu este activată virtualizarea, se verifică în BIOS și se activează (Enable Virtualization Technology -
https://www.technorms.com/8208/check-if-processor-supports-virtualization). Este posibil ca după activarea
virtualizării în BIOS să fie necesară închiderea și repornirea PC-ului, restartul nefiind suficient.
Notă – activarea virtualizării trebuie realizată înaintea instalării HyperV. Dacă HyperV este deja instalat și nu
funcționează, acesta se dezinstalează, se activează virtualizarea și se reinstalează HyperV
Pentru instalarea HyperV se verifică cerințele necesare [2]. Pentru activarea caracteristicii HyperV, dacă nu este
deja activată, se accesează Control Panel -> Turn Windows features on or off [4]. Este necesară repornirea
calculatorului după activarea HyperV:
Pagina 2
Laborator Sisteme de Operare. Lucrarea 5
Pentru a instala un sistem de operare într-o mașină virtuală este necesar să aveti un kit de instalare CD/DVD sau
format ISO. Dacă nu aveți deja un kit de instalare, se poate folosi instrumentul de creare suporturi de instalare
de la Microsoft la adresa https://www.microsoft.com/ro-ro/software-download/windows10:
Pagina 3
Laborator Sisteme de Operare. Lucrarea 5
Pagina 4
Laborator Sisteme de Operare. Lucrarea 5
Pagina 5
Laborator Sisteme de Operare. Lucrarea 5
......
Fig. 12. Instalarea sistemului de operare
Se observă proprietățile sistemului de operare virtual (memorie, disk, procesoare), acestea respectă setările de
la crearea mașinii virtuale:
Pagina 6
Laborator Sisteme de Operare. Lucrarea 5
EXERCIȚIUL 1:
Modificați resursele alocate mașinii virtuale (RAM, CPU), reporniți și verificați ulterior în mașina virtuală Windows
resursele utilizate.
Unul din avantajele virtualizării îl reprezintă ușurința lucrului cu mașinile virtuale: backup, restaurare, multiplicare
(clonare), administrare centralizată, alocarea resurselor în mod dinamic. Astfel consola HyperV permite pornirea,
oprirea, backupul, restaurarea, copierea unei mașini virtuale.
De exemplu plecând de la o mașină virtuală gata configurată se pot crea mai multe mașini virtuale identice
(clonare). Din consola de management HyperV opriți mașina virtuala. Exportați mașina:
Pagina 7
Laborator Sisteme de Operare. Lucrarea 5
Notă – se va avea în vedere faptul că operațiile cu mașini virtuale (export/import) durează mai multe minute și
ocupă aprox. 10GB spațiu pe disk.
Pagina 8
Laborator Sisteme de Operare. Lucrarea 5
EXERCIȚIUL 2 (OPȚIONAL):
Repetați pașii de mai sus folosind instrumental de virtualizare Oracle VM VirtualBox [5]. Înainte de instalarea
VirtualBox va trebui dezinstalat HyperV.
BIBLIOGRAFIE
[1]. Introduction to Hyper-V on Windows 10 https://docs.microsoft.com/en-us/virtualization/hyper-v-on-
windows/about/
[2]. Windows 10 Hyper-V System Requirements https://docs.microsoft.com/en-us/virtualization/hyper-v-on-
windows/reference/hyper-v-requirements
[3]. Supported OS https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/about/supported-
guest-os
[4]. Install Hyper-V on Windows 10 https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/quick-
start/enable-hyper-v
[5] Oracle VM VirtualBox https://www.virtualbox.org/
Pagina 9
Laborator Sisteme de Operare. Android
LUCRAREA 6
ANDROID
Android este un sistem de operare open source destinat în principal dispozitivelor mobile cu ecran tactil
(touchscreen) precum telefoane mobile sau tablete [1]. Versiuni speciale de Android sunt destinate și altor tipuri
de dispozitive precum ceasurile (dispozitive wereables), televizoarele, automobilele. De asemenea versiuni de
Android pot rula și pe dispozitive PC, console gaming, camere digitale.
1. ARHITECTURA ANDROID
Arhitectura sistemului Android conține următoarele componente:
- Kernel. Această componentă este bazată pe un nucleu Linux având rolul de a gestiona resursele hardware
și software precum procesorul, memoria ram, spațiul de stocare, procesele, fișierele, resursele,
dispozitivele I/O, conectivitatea.
- Nivelul de abstractizare (Hardware abstraction layer - HAL). Această componentă asigură interfața dintre
dispozitivele hardware produse de diverși producători și nivelele software superioare. HAL
definește/impune interfețe standard între dispozitivele hardware (de nivel scăzut) și cele software (de
nivel mai înalt).
- Serviciile sistem (System Services). Sunt servicii oferite de platforma Android pentru nivelele superioare.
Aceste servicii sunt grupate în două categorii: servicii sistem (search, notification, windows, etc) și servicii
media (camera, play, record, etc).
- Nivelul de legătură pentru comunicarea interproces (Binder Inter-Process Communication - IPC). Acesta
asigură abstractizarea nivelelor inferioare precum serviciile Android și punerea la dispoziție a acestora
pentru nivelul superior (cel de aplicație).
- Nivelul de aplicație. Acest nivel este de obicei expus către dezvoltatori sub forma unui API (Application
Programming Interface) pentru crearea și rularea aplicațiilor Android.
Pagina 1
Laborator Sisteme de Operare. Android
Pagina 2
Laborator Sisteme de Operare. Android
Notă – se vor verifica cerințele de sistem necesare instalării Android Studio. Se va avea în vedere faptul că
Android Studio necesită cel puțin 4GB spațiu disponibil pe disk, iar un emulator specific unui anumit model de
dispozitiv Android necesită 1.5GB.
Pagina 3
Laborator Sisteme de Operare. Android
La crearea unei aplicații Android este obligatoriu să se specifice versiunea minimă de SDK necesară pentru
rularea aplicației. O aplicație dezvoltată pentru o versiune poate rula pe dispozitive care au cel puțin acea
versiune instalată, nu poate rula pe dispozitive având versiune inferioară, dar poate rula pe dispozitive cu
versiuni mai noi (https://developer.android.com/guide/practices/compatibility). Magazinul Google Play poate
restricționa accesul la aplicație în funcție de versiune, dispozitive/funcționalitățile disponibile, configurația
ecranului. Dezvoltatorul unei noi aplicații trebuie să ia o decizie privind versiunea minimă pentru care dezvoltă
o aplicație. Pe de o parte este de dorit ca versiunea utilizată să fie cât mai nouă astfel încât aplicația să
beneficieze de ultimele funcționalități disponibile în platforma Android, dar pe de altă parte este posibil ca un
număr important de utilizatori și/sau dispozitive de pe piață să nu fie actualizate sau să nu aibă disponibile
ultimele versiuni de Android, astfel că aplicația se va adresa unui public relativ restrâns. O posibilă decizie poate
fi aceea de a alege o versiune cat mai mică deci cu o piață cât mai largă, dar fără a renunța la unele facilități
necesare aplicației. De asemenea o altă posibilă soluție este de a realiza versiuni separate ale aplicației, una
care să acopere versiuni mai vechi de Android, cu facilități reduse, alta pentru versiuni mai noi de Android cu
facilități sporite, și astfel cu un efort suplimentar de dezvoltare se poate acoperi optim un număr cât mai mare
și mai diversificat de utilizatori și dispozitive Android.
O aplicație Android poate conține o serie de componente distincte specifice arhitecturii Android. Există patru
tipuri de componente ale unei aplicații Android
(https://developer.android.com/guide/components/fundamentals):
- Activități (activities) pentru interacțiunea cu utilizatorul
- Servicii (Services) pentru rularea de aplicații în bakground
- Broadcast receivers pentru interacțiunea cu evenimente externe
- Furnizori de conținut (Content providers) pentru acces la resurse externe aplicației precum sistemul de
fișiere, baza de date SQLite, resurse WEB.
Fiecare tip de componentă este gestionată diferit de către sistemul Android, având un ciclu de viață bine definit
în funcție de care aplicația este creată, rulează și este distrusă.
În continuare vom exemplifica crearea unei aplicații de tip activitate. O activitate reprezintă un punct de
intrare, o interfață de interacțiune cu utilizatorul. O activitate se implementează sub forma unui ecran
conținând interfața cu utilizatorul, conține controale pentru afișarea de informații, preluarea de date sau
acțiuni de la utilizator.
Se pornește Android Studio și se alege opțiunea de a crea un nou proiect [5]. Se selectează un tip de proiect, în
cazul de față se alege tipul Empty Project. Se specifică numele proiectului, locația, limbajul utilizat și versiunea
minimă de Android SDK:
Pagina 4
Laborator Sisteme de Operare. Android
După ce un proiect a fost creat se va afișa interfața IDE-ului Android Studio și se poate vedea structura
proiectului. Template-ul ales creează un proiect cu o componentă de tip activitate (activity), MainActivity:
Pagina 5
Laborator Sisteme de Operare. Android
La rularea aplicației, Android Studio instalează aplicația în emulator sau pe dispozitivul fizic selectat și o
pornește. În cazul exemplului de mai sus, activitatea principală afișează mesajul Hello World!.
Layoutul interfeței este descris în fișierul activity_main.xml:
Pagina 6
Laborator Sisteme de Operare. Android
modul Design și Blueprint. Se observă în structura ce definește layoutul (Component tree) că interfața conține
un singur control de tip TextView:
Pagina 7
Laborator Sisteme de Operare. Android
Pentru a realiza o acțiune la apăsarea butonului Button, accesați activitatea principală (clasa MainActivity), și
completați cu metoda setText:
public void setText(View view) {
// Do something in response to button
}
Țineți cursorul pe View și selectați opțiunea More actions -> Import Class, sau completați manual fișierul
MainActivity.java și importați clasa View:
Pagina 8
Laborator Sisteme de Operare. Android
BIBLIOGRAFIE
[1]. Sistemul Android https://www.android.com/what-is-android/
[2]. Arhitectura sistemului Android https://source.android.com/devices/architecture
[3]. Android Developer https://developer.android.com/
[4]. Android API reference https://developer.android.com/reference?hl=en
[5]. Create an Android project https://developer.android.com/training/basics/firstapp/creating-project?hl=en
Pagina 9
Laborator Sisteme de Operare. Lucrarea 1
LUCRAREA 1
PROCESE. THREADURI.
Sistemele de operare multitasking sunt bazate pe procese și thread-uri. Sistemele de operare pot controla
funcționarea concurentă a mai multor procese și procesoare. În sistemele Windows, entitatea de alocare a
timpului procesorului (sau procesoarelor) este thread-ul. Fiecare proces conține cel puțin un thread de execuție
(thread-ul principal) și poate crea thread-uri noi, care partajează spațiul de adresă al procesului care le-a creat,
precum și resursele acestuia (fișiere, obiecte de sincronizare). Un proces Windows este identificat atât printr-un
instrument de acces, un handle, care este o intrare în tabela de resurse a sistemului, cât și printr-un identificator
(id sau pid), care este un număr unic, atribuit unui proces, și care îl caracterizează pe toată durata de existență a
acestuia [1].
Lansarea în execuție a unui program executabil creează un proces nou, cu propriul spațiu de adrese, și cu
minimum un thread de execuție (thread-ul principal).
1. CREAREA PROCESELOR
Un proces este creat și lansat în execuție de către sistemul de operare. Fiecare proces execută un anumit cod
conform scopului aplicației respective. De exemplu sistemul de operare Windows poate lansa în execuție
programe conținute în fișiere executabile (.exe). Un astfel de fișier se poate genera în urma editării și compilării
unui cod program cu ajutorul unui mediu de dezvoltare. În continuare vom utiliza mediul de dezvoltare Microsoft
Visual Studio ediția 2015 pentru a edita, compila și genera cod executabil (programe, aplicații).
Se va crea o aplicație mod consolă:
Pagina 1
Laborator Sisteme de Operare. Lucrarea 1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
}
}
}
Funcția membră Main a clasei Program reprezintă punctul de intrare în aplicație, aceasta este prima metodă
care se apelează la rularea programului [2].
Se va completa codul astfel încât să afișeze un mesaj și să aștepte apăsarea unei taste pentru a termina
execuția programului:
...
Console.WriteLine("Hello world!");
Console.ReadLine();
...
Pagina 2
Laborator Sisteme de Operare. Lucrarea 1
Se observă că în acest caz nu se mai deschide o nouă fereastră mod consolă ci mesajele și interacțiunea cu
utilizatorul au loc în această consolă. Pentru rularea aplicației este necesară specificarea căii complete până la
fișierul executabil, nu este suficientă specificarea numelui executabilului decât daca ne aflăm în directorul
acestuia:
Pagina 4
Laborator Sisteme de Operare. Lucrarea 1
using System.Diagnostics;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start master!");
Process.Start(@"E:\Facultate\SO\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.e
xe");
Console.WriteLine("Stop master!");
Console.ReadLine();
}
}
}
EXERCIȚIUL 1:
Rulați aplicația dintr-o fereastra consola Windows.
EXERCIȚIUL 2:
Modificați programul astfel încât să porniți un browser Web instalat pe stația de lucru. Modificați programul
astfel încât să porniți un browser Web care să acceseze o pagină de Internet.
EXERCIȚIUL 3:
Pagina 5
Laborator Sisteme de Operare. Lucrarea 1
Modificați programul astfel încât să deschideți fișierul cod sursă al programului de mai sus în editorul text
Notepad.
EXERCIȚIUL 4:
Modificați programul ConsoleApplication1 din cap. anterior astfel încât să primească un parametru de tip string
pe care sa îl afiseze. Rulați aplicația ConsoleApplication1 cu un parametru oarecare de tip string.
3. CONTROLUL PROCESELOR
Durata de viață și proprietățile proceselor pot fi controlate programatic. Procesele pot fi pornite, se pot
transmite parametri, pot fi oprite prin intermediul clasei Process prezentată în cap. precedent. Aceasta se poate
face printr-un obiect de tip Process obținut la pornirea procesului [3], respectiv printr-un obiect
ProcessStartInfo pentru controlul parametrilor de rulare [4]:
Process p = Process.Start(@"..\ConsoleApplication1.exe");
Pagina 6
Laborator Sisteme de Operare. Lucrarea 1
Atenție – nu opriți procese ale sistemului de operare sau alte procese importante din sistem!
De asemenea procesul poate fi oprit și programatic prin apelul metodei Kill atât timp cât aplicația care l-a creat
încă rulează:
Process p = Process.Start(@"..\ConsoleApplication1.exe");
...
p.Kill();
Procesele pot fi accesate chiar dacă procesul curent nu le-a creat. De exemplu metoda statica GetProcesses
generează lista proceselor ce rulează în sistem:
Process[] localProcesesses = Process.GetProcesses();
foreach(Process p in localProcesesses)
{
Console.WriteLine(string.Format("Process PID {0} is {1}", p.Id, p.ProcessName));
}
Pentru o mai bună vizibilitate sortăm procesele alfabetic după nume, și dacă procesul este ConsoleApplication1
îl putem opri:
Pagina 7
Laborator Sisteme de Operare. Lucrarea 1
ProcessStartInfo startInfo = new
ProcessStartInfo(@"E:\Facultate\SO\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication
1.exe");
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
Process.Start(startInfo);
Atenție – nu opriți procese ale sistemului de operare sau alte procese importante din sistem!
Sistemul oferă acces și la alte proprietăți ale proceselor ca de exemplu memoria utilizată sau timpul de
procesor. Pentru a simula o aplicație care utilizează intens aceste resurse modificați aplicația
ConsoleApplication1 pentru a aloca memorie si a utiliza timpul de procesor pentru accesarea memoriei alocate:
Console.WriteLine("Hello world!");
List<byte[]> list = new List<byte[]>();
for (int i = 0; i < 20; i++)
{
byte[] b = new byte[1024 * 1024 * 100];
for (int j = 0; j < 1024 * 1024 * 100; j++)
b[j] = (byte)(j % 256);
list.Add(b);
Console.WriteLine(string.Format("Running..."));
Thread.Sleep(1000);
}
Console.ReadLine();
Din aplicația master se accesează proprietățile procesului creat, în cazul de mai jos se preia memoria utilizată și
timpul total de procesor:
Console.WriteLine("Start master!");
Process p = Process.Start(@"...\ConsoleApplication1.exe");
Pagina 8
Laborator Sisteme de Operare. Lucrarea 1
Console.WriteLine("Stop master!");
Console.ReadLine();
Notă – pentru utilizarea anumitor metode sau clase este necesară includerea namespace-ului corespunzator.
De exemplu Thread.Sleep() necesită adăugarea namespace-ului System.Threading:
using System.Threading;
..
Thread.Sleep(1000);
EXERCIȚIUL 5:
Modificați programul astfel încât să afișeze memoria utilizată în KB, MB și GB.
EXERCIȚIUL 6:
Modificați programul astfel încât să afișeze și alte proprietăți ale procesului, de exemplu calea către executabil,
prioritatea, etc.
EXERCIȚIUL 7:
Modificați programul astfel încât să afișeze memoria utilizată și timpul total de procesor pentru toate procesele
ce rulează. Sortați descrescător după memoria ocupată.
EXERCIȚIUL 8:
Modificați programul astfel încât să afișeze la fiecare 5 secunde procesul care utilizează cel mai mult procesorul
(se poate folosi clasa PerformanceCounter).
BIBLIOGRAFIE
[1]. PRINCIPIILE CALCULULUI PARALEL, Felicia Ionescu, EDITURA TEHNICĂ, București, 1999
[2]. C# Programming Guide – main function https://docs.microsoft.com/en-us/dotnet/csharp/programming-
guide/main-and-command-args/
Pagina 9
Laborator Sisteme de Operare. Lucrarea 2
LUCRAREA 2
PROCESE. THREADURI.
Sistemele de operare multitasking sunt bazate pe procese și threaduri. Sistemele de operare pot controla
funcționarea concurentă a mai multor procese și procesoare. În sistemele Windows, entitatea de alocare a
timpului procesorului (sau procesoarelor) este threadul. Fiecare proces conține cel puțin un thread de execuție
(threadul principal) și poate crea threaduri noi, care partajează spațiul de adresă al procesului care le-a creat,
precum și resursele acestuia (fișiere, obiecte de sincronizare). Un proces Windows este identificat atât printr-un
instrument de acces, un handle, care este o intrare în tabela de resurse a sistemului, cât și printr-un identificator
(id sau pid), care este un număr unic, atribuit unui proces, și care îl caracterizează pe toată durata de existență a
acestuia [1]. Lansarea în execuție a unui program executabil creează un proces nou, cu propriul spațiu de adrese,
și cu minimum un thread de execuție (threadul principal).
Sistemul de operare vede execuţia unui proces tipic ca o trecere printr-o succesiune de stări: starea dormantă
(dormant), starea gata de execuţie (ready), starea de execuţie (running) şi starea suspendată (suspended).
Starea dormantă este starea în care procesul nu este cunoscut de sistemul de operare, pentru că nu a fost încă
creat. Toate programele care nu au fost încă lansate în execuţie pot fi privite ca procese dormante.
În starea gata de execuţie, procesul are alocate toate resursele necesare pentru execuţie, cu excepţia
procesorului. Planificatorul sistemului de operare (scheduler) selectează un proces gata de execuţie, pe care îl
trece în starea de execuţie, operaţie numită planificarea procesului (process scheduling).
Un proces în starea de execuţie posedă toate resursele necesare, inclusiv procesorul. În sistemele uniprocesor,
cel mult un proces poate să se afle în starea de execuţie, în orice moment de timp. Procesul în execuţie parcurge
secvenţa instrucţiunilor maşină şi poate, la un moment dat să execute un apel către sistemul de operare pentru
execuţia unui serviciu, ca, de exemplu, o operaţie de intrare/ieşire sau o operaţie de sincronizare. În funcţie de
politica de planificare a sistemului de operare, acesta poate să redea imediat controlul procesului după execuţia
serviciului, sau poate alege alt proces disponibil şi să-l planifice în execuţie.
Un proces suspendat este un proces care aşteaptă un eveniment, ca de exemplu un semnal de sincronizare. Astfel
de procese sunt excluse din competiţia pentru execuţie, până ce condiţia de suspendare nu este eliminată. Un
proces în execuţie poate deveni suspendat prin apelul unei operaţii de intrare/ieşire al cărui rezultat este necesar
pentru continuarea activităţii, sau prin aşteptarea unui eveniment care nu s-a produs încă. Sistemul de operare
memorează cauza suspendării procesului, astfel încât să-l poată relua atunci când condiţia de suspendare a
dispărut, prin acţiunea altor procese sau prin apariţia unui eveniment extern.
Sistemul de operare grupează toate informaţiile despre un proces într-o structură de date numită blocul de
control al procesului (process control block, PCB). Atunci când este creat un proces nou, sistemul creează un bloc
de control nou, pentru a-l utiliza ca descriptor de-a lungul existenței procesului. Când procesul se termină, blocul
lui de control este distrus şi eliminat din memorie. În starea dormantă, un proces nu are un bloc de control şi nu
poate intra în competiție pentru obținerea de resurse. În mod tipic, blocul de control menține următoarele
informaţii despre proces:
• Numele procesului (identificator)
• Prioritatea
• Starea de execuţie (gata, în execuţie, suspendat)
• Starea hardware (registrele procesorului şi flag-uri)
Pagina 1
Laborator Sisteme de Operare. Lucrarea 2
• Informaţii de planificare.
• Informaţii despre resursele utilizate (fişiere, intrări/ieşiri).
Tranziţia între două procese active într-un sistem de operare cu multitasking sau multiprocesare se numeşte
comutarea proceselor (process switch), şi are loc ca răspuns la un eveniment din sistem. Comutarea proceselor
este o operaţie complexă, care implică un cost (overhead) important şi, datorită frecvenţei cu care are loc într-
un sistem, poate influenţa semnificativ performanţele acestuia. Comutarea proceselor este necesară deoarece
numărul de procese ce rulează pe o platformă poate fi (mult) mai mare decât numărul de procesoare sau de
nuclee de execuție (core) astfel că se acordă pe rând timp de procesor (timp de execuție) fiecărui proces pe baza
unui algoritm și a unei strategii specifice sistemului de operare.
Eficienţa operaţiei de comutare a proceselor poate fi crescută prin prevederea unor facilităţi hardware (ca, de
exemplu seturi de registre multiple sau circuite hardware care să faciliteze comutarea rapidă), sau printr-o
modalitate de structurare a procesului, de exemplu utilizând threaduri ce pot fi comutate mai eficient.
Threadurile reprezintă o modalitate software de a îmbunătăţi performanţele sistemului prin reducerea costului
de comutare a proceselor. Un thread este un proces “mai uşor”, cu o stare redusă, mai mică, comparativ cu un
proces. Reducerea stării se obţine prin gruparea unui număr de threaduri corelate între ele pentru a partaja
diferite resurse de calcul, ca de exemplu, memoria şi fişierele. În sistemele bazate pe threaduri, threadul devine
cea mai mică entitate de planificare și execuție, iar procesul sau task-ul serveşte ca un mediu de execuţie a
threadurilor. În astfel de sisteme, un proces cu un singur thread este identic cu un proces clasic.
Fiecare thread reprezintă un flux separat de execuţie şi este caracterizat prin propria sa stivă şi prin stare
hardware (registre, flag-uri).
De vreme ce toate celelalte resurse, cu excepţia procesorului, sunt gestionate de către procesul care le
înglobează, comutarea între threadurile care aparţin aceluiaşi proces, care implică doar salvarea, respectiv
restaurarea stării hardware şi a stivei, este rapidă şi eficientă. Totuşi, comutarea între threadurile care aparţin
unor procese diferite implică tot costul de comutare a proceselor.
Threadurile reprezintă un mecanism eficient de exploatare a concurenţei programelor. Un program poate fi
împărţit în mai multe threaduri, fiecare cu o execuţie mai mult sau mai puţin independentă, şi pot comunica între
ele prin accesul la spaţiul de adresă a memoriei procesului, pe care îl partajează între ele.
1. CREAREA THREADURILOR
În continuare vom utiliza mediul de dezvoltare Microsoft Visual Studio ediția 2015 pentru a edita, compila și
genera cod executabil (programe, aplicații). În C# se folosește clasa System.Threading.Thread pentru lucrul cu
theaduri [2]. Se va crea o aplicație mod consolă:
Pagina 2
Laborator Sisteme de Operare. Lucrarea 2
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
}
}
}
Se va completa codul astfel încât să afișeze un mesaj și să aștepte apăsarea unei taste pentru a termina
execuția programului:
Console.WriteLine("Hello world!");
Console.ReadLine();
Se compilează codul și dacă nu sunt erori se va rula aplicația ConsoleApplication1.exe aflată în directorul
proiectului – subdirectorul \bin\Debug:
Pagina 3
Laborator Sisteme de Operare. Lucrarea 2
Notă – pentru utilizarea anumitor metode sau clase este necesară includerea namespace-ului corespunzator.
De exemplu clasa Thread necesită adăugarea namespace-ului System.Threading:
using System.Threading;
Compilați și executați programul. În acest moment aplicația (procesul) conține doua threaduri distincte, fiecare
executând cod diferit: threadul principal execută funcția Main iar threadul copil metoda ThreadFunction.
EXERCIȚIUL 1:
Modificați programul astfel încât să creați și să rulați mai multe threaduri care să ruleze metoda statică
ThreadFunction.
EXERCIȚIUL 2:
Modificați programul astfel încât să creați și să rulați mai multe threaduri și fiecare să ruleze o metodă diferită.
Threadurile se execută concurent în cadrul sistemului de operare, acesta decide modul (ordinea) de execuție,
cele două threaduri se pot executa simultan dacă există resurse disponibile (procesoare/core-uri disponibile), sau
secvențial (sistemul de operare serializează execuția) dacă nu există resurse disponibile.
După apelul metodei childThread.Start() în sistem vor fi două threaduri ce rulează concurent, mesajele
“MainThread: this is main thread” și “ChildThread: this is child thread” se vor afișa într-o ordine aleatoare în
funcție de threadul ales de către sistemul de operare să se execute primul. Pentru a observa mai clar acest lucru
completati codul celor două metode statice și adăugați linia Thread.Sleep(100); înainte de afișarea celor două
mesaje menționate anterior, compilați codul. Rulați de mai multe ori aplicația ConsoleApplication1.exe aflată în
directorul proiectului – subdirectorul \bin\Debug și observați ordinea de afișare a mesajelor:
Pagina 5
Laborator Sisteme de Operare. Lucrarea 2
garantează rularea imediată a threadului, threadul principal poate rula multe alte intrucțiuni până când sistemul
de operare decide execuția threadului copil. Acest lucru poate fi observat dacă modificăm codul astfel:
public static void ThreadFunction(){
for (int i = 0; i < 100; i++)
Console.WriteLine(string.Format("ChildThread: this is child thread {0}", i));
}
static void Main(string[] args){
Console.WriteLine("MainThread: creating child thread");
Thread childThread = new Thread(ThreadFunction);
childThread.Start();
for (int i = 0; i < 100; i++)
Console.WriteLine(string.Format("MainThread: this is main thread {0}", i));
Console.ReadLine();
}
Compilați și rulați de mai multe ori aplicația, observați momentul (impredictibil) când threadul copil rulează
prima instrucțiune, precum și rularea concurentă a celor două threaduri prin modul de intercalare a mesajelor
“MainThread: this is main thread #” și “ChildThread: this is child thread #”:
Aborted The thread state includes AbortRequested and the thread is now dead, but its state has not yet
changed to Stopped.
Pagina 6
Laborator Sisteme de Operare. Lucrarea 2
AbortRequested The Abort(Object) method has been invoked on the thread, but the thread has not yet received the
pending ThreadAbortException that will attempt to terminate it.
Background The thread is being executed as a background thread, as opposed to a foreground thread. This state
is controlled by setting the IsBackground property.
Running The thread has been started and not yet stopped.
StopRequested The thread is being requested to stop. This is for internal use only.
Unstarted The Start() method has not been invoked on the thread.
WaitSleepJoin The thread is blocked. This could be the result of calling Sleep(Int32) or Join(), of requesting a lock -
for example, by calling Enter(Object) or Wait(Object, Int32, Boolean) - or of waiting on a thread
synchronization object such as ManualResetEvent.
Tab. 1. Stările unui thread .net
Starea curentă a unui thread poate fi accesată prin proprietatea ThreadState a clasei Thread. Modificați codul
aplicației astfel:
public static void ThreadFunction(){
for (int i = 0; i < 100; i++)
Console.WriteLine(string.Format("ChildThread: this is child thread {0}", i));
}
static void Main(string[] args){
Console.WriteLine("MainThread: creating child thread");
Thread childThread = new Thread(ThreadFunction);
Console.WriteLine(string.Format("MainThread: child thread state {0}", childThread.ThreadState));
childThread.Start();
for (int i = 0; i < 100; i++)
Console.WriteLine(string.Format("MainThread: child thread state {0}",
childThread.ThreadState));
Thread.Sleep(100);
Console.WriteLine(string.Format("MainThread: child thread state {0}", childThread.ThreadState));
Console.ReadLine();
}
Pagina 7
Laborator Sisteme de Operare. Lucrarea 2
De asemenea observați modul cum execuția threadului copil este reluată de la instrucțiunea unde a rămas la
momentul suspendării:
4. PARTAJAREA MEMORIEI
Threadurile copil partajează spațiul de memorie al procesului în care rulează, acestea pot accesa memoria pentru
citire și/sau scriere. Threadurile pot comunica între ele prin citirea și scrierea memoriei comune, pot transmite și
pot primi parametri, date, variabile în vederea realizării sarcini de calcul complexe în mod concurent și/sau
colaborativ. Pot fi situații sau aplicații în care fiecare thread realizează o sarcină de lucru în mod independent de
celelalte threaduri sau de threadul principal, acestea pot sa nu primească și să nu transmită date către alte
threaduri. Dar în cazul când threadurile trebuie să comunice între ele, sistemul de operare facilitează accesul la
spațiul de memorie comun partajat și astfel acestea pot comunica eficient utilizând memoria partajată în cadrul
procesului curent. Fiecare proces are un spațiu de memorie propriu, acesta este partajat de către toate
threadurile sale.
class Program{
private static int a = 1;
private static int b = 0;
Pagina 9
Laborator Sisteme de Operare. Lucrarea 2
childThread.Start(10);
Thread.Sleep(1000);
Console.WriteLine(string.Format("a={0}, b={1}", a, b));
Console.ReadLine();
}
}
EXERCIȚIUL 3:
Modificați programul astfel încât să implementați adunarea a doi vectori de aceeași lungime N (C = A + B), fiecare
thread realizând adunarea unui element (numărul de threaduri este egal cu numărul de elemente, vectorii A, B,
C, respectiv valoarea N pot fi declarate ca variabile statice).
EXERCIȚIUL 4:
Modificați programul astfel încât să implementați adunarea a doi vectori de aceeași lungime N (C = A + B)
folosind două threaduri (fiecare realizează adunarea pe o partiție de N/2 elemente).
EXERCIȚIUL 5:
Modificați programul de la exercițiul 4 astfel încât să utilizați un număr de K threaduri (K ≥ 1, K ≤ N/2, pentru
simplificare se presupune ca N si K sunt puteri ale lui 2).
Compilați și rulați. Observați rezultatul afișat al variabilei a, acesta diferă de la rulare la rulare deoarece este
posibil ca nu toate threadurile lansate să se și execute până în momentul când threadul principal afișează
valoarea lui a. La o rulare secvențială, corectă, suma primelor K numere naturale este K(K+1)/2, comparați cu
rezultatul rulării multithreading. De notat că bucla for iterează de la 0 până la valoarea K-1 inclusiv deci sunt
adunate primele K-1 numere naturale.
Însă chiar dacă threadul principal așteaptă finalizarea tuturor threadurilor copil totuși există posibilitatea rulării
eronate. La o execuție multithreading (paralelă, concurentă) fiecare thread citește valoarea lui a, o
incrementează utilizând b și o salvează, însă în același timp și alte threaduri pot citi aceeași valoare a lui a și
incrementa cu propria valoare a parametrului parameter specifică acelui thread, astfel la final se obțin rezultate
incorecte (nu sunt efectuate toate adunările). Fără un mecanism de sincronizare a accesului concurent la
variabila a, rularea unui astfel de cod produce rezultate incorecte. Incrementarea variabilei a (citirea,
Pagina 11
Laborator Sisteme de Operare. Lucrarea 2
incrementarea și salvarea) trebuie realizată atomic de către fiecare thread. Acest lucru nu este oferit automat
de către sistemul de operare sau de către hardware, dar sistemul de operare pune la dispoziție mecanisme ce
permit implementarea accesului securizat, exclusiv sau atomic la date partajate.
Pentru controlul accesului exclusiv la resurse comune sau pentru implementarea atomicității operațiilor se
poate folosi un mutex. Un mutex este o resursă de sincronizare (primitivă de sincronizare) gestionată de către
sistemul de operare, procesul sau threadul care deține mutexul poate rula în continuare, în același timp un alt
proces sau thread care încearcă să obțină același mutex va fi blocat până când acesta este eliberat de procesul
sau threadul care l-a deținut.
class Program{
..
private static Mutex mutex = new Mutex();
...
public static void ThreadFunction(object parameter){
mutex.WaitOne();
int b = a;
b = b + (int)parameter;
Thread.Sleep(1);
a = b;
mutex.ReleaseMutex();
}
..
Primitiva de sincronizare mutex, prin apelul WaitOne, asigură theadului căruia i se acordă mutexul posibilitatea
exclusivă să incrementeze variabila a. După ce a finalizat operațiile, threadul eliberează mutexul prin apelul
ReleaseMutex, astfel se permite altui thread să obțină mutexul și deci să incrementeze a. Daca un thread nu
obține un mutex, apelul WaitOne este blocant, nu poate continua execuția (și deci accesul la variabila a) până
când mutexul nu este eliberat. Sistemul de operare asigură că cel mult un thread deține acel mutex. În acest fel
se realizează un acces exclusiv, o serializare a accesului la variabila a și astfel se calculează corect suma primelor
K numere naturale.
EXERCIȚIUL 6:
Modificați programul astfel încât să implementați calculul sumei elementelor unui vector de N numere, folosind
N threaduri, suma va fi afișată de către un thread copil (unul singur va afișa, la alegere).
EXERCIȚIUL 7:
Modificați programul astfel încât să implementați calculul mediei elementelor unui vector de N numere, folosind
N threaduri, valoarea va fi afișată de către threadul principal.
EXERCIȚIUL 8:
Modificați programul astfel încât să determinați valoarea minimă a elementelor unui vector de N numere,
folosind N threaduri, valoarea va fi afișată de către threadul principal.
BIBLIOGRAFIE
[1]. PRINCIPIILE CALCULULUI PARALEL, Felicia Ionescu, EDITURA TEHNICĂ, București, 1999
[2]. C# Programming Guide – Thread Class https://docs.microsoft.com/en-
us/dotnet/api/system.threading.thread?view=netframework-4.8
Pagina 12
Laborator Sisteme de Operare. Lucrarea 3
LUCRAREA 3
Sistemele de fișiere reprezintă soluția pentru structurarea și organizarea datelor în mod persistent. Memoria
RAM este o memorie volatilă astfel că procesele au nevoie de un mecanism de persistență a datelor după ce
aceste procese își încetează activitatea. În general fișierele sunt organizate în structuri ierarhice, directoare,
organizate sub formă de arbore (directoare -> subdirectoare -> … -> fișier), întreaga structură fiind administrată
de către sistemul de operare. Acesta face legătura dintre structura abstractă de directoare și fișiere și capacitățile
fizice de stocare (disk, resurse de rețea precum NAS – Network Attached Storage, File Servere - servere de fișiere,
memorii USB, carduri de memorie, etc.).
1. LUCRUL CU DIRECTOARE
Platforma .Net, ca de altfel majoritatea platformelor de dezvoltare sau limbaje de programare, permite lucrul
directoare și fișiere. Uzual aplicațiile pot crea, muta, șterge directoare, pot afla lista subdirectoarelor dintr-un
director sau proprietățile unui director, pot crea, citi, scrie, copia, muta sau șterge fișiere, pot accesa proprietățile
unui fișier [4].
În continuare vom utiliza mediul de dezvoltare Microsoft Visual Studio ediția 2015 pentru a edita, compila și
genera cod executabil (programe, aplicații). Se va crea o aplicație mod consolă:
Pagina 1
Laborator Sisteme de Operare. Lucrarea 3
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
}
}
}
Se va completa codul astfel încât să afișeze un mesaj și să aștepte apăsarea unei taste pentru a termina
execuția programului:
Console.WriteLine("Hello world!");
Console.ReadLine();
Se compilează codul și dacă nu sunt erori se va rula aplicația ConsoleApplication1.exe aflată în directorul
proiectului – subdirectorul \bin\Debug:
Notă – pentru utilizarea anumitor metode sau clase este necesară includerea namespace-ului corespunzator.
De exemplu clasele pentru lucrul cu directoare sau fișiere necesită adăugarea namespace-ului System.IO:
using System.IO;
EXERCIȚIUL 1:
Consultați proprietățile clasei DriveInfo. Modificați programul astfel încât să afișați la consolă diversele
informații despre dive-uri, precum formatul, spațiul liber disponibil.
EXERCIȚIUL 2:
Modificați programul de la exercițiul 1 și afișați informațiile despre dimensiune și spațiu in KB, MB respectiv GB.
EXERCIȚIUL 3:
Introduceți un memory stick, memory card sau un drive extern (conectat prin USB) și rulați din nou programele
de la exercițiile 1 și 2.
Structura arborescentă de directoare are drept rădăcină drive-urile. Astfel plecând de la un drive (de ex. C:)
sistemul de operare gestionează structura de directoare și subdirectoare precum și fișierele aflate în aceste
directoare. Putem utiliza clasa Directory pentru lucrul cu directoare. Aceasta permite accesul la informații
despre directoarea, crearea, mutarea sau ștergerea de directoare.
Pentru listarea subdirectoarelor dintr-un director se apelează metoda statică EnumerateDirectories:
string directory = @"E:\Facultate\SO\ConsoleApplication1";
Pagina 3
Laborator Sisteme de Operare. Lucrarea 3
List<string> dirs = new List<string>(Directory.EnumerateDirectories(directory));
foreach (var dir in dirs){
Console.WriteLine("Director: {0}", dir);
}
Notă – pentru utilizarea anumitor metode sau clase este necesară includerea namespace-ului corespunzator.
De exemplu clasele pentru lucrul cu colecții necesită adăugarea namespace-ului System.Collections.Generic:
using System.Collections.Generic;
EXERCIȚIUL 4:
Creați o funcție recursivă pentru afișarea tuturor subdirectoarelor dintr-un director, indiferent de nivelul pe
care acestea se află (afișați întreaga arborescență).
Pentru crearea unui subdirector într-un director se apelează metoda statică CreateDirectory. Completați codul
cu următoarea secvență ce creează un nou director în calea specificată:
string newDirectory = @"Test";
string fullPath = string.Format("{0}{1}{2}", directory, Path.DirectorySeparatorChar, newDirectory);
if (Directory.Exists(fullPath)){
Console.WriteLine("That path exists already.");
return;
}
Directory.CreateDirectory(fullPath);
Pagina 4
Laborator Sisteme de Operare. Lucrarea 3
• numele fișierelor sau al directoarelor nu poate depăși 255 de caractere (inclusiv extensia).
• numele păstrează literele mici/mari, dar nu sunt case sensitive.
• numele nu poate conține caracterele:
? " / \ < > * | :
Fiecare sistem particular poate avea convenții diferite. De exemplu utilizând metoda
Path.GetInvalidFileNameChars se pot obține caracterele invalide pentru sistemul curent:
char[] invalidFileChars = Path.GetInvalidFileNameChars();
foreach (char c in invalidFileChars){
if (Char.IsWhiteSpace(someChar)) {
Console.WriteLine(",\t{0:X4}", (int)c);
}
else {
Console.WriteLine("{0:c},\t{1:X4}", c, (int)c);
}
}
Separatorul de directoare folosit în sistemul curent poate fi obținut prin apelul metodei
Path.DirectorySeparatorChar:
Console.WriteLine("Path.DirectorySeparatorChar: {0}", Path.DirectorySeparatorChar);
Pentru ștergerea unui subdirector se apelează metoda statică Delete. Completați codul cu următoarea secvență
ce șterge un director aflat la calea specificată:
string directory = @"E:\Facultate\SO\ConsoleApplication1";
string newDirectory = @"Test";
string fullPath = string.Format("{0}{1}{2}", directory, Path.DirectorySeparatorChar, newDirectory);
if (!Directory.Exists(fullPath)){
Console.WriteLine("That path do not exists");
return;
}
Directory.Delete(fullPath);
List<string> dirs = new List<string>(Directory.EnumerateDirectories(directory));
foreach (var dir in dirs){
Console.WriteLine("Director: {0}", dir);
}
Console.ReadLine();
Atenție – nu ștergeți sau modificați directoare sau fișiere ale sistemului de operare sau alte directoare sau
fișiere importante din sistem! Lucrați cu directoare sau fișiere create special pentru teste.
Accesul la directoare se poate face specificând calea completă (calea absolută) sau calea parțială (relativ la
calea aplicației). În exemplele anterioare s-a utilizat calea absolută. Calea curentă a aplicației se poate afla
apelând metoda statică GetCurrentDirectory:
string directory = @"E:\Facultate\SO\ConsoleApplication1";
string newDirectory = @"Test";
string fullPath = string.Format("{0}{1}{2}", directory, Path.DirectorySeparatorChar, newDirectory);
Directory.CreateDirectory(fullPath);
newDirectory = @"Test2";
Pagina 5
Laborator Sisteme de Operare. Lucrarea 3
fullPath = string.Format("{0}{1}{2}", Directory.GetCurrentDirectory(), Path.DirectorySeparatorChar,
newDirectory);
Directory.CreateDirectory(fullPath);
newDirectory = @"Test3";
Directory.CreateDirectory(newDirectory);
În cazul directoarelor Test2 si Test3 calea este cea curentă, a aplicației, aflată în directorul proiectului,
subdirectorul bin\Debug.
EXERCIȚIUL 5:
Plecând de la un director sursă care conține subdirectoare pe mai multe nivele, creați o structură identică de
subdirectoare într-un nou director aflat pe alt drive. La final ștergeți subdirectoarele și directorul sursă.
EXERCIȚIUL 6:
Se dă un director sursă care conține subdirectoare, redenumiți directorul sursă.
2. LUCRUL CU FIȘIERE
Pentru lucrul cu fișiere se poate folosi clasa File [2]. Clasa File poate fi folosită pentru operații de copiere,
mutare, redenumire, creare, deschidere, ștergere, sau scriere aplicate unui singur fișier la un moment dat. De
asemenea clasa File permite citirea sau scrierea atributelor unui fișier sau acces la informațiile privind data
creării, accesului sau scrierii unui fișier [3].
Platforma .Net oferă mai multe posibilități pentru crearea și scrierea de fișiere. De exemplu utilizând metoda
statică File.WriteAllLines se poate scrie o linie de text sau mai multe într-un nou fișier:
string directory = Directory.GetCurrentDirectory();
string newFile = @"Test.txt";
string fullPath = string.Format("{0}{1}{2}", directory, Path.DirectorySeparatorChar, newFile);
string[] lines = { "New line 1", "New line 2" };
File.WriteAllLines(fullPath, lines);
Codul de mai sus creează un fișier text și scrie în acesta două linii de text.
Pagina 6
Laborator Sisteme de Operare. Lucrarea 3
EXERCIȚIUL 6:
Completați codul de mai sus pentru a citi liniile de text din fișier si a scrie în același fișier o noua linie de text
între două linii existente.
EXERCIȚIUL 7:
Modificați codul de mai sus pentru crea un fișier HTML care să conțină un document HTML cu elemente
specifice acestui tip de format (body, head, table, div, etc). Deschideți fișierul cu un browser Web.
EXERCIȚIUL 8:
Modificați codul de mai sus pentru crea un fișier CSV (comma separated values) care să conțină un tabel cu trei
coloane text și numeric, tabelul să conțină pe prima linie un cap de tabel, completați cel puțin 10 linii cu diverse
date numerice și text conform capului de tabel. Deschideți fișierul cu un program de calcul tabelar tip Excel.
Pagina 7
Laborator Sisteme de Operare. Lucrarea 3
Pentru copierea, mutarea sau ștergerea fișierelor se pot folosi metodele statice Copy, Move, Delete ale clasei
File.
Operații cu fișiere multiple se pot realiza utilizând Directory.GetFiles sau DirectoryInfo.GetFiles. În continuare
este prezentat un exemplu de apel și de obținere a listei de fișiere dintr-un director:
string directory = Directory.GetCurrentDirectory();
string[] fileEntries = Directory.GetFiles(directory);
foreach (string fileName in fileEntries)
Console.WriteLine("File name '{0}', file extension '{1}'",
Path.GetFileNameWithoutExtension(fileName), Path.GetExtension(fileName));
Console.ReadLine();
EXERCIȚIUL 9:
Creați o aplicație care copiază toate fișierele dintr-un director într-un director nou creat.
EXERCIȚIUL 10:
Modificați aplicația de la exercițiul anterior pentru a copia doar fișiere cu o anumita extensie (de ex. HTML).
BIBLIOGRAFIE
[1]. C# Programming Guide – DriveInfo Class https://docs.microsoft.com/en-
us/dotnet/api/system.threading.thread?view=netframework-4.8
[2]. C# Programming Guide – File Class https://docs.microsoft.com/en-
us/dotnet/api/system.io.file?view=netframework-4.8
[3]. C# Programming Guide – operații uzuale I/O https://docs.microsoft.com/en-
us/dotnet/standard/io/common-i-o-tasks?view=netframework-4.8
[4]. C# Programming Guide – File and Stream I/O https://docs.microsoft.com/en-us/dotnet/standard/io/
Pagina 8
Laborator Sisteme de Operare. Lucrarea 3
LUCRAREA 4
În această lucrare ne propunem realizarea unei aplicații de tip Windows Explorer pentru lucrul cu directoare și
fișiere. Vor fi folosite noțiunile din lucrările precedente privind lucrul cu procese, directoare sau fișiere.
Vom utiliza mediul de dezvoltare Microsoft Visual Studio ediția 2015 pentru a edita, compila și genera cod
executabil (programe, aplicații). Se va crea o aplicație Visual C# de tip Windows Form Application. Vom denumi
aceasta aplicație WindowsExplorer:
Fig. 1. Crearea unui nou proiect C# de tip Windows - Windows Form Application
Codul creat implicit de către IDE este:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsExplorer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Pagina 1
Laborator Sisteme de Operare. Lucrarea 3
}
}
}
Codul va crea o interfață windows (formular – Form Windows) Form1 derivată din clasa Form.
Pagina 2
Laborator Sisteme de Operare. Lucrarea 3
Se setează modul de vizualizare Details pentru controlul ListView astfel încât să fie afișate elementele sub forma
tabelară (tabel cu cele trei coloane Nume, Dimensiune, Tip):
Pagina 3
Laborator Sisteme de Operare. Lucrarea 3
Pagina 4
Laborator Sisteme de Operare. Lucrarea 3
Se va completa metoda OnLoad cu o secvență de cod pentru încărcarea arborelui TreeView. Rădăcina se va crea
cu numele My PC, si pe următorul nivel se vor adăuga drive-urile din sistem:
DriveInfo[] allDrives = DriveInfo.GetDrives();
În continuare se va completa metoda OnLoad cu o secvență de cod pentru definirea imaginilor (iconițelor)
specifice drive-urilor, directoarelor și fișierelor. În acest fel elementele afișate în controlul ListView vor fi
diferențiate în funcție de tipul lor:
ImageList imageListSmall = new ImageList();
ImageList imageListLarge = new ImageList();
imageListSmall.Images.Add(Bitmap.FromFile(@"E:\..\WindowsExplorer\Drive.bmp"));
imageListSmall.Images.Add(Bitmap.FromFile(@"E:\..\WindowsExplorer\Director.bmp"));
imageListSmall.Images.Add(Bitmap.FromFile(@"E:\..\WindowsExplorer\Fisier.bmp"));
imageListLarge.Images.Add(Bitmap.FromFile(@"E:\..\WindowsExplorer\Drive.bmp"));
imageListLarge.Images.Add(Bitmap.FromFile(@"E:\..\WindowsExplorer\Director.bmp"));
imageListLarge.Images.Add(Bitmap.FromFile(@"E:\..\WindowsExplorer\Fisier.bmp"));
listView1.LargeImageList = imageListLarge;
listView1.SmallImageList = imageListSmall;
În prealabil se vor crea trei imagini BMP de dimensiuni reduse (ex. 32x32 pixeli) utilizând aplicația Paint și se vor
salva în directorul proiectului. Se vor completa căile exacte către aceste fișiere în codul prezentat mai sus.
Pagina 5
Laborator Sisteme de Operare. Lucrarea 3
Fig. 10. Tratarea evenimentului de selectare a unui nod (drive sau director) din TreeView
IDEul va crea metoda treeView1_NodeMouseClick:
private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e){
}
listView1.Items.Clear();
Pagina 6
Laborator Sisteme de Operare. Lucrarea 3
if (e.Node.ToolTipText == "My PC")
{
DriveInfo[] allDrives = DriveInfo.GetDrives();
foreach (DriveInfo d in allDrives)
{
ListViewItem item = new ListViewItem(d.Name,0);
item.ToolTipText = Path.Combine(e.Node.ToolTipText, d.Name);
item.SubItems.Add("Drive");
item.SubItems.Add((d.TotalSize / 1024 / 1024).ToString()); //MB
listView1.Items.Add(item);
}
}
else
{
List<string> dirs = new
List<string>(Directory.EnumerateDirectories(e.Node.ToolTipText));
foreach (var dir in dirs)
{
ListViewItem item = new ListViewItem(dir,1);
item.ToolTipText = Path.Combine(e.Node.ToolTipText, dir);
item.SubItems.Add("Director");
item.SubItems.Add(String.Empty);
listView1.Items.Add(item);
}
În codul de mai sus se va observa tratarea diferită a celor trei tipuri de elemente: drive, director, fișier. De
asemenea se va observa apelul constructorului ListViewItem unde al doilea parametru reprezintă indexul din lista
de iconițe aferente tipului respectiv: 0 – drive, 1 – director, 2 – fișier.
Până în acest moment controlul de tip TreeView afișează doar primele două nivele din arborescența complexă a
drive-urilor dintr-un calculator:
Pagina 7
Laborator Sisteme de Operare. Lucrarea 3
Se va completa metoda astfel încât la selectarea unui nod din arbore să încarce în norul curent lista de
subdirectoare și să expandeze acest nod:
textBox1.Text = e.Node.ToolTipText;
if (e.Node.Nodes.Count > 0)
return;
EXERCIȚIUL 1:
Completați lista de iconițe pentru a afișa imagini particularizate pentru anumite tipuri de fișiere (ex. DOCX, PDF,
XLSX, TXT, etc.).
EXERCIȚIUL 2:
Modificați programul astfel încât să se afișeze informațiile despre dimensiune utilizând B, KB, MB respectiv GB,
în funcție de dimensiunea fiecărui element. Ex.: daca fișierul are dimensiune de ordinul B să fie afișat 23 B, dacă
fișierul are dimensiune de ordinul KB să fie afișat 123 KB, samd.
Pagina 9
Laborator Sisteme de Operare. Lucrarea 3
}
..
Se va trata evenimentul ItemSelectionChanged care se declanșează la selectarea unui element din controlul
ListView, evenimentul se va trata în metoda listView1_ItemSelectionChanged:
Pentru a implementa operațiile cu fișiere și directoare vom crea în continuare meniul contextual necesar. Din
modul Design se adaugă un control de tip ContextMenuStrip în formularul aplicației:
Pagina 10
Laborator Sisteme de Operare. Lucrarea 3
Tratarea opțiunilor de meniu presupune tratarea evenimentului Click pentru fiecare din cele trei opțiuni. În modul
Design selectați controlul contextMenuStrip1 și dublu click pe fiecare din cele trei opțiuni de meniu, astfel se vor
cerea metodele de implementare a evenimentului Click pentru fiecare din acestea:
Pagina 11
Laborator Sisteme de Operare. Lucrarea 3
Vom implementa operația Open, care deschide un fișier, de exemplu deschizând browserul pentru vizualizarea
unui fișier HTML:
private void toolStripMenuItem3_Click(object sender, EventArgs e)
{
if (selectedListItem.SubItems[1].Text.ToString() == "Fisier")
{
if (!string.IsNullOrEmpty(selectedListItem.ToolTipText) &&
Path.GetExtension(selectedListItem.ToolTipText) == ".HTML")
{
Process.Start("chrome.exe", selectedListItem.ToolTipText);
}
}
}
Pagina 12
Laborator Sisteme de Operare. Lucrarea 3
}
}
Pentru a implementa modificarea numelui unui fișier sau director este necesar să avem un formular (Windows
Form) suplimentar pentru specificarea noului nume. Se accesează panoul Solution explorer si click dreapta pe
numele proiectului, se adaugă un nou element Form2:
Pagina 13
Laborator Sisteme de Operare. Lucrarea 3
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsExplorer
{
public partial class Form2 : Form
{
public string Nume;
public Form2(string text)
{
InitializeComponent();
textBox1.Text = text;
}
if (selectedListItem.SubItems[1].Text.ToString() == "Fisier")
{
Form2 form = new Form2(Path.GetFileName(selectedListItem.Text));
form.ShowDialog();
if (string.IsNullOrEmpty(form.Nume) || form.Nume ==
Path.GetFileName(selectedListItem.Text))
return;
string newFolder =
Path.Combine(Directory.GetParent(selectedListItem.ToolTipText).FullName, form.Nume);
Pagina 14
Laborator Sisteme de Operare. Lucrarea 3
Directory.Move(selectedListItem.ToolTipText, newFolder);
selectedListItem.ToolTipText = newFolder;
selectedListItem.Text = newFolder;
listView1.Refresh();
}
}
EXERCIȚIUL 3:
Completați metoda toolStripMenuItem3_Click pentru a putea trata și alte tipuri de fișiere, ca de exemplu XLSX,
DOCX, PDF. Completați metoda toolStripMenuItem3_Click pentru a putea trata fișiere executabil (rularea
acestora).
EXERCIȚIUL 4:
Completați metoda toolStripMenuItem3_Click pentru a putea trata și directoare, în acest caz să deschidă aplicația
Explorer din Windows la directorul respectiv.
Pagina 15