14
MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama 1 OPERATIVNI SISTEMI OSLAB04 Vežba 4 – Formiranje DLL fajla i povezivanje aplikacije sa istim, Uvod u Niti (Threads) UVOD Cilj prvog dela ove vežbe je da se kroz seriju primera demonstrira formiranje DLL fajla i povezivanje aplikacije i DLL fajla. U drugom delu vežbe izlažu se osnove Multithreadinga i objašnjava se šta je HANDLE uz realizaciju jedne pozadinske niti. OSLAB04A KREIRANJE DLL FAJLA I UČITAVANJE DLL FAJLA U KONZOLNU APLIKACIJU DLL – Dynamic Link Library file se u razvojnom okruženju Code::Blocks formira sledeći naredne korake: 1. Klikom na meni File, izabrati stavku New, i zatim stavku Project iz padajućeg podmenija 2. Pojavljuje se dijalog New from template gde se bira Dynamic Link Library kao vrsta projekta izborom odgovarajuće ikonice i klikom na taster OK.

OPERATIVNI SISTEMI OSL 04 - tfzr.uns.ac.rs - R2.pdf · MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama 3 Nakon koraka broj 4

  • Upload
    others

  • View
    6

  • Download
    1

Embed Size (px)

Citation preview

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

1

OPERATIVNI SISTEMI – OSLAB04

Vežba 4 – Formiranje DLL fajla i povezivanje aplikacije sa istim, Uvod u

Niti (Threads)

UVOD

Cilj prvog dela ove vežbe je da se kroz seriju primera demonstrira formiranje DLL fajla i povezivanje aplikacije i DLL fajla. U drugom delu vežbe izlažu se osnove Multithreadinga i objašnjava se šta je HANDLE uz realizaciju jedne pozadinske niti.

OSLAB04A – KREIRANJE DLL FAJLA I UČITAVANJE DLL FAJLA U

KONZOLNU APLIKACIJU

DLL – Dynamic Link Library file se u razvojnom okruženju Code::Blocks formira sledeći naredne korake:

1. Klikom na meni File, izabrati stavku New, i zatim stavku Project iz padajućeg podmenija

2. Pojavljuje se dijalog New from template gde se bira Dynamic Link Library kao vrsta projekta izborom odgovarajuće ikonice i klikom na taster OK.

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

2

3. Zatim se unosi naziv projekta, a samim time i naziv koji će DLL fajl imati kada se izvorni

fajlovi kompajliraju. Klikom na taster Next predlazi se na sledeći dijalog.

4. Na sledećem dijalogu bira se kompajler. Ukoliko je sve adekvatno konfigurisano,

podrazumevano će biti odabran GNU GCC, u suprotnom potrebno je odabrati navedeni kompajler i kliknuti na taster Finish.

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

3

Nakon koraka broj 4 DLL projekat će biti kreiran na osnovu prethodno unetih podešavanja. Kreirani DLL projekat, po šablonu, kreira dva fajla : main.h (u ovom primeru preimenovan u dllheader.h) i main.cpp (u ovom primeru preimenovan u dllheader.cpp). Preimenovanje *.h i *.cpp fajla nije mandatorno ali je svakako poželjno pošto se time dobija preglednost projekta.

U header file (dllheader.h) se smeštaju prototipovi funkcija koje će se pozivati iz DLL fajla. U ovom primeru, radi se o prorotipu funkcije koja će generisati nasumičan broj. Generisanje nasumilnog broja biće zasnovano na srand i rand funkcijama, dok će se seed nasumičnog broja generisati na osnovu time funkcije. Prema tome, potrebno je uključiti time.h biblioteku

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

4

u dllheader.h fajl. Da bi funkcija bila dostupna iz DLL fajla, potrebno je označiti funkciju

sa DLL_EXPORT.

Sadržaj dllheader.h fajla je sledeći:

#ifndef __DLLHEADER_H__ #define __DLLHEADER_H__ #include <windows.h> #include <time.h> /* To use this exported function of dll, include this header * in your project. */ #ifdef BUILD_DLL #define DLL_EXPORT __declspec(dllexport) #else #define DLL_EXPORT __declspec(dllimport) #endif #ifdef __cplusplus extern "C" { #endif void DLL_EXPORT SomeFunction(const LPCSTR sometext); int DLL_EXPORT nasumicniBroj(int donjaGranica, int gornjaGranica); #ifdef __cplusplus } #endif #endif // __DLLHEADER_H__

Implementacija navedene funkcije piše se u dllheader.cpp fajlu. Pvo se formira seed pozivom

srand funkcije koja priprema seed na osnovu vrednosti koja vraća time funkcija. time funkcija je opisana u vežbi OSLab02a i neće biti ponovo izlagana. Dalje se poziva funkcija rand koja iz pripremljenog seed-a izvlači nasumični broj na osnovu kojeg treba da se formira nasumični broj iz određenog intervala. Imajući u vidu da generisani broj ne sme da bude manji od donje granice i veći od gornje granice, na donju granicu se dodaje rezultat deljenja po mod-u sa dozvoljenim intervalom (gornja granica – donja granica) i dodaje se 1 ukoliko su gornja i donja granica iste da ne bi došlo do deljenja nulom.

#include "dllheader.h" void DLL_EXPORT SomeFunction(const LPCSTR sometext) { MessageBoxA(0, sometext, "DLL Message", MB_OK | MB_ICONINFORMATION); }

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

5

int DLL_EXPORT nasumicniBroj(int donjaGranica, int gornjaGranica) { srand(time(NULL)); int broj = donjaGranica + (rand() % (int)(gornjaGranica - donjaGranica + 1)); return broj;

} extern "C" DLL_EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: // attach to process // return FALSE to fail DLL load break; case DLL_PROCESS_DETACH: // detach from process break; case DLL_THREAD_ATTACH: // attach to thread break; case DLL_THREAD_DETACH: // detach from thread break; } return TRUE; // succesful }

Nakon definisana funkcije potrebno je bildovati projkat i pokušati „pokrenuti“ DLL klikom na Build and Run taster (Zubčanik sa Zelenom strelicom u desno). Ukoliko nema sintaksnih grešaka, projekat će se bildovati, i napraviti 3 fajla u bin/Debug direktorijumu kreiranog projekta.

Zatim će pokušati da pokrene DLL fajl i prikazaće sledeću poruku.

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

6

DLL fajl ne može da se sam pokrene, već mu je potrebna host aplikacija koja će koristiti

njegove funkcije.

Kao što je već navedeno, da bi pozvali funkciju iz DLL-a potreban je zaseban program koji će koristiti usluge formiranog DLL-a. Prema tome potrebno je kreirati novi projekat, kao što je opisano u vežbi OSLab01 i nazvati isti OSLab04a. Zatim potrebno je Linkeru saopštiti da koristi link biblioteku formiranog DLL fajla. Navedeno se realizuje sledeći naredne korake:

1. Klikom na Meni Settings glavnog menija Code::Blocks razvojnog okruženja prikazuje se podmeni i bira se stavka Compiler

2. Izborom navedene stavke otvara se dijalog Global compiler settings na kojem je potrebno izabrati ekransku karticu Linker settings i zatim pronaći i dodati fajl iz bin/Debug direktorijuma formiranog DLL-a koji se u ovom slučaju zove libOSLab04aDLL.a

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

7

3. U sledećem koraku potrebno je iz projekta OSLab04aDLL prekopirati header fajl

dllheader.h u projekat kreirane aplikacije OSLab04a

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

8

4. Zatim dodati prekopirani header fajl u projekat desnim klikom na sam projekat OSLab04a izborom opcije Add Files, izborom header fajla dllheader.h i dodavanjem istog u Debug i Release bildove.

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

9

5. Na kraju procesa povezivanja, projekti OSLab04aDLL i OsLab04a trebaju da izgledaju kao na sledećoj slici.

Sadržaj main.cpp fajla OSLab04a projeka treba da bude sledeći:

#include <stdio.h> #include <stdlib.h> #include "dllheader.h" int main() { printf("Generisani nasumicni broj je: %d",nasumicniBroj(1,50)); return 0; }

Pokretanjem ovog programa treba da se prikaže sledeće upozorenje / greška.

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

10

Navedeno znači da je unutar programa pozvana funkcija koja se nalazi unutar DLL fajla a

traženi DLL fajl nije u radnom direktorijumu programa (procesa) koji ga poziva. Prema tome, poslednji korak jeste kopiranje libOSLab04aDLL.dll fajla u bin/Debug direktorujum

OSLab04a projekta. Ukoliko je sve realizovano korektno, program će se pokrenuti, i prilikom poziva funkcije randomNumber učitati traženi DLL fajl, pozvati traženu funkciju koja će izgenerisati nasumični broj, i zatim ispisati isti u konzolu.

ZA VEŽBU NA ČASU

Primeru iz vežbe OSLab04aDLL dodati sledeće mogućnosti:

- Napisati funkciju koja poziva program sa parametrom nakon određenog vremena. Funkcija treba da ima 3 ulazna parametra : naziv programa / komandu, argumenti koje se prosleđuju programu / komadi, i broj milisekundi pauze

- Napisati funkciju koja računa faktorijel broja - Napisati funkciju koja ispisuje Fibonačijev niz za uneti broj elemenata (ispisati prvih N

lemenata Fibonačijevog niza) - Napisati funkciju koja izračunava stepen broja X za stepen N - Napisati funkciju koja vraća string enkriptovan XOR logičkim operatorom na osnovu

nekog numeričkog ključa

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

11

OSLAB04B – MULTITHREADING, UVOD U NITI, POZADINSKI

PROCESI

Cilj ovog dela vežbe je da se pokaže kako se kreira jedna pozadinska nit kroz jednostavan program pogađanja broja iz određenog intervala. Prvo se nasumičan broj iz određenog intervala generiše. Za generisanje se koristi DLL i napisana funkcija iz primera OSLab04aDLL. Nakon generisanja broja, od korisnika se traži da unosi broj sve dok isti ne pogodi, i pri tome se, ukoliko korisnik ne pogodi broj signalizira da li je broj veći ili manji od traženog. Pri tome, u formi pozadinske niti potrebno je pratiti broj proteklih sekundi od početka pogađanja, dok korisnik ne pogodi traženi broj.

Procesom se smatra program u izvršenju. Imajući u vidu da svaki program počinje funcijom main, moguće je, do određene mere, postaviti jednalinu i reći da je Process u stvari neka funkcija koja se izvršava. U prethodnoj vežbi je pokazano da se iz jednog programa (procesa) određenim tehnikama (pozivom ShellExecute funkcije) mogu pokrenuti i drugi procesi. Navedeno jasno ukazuje da jedan proces može da kreira drugi proces. U kontekstu niti i pozadinskih procesa,

može se reći da je glavna nit programa ustvari main metoda, i da ista može kreirati zavisne

pozadinske procese (niti). Pri tome, tako kreirani procesi (niti) su zavisni od procesa koji

ih kreira, odnosno terminacijom glavne niti, terminiraju se i svi pozadinski procesi koje je

ista kreirala.

Da bi funkcija mogla da se izvršava u svojstvu WIN32 API niti, ista mora da ispuni određenu specifikaciju sa aspekta strukture signature iste. Takva funkcija mora da bude obeležena WINAPI

tagom i da prima jedan parametar tipa LPVPOID. Sledi primer takve funkcije.

DWORD WINAPI BrojacSekundi(LPVOID lpParam) { // string buffer za naziv konzole char cTitle[80]; // dok nije broj pronadjen while(!brojPronadjen) { Sleep(1000); protekleSekunde++; sprintf(cTitle,"Proteklo vreme %d sekundi.", protekleSekunde); // ispisuje string u nazivu konzole SetConsoleTitle(cTitle); } // nit sama terminira kada se broj pronadje (izlazi iz while petlje) return 0; }

Pre navedene funkcije potrebno je definisati određene varijable i uključiti potrebne biblioteke.

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

12

#include <stdio.h> #include <stdlib.h> #include "dllheader.h" int protekleSekunde = 0; BOOL brojPronadjen = FALSE;

Svaki proces identifikovan je njegovim HANDLE-om i isti se smatra jedinstvenim

identifikatorm procesa. Prema tome, main funkcija kao proces, i program u memoriji ima

svoj HANDLE. Nit (podproces) koju navedena nit kreira takođe ima svoj HANDLE itd. HANDLE je potreban jer se putem istog nit može pauzirati, izdati se naredba za nastavak sa radom, terminirati i dr.

Nit se kreira pozivom funkcije CreateThread – MSDN - https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx

Pozivom CreateThread funkcije se kreira nit (pozadinski proces) koji izvršava na

specifičan način napisanu i prosleđenu funkciju. Pri tome, naziv prosleđene funkcije

predstavlja početnu adresu niti.

Navedena funkcija prima 6 parametara i signatura iste prikazana je u sledećem linstingu koda.

HANDLE WINAPI CreateThread(

_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,

_In_ SIZE_T dwStackSize,

_In_ LPTHREAD_START_ROUTINE lpStartAddress,

_In_opt_ LPVOID lpParameter,

_In_ DWORD dwCreationFlags,

_Out_opt_ LPDWORD lpThreadId

);

Iz priloženog da se zaključiti sledeće:

1. Create Thread je WINAPI funkcija koja vraća HANDLE procesa ukoliko se uspešno izvrši 2. Od 6 parametara 5 su ulazni parametri od kojih su 3 obavezna 3. Parametar lpThreadAttributes tipa LPSECURITY_ATTRIBUTES prestavlja

bezbednosne atribute niti. Ukoliko se postavi vrednost NULL nit se kreira sa podrazumevanim bezbednosnim atributima

4. Parametar dwStackSize tipa SIZE_T naznačava inicijalnu veličinu steka koja je namenjana funkciji. Ako se stavi 0 uzima se podrazumevana izvršna veličina steka

5. Parametar lpStartAddres tipa LPTHREAD_START_ROUTINE naznačava adresu funkcije koju nit treba da izvršava. Nativ funkcije jeste njena adresa.

6. Parametar dwCreationFlags je tipa DWORD i naznačava u kom modu treba kreirati nit. Ukoliko je vrednost ovog parametra 0 nit počinje da se izvršava odmah nakon kreiranja

7. Parametar lpThreadId je tipa LPDWORD i naznačava i vraća identifikator hiti. Ukoliko se umesto ovog parametra navede NULL, identifikator niti se ne vraća.

U telu main metode demonstriran je način kreiranja niti pozivom funkcije CreateThread

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

13

int main() { // ispisuje string u nazivu konzole SetConsoleTitle("Pogadjanje nasumicnog broja iz intervala 1 do 100"); DWORD dwCharThreadId; HANDLE hTimerNit; int nBroj = nasumicniBroj(1,100); hTimerNit = CreateThread( NULL, // bez bezbednostih atributa, koristi podrazumevane 0, // koristi podrazumevanu velicinu steka BrojacSekundi, // naziv niti (naziv funkcije koja se pokrece kao nit) NULL, // argumenti (parametri) koji se prosledjuju niti 0, // korisiti podrazumevane tagove za kreiranje niti ( 0 - odmah pusti nit da se izvrsava) &dwCharThreadId); // vrati identifikator niti // ukoliko se proces nije kreirao, HANLDE ce imati vrednost NULL if (hTimerNit==NULL) return -1; int broj = 0; printf("Pogadjanje nasumicnog broja iz intervala 1 do 100...\n\n"); do { scanf("%d",&broj); if (broj!=nBroj) { if (broj<nBroj) printf("Trazeni broj je veci od unetog... Pokusajte ponovo...\n"); else printf("Trazeni broj je manji od unetog... Pokusajte ponovo...\n"); } else printf("\n\nPogodili ste trazeni broj za %d sekundi...\n\n", protekleSekunde); } while (nBroj!=broj); // terminiranje (zatvaranje) pozadinske niti na osnovu HANDLE-a, ukoliko nit nije terminirana CloseHandle(hTimerNit); return 0; }

Funkcija CloseHandle signalizira da objekat koji poseduje prosleđeni HANDLE treba da se uništi. Na navedeni način se na adekvatan način terminiraju niti. https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211%28v=vs.85%29.aspx Funkcija SetConsoleTitle ispisuje prosleđeni string u naslovu konzole https://msdn.microsoft.com/en-us/library/windows/desktop/ms686050%28v=vs.85%29.aspx

MSc Predrag Pecev - Operativni Sistemi – Vežbe – 2015/2016 – Revizija 2 – Podložno izmenama

14

ZA VEŽBU NA ČASU

Modifikovati primer / vežbu OSLab04b i istoj dodati sledeću funcionalnost:

- Dodati mogućnost pokretanja programa kroz argumente komandne linijesa 2 parametara. Prvi parametar je donja granica generisanog broja, dok je drugi parametar gornja granica kenerisanog broja. Ukoliko se parmatri ne unesu, interval generisanog broja treba da bude od 1 do 100.

- Dodati i ispisati broj pokušaja pogađanja broja. Svaki put kada korisnik ne pobodi broj, proj pokušaja se povećava

- Dodati mogućnost da kada korisnik pogodi broj isti unese svoje ime i prezime. Nakon unosa imena i prezimena, na kraj tekstualnog fajla highscores.txt upisuje se uneto ime i prezime, sa pogođenim brojem i vremenom koje mu je trebalo da isti pogodi. Podatci unutar tekstualnog fajla se razdvajaju tabulatorima, odnosno \t