Sunteți pe pagina 1din 55

Windows Compendiu

7 Arhitectura sistemului de operare Windows....................................................................................................2 7.1 Versiuni Windows argumente pro i contra...............................................................................................2 7.1.1 Diferene ntre Windows familia NT i familia Windows 9X...............................................................2 7.1.2 Caracteristicile principale ale seriei NT.................................................................................................3 7.1.3 Nivele de protecie: Kernel Space vs User Space..................................................................................3 7.2 Arhitectura SO Windows...............................................................................................................................4 7.2.1 Arhitectura intern a nucleului Windows .............................................................................................4 7.2.1.1 Structura SO Windows...................................................................................................................4 7.2.1.2 Nucleul Windows............................................................................................................................4 7.2.1.3 Executivul NT.................................................................................................................................5 7.2.1.4 Microkernel, drivere i HAL...........................................................................................................7 7.2.2 Subsistemele oferite de sistemul de operare Windows..........................................................................8 7.2.2.1 Caracteristicile subsistemelor Win32, POSIX i OS/2 ..................................................................8 7.2.2.2 Procesele de baz ale sistemului de operare...................................................................................9 7.2.2.3 Servicii Windows............................................................................................................................9 7.3 Elemente de acces la fiiere sub Windows..................................................................................................10 7.3.1 Funcii Win32 API pentru lucru cu fiiere..........................................................................................10 7.3.1.1 Creare fiier...................................................................................................................................10 7.3.1.2 Deschidere fiier...........................................................................................................................10 7.3.1.3 Scriere ntr-un fiier......................................................................................................................10 7.3.1.4 Citire dintr-un fiier......................................................................................................................11 7.3.1.5 Inchidere fiier..............................................................................................................................11 7.3.2 Blocare de fiiere n Windows la nivel zon de date...........................................................................11 7.3.2.1 Funciile folosite la blocare...........................................................................................................11 7.3.2.2 Un exemplu de blocare.................................................................................................................12 7.4 Procese Window, comunicare ntre procese...............................................................................................13 7.4.1 Procese Windows.................................................................................................................................13 7.4.1.1 Aciunile componentelor sistem...................................................................................................13 7.4.1.2 Crearea unui proces Windows......................................................................................................14 7.4.1.3 Terminarea unui proces Windows................................................................................................15 7.4.2 Pipe sub Windows NT.........................................................................................................................16 7.4.2.1 Pipe anonim Windows..................................................................................................................16 7.4.2.2 Pipe cu nume sub windows...........................................................................................................17 7.4.2.3 Un exemplu de comunicare prin pipe...........................................................................................18 7.4.3 Comunicarea ntre procese Windows prin memorie partajat.............................................................21 7.4.3.1 Fiiere I/O mapate n memorie i zone partajate..........................................................................21 7.4.3.2 Implementarea foii de calculsub Windows...................................................................................23 7.4.4 Sincronizarea proceselor prin semafoare Windows............................................................................25 7.4.4.1 Semafoare cu nume i anonime; operaii......................................................................................25 7.4.4.2 Exemple de utilizare a semafoarelor Windows............................................................................29 7.4.5 Comunicarea ntre procese Windows prin Mailslot ...........................................................................31 7.4.5.1 Arhitectura mailslot......................................................................................................................31 7.4.5.2 Programarea folosind mailslot......................................................................................................31 7.4.5.3 Exemplu de lucru cu mailslot.......................................................................................................33 7.5 Thread-uri pe platforme Microsoft: Windows NT, 2000............................................................................35 7.5.1 Caracteristici ale thread-urilor sub Windows NT................................................................................35 7.5.2 Operaii asupra thread-urilor: creare, terminare..................................................................................36 7.5.2.1 Crearea unui thread.......................................................................................................................36 7.5.2.2 Terminarea unui thread.................................................................................................................37 7.5.3 Instrumente standard de sincronizare..................................................................................................38 7.5.3.1 Funcii de ateptare.......................................................................................................................38 7.5.3.2 Variabile mutex.............................................................................................................................38 7.5.3.3 Semafoare fr nume....................................................................................................................39 7.5.3.4 Seciuni critice..............................................................................................................................39 7.5.3.5 Alte obiecte de sincronizare..........................................................................................................40 7.5.4 Atributele i planificarea thread-urilor NT..........................................................................................40 7.5.4.1 Atributele thread-urilor.................................................................................................................40 7.5.4.2 Prioritile thread-urilor................................................................................................................41 7.5.5 Faciliti speciale ale lucrului cu thread-uri NT..................................................................................42 7.5.5.1 Date specifice thread-urilor...........................................................................................................42 7.5.5.2 Thread-uri utilizator: fibre NT......................................................................................................43 7.5.5.3 Utilizarea thread-urilor n regim multiprocesor............................................................................44

Windows Compendiu 2 7.5.6 Exemple clasice de lucru cu thread-uri....45 7.5.6.1 Adunarea n paralel a n numere....................................................................................................45 7.5.6.2 Problema productorilor i consumatorilor..................................................................................47 7.5.6.3 Problema cititorilor i scriitorilor..................................................................................................50 7.5.7 Particulariti de utilizare WINDOWS (WINSOCK)........................................................................52 7.5.7.1 RPC sub Windows NT.................................................................................................................54

7 Arhitectura sistemului de operare Windows


7.1 Versiuni Windows argumente pro i contra
Din punct de vedere istoric, etapele parcurse de Microsoft pentru elaborarea sistemelor Windows pot fi mprite n trei mari direcii: Familia Windows 3X, cu reprezentaii Windows 3.1 i Windows 3.11 for workgroups. Aceste sisteme au cod pe 16 bii i au fost practic abandonate dup 1993. Familia Windows 9X: - personale - cu reprezentanii Windows 95, Windows 98 i Millenium. Sunt primele sisteme Windows pe 32 de bii. Au fost practic abandonate dup 2000. Familia Windows NT - profesionale - cu versiunile Windows 2000 i XP ca reprezentani majori, sunt sistemele Windows actuale. Toate aspectele descrise n acest capitol se vor concentra aproape exclusiv pe arhitectura sistemelor de operare din familia Windows NT, cu precdere pe ultimele versiuni ale aceastei familii, Windows 2000 i Windows XP. Odat cu dezvoltarea i lansarea Windows XP, Microsoft a renunat practic la linia de dezvoltare Windows 95, Windows 98, Windows Millennium, platforme de altfel destul de criticate de majoritatea specialitilor dar i a utilizatorilor. De aceea, referirile la sistemele de operare Windows 95, 98 i Millennium vor avea cu precdere un caracter istoric i comparativ. Dei gndite pentru sistemele de operare din familia Windows NT, unele referiri la concepte, noiuni, poriuni de cod sau funcii vor fi valabile i pentru versiunile Windows din familia 9x i Millennium. Nu toate exemplele i conceptele vor fi valabile ns pentru aceste din urm sisteme de operare. 7.1.1 Diferene ntre Windows familia NT i familia Windows 9X

Prezentm pe scurt aceste diferene, fr a intra n detalii de arhitectur intern: Sistemele de operare din familia Windows 9X: 95, 98, Millennium au fost construite peste sistemul de operare DOS, sistem de operare monoutilizator i monotasking. Nu este greit s afirmm c Windows 95 este de fapt o extensie grafic multitasking pentru DOS (ca i Windows 3.1 de altfel). Dei Windows 95, 98 i Millenium sunt sisteme de operare pe 32 de bii, o mare parte a codului acestor sisteme de operare este cod pe 16 bii motenit de la DOS i Windows 3.1. Datorit dorinei de a pstra compatiblitatea cu aplicaiile DOS existente, sau facut concesii legate de securitate. Multe dintre aplicaiile DOS existente, n special jocuri, pentru a putea rula corect aveau nevoie de acces direct la echipamentele hardware. De asemenea, caracterul monoutilizator al acestor sisteme de operare a atras dup sine o lips a unor mecanisme de securitate primare. Nu exist o protecie a resurselor (fiiere, procese) la nivel de utilizator (noiunea de utilizator este de altfel inexistent). De asemenea, sistemul de fiiere folosit de aceste sisteme de operare este cel folosit de DOS (cu mici mbuntiri legate exclusiv de folosirea de hard-discuri de capacitate mai mare). Versiunile Windows din familia NT, au fost proiectate din start pentru a fi folosite ntr-un mediu multiutilizator. Mecanismele de securitate legate de protecia fiierelor i a memoriei

Windows Compendiu

nu puteau s lipseasc. Sistemele de operare Windows din aceast familie s-au bucurat pe deplin de noile mecanisme de securitate oferite de noile procesoare din familia x86: modul de lucru protejat i posibilitatea de a rula instruciuni cu privilegii mai mari sau mai mici (aa numitele nivele de protecie x86). Pentru Windows NT, fiind un sistem de operare orientat din start spre a fi folosit de mai muli utilizatori ntr-un mediu distribuit, s-a renunat din start la unele faciliti nerelevante scopului pentru care a fost creat: compatibilitatea cu aplicaiile DOS existente. Interzicerea accesului direct la echipamentele hardware i permiterea accesului doar prin intermediul driverelor, a adus mecanisme de securitate suplimentare. Pe parcusul ultimilor zece ani, Microsoft a fost destul de criticat pentru familia Windows 95, 98 i Millennium. Instabilitile acestor sisteme de operare, puse de cele mai multe ori n crca Microsoft s-au datorat (nu n majoritatea cazurilor ) i aplicaiilor DOS cu care aceste sisteme de operare doreau s fie compatibile. Totui, n tot acest timp familia NT era dezvoltat, folosit i, foarte important, mult mai sigur i mai stabil. Dac Microsoft ar fi susinut i dezvoltat doar platforma NT, sistemele de operare Windows nu s-ar fi bucurat de acelai succes (nimeni nu ar fi vrut la mijlocul anilor 90 s cumpere un sistem de operare care s nu ruleze majoritatea aplicaiilor deja existente pe pia). La nceputul anilor 2000, raportul s-a schimbat, majoritatea aplicaiilor de pe pia fiind dezvoltate pentru Windows, indiferent de variant. Microsoft a putut astfel renuna la seria Windows 95, 98, Millennium. Versiunile Windows 9x i Millennium s-au dovedit a nu fi altceva dect versiuni de tranziie. 7.1.2 Caracteristicile principale ale seriei NT

Principalele cerine care au stat la baza dezvoltrii sistemului de operare Windows NT i a versiunilor urmtoare de Windows bazate pe acelai nucleu au fost: Independena de platform i de arhitectura hardware. Suport pentru arhitecturi cu mai multe procesoare. Orientare att pentru partea de client (desktop), ct i pentru partea de server. Suport pentru setul de caractere UNICODE care permite din start o internaionalizare i localizare uoar a sistemului de operare. Mecanisme avansate de securitate: multiutilizator, protecia sistemului de fiiere, protecia proceselor, firewall etc. Memorie virtual i multitaksing preemtiv; Compatibilitate POSIX. In legtur cu independena de platform, trebuie artat c dei nucleele iniiale NT 3.5 i NT 4.0 au fost portate cu succes pe o serie de alte arhitecturi diferite de x86 cum ar fi Dec Alpha, MIPS SPARC i Power PC, versiunile ulteriore ale seriei NT s-au orientat cu mici excepii doar asupra arhitecturii x86. Windows 2000 a fost portat ntr-o oarecare msur pe procesoare PowerPC pentru a putea pune n micare consola de jocuri Xbox oferit de Microsoft. De asemenea versiunile noi ale Windows XP suport noile arhitecturi pe 64 de bii oferite de Intel i AMD. Toate acestea ne permit s tragem concluzia ca seria NT este n continuare deosebit de portabil, ns portarea pe noi arhitecturi nu se face din considerente comerciale. In sfrit, se apreciaz c s-a realizat numai ntr-o msur discutabil compatibilitatea POSIX. 7.1.3 Nivele de protecie: Kernel Space vs User Space

Sistemele de operare moderne, pentru a oferi stabilitate i securitate, ruleaz n mod diferit unele dintre componentele sistemului de operare comparativ cu aplicaiile utilizator. Instruciunile sistemului de operare ruleaz ntr-un mod privilegiat, care permite accesul direct la toat memoria i accesul direct la periferice. In schimb, aplicaiile utilizator ruleaz

Windows Compendiu

ntr-un spaiu de memorie virtual (fiecare proces vede doar propriul spaiu de memorie), accesul la periferice fcndu-se exclusiv prin intermediul apelurilor sistem oferite de ctre sistemul de operare. Pentru a se putea face ns aceast distincie, ntre modul de execuie a instruciunilor sistemului de operare i instruciunile unei aplicaii utilizator, platforma i procesorul pe care ruleaz sistemul de operare trebuie s permit i s suporte o asemenea funcionalitate (facilitate). Arhitecturile x86 pe 32 de bii prevd n acest sens patru aa numite nivele de protecie, nivelul de protecie 0 avnd privilegiile cele mai mari, iar nivelul de protecie 3 cele mai sczute. Sistemele de operare actuale folosesc doar dou nivele de protecie, nivelul de protecie 0 pentru a rula instruciunile sistemului de operare i nivelul de protecie 3 - pentru rularea aplicaiilor utilizator. Componentele sistemului de operare ce ruleaz sub nivelul de protecie 0 alctuiesc nucleul (sau kernelul) sistemului de operare. Nu toate componentele sistemului de operare ruleaz ns sub nivel de protecie 0. Exist o serie de componente ale sistemului de operare care ruleaz sub nivel de protecie 3, n aa numitul user space, alturi de aplicaiile obinuite utilizator. Un exemplu de astfel de componente sunt serviciile Windows (Service Processes) - de fapt serviciile Windows sunt echivalentul daemon-ilor Unix.

7.2 Arhitectura SO Windows


7.2.1 Arhitectura intern a nucleului Windows

7.2.1.1 Structura SO Windows In fig. 7.1 este prezentat arhitectura sistemului de operare Windows. Toate referirile la componentele sistemului de operare Windows se vor face pe aceasta arhitecutur. Pe parcursul capitolului vom detalia pe rnd fiecare dintre aceste componente. 7.2.1.2 Nucleul Windows Nucleul sistemul de operare Windows este unul monolitic, asemntor majoritii nucleelor sistemelor de operare Unix, inclusiv Linux. Toate componentele vitale ale sistemului de operare (managementul proceselor i a threadurilor, managementul fiierelor, programarea operaiilor de intrare / ieire, sincronizarea i comunicarea ntre procesoare) se fac n cadrul nucleului sistemului de operare. Deoarece o component de baz a nucleului Windows poart numele de micronucleul (Windows), unele documentaii, non-Microsoft, catalogheaz greit nucleul sistemului de operare Windows ca fiind un micronucleu. In cazul sistemelor de operare ce posed micronucleu, managementul memoriei, a proceselor, a operaiilor de intrare / ieire, stivele de protocoale, ruleaz ca procese de sine stttoare, tipar n care sistemul de operare Windows nu se ncadreaz. Managementul memoriei, a proceselor, operaiile de intrare / ieire, stivele de protocoale sunt implementate de ctre sistemul de operare Windows integral n kernel space, n cadrul nucleului.

Windows Compendiu

User space

Servicii (Service processes) Wn 32 i Subsystem


Procese de baza ale sistemului de operare (System support processes)

Aplicaii utilizator

POSIX Subsystem

I/O Manager

Process Manager

IPC Manager

Window Manager GDI

OS/2 Subsystem Executivul NT

Kernel Space

Man ager de obiecte (Object Manager)

Micronucleu (microkernel)

Drivere

Hardware Abstraction Layer (HAL)

Figura 7.1 Arhitectura SO Windows 7.2.1.3 Executivul NT Orice sistem de operare, indiferent de arhitectura lui intern, are ca sarcini, printre altele: gestiunea echipamentelor periferice i a operaiilor de intrare / ieire efectuate de ele, ntreinerea unor resurse specifice ca: procese, threaduri, fiiere, semafoare, chei .a.m.d. In cadrul sistemului de operare Windows, gestiunea acestor resurse se face la nivelul unei componente a sistemului de operare Windows denumit Executiv NT (vezi fig. 7.1) Executivul NT ruleaz n kernel space i are ca sarcin, n primul rnd, gestiunea acestor resurse (crearea, referirea, distrugerea acestora) dar i implementarea operaiilor propriu-zise pe aceste resurse. Datorit nivelului la care sunt manipulate n cadrul sistemului de operare Windows, aceste resurse sunt referite n documentaiile i manualele Microsoft sub numele de Executive Objects.

Windows Compendiu

Componenta de la nivelul executivului NT n a crei sarcin cade gestiunea acestor resurse se numete Object Manager. Object Manager-ul este responsabil de crearea obiectelor, distrugerea acestora, pstrarea unei evidene a utilizrii obiectelor existente n sistem: cte referiri exist la un obiect, cte handle-uri exist asociate obiectului, lista proceselor care dein un handle spre acest obiect. Object Manager-ul, cuantificnd astfel utilizarea unei resure (adic a unui obiect), poate impune limitri (restricii) asupra resurselor folosite de un proces sau de totalitatea proceselor unui utilizator: fiiere deschise, memorie folosit, etc. Principalele tipuri de obiecte gestionate de Executivul NT sunt: fiier: un obiect fiier este instan a unui fiier deschis, prezent n sistemul de fiiere sau a unui dispozitiv de intrare / ieire (abordarea Windows este astfel identic sistemelor de operare Unix , care trateaz orice dispozitiv periferic ca pe un fiier, spre exemplu porturile seriale, paralele, controlerul audio etc.; proces: indentific spaiul virtual de adresare necesar execuiei unui thread sau a unei mulimi de threaduri; thread (fir de execuie): secven de instruciuni executabil de sine stttor din cadrul unui proces. Fiecare proces Windows trebuie s aib asociat cel puin un thread; job: mulime de procese, de obicei procese instane ale unui aceluiai pachet de programe; mutex i semafor: obiecte folosite pentru sincronizare, precum i pentru accesul exclusiv la alte resure. Desktop: un desktop are asociat o suprafa de desenare a ferestrelor, ferestre, meniuri, diverse shortcut-uri (combinaii de taste) pentru lansarea mai rapid a anumitor aplicaii (vom reveni cu detalii asupra utlizrii obiectelor WindowStation i Desktop). WindowStation: un astfel de obiect are asociat un clipboard i unul sau mai multe Desktopuri; Operaiile, specifice pentru fiecare tip de obiect n parte, cad n sarcina altor componente ale Executivului Windows NT, cum ar fi: Managerul de Procese (Proccess Manager) - pentru gestiunea proceselor i a threadurilor, IPC Manager (pentru gestiunea semafoarelor si a zonelor de memorie partajat), Managerul de Intrare / Iesire (I/O Manager) - pentru gestiunea fiierelor, .a.m.d. In fig. 7.2 sunt prezentate legturile Executivului cu celelalte componente.
Aplicaii (att utilizator cat si sistem) s Windows API (kernel32.dll)

Object Manager Executivul Windows NT Process Manager I/O Manager ... IPC Manager

Figura 7.2 Managerii de la nivelul Executivului NT Pentru a sublinia i clarifica rolul componentelor executivului NT, lum ca exemplu manipularea unui fiier. Operaiile de creare efectiv a unui fiier, n cadrul sistemului de fiiere, operaiile de citire, scriere, nchidere a fiierului, cad efectiv n sarcina Managerului de Intrare / Ieire (I/O Manager). Crearea unui obiect intern n cadrul nucleului sistemului de

Windows Compendiu

operare, care s fac posibile toate operaiile de mai sus (obiect pe a crui instan s fie aplicate toate aceste operaii), cade n sarcina Managerului de Obiecte (Object Managerului). Obiectele din cadrul executivului NT nu sunt vizibile n user space-ul unei aplicaii. Procesele utilizator pot accesa ns indirect aceste obiecte prin intermediul unor descriptori numii n documentaiile Microsoft handle(s). Aceti descriptori sunt de fapt indici care pointeaz n tabelele interne gestionate de ctre Object Manager. Att operaiile de gestiune a Executive Objects, implementate de ctre Object Manager, ct i operaiile specifice fiecrui tip de obiect n parte implementate de ctre celelalte managere prezente la nivelul executivului Windows NT, sunt mapate n apelurile sistem Windows. Apelurile sistem Windows fac parte din Win32 API (mpreun cu alte funcii ce rezid n biblioteci DLL). Aplicaiile utilizator i procesele critice ale sistemului de operare (Service Processes) cer prin intermediul apelurilor sistem diferite servicii Executivului NT. De asemenea, accesul la obiectele de la acest nivel (adic la Executive Objects), se face tot prin intermediul apelurilor sistem. Codul apelurilor sistem Windows i maparea acestora la servicii oferite de ctre Executivul NT rezid n principal n fiierele Kernel32.dll i Advapi32.dll. Pe lng obiectele de la nivelul Executivului NT (Executive Objects), sistemul de operare Windows menine o serie de obiecte interne denumite obiecte kernel (Kernel Objects). Aceste obiecte, n principal thread-uri, mutex-uri, semafoare, sunt private executivului NT i nu sunt vizibile n afara acestuia. Nu exist funcii Windows API care s permit accesul la aceste obiecte din cadrul aplicaiilor care ruleaz n User Space. Ele sunt folosite doar la nivelul executivului pentru a oferi functionaliti de baz celorlalte compontente ale nucleului Windows, spre exemplu n a asigura accesul exclusiv la o alt resurs cum ar fi un Executive Object. Obiectele kernel sunt implementate la nivelul microkernelului (vezi fig. 7.1). 7.2.1.4 Microkernel, drivere i HAL Toate managerele de la nivelul Executivului NT apeleaz subrutine ale microkernelului i ale driverelor. Rutinele de la nivelul microkernelul sunt responsabile de tratarea ntreruperilor i a excepiilor (vezi [52]). Tot la acest nivel sunt implementate i obiectele kernel. Rutinele microkernelului sunt cel mai des invocate de ctre Managerul de Procese (dar nu numai), n timp ce driverele sunt cel mai des invocate la nivelul I/O Managerului, n special driverele echipamentelor de stocare, sau la nivelul Window Managerului, driverele adaptoarelor grafice. Rolul driverelor este acela de a uniformiza accesul la echipamentele periferice. In plus, aplicaiile utilizator din User Space nu pot accesa echipamentele perferice direct, ci doar prin intermediul driverelor, via Executivul NT. Astfel, la nivelul Executivului NT se pot lua o serie de decizii legate de drepturile de acces ale unui anumit proces sau utilizator la un anumit periferic. La rndul lui, microkernelul invoc rutine ale HAL (Hardware Abstraction Layer). HAL-ul poate fi considerat un driver al arhitecturii pe care ruleaz sistemul de operare Windows. Prin arhitectur ne referim att la procesor, ct i la chipset-ul plcii de baz a calculatorului (vezi [52]). Portarea pe o nou arhitectur a sistemului de operare Windows presupune, n mare msur, doar portarea acestei componente a sistemului. Impreun cu micronucleul, HAL-ul este componenta nucleului Windows responsabil de sincronizarea i comunicarea ntre procesoare pe mainile multiprocesor. Exist mai multe versiuni de HAL, n funcie de tipul mainii pe care ruleaz sistemul de operare Windows (x86 monoprocesor, x86 multiprocesor,

Windows Compendiu

PowerPC). In funcie de tipul mainii pe care ruleaz sistemul, doar un singur HAL este instalat n sistem (versiunea de HAL este aleas n timpul procesului de instalare a sistemului de operare). 7.2.2 Subsistemele oferite de sistemul de operare Windows

7.2.2.1 Caracteristicile subsistemelor Win32, POSIX i OS/2 Primele versiuni ale sistemului de operare Windows NT de la mijlocul anilor '90, se doreau un nlocuitor al sistemului de operare OS/2 oferit de IBM. S-a dorit astfel din start, ca noul sistem de operare s poat rula att aplicaii native Windows, ct i aplicaii OS/2. De asemenea, din motive legate de standardizarea sistemului de operare, s-a prevzut ca pe sistemul de operare Windows NT, s se poat porta uor i aplicaii conforme standardului POSIX (Portable Operating System Interface based on Unix). Toate aceste sarcini sunt realizate prin intermediul unor componente ale sistemului de operare Windows ce ruleaz n User Space, denumite subsisteme (Win32, OS/2 i POSIX). Din pcate, pe msur ce subsistemul Win32 a fost dezvoltat, iar sistemul de operare Windows a devenit predominant pe pia (putem spune, fr a grei, c i-a catigat o identitate proprie), celelalte subsisteme (OS/2 i POSIX) nu au mai fost dezvoltate. Astfel, ele ofer n prezent un set restrns din functionalitile cerute de actualele standarde POSIX i OS/2. Toate cele trei subsisteme sunt prezente pn la versiunea Windows 2000, inclusiv. Incepnd cu Windows XP, singurul subsistem prezent este Win32, ns Microsoft ofer separat pentru Windows XP un subsistem POSIX complet, n cadrul pachetului Window Services for Unix. O aplicaie proiectat (compilat) pentru unul din subsisteme ruleaz apelnd exclusiv funcii specifice API-ului subsistemului respectiv. Spre exemplu, aplicaiile native Windows, proiectate pentru subsistemul Win32 apeleaz intensiv funciile din Win32 API, n timp ce o aplicaie POSIX apeleaz funcii din API-ul POSIX. La rndul lui, fiecare API specific unui subsistem, translateaz aceste apeluri n apeluri native care sunt deservite de Executivul NT. Fiecare subsistem const de fapt din dou componente importante: 1. un proces asociat (numit n documentaiile Microsoft subsystem process), responsabil cu meninerea strii subsistemului respectiv; 2. bibliotecile DLL asociate, biblioteci care pun la dipoziie API-ul necesar rulrii aplicaiilor destinate subsitemului. Astfel, fiecare susbsistem are asociat un proces de control: csrss.exe pentru Win32, psxss.exe pentru POSIX, os2ss.exe pentru OS2), Bibliotecile DLL asociate, care ofer API sunt: Kernel32.dll i Advapi32.dll export aplicaiilor API-ul Win32, Psxdll.dll export API-ul POSIX, Os2dll.dll export aplicaiilor API-ul OS/2. Toate aceste componente ale subsistemelor ruleaz n user space, subsistemul fiind practic interfa ntre o aplicaie i Executivul NT. In plus, subsistemul Win32 include i o component ce ruleaz n kernel space, Managerul de Ferestre (Window Manger-ul) (vezi fig. 7.1).

Windows Compendiu

Procesele asociate subsistemelor POSIX i OS/2 sunt rulate la cerere, odat cu lansarea n sistem a primului executabil compilat pentru aceste subsisteme (de fapt linkeditat dinamic spre API-ul acestor subsisteme). 7.2.2.2 Procesele de baz ale sistemului de operare Pe lng aplicaiile utilizator, disponibile pentru cele trei subsisteme, n user space ruleaz i procesele de baz ale sistemului de operare (System Support Processes n documentaiile Microsoft). Printre procesele de baz care ruleaz n spaiul utilizator amintim Session Manager i Logon Process. Dei aceste procese sunt pornite automat la pornirea sistemului, ele nu intr n categoria serviciilor Windows, nesupunndu-se aciunilor Service Control Managerul-ului. Sarcinile celor dou procese sunt: Logon Process este responsabil cu autentificarea utilizatorilor care doresc s se autentifice pe maina Windows. Session Manager este responsabil de iniializarea sistemului i de rularea procesului de control a subsistemului Win32 (csrss.exe). Ambele procese sunt responsabile cu asignarea cte unui obiect de tip WindowStation i de tip Desktop noii sesiuni de lucru create la autentificarea local sau remote a unui utilizator. Pe variantele Server ale sistemului de operare Windows, serviciul Terminal Servicess mpreun cu componenta client Remote Desktop, asigur conectarea simultan de la distan a mai multor utilizatori. Fiecrui utilizator conectat i se atribuie cte un obiect de tip WindowStation i de tip Desktop. Un mecanism asemntor este disponibil i pentru Windows XP, varianta Professional, cu observaia c este permis conectarea remote sau local a unui singur utilizator. Sistemul de operare Windows XP permite de asemenea conectarea simultan local a mai multor utilizatori (prin intermediul unui mecanism numit Fast User Switching), dar a unei singure sesiuni de lucru active. De fapt, sesiunea fiecrui utilizator autentificat local are asociat un obiect de tip Desktop diferit, obiecte care pe rnd pot s intre n lucru pentru a-i lua locul de desktop propriu-zis. 7.2.2.3 Servicii Windows Tot n user space ruleaz i serviciile Windows (Service Processes). Serviciile Windows, echivalentul proceselor daemon Unix, includ o serie de procese server sau client, care sunt pornite automat la iniializarea sistemului. Aceste procese sunt necesare fie pentru buna funcionare local a staiei (spre exemplu Event Log Service, Task Scheduler Service, Spooler Service), fie pentru integrarea funcional a staiei n cadrul unei reele de calculatoare (DHCP Client Service). Toate serviciile Windows folosesc exclusiv subsistemul Win32 i sunt controlate de un serviciu Windows privilegiat numit Service Control Manager (services.exe). Service Control Manager-ul este lansat la boot-are de ctre Logon Process. Pornirea celorlalte servicii cade n sarcina Service Control Managerului, conform informaiei prezente n regitrii Windows (KLM\SYSTEM\CurrentControlSet\Services) Service Control Managerul asociaz fiecrui serviciu rulat un obiect de tip WindowStation, obiect ce refer un Desktop virtual (vezi tipurile de obiecte ale Executivului NT). Acest

Windows Compendiu

10

Desktop este diferit de cel asociat sesiunii de lucru curente la consola staiei Windows; din acest motiv nici un serviciu Windows nu va putea afia mesaje interactive, ferestre, meniuri, pe Desktop-ul unei sesiuni de lucru utilizator. Eventualele mesaje pe care un serviciu doreste s le afieze, pot fi trimise unui serviciu specializat numit Event Log Service.

7.3 Elemente de acces la fiiere sub Windows


7.3.1 Funcii Win32 API pentru lucru cu fiiere

Reprezentarea unui fiier ntr-un program Windows se face printr-un descriptor de tip HANDLE, obinut prin CreateFile sau OpenFile. Descriem aici prototipurile principalelor funciilor de lucru cu fiiere sub Windows. 7.3.1.1 Creare fiier
HANDLE CreateFile ( LPCTSTR numeFisier, DWORD acces, DWORD partajare, LPSECURITY_ATTRIBUTES descr_sec, DWORD mod_deschid, DWORD atributeFisier, HANDLE fis_atrib );

numeFisier - numele fiierului care se va crea; acces - modul de acces (n citire i / sau scriere); partajare - modul de partajare a fiierului: descr_sec descriptorul de securitate, la NULL sistemul d valori implicite; mod_deschid - modul de creare a fiierului; atributeFisier - atribute fiier. fis_atrib atributele fiierului, la NULL, sistemul d valori implicite. 7.3.1.2 Deschidere fiier
HANDLE OpenFile ( LPCSTR numeFisier, LPOFSTRUCT lpBuf, UINT uActiune );

numeFisier - numele fiierului care se va deschide; lpBuf - pointer la o structur care reine informaii despre fiier; uActiune - indic operaia care se va efectua asupra fiierului. 7.3.1.3 Scriere ntr-un fiier
BOOL WriteFile ( HANDLE hFisier, LPCVOID lpBuf, DWORD nNrOctetiDeScris, LPDWORD lpNumarOctetiScrisi, LPOVERLAPPED lpStructIO );

hFisier - handle-ul fisierului n care se va scrie; lpBuf - pointer la datele care se vor scrie n fiier;

Windows Compendiu

11

nNrOctetiDeScris numr de octei de scris; lpNumarOctetiScrisi - pointer la numrul de octei scrii efectiv; lpStructIO - de obicei are valoarea NULL. 7.3.1.4 Citire dintr-un fiier
BOOL ReadFile ( HANDLE hFisier, LPCVOID lpBuf, DWORD nNrOctetiDeCitit, LPDWORD lpNumarOctetiCititi, LPOVERLAPPED lpStructIO );

hFisier - handle-ul fisierului din care se va citi; lpBuf - pointer la un bufer n care se vor citi datele; nNrOctetiDeScris - numr de octei de citit; lpNumarOctetiCititi - pointer la numrul de octei citii efectiv; lpStructIO - de obicei are valoarea NULL. 7.3.1.5 Inchidere fiier
BOOL CloseHandle ( HANDLE hObject );

hObject - handle-ul la obiectul (fiier) care trebuie nchis. Pentru mai multe informatii legate de prototipurile acestor funcii, se recomand consultarea documentaiei MSDN [55]. 7.3.2 Blocare de fiiere n Windows la nivel zon de date

Apelurile LockFile i LockFileEx permit blocarea de zone de date din cadrul unui fiier. Apelurile UnlockFile i UnlockFileEx permit deblocarea zonelor blocate anterior. Aceste funcii folosesc handle-uri Windows de acces la fiiere. Fiierele trebuie deschise nainte de blocare. 7.3.2.1 Funciile folosite la blocare
BOOL LockFile( HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh ); BOOL LockFileEx( HANDLE hFile, DWORD dwFlags, DWORD dwReserved, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh, LPOVERLAPPED lpOverlapped ); BOOL UnlockFile( HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,

Windows Compendiu 12 DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh ); BOOL UnlockFileEx( HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped );

Parametrii cei mai importani ai acestor funcii au urmtoarea semnificaie: dwFileOffsetHigh i dwFileOffsetLow: specific cuvintele cel semnificativ i cel mai puin semnificativ din adresa pe 4 octei unde ncepe blocarea / deblocarea; nNumberOfBytesToLockLow i nNumberOfBytesToLockHigh: specific lungimea pe 4 octei a zonei de blocat; dwFlags poate lua urmtoarele valori: o LOCKFILE_FAIL_IMMEDIATELY - funcia ntoarce un cod de eroare dac nu poate s blocheze zona specificat; o LOCKFILE_EXCLUSIVE_LOCK - funcia va realiza o blocare de tip exclusiv. Nici un alt proces nu va putea accesa zona respectiv. In cazul n care aceast valoare nu se specific, funcia cere o blocare de tip partajat. lpOverlapped - structur de date ce se folosete la operaii asincrone. Funciile returneaz TRUE sau FALSE n funcie de modul de terminare a operaiilor de blocare / deblocare. Observaii: blocarea n mod exclusiv a unei regiuni a unui fiier interzice orice fel de acces la regiunea respectiv. Blocarea de tip partajat permite accesarea regiunii n citire de ctre alte procese; regiunile blocate ntr-un fiier nu sunt motenite de ctre procesele fiu; nu se pot executa mai multe operaii de blocare asupra unor regiuni ale unui fiier care se suprapun, chiar daca ele sunt fcute de ctre acelai proces; dac un proces se termin nainte de a debloca una sau mai multe regiuni anterior blocate acestea vor fi deblocate de ctre sistemul de operare. Acest lucru se realizeaz ns doar atunci cnd sistemul nu mai are resurse suficiente pentru a opera rezultnd un management costisitor; dac un proces p1 se termin nainte de a debloca una sau mai multe regiuni anterior blocate i un alt proces p2 cere accesul la o regiune din fiier nainte ca sistemul de operare s deblocheze zona n locul procesului terminat p1 atunci procesului p2 i va fi interzis accesul la fiier. 7.3.2.2 Un exemplu de blocare Prezentm n continuare un scurt program care exemplific blocarea n mod exclusiv a primilor 10 octei dintr-un fiier. Sursa programului este dat n fig. 7.3.
#include #include #include #include #include "stdafx.h" <windows.h> <stdio.h> <stdlib.h> <memory.h>

int main(int argc, char* argv[])

Windows Compendiu { HANDLE fd;

13

// deschidem fisierul fd = CreateFile(L"date.out", FILE_ALL_ACCESS | GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (fd == INVALID_HANDLE_VALUE) { fprintf(stderr, "Eroare la deschiderea fisierului\n"); exit(1); } // blocam de la offset 0, primi 10 octeti din fisier printf("Incerc sa blochez exclusiv 10 octeti de la inceputul fisierului.\n"); if (LockFile(fd, 0, 0, 10, 0) > 0) fprintf(stderr, "Eroare la blocare\n"); printf("Portiune din fisier blocata cu succes. Tin fisierul blocat pentru 20 secunde.\n"); // Tinem portiunea din fisier blocata pentru 20 de secunde Sleep(20000); // Deblocam UnlockFile(fd, 0, 0, 20, 0); CloseHandle(fd); return 0; }

Figura 7.3 Fiierul surs blocare.cpp Programul blocare.cpp din figura 7.3 poate fi compilat folosind Microsoft Visual Studio .NET. Pentru a vedea efectele blocrii, recomandm cititorului s ruleze n paralel dou instane diferite ale executabilului obinut, n dou ferestre Command Prompt diferite. Directorul curent de lucru n cele dou ferestre Command Prompt trebuie s fie acelai, pentru ca ambele instane ale programului s lucreze pe acelai fiier date.out.

7.4 Procese Window, comunicare ntre procese


7.4.1 Procese Windows

7.4.1.1 Aciunile componentelor sistem Crearea unui nou proces Windows se face diferit, n funcie de subsistemul din care dorim s crem noul proces. Din subsistemul Win32, crearea unui nou proces se face cu ajutorul apelului API CreateProcess, care este tradus de ctre API-ul Win32 ntr-un apel sistem de la nivelul Executivului NT. Din subsistemul POSIX, un proces nou se creaz cu fork (da!, dei srac, subsistemul POSIX implementeaz aceast funcie ). La rndul su, API-ul POSIX implementeaz funcia fork prin apeluri nedocumentate de la nivelul Executivului NT. In cele ce urmeaz ne vom referi strict la crearea unui proces Win32. Crearea unui nou proces implic efortul a trei componente ale sistemului de operare Windows: la nivelul API-ului Win32 (Kernel32.dll), la nivelul Executivului NT i la nivelul procesului de control asociat subsistemului (csrss.exe n cazul Win32). Astfel, paii executai de apelul CreateProcess n vederea crerii unui proces Windows sunt:

Windows Compendiu

14

Deschiderea fiierului executabil care se dorete a fi executat. In acest moment se determin tipul imaginii fiierului executabil (dac este destinat subsistemului Win32, POSIX sau OS/2); Crearea la nivelul Executivului NT a obiectului proces asociat; alocarea memoriei i crearea spaiului de adresare virtual pentru noul proces; Crearea la nivelul Executivului NT a obiectului thread asociat procesului care se dorete a fi executat, precum i crearea stivei i a contextului de execuie pentru noul thread; Notificarea procesului de control al subsistemului (Win32 - csrss.exe) despre noul proces i thread. Procesul de control al subsistemului Win32 cuantific numrul de procese instane ale unui program; Dac programul rulat nu prezint nici o alt instan n sistem (procesul de control al subsistemului Win32 cuantific numrul de instane ale programului), atunci ncarc eventualele fiiere DLL necesare.

7.4.1.2 Crearea unui proces Windows Crearea unui proces n Windows se face prin apelul funciei CreateProcess de ctre un thread (fir de execuie) dintr-un alt proces. Funcia are urmtorul prototip:
BOOL CreateProcess ( LPCTSTR lpszImageName, LPCTSTR lpszCommandLine, LPSECURITY_ATTRIBUTES lpsaProcess, LPSECURITY_ATTRIBUTES lpsaThread, BOOL fInheritHandles, DWORD fdwCreate, LPVOID lpvEnvironment, LPTSTR lpszCurDir, LPSTARTUPINFO lpsiStartInfo, LPPROCESS_INFORMATION lppiProcInfo );

Atunci cnd un fir de execuie apeleaz funcia CreateProcess, sistemul creaz un spaiu de adresare i ncarc noul proces n acest spaiu. Dup aceast operaie, sistemul creaz thread-ul primar pentru noul proces i-l lanseaz n execuie. S vedem semnificaia parametrilor funciei CreateProcess: lpszImageName - parametrul identific numele fiierului executabil n care este memorat codul pentru procesul ce se va crea. CreateProcess va cuta fiierul mai nti n directorul curent, i apoi n directoarele descrise de variabila de mediu PATH. Dac acest parametru este NULL, funcia va lua numele fiierului din primul cuvnt al liniei de comand specificat de parametrul lpszCommandLine. lpszCommandLine - parametrul specific argumentele care trebuie transmise ctre noul proces. La pornirea procesului, aceste argumente vor fi regsite n argumentul lpszCmdLine al funciei WinMain. lpszCommandLine puncteaz ctre un sir de caractere ANSI. lpsaProcess i lpsaThread identific atributele de securitate care trebuie date noului proces i threadului su primar. Dac valorile lor sunt NULL, sistemul va furniza valori implicite pentru aceti parametri.

Windows Compendiu

15

fInheritHandles - specific modul n care se motenesc referinele la obiecte de ctre procesul fiu. In mod normal, n Windows acestea nu sunt aceleai pentru fiecare proces. Chiar dac este vorba de acelai obiect sistem, procese diferite pot lucra cu valori diferite. Dac dorim ca aceste valori s fie identice n procesul tat i procesul fiu, atunci putem specifica valoarea TRUE acestui parametru. fdwCreate - conine comutatorii (flags) ce afecteaz modul n care este creat noul proces. Se pot specifica mai muli comutatori combinndu-i prin operatorul | (sau logic). lpvEnvironment - refer un bloc de memorie n care sunt memorate irurile, care descriu variabilele de mediu ce trebuiesc utilizate de noul proces. De obicei, valoarea acestui parametru trebuie s fie NULL, caz n care procesul fiu primete aceleai variabile de mediu ca i printele. lpszCurDir - parametrul permite specificarea unui nou disc i a unui nou director curent pentru procesul nou creat. Dac valoarea transmis pentru acest parametru este NULL, directorul i discul de lucru ale noului proces sunt aceleai ca i cele ale procesului printe. lpsiStartInfo puncteaz ctre o structur STARTUPINFO. Aceste informaii sunt necesare pentru subsistemul Win32 la crearea unui nou proces. Aceast structur este prea complex pentru a o descrie aici. S reinem doar c n ea se pot specifica caracteristicile unei ferestre consol: culoare, numr de linii, coloane, poziie pe ecran, titlu, etc.; informaii despre modul n care va fi afiat fereastra aplicaiei: maximizat, icon, etc.; informaii despre comportarea sistemului n timpul ncrcrii aplicaiei: forma cursorului, etc. lppiProcInfo - conine adresa unei structuri care va fi completat cu informaii de ctre funcia CreateProces nainte ca aceasta s-i termine execuia. Aceast structur va conine referinele pentru noul proces, pentru threadul su primar, precum i identificatorii acestora. Structura este declarat astfel:
typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION;

7.4.1.3 Terminarea unui proces Windows Un proces poate fi terminat pe dou ci: apelnd din interior funcia ExitProcess sau apelnd din exterior funcia TerminateProcess. Este preferabil prima cale, cea de-a doua trebuie folosit doar pentru situaii extreme. Prototipul funciei ExitProcess este:
VOID ExitProcess (UINT fuExitCode);

Funcia termin procesul care a apelat-o i i seteaz acestuia codul de retur pe valoarea fuExitCode. Funcia TerminateProcess are urmtorul prototip:
BOOL TerminateProcess (HANDLE hProcess, UINT fuExitCode);

Cu aceast funcie, un proces poate termina orice alt proces din sistem, inclusiv pe el nsui. Procesul terminat este dat de parametrul hProcess. Codul de retur a procesului terminat va fi obinut n valoarea parametrului fuExitCode. Folosirea funciei TerminateProcess

Windows Compendiu

16

poate fi periculoas la o programare nengrijit. In mod normal, evenimentul de terminare a unui proces este semnalat de ctre Windows tuturor DLL-urilor ataate de proces. La terminarea procesului folosind funcia TerminateProcess, aceste DLL-uri nu vor fi avertizate, producndu-se eventuale pierderi de date. Sistemul de operare Windows garanteaz totui c toate resursele sistem utilizate de proces vor fi eliberate indiferent cum este terminat procesul. La terminarea unui proces, Executivul NT are grij s nchid toate handle-urile diferitelor resurse folosite de procesul respectiv (dac procesul respectiv nu a fcut-o deja). 7.4.2 Pipe sub Windows NT

In Windows, exist dou posibiliti de a folosi pipe, ambele puse la dispoziie de IPC Manager. O prim variant este pipe anonim, care se poate folosi pentru comunicarea ntre procesele ce ruleaz pe aceeai main, dac procesele care doresc s comunice sunt descendente ale acelai printe. A doua variant este pipe cu nume, folosit pentru comunicarea ntre procese ce pot rula i pe maini diferite Windows, sau ntre orice dou procese de pe aceeai main. Ambele tipuri de pipe Windows sunt construite peste obiecte ale Executivului NT de tip fiier. Aplicaiile care folosesc pipe-ul vd obiectul fiier de la nivelulul Executivului NT doar prin intermediul a dou handle-uri. 7.4.2.1 Pipe anonim Windows Un pipe anonim poate fi folosit, la fel ca i pipe-ul de sub Unix, pentru comunicarea ntre procese descendente din creatorul pipe-ului. In urma crerii, procesul creator obine doi descriptori - handle - unul de citire i altul de scriere. Procesul creator poate trimite fiilor (nepoilor etc.) handle-urile pipe-ului, n momentul crerii proceselor fii prin apeluri ale funciei CreateProces (). Pentru ca fiul s moteneasc handle-ul la pipe, printele trebuie s seteze parametrul fInheritedHandle, din apelul CreateProces(), la valoarea TRUE. Un pipe fr nume se creeaz folosind apelul CreatePipe(). Pipe-ul se nchide cu ajutorul funciei CloseHandle(). Funcia CreatePipe() creeaz un pipe far nume i are urmtorul prototip:
BOOL CreatePipe (PHANDLE phRead,PHANDLE phWrite, LPSECURITY_ATTRIBUTES lpsa, DWORD cbPipe );

Funcia ntoarce TRUE n caz de succes sau FALSE la eec. phRead i phWrite sunt pointerii spre cele dou handle-uri (de citire i de scriere) obinute n urma crerii. Parametrul lpsa are o dubl semnficaie: determin dac handle-ul la pipe, returnat de funcie poate fi motenit n procesele fii, proprietate care are loc pentru o valoare diferit de NULL. Acelai parametru reprezint un descriptor de securitate. Dac se specific pentru acest parametru valoarea NULL, sistemul va fixa atributele de securitate implicite.

Windows Compendiu

17

cbPipe specific dimensiunea buferului rezervat pentru operaiile de I/O prin pipe. Dac aceast valoare este 0, atunci dimensiunea implicit a buferului o stabilete sistemul de operare. Scrierea i citirea din pipe-urile anonime se face folosind funciile ReadFile() i WriteFile(). Cele dou operaii sunt atomice. O citire va opri procesul pn cnd va reui s se execute. In mod similar, o scriere va bloca procesul pn cnd va avea suficient spaiu n pipe pentru a efectua operaia de scriere dorit. Att procesul creator, ct i procesele fii care motenesc cele dou handle-uri ale pipe-ului, le pot folosi ntre momentul primirii lor i momentul nchiderii. Astfel, procesul creator poate ncepe imediat s foloseasc pipe prin apelurile ReadFile() i WriteFile(). Dup crearea i primirea handle-urilor, procesele fii pot s foloseasc la rndul lor funciile ReadFile() i WrieFile() pentru a citi sau scrie din pipe. 7.4.2.2 Pipe cu nume sub windows Pipe-ul cu nume este un mecanism de comunicare ntre dou sisteme diferite, ambele fiind operaionale pe sistemele de operare Microsoft precum i platforme mai vechi, ca OS/2, Novell, DOS.
1. CreateNamedPipe 2. ConnectNamedPipe 3. ReadFile 4. WriteFile 5. DisconnectNamedPipe 6. CloseHandle

1. 2. 3. 4.
Pipe cu nume

CreateFile ReadFile WriteFile CloseHandle

Server

Client

Figura 7.4 Succesiunea de apeluri pentru pipe cu nume In fig. 7.4 sunt prezentate succesiunile apelurilor sistem, att pentru server, ct i pentru client. Cititorul poate uor observa particularizrile necesare pentru comunicarea prin pipe anonim. Apelurile specifice pentru pipe cu nume sunt CreateNamedPipe, ConnectNamedPipe i DisconnectNamedPipe. Prototipurile lor sunt:
HANDLE CreateNamedPipe(LPSTR numePipe, DWORD optiuniModOpen, DWORD optiuniModPipe, DWORD nMaxInstances, DWORD lungBufOut, DWORD lungBufIn, DWORD timeOut, LPSECURITY_ATTRIBUTES lpsa ); ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpo) DisconnectNamedPipe(HANDLE hNamedPipe);

Windows Compendiu

18

numePipe este un string prin care se indic numele pipe-ului. Conveniile Microsoft de specificare a acestor nume impun dou sintaxe, una pentru pipe local i alta pentru pipe de pe o alt main. Aceste specificri sunt: \\.\PIPE\numePipePeMAsina \\adresaMasina\PIPE\numePipePeMasina Adresa mainii este fie o specificare Internet, fie o adres IP [44] [48]. optiuniModOpen este un flag ce specific direcia de deschidere a pipe-ului. Valoarea lui poate fi una dintre constantele: PIPE_ACCESS_DUPLEX, PIPE_ACCESS_INBOUND, PIPE_ACCESS_OUTBOUND, indicnd fie ambele sensuri, fie numai de la client la server, fie numai de la server spre client. optiuniModPipe este un flag ce specific caracteristicile acestui pipe. Pentru specificare, sunt valabile constantele (sau combinaii sau logice ntre aceste constante): PIPE_TYPEBYTE, PIPE_TYPE_MESSAGE pentru scrierea ca flux de octei, respectiv ca i mesaj; PIPE_READMODE_BYTE, PIPE_READMODE_MESSAGE pentru citirea dup regulile fluxului de octei sau citirea ca i mesaj; PIPE_WAIT, PIPE_NOWAIT pentru comunicarea sincron, respectiv asincron. nMaxInstances specific numrul de clieni care se vor conecta la acest pipe. La nevoie, se poate folosi constanta PIPE_UNLIMITED_INSTANCES. lungBufOut i lungBufIn specific dimensiunile bufferelor, pentru ieire i intrare. Sistemul ia aceste numere doar ca sugestii, n funcie de context el putnd s redimensioneze aceste buffere. timeOut indic, n milisecunde, durata maxim de ateptare. lpsa specific atributele de securitate. De cele mai multe ori se specific NULL, lsnd astfel pe seama sistemului fixarea acestora. Apelul sistem ntoarce un handle, care va fi folosit ca i argument n celelalte apeluri sistem legate de pipe. Dup crearea unui pipe cu nume, pe partea de server se va folosi ConnectNamedPipe. Primul parametru al acestui apel este handle este cel ntors de CreateNamedPipe. Al doilea parametru, de regul NULL, indic faptul c se ateapt la conectare pn cnd un client se conecteaz efectiv la pipe. La fel ca i la pipe anonime, se folosesc apelurile ReadFile i WriteFile pentru schimbul cu pipe. Serverul i ncheie activitatea apelnd DisconnectNamedPipe i apoi CloseHandle: Pentru client, conectarea la un pipe cu nume se face cu un apel sistem CreateFile: 7.4.2.3 Un exemplu de comunicare prin pipe Vom prezenta n continuare o pereche de programe care comunic prin intermediul unui PIPE Windows anonim. Exemplul demonstrez att crearea unui proces, cu ajutorul funciei CreateProcess ct i comunicarea printr-un pipe anonim ntre un proces printe i procesul su fiu. Procesul printe citete de la intrarea standard linii pe care le depune n pipe, spre a fii citite de procesul fiu. Procesul fiu preia aceste linii i le afieaz la ieirea sa standard. Procesul fiu se termin la primirea prin pipe a liniei "exit". Procesul printe se termin dup terminarea procesului fiu.

Windows Compendiu

19

Fiierul surs scriitor.cpp (procesul printe) este prezentat n fig. 7.5.


#include "stdafx.h" #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <memory.h> #define MESSAGE_SIZE 1000 int main(int argc, char* argv[]) { DWORD dwCodDeReturn; SECURITY_ATTRIBUTES saAtribute; STARTUPINFO siInfo; PROCESS_INFORMATION fiu; HANDLE desc_citire, desc_scriere; // Configuram atributele de securitate pe care pipe-ul // le va mosteni memset(&saAtribute, 0, sizeof(SECURITY_ATTRIBUTES)); saAtribute.nLength = sizeof(SECURITY_ATTRIBUTES); saAtribute.bInheritHandle = TRUE; // Cream un pipe anonim if (CreatePipe ( &desc_citire, &desc_scriere, &saAtribute, MESSAGE_SIZE) == FALSE) { fprintf(stderr, ("Eroare la creare pipe anonim\n")); exit(1); } // Creez procesul fiu memset(&siInfo, 0, sizeof (STARTUPINFO)); siInfo.cb = sizeof(STARTUPINFO); siInfo.lpTitle = L"Proces Fiu"; printf("Sunt procesul parinte. Incerc sa creez procesul fiu.\n"); printf("Fiul trebuie sa citeasca din pipe-ul cu descriptorul %d\n", desc_citire); wchar_t param[MAX_PATH]; wsprintf(param, L"cititor.exe %d", desc_citire); wprintf(L"Comanda de executie a fiu-ului %s\n", param); if (CreateProcess(L"cititor.exe", param, &saAtribute, &saAtribute, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &siInfo, &fiu) == FALSE) { fprintf(stderr, "Eroare la crearea procesului fiu.\n"); exit(1);

// Asteptam terminarea procesului fiu do { GetExitCodeProcess(fiu.hProcess, &dwCodDeReturn); if (dwCodDeReturn == STILL_ACTIVE) { // Trimitem linii procesului fiu char linie[1000]; DWORD cati;

Windows Compendiu BOOL code;

20 printf("Introduce-ti o linie pe care doriti sa o trimiteti procesului fiu:\n"); fgets(linie, 1000, stdin); code = WriteFile(desc_scriere, linie, strlen(linie), &cati, NULL); if (code > 0) printf("Mesajul a fost scris cu succes in pipe\n"); Sleep(1);

} } while (dwCodDeReturn == STILL_ACTIVE); CloseHandle (desc_scriere); printf("Procesul fiu tocmai s-a terminat.\n"); return (0);

Figura 7.5 Sursa scriitor.cpp Fiierul surs cititor este prezentat n fig. 7.6.
#include "stdafx.h" #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <memory.h> #define MAX 1000 void CitesteDinPipe(HANDLE desc_citire) { // numarul de octeti cititi din pipe DWORD cati; char linie[MAX] = ""; BOOL code; printf("Citesc din pipe-ul cu descriptorul %d\n", desc_citire); while (strcmp(linie, "exit\n") != 0) { code = ReadFile(desc_citire, linie, MAX, &cati, NULL); if (code == FALSE) { printf("Eroare la citire din pipe-ul cu descriptorul %d\n", desc_citire); break; } else { linie[cati]=0; printf("Am primit de la parinte mesajul %s", linie); } } } int main(int argc, char* argv[]) { HANDLE desc_citire; if (argc != 2) { fprintf(stderr, "Eroare. Trebuie specificat ca parametru descriptorul de citire din pipe\n"); exit(1);

Windows Compendiu }

21

desc_citire = (HANDLE) atoi(argv[1]); printf("Sunt procesul cititor. Citesc din pipe-ul anonim avand descriptorul %d\n", desc_citire); CitesteDinPipe(desc_citire); return 0; }

Figura 7.6 Sursa cititor.cpp Fiierele surs din figurile 7.5 i 7.6 se pot compila folosind Microsoft Visual Studio .NET. Se va lansa n execuie doar programul scriitor.exe, programul cititor.exe fiind lansat automat de ctre scriitor (procesul printe). Pentru alte detalii privind comunicarea prin pipe sub Windows se poate consulta [9] [20]. 7.4.3 Comunicarea ntre procese Windows prin memorie partajat

7.4.3.1 Fiiere I/O mapate n memorie i zone partajate Maparea (gzduirea) fiierelor n memoria intern este o facilitate preluat de Windows NT de la sistemul de operare DOS. Aceast mapare permite aplicaiilor s acceseze pri ale fiierului mapat folosind pointeri din memoria intern, n loc de accesare a discului. Prin aceasta se obine o vitez de acces mult mai mare. Printre alte avantaje ale maprii, mai amintim faptul c se beneficiaz de mecanismul de cache-ing i de paginare a memoriei interne, oferit de WindowsNT. O aplicaie poate mapa fiiere de la 1 octet pn la 2G octei. Fierele mapate n memorie permit, de asemenea, ca dou sau mai multe procese s partajeze aceste fiiere i implicit s partajeze memoria intern. In continuare, prezentm o succesiune de 5 (cinci) pai ce trebuie urmai de orice aplicaie Windows care folosete o zon de memorie partajat: 1. Crearea segmentului de memorie partajat folosind funcia CreateFileMapping. Aceast operaie se poate realiza n dou moduri: a) folosind un fiier definit anterior de ctre utilizator, cu ajutorul apelurilor CreateFile sau OpenFile. b) folosind o pagin sistem, specificat cu ajutorul unui handler predefinit, cu valoarea 0xFFFFFFFF. Dac segmentul de memorie partajat exist deja, deschiderea accesului la segment se obine cu ajutorul funciei OpenFileMapping. Obiectul Windows, asociat segmentului de memorie partajat, poart numele de file-mapping. Att funcia CreateFileMapping, ct i funcia OpenFileMapping ntorc un handle la obiectul file-mapping asociat segmentului de memorie partajat referit. 2. Maparea propriu-zis a segmentului de memorie partajat (reprezentat prin obiectul filemapping), n spaiul de memorie al procesului curent, este realizat cu ajutorul apelului MapViewOfFile. Aceast funcie ntoarce un pointer la o poriune din aceast memorie partajat. 3. Pointerul obinut n urma apelului precedent, permite aplicaiei apelante s acceseze zona de memorie partajat n citire i/sau scriere, n funcie de parametrii specificai la apel.

Windows Compendiu

22

4. Operaia complementar celei de mapare, se realizeaz cu autorul apelului UnmapViewOfFile. Astfel, se realizeaz detaarea aplicaiei curente de la segmentul de memorie partajat, prin eliberarea spaiului de memorie ocupat prin operaia de mapare. 5. In final, nchiderea handle-ului la obiectul file-mapping se realizeaz cu funcia CloseHandle. De asemenea, folosind CloseHandle, se nchide fiierul deschis cu CreateFile sau OpenFile. Crearea / deschiderea n varianta 1.b, cu handle-ul special 0xFFFFFFFF permite folosirea unei singure zone partajate n sistem. Folosind numai acest handle, NU este posibil ca pe acelai sistem s existe mai multe zone de memorie partajat. Pentru a permite ca fiecare grup de procese s-i foloseasc propria zon de memorie partajat, trebuie s se foloseasc crearea / deschiderea n varianta 1.a, CreateFile / OpenFile. Astfel, se creaz / deschide cte un fiier mapat n memorie pentru fiecare zon de memorie partajat dorit. In continuare, punctm o comparaie, la nivel API, ntre operaiile de lucru cu memorie partajat sub Unix i sub Windows: CreateFileMapping MapViewOfFile UnmapViewOfFile CloseHandle --> --> --> --> shmget shmat shmdt shmctl

Sintaxa exact, a funciilor implicate n utilizarea segmentelor de memorie partajat sub Windows, va fi descris mai jos. Prototipurile funciilor CreateFile, ReadFile i WriteFile au fost prezentate n 3.2.3.2. Prototipurile celorlalte funcii folosite sunt descrise n continuare:
HANDLE CreateFileMapping(HANDLE hFis, LPSECURITY_ATTRIBUTES descr_sec, DWORD prot, DWORD maxHigh, DWORD maxLow, LPCTSTR nume_file_mapping);

hFis handle la fiierul de mapat; valoarea 0xFFFFFFFF pentru acest parametru indic o pagin implicit sistem. descr_sec descriptor de securitate; dac se specific valoarea NULL pentru acest parametru, sistemul va folosi atributele de securitate implicite.
prot atribut de protecie pentru obiectul de mapat. maxHigh i maxLow compun dimensiunea pe 32 de bii a obiectului de mapat. nume_file_mapping numele obiectului file-mapping

HANDLE OpenFileMapping( DWORD acces, BOOL bHandleMostenire, LPCTSTR nume_file_mapping );

acces indic modul de acces (citire i/sau scriere) bHandleMostenire indic dac obiectul file-mapping va fi motenit de eventualele procese fii nume_file_mapping numele obiectului file-mapping

Windows Compendiu 23 LPVOID MapViewOfFile( HANDLE hFileMap, DWORD acces, DWORD offsetHigh, DWORD offsetLow, DWORD nrOctetiDeMapat );

hFileMap handle la CreateFileMapping)

obiectul

file-mapping

(handle

returnat

de

acces indic modul de acces (citire i/sau scriere) offsetHigh i offsetLow compun offsetul pe 32 de bii, a zonei de memorie de mapat nrOctetiDeMapat numrul de octei de mapat

BOOL UnmapViewOfFile(LPCVOID lpAdresaMapata);


lpAdresaMapata adresa de memorie unde ncepe maparea

Pentru mai multe informatii legate de semnificaia parametrilor i prototipurile funciilor ce operareaz cu memorie partajat sub Windows, se recomand consultarea documentaiei MSDN. 7.4.3.2 Implementarea foii de calculsub Windows Aplicaia de mai jos implementeaz calculul tabelar pentru problema definit n 3.3.1. Programul 3.35 este fiierul header al aplicaiei, similar programului 3.31.
#include <assert.h> #include <ctype.h> #include <stdio.h> #include <time.h> #include <winsock2.h> #include <windows.h> #include "semnaturaTemporala.cpp" #define SMAX 1000 struct foaie_de_calcul { char s[4][100]; int v[4]; }; #define LUNGIME sizeof(struct foaie_de_calcul) #define NUME_SEGMENT_MEMORIE_PARTAJATA "MemoriaMeaPartajata"

Programul 3.1 Fiierul header memPartFoaieWin.h Fiierul header definete foaia de calcul, lungimea acesteia i definete numele segmentului de memorie partajat. La fel ca n cazul comunicrii folosind pipe, i mecanismul de comunicare folosind memorie partajat, sub Windows, presupune c creatorul obiectului file-mapping este activ, ct timp scriitorii i cititorii partajeaz resursa respectiv. De acceea, vom include operaia de creare a obiectului file-mapping n programul care acceseaz obiectul file-mapping n scriere. Sursele programelor scriitor i cititor sunt prezentate n programul 3.36, respectiv 3.37.
#include "memPartFoaieWin.h" // 0 #define NR_ITER 100

Windows Compendiu int main () { int it,shmd,i,x; struct foaie_de_calcul *mp; char s[SMAX]; HANDLE handleMemoriePartajata;

24

//creeaza obiectul file-mapping handleMemoriePartajata=CreateFileMapping ((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0, sizeof(struct foaie_de_calcul),NUME_SEGMENT_MEMORIE_PARTAJATA); assert(handleMemoriePartajata!=NULL); mp = (struct foaie_de_calcul *)MapViewOfFile( handleMemoriePartajata, FILE_MAP_WRITE, 0, 0, 100); assert(mp!=NULL); // 1 for(it=0;it<NR_ITER;Sleep(1000*(rand()%2)),it++) { i = rand()%3; x = 1 + rand()%99; strcpy(s, semnaturaTemporala()); // 2 strcpy(mp->s[i], s); mp->v[3] = mp->v[3] - mp->v[i] + x; mp->v[i] = x; strcpy(mp->s[3], s); // 3 } UnmapViewOfFile(mp); CloseHandle(handleMemoriePartajata); // 4 }

Programul 3.2 Sursa memPartScriitorFoaie.cpp


#include "memPartFoaieWin.h" #define NR_ITER 50 int main () { int it,shmd,i,x,j; struct foaie_de_calcul *mp,*copie; char s[SMAX]; HANDLE handleMemoriePartajata; //deschide un obiect file-mapping handleMemoriePartajata=OpenFileMapping( FILE_MAP_READ,0,NUME_SEGMENT_MEMORIE_PARTAJATA); assert(handleMemoriePartajata!=NULL); mp = (struct foaie_de_calcul *) MapViewOfFile(handleMemoriePartajata,FILE_MAP_READ,0,0, 0); assert(mp!=NULL); copie=(struct foaie_de_calcul* )malloc(sizeof(struct foaie_de_calcul)); for(it=0;it<NR_ITER;Sleep(1000*(rand()%2)),it++) { for (j=0;j<4;j++) {

Windows Compendiu 25 copie->v[j]=mp->v[j]; strcpy(copie->s[j],mp->s[j]); } strcpy(s, semnaturaTemporala()); x = copie->v[3]-copie->v[0]-copie->v[1]-copie->v[2]; i = 0; if(strcmp(copie->s[3],copie->s[0]) && strcmp(copie->s[3], copie->s[1]) && strcmp(copie->s[3], copie->s[2])) i = 1; printf("citire: %s", s); if (i) printf("\tSemnatura total eronata!"); if (x) printf("\t%d este diferenta la total!",x); printf("\n"); for(i=0; i<4; i++) printf("%s\t%d\n", copie->s[i], copie->v[i]); printf("\n");

UnmapViewOfFile(mp); CloseHandle(handleMemoriePartajata);

Programul 3.3 Sursa memPartCititorFoaie.cpp

7.4.4

Sincronizarea proceselor prin semafoare Windows

7.4.4.1 Semafoare cu nume i anonime; operaii Windows modeleaz n mod unic att sincronizarea ntre procese, ct i sincronizarea threadurilor n cadrul aceluiai proces. Crearea unui obiect semafor windows de ctre un proces se face folosind apelul CreateSemaphore(). Semaforul windows poate fi: semafor cu nume, folosit pentru comunicarea ntre procesele de pe aceeai main. In acest scop, numele este elementul de identificare al semaforului de ctre toate procesele interesate, care folosesc acest nume n apelul OpenSemaphore(). semafor anonim, folosit pentru comunicarea ntre thread-urile aceluiai proces. Elementul de identificare n aceast situaie este constituit de handle-ul ntors de funcia CreateSemaphore(), care este motenit de thread-urile fii interesate.

Prototipul CreateSemaphore() este: HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES securitate, LONG valoareInitiala, LONG valoareMaxima, LPCTSTR numeSemafor);

securitate reprezint un descriptor de securitate, care specific dac handlerul la semafor va fi motenit n procesele fii; pentru valoarea NULL a acestui parametru, handlerul nu poate fi motenit.

Windows Compendiu

26

valoareInitiala specific valoarea iniial a semaforului; trebuie s fie mai mare sau egal dect 0 i mai mic sau egal dect valoareMaxima. valoareMaxima indic valoarea maxim pe care o poate lua semaforul i trebuie s fie mai mare dect 0. numeSemafor specific numele semaforului. Funcia ntoarce un handle la semafor, care poate fi utilizat n thread-urile fii. valoareInitiala i valoareMaxima sunt numere nenegative. Dac numeSemafor este un string, atunci avem de-a face cu un semafor cu nume, iar dac aceast valoare este NULL avem de-a face cu semafor anonim. Se observ, c spre deosebire de semafoarele Unix, n acest caz se impune o limit superioar a valorii acestora. Odat creat un semafor cu nume, se poate face deschiderea accesului la semafor de ctre un proces. Deschiderea se face cu OpenSemaphore(), care are prototipul: HANDLE OpenSemaphore (DWORD modDeAcces, BOOL mostenire, LPCTSTR numeSemafor); modDeAcces indic modul de acces la semafor i poate lua una dintre valorile (sau combinaii ale acestora): SEMAPHORE_ALL_ACCESS, SEMAPHORE_MODIFY_STATE, SYNCHRONIZE. (aceste valori pot fi restricionate prin atributele de securitate specificate la crearea semaforului). mostenire atribut de motenire: indic (pentru valoarea TRUE) dac handlerul la semafor va fi motenit n procesele fii Orice semafor windows este caracterizat prin starea lui, care poate fi una dintre urmtoatele: setat sau semnalat atunci cnd valoarea semaforului este strict pozitiv; nesetat sau nesemnalat atunci cnd valoarea este egal cu zero.

Cresterea valorii unui semafor se face prin apelul: BOOL ReleaseSemaphore(HANDLE hSemafor, LONG valoareDeAdaugat, LPLONG adresaVechiiValori ); hSemafor specific handlerul semaforului de incrementat. valoareDeAdaugat indic valoarea cu care se incrementeaz valoarea curent a semaforului.
adresaVechiiValori specific adresa unde va fi memorat vechea valoare a semaforului.

Dac semaforul hSemafor are valoarea maxim (specificat la creare), atunci acest apel de incrementare eueaz. De asemenea, dac se specific o valoare negativ pentru parametrul valoareDeAdaugat, apelul eueaz.

Windows Compendiu

27

Decrementarea valorii semaforului este realizat atunci cnd asupra obiectului semafor se aplic o operaie de ateptare. Aceast operaie se realizeaz prin intermediul funciilor de ateptare. Acestea au doi parametri: 1. handleSemafor ctre obiectul semafor la care se ateapt. 2. timeout o constant ce indic un timp maxim de ateptare; frecvent, aceast constant are valorea INFINITE, pentru a indica ateptare pn la apariia condiiei legat de starea semaforului. Funcii de ateptare Win32 API ofer un set de funcii de ateptare pentru a permite unui thread s-i ntrerup temporar execuia. Exist trei tipuri de funcii de ateptare: i) ii) iii) single-object multiple-object alertable

Funciile de ateptare blocheaz execuia programului pn cnd criteriul specificat a fost ndeplinit. Tipul funciei de ateptare determin criteriul utilizat. n timpul ateptrii, threadul consum foarte puine resurse sistem (este vorba de o ateptare pasiv). i) Funciile de ateptare de tip single-object: WaitForSingleObject, SignalObjectAndWait i WaitForSingleObjectEx. DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds); hHandle -handler-ul obiectului la care se ateapt dwMilliseconds -intervalul de timp maxim de ateptare. Valoarea INFINITE indic ateptare pn la semnalarea obiectului. Funcia poate s ntoarc urmtoarele valori: WAIT_ABANDONED -dac obiectul nu a fost eliberat de threadul su proprietar nainte ca apelul s se termine WAIT_OBJECT_O -dac obiectul a fost semnalat WAIT_TIMEOUT -dac intervalul de timp a expirat DWORD WaitForSingleObjectEx(HANDLE hHandle, DWORD dwMilliseconds, BOOL bAlertable);
Pentru a descrie semnificaia parametrului bAlertable, menionm c funciile I/O asincrone precum ReadeFileEx sau WriteFileEx au ca parametru numele unei funcii numite rutine de terminare a operaiei de I/O asincrone. La ncheierea unei operaii asincrone I/O, sistemul va memora ntr-o coad de ateptare rutina de terminare asociat operaiei I/O respective.

Rutina de terminare este executat numai dac a fost apelat o funcie de ateptare de tipul alertable (de exemplu WaitForSingleObjectEx) avnd parametrul bAlertable cu valoarea TRUE. Aceast funcie revine din ateptare, pentru a executa rutina de terminare a operaiei I/O asincrone terminate [83, 102].

BOOL SignalObjectAndWait(HANDLE hObjectToSignal,

Windows Compendiu

28

HANDLE hObjectToWaitOn, DWORD dwMilliseconds, BOOL bAlertable); hObjectToSignal - handler la obiectul de semnalat hObjectToWaitOn - handler la obiectul a crei semnalare se ateapt dwMiliseconds - intervalul de timp maxim de ateptare bAlertable are semnificaia descris la funcia WaitForSingleObjectEx Aceste funcii se termin atunci cnd apare unul din urmtoarele evenimente: obiectul specificat este semnalat intervalul de timp maxim de ateptare (poate avea valoarea INFINITE). Funcia SignalObjectAndWait permite threadului apelant s stabileasc atomic starea unui obiect de sincronizare ca fiind semnalat i apoi s atepte ca un alt obiect de sincronizare s fie semnalat. ii) Funciile de ateptare de tip multiple-object: WaitForMultipleObjects, WaitForMultipleObjectsEx, MsgWaitForMultipleObjects, i MsgWaitForMultipleObjectsEx. Aceste funcii permit threadului apelant s specifice un tablou care conine unul sau mai multe handler-e la obiecte de sincronizare. Un astfel de apel se termin atunci cnd se schimb starea unuia dintre obiectele de sincronizare, sau a expirat timpul specificat. iii) Funciile de tip SignalObjectAndWait, WaitForSingleObjectEx alertable: MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx i

Funciile de tip alertable difer de celelalte funcii de ateptare prin faptul c ofer, n plus, posibilitatea revenirii din ateptare la terminarea unei operaii de intrare/ieire asincrone, spre a executa rutina de terminare aferent [83]. Funciile de ateptare pot modifica starea unui obiect de sincronizare astfel: valoarea unui semafor este incrementat i starea semaforului setat ca nesemnalat, dac valoarea sa este zero. starea unei variabile mutex sau a unui timer de sincronizare este setat ca nesemnalat.

Tabelul urmtor prezint pe scurt principalele funcii de ateptare. Funcie wait WaitForSingleObject() WaitForSingleObjectEx() Descriere Ateapt dup un anumit obiect ca acesta s ajung n starea setat (valoarea semaforului > 0). Ca i precedentul, plus ateptarea a altor dou evenimente: terminarea unei operaii de intrare ieire, sau sosirea unui apel asincron n threadul curent. Ateapt dup o mulime de obiecte. Ieirea din ateptare se poate face fie cnd unul dintre obiecte este setat, fie cnd toate obiectele ajung n starea setat. Ca i precedentul, plus ateptarea celor dou

WaitForMultipleObjects()

WaitForMultipleObjectsEx()

Windows Compendiu

29

evenimente specificate WaitForSingleObjectEx.

cazul

funciei

7.4.4.2 Exemple de utilizare a semafoarelor Windows Ca prim exemplu de utilizare a semafoarelor, relum implementarea foii de calcul sub Windows, de data aceasta asigurnd protecia informaiilor, mai precis a semnturilor din vectorul s i a valorilor din vectorul v, folosind semafoare. Definim n fiierul antet semaforBinarWin.h (programul 3.43) operaiile specificate n 3.4.1.5 i implementate sub Unix n programul 3.38.
#define valMaxSem 1000 #include "err_sys.cpp" HANDLE semId; int creareSemBin() { //creare semafor semId=CreateSemaphore(NULL,1,valMaxSem,NULL); if (semId==NULL) err_sys("Eroare la crearea semaforului.\n"); return 0; } int stergereSemBin() { CloseHandle(semId); return 0; } void P(HANDLE semId) { WaitForSingleObject(semId,INFINITE); } void V(HANDLE semId) { ReleaseSemaphore(semId,1,NULL); } void blocare() { P(semId); } void deblocare() { V(semId); }

Programul 3.4 Sursa semaforBinarWin.h Avnd definite aceste operaii, sincronizarea accesului pentru scriere la datele din foaia de calcul se face nlocuind n programul 3.36 liniile comentate, cu apeluri de funcii de lucru cu semafoare, conform schemei din figura 3.13. Acest fiier antet, mpreun cu operaiile pe care le definete, este folosit i n cazul problemei Productor Consumator implementat sub Windows. La fel ca n implementarea sub Unix (3.4.1.5) i aici vom folosi ca resurs partajat un pipe cu nume, protejat cu ajutorul aceluiai set de semafoare: gol, plin i mutex.

Windows Compendiu

30

Vom introduce din sursa programului (3.44) producator.cpp doar fragmentul care creeaz semafoarele i rutina depune(), care introduce un obiect n pipe. Rutinele creeaza() i producator() sunt aceleai cu cele descrise n programul 3.41.
//... //depune "obiect" in fifo int depune(int obiect) { printf("Producatorul depune obiectul: %d\n",obiect); unsigned long no; WriteFile(fw,&obiect,sizeof(int),&no,NULL); //la al 30-lea obiect, producatorul isi incheie executia if ((obiect%30)==0) { CloseHandle(gol); CloseHandle(plin); CloseHandle(mutex); CloseHandle(fw); exit(1); } Sleep(1000*(rand()%3)); return 1; } main() { gol=CreateSemaphore(NULL,DimBuf,DimBuf,"Gol"); err_sys(gol!=NULL); plin=CreateSemaphore(NULL,0,DimBuf,"Plin"); err_sys(plin!=NULL); mutex=CreateSemaphore(NULL,1,1,"Mutex"); err_sys(mutex!=NULL); //creeaza pipe cu nume //apeleaza Producator(); }

Programul 3.5 Fragment din sursa producator.cpp Similar, din sursa programului consumator.cpp, introducem doar rutina extrage(), care obine un obiect din fifo, i fragmentul din funcia main, care deschide accesul la semafoarele utilizate. (Rutinele consuma() i Consumator() sunt aceleai cu cele descrise n programul 3.42).
//.. int extrage(int* obiect) { unsigned long no; ReadFile(fr,obiect,sizeof(int),&no,NULL); return 1; } main() { gol=OpenSemaphore(SEMAPHORE_ALL_ACCESS,TRUE,"Gol"); err_sys(gol!=NULL); plin=OpenSemaphore(SEMAPHORE_ALL_ACCESS,TRUE,"Plin"); err_sys(plin!=NULL); mutex=OpenSemaphore(SEMAPHORE_ALL_ACCESS,TRUE,"Mutex"); err_sys(mutex!=NULL); //se conecteaza la resursa pipe cu nume // apeleaza rutina Consumator(); }

Programul 3.6 Fragment din sursa consumator.cpp

Windows Compendiu

31

7.4.5

Comunicarea ntre procese Windows

prin Mailslot

Arhitectura mailslot permite dezvoltarea de aplicaii client/server care pot s comunice unidirecional, de la client la server, n reele LAN, pe canale rapide dar nesigure bazate pe datagrame [11]. Datagramele pot fi direcionate spre un anume proces, sau spre un grup de procese care ruleaz ntr-un domeniu definit de administratorul reelei. 7.4.5.1 Arhitectura mailslot Mailslot este o facilitate de comunicaii n care un client, (de obicei staie de lucru NT), poate transmite prin datagram un mesaj: unui server mailslot -unicast; serverelor mailslot din reeaua local care aparin unui domeniu stabilit de administrator multicast; tuturor serverelor mailslot din reeaua local - broadcast;

O comunicare de la client spre server se constituie ntr-un mesaj, deci serverul, dac primete mesajul, este sigur c acesta conine mesajul complet. Din cauz c se opereaz cu datagrame (pentru spor de vitez), nu este asigurat livrarea, nici livrarea de duplicate. Aa c proiectantul trebuie s-i proiecteze mecanisme proprii de control al livrrilor. Este adevrat c, ntr-o reea LAN bine construit i bine configurat, probabilitatea de eec a unei livrri este foarte mic. Din punct de vedere al succesiunii de protocoale care particip la comunicaia mailslot, aceasta este ilustrat n figura 3.16. Figura 3.7 Arhitectura mailslot
S e rv e r m a ils lo t S e rv e r N T

N e tB IO S

T C P /IP

IP X

Sintaxa numelor folosite n mailslot este prezentat n tabelul din fig. 3.17: Sintaxa \\.\mailslot\numeMailslot \\adresaMasina\mailslot\numeMailslot Descriere Serverul mailslot de pe maina local Serverul mailslot de pe maina avnd adresa specificat \\numeDomeniu\mailslot\numeMailslot Multicast spre toate serverele mailslot care fac parte din domeniul specificat \\*\mailslot\numeMailslot Broadcast spre toate serverele mailslot din reeaua LAN Figura 3.8 Sintaxa numelor folosite n mailslot

7.4.5.2 Programarea folosind mailslot

Windows Compendiu

32

Un server mailslot creeaz un obiect mailslot care citeste mesajele trimise de clieni. Serverul i clienii se pot afla pe aceeai main (local) sau pot rula pe dou maini diferite. Crearea unui server mailslot se face cu funcia CreateMailslot(). Stabilirea unui contact de ctre un client cu serverul - deschidere -de mailslot se face prin funcia CreateFile(). Citirea unui mesaj de ctre server se face prin funcia ReadFile(). Scrierea de ctre client a unui mesaj se face prin funcia WriteFile(). Inchiderea unui mailslot, att la client ct i la server se face folosind funcia CloseHandle(). Prototipurile funciilor CreateFile, OpenFile, CloseHandle au fost prezentate n 3.2.3.2. ReadFile, WriteFile i

In continuare, prezentm prototipul funciei CreateMailslot. HANDLE CreateMailslot (LPCTSTR lpszName, DWORD cbMaxMsg, DWORD dwReadTimeout, LPSECURITY_ATTRIBUTES lpsa); lpszName specific numele mailslot indicnd obligatoriu maina local, conform tabelului de mai sus. cbMaxMsg indic dimensiunea maxim a mesajului n octei. Serverul poate primi unul sau mai multe mesaje, fiecare trebuind s fie egal sau mai mic dect dimensiunea maxim specificat. Nu exist limit pentru numrul de mesaje care se pot recepiona. Serverul poate specifica NULL ca i dimensiune maxim pentru a putea recepiona mesaje de orice dimensiune acceptat de sistem. dwReadTimeout specific, n milisecunde, perioada implicit de time-out asociat citirii unui mesaj din mailslot cand acesta este gol. Valoarea 0 inseamn c ReadFile() se opreste imediat fr nici o eroare cnd mailslot-ul este vid. Valoarea MAILSLOT_WAIT_FOREVER asigur serverul c ReadFile() nu se va bloca pan cnd este accesibil un mesaj. Dac exist deja un mesaj n coada de mesaje atunci functia ReadFile() se va executa imediat indiferent de valoarea dwReadTimeout. lpsa permite asocierea unei variabile de tipul SECURITY_ATRIBUTES mailslot-ului. Astfel, serverul poate restriciona accesul la mailslot. Specificnd NULL ca i atribut de securitate, se las n seama sistemului de operare. n urma apelului CreateMailslot() serverul obine un handle ctre noul mailslot creat, pe care l foloseste pentru a citi mesaje folosind funcia ReadFile(). Serverul specific funciei handle-ul respectiv i un buffer unde dorete s recepioneze mesajele. Dac exist deja un mesaj n coada de mesaje funcia ReadFile() se execut imediat copiind n bufer mesajul. Dac coada de mesaje este goal, funcia va atepta pn n mailslot vor fi atia octei ci ateapt funcia. Un client mailslot este un program care trimite mesaje ctre un server mailslot. dimensiunea mesajului este limitat la 64K octei atunci cnd clientul scrie ctre un anumit mailslot, respectiv la 400 octei atunci cnd clientul trimite un broadcast ctre toate serverele mailslot din domeniu.

Windows Compendiu

33

7.4.5.3 Exemplu de lucru cu mailslot Credem c cel mai potrivit exemplu de lucru cu mailslot, pentru a se putea face comparaiile de rigoare, este rezolvarea problemei prezentate n 3.5.1. Deoarece la acest mod de comunicare nu exist implicit noiunea de tip de mesaj, vom renuna la stabilirea unui tip, aa cum am fcut-o n 3.5.2.3. In rest, similar exemplului prezentat n 3.5.2.3, fiecare mesaj, care va fi scris/citit, din/n mailslot, este format dintr-o semnatur (a scriitorului) i un string text (citit de la tastur). Aceste observaii reies i din fiierul antet 3.50, care definete structura unui mesaj din mailslot.
#include <stdio.h> #include <time.h> #include <winsock2.h> #include <windows.h> #include "err_sys.cpp" #define DSEMN 56 #define SMAX 100 #define DIM_MAX_MESAJ DSEMN+SMAX #define TIMEOUT_CITIRE (5 * 60 * 1000) // 5 minute #define NUME_MAILSLOT "\\\\.\\mailslot\\coada" #define MESAJ_DE_TERMINARE "STOP" typedef struct { //definirea structurii unui mesaj char semn[DSEMN]; char linie[SMAX]; } Mesaj;

Programul 3.7 Fiierul header antetMailslot.h Aa cum am specificat i n prezentarea teoretic, scenariul de lucru cu mailslot-uri presupune un proces cititor i scriitor, care trebuie s fie activate n aceast ordine. Explicaiile de mai jos vor clarifica aceast afirmaie. Cititorul creeaz resursa mailslot (folosind funcia CreateMailslot), dup care ateapt mesajele introduse de eventualii scriitori, n mailslot-ul creat. Un scriitor deschide mailslot-ul pentru scriere (folosind CreateFile), dup care scrie n acest mailslot, pe rnd, n mod repetat, un string citit de la tastatur, mpreun cu semntura returnat de apelul semnaturaTemporala. Dup fiecare scriere, procesul scriitor ateapt un timp aleator, ntre 0 i 10 secunde. Activitatea scriitorului nceteaz la citirea de la tastatur a stringului STOP, iar activitatea cititorului, la extragerea din mailslot a aceluiai string (indiferent de string-ul reprezentnd semntura). Prezentm mai jos (programele 3.51 i 3.52) sursele corespunztoare programului cititor i scriitor.
#include "antetMailslot.h" #include "semnaturaTemporala.cpp" HANDLE hMailSlot;

Windows Compendiu void mailslotCititor() { Mesaj mesg; unsigned long dwOctetiCititi; char szBuferMesaj[SMAX]; int dwCodReturn;

34

for (;;) { //citirea mesajului de la clienti if (! ReadFile ( hMailSlot, // handle mailslot &mesg, // Bufer de receptionare a mesajului DIM_MAX_MESAJ, //numarul de octeti de citit &dwOctetiCititi,//numar octeti cititi efectiv NULL )) { //citire din nou daca timpul a expirat if ((dwCodReturn = GetLastError()) == WAIT_TIMEOUT) continue; //alte erori err_sys("Eroare la citirea din mailslot.\n"); } mesg.semn[strlen(mesg.semn)]='\0'; mesg.linie[dwOctetiCititi-DSEMN]='\0'; //tiparirea mesajului de la client if (dwOctetiCititi) { printf("|%s|%s|%s\n",mesg.semn,mesg.linie, semnaturaTemporala()); } //inchiderea mailslotului if (!strcmp(mesg.linie,"STOP")) { printf("Inchidere mailslot.\n"); CloseHandle (hMailSlot); break; } }//for } int main ( char argc, char ** argv){ char szBuferMesaj [ DIM_MAX_MESAJ + 1]; DWORD dwOctetiCititi, dwCodDeReturn; BOOL fSfarsit = FALSE; //crearea unui MailSlot if ( (hMailSlot = CreateMailslot( NUME_MAILSLOT, // numele DIM_MAX_MESAJ, // Limita mesajului TIMEOUT_CITIRE, //Asteptare max. citire NULL //Securitate implicita ))== INVALID_HANDLE_VALUE) err_sys("Eroare la creare mailslot.\n"); mailslotCititor();

Programul 3.8 Sursa mailslotCititor.cpp


#include "antetMailslot.h" #include "semnaturaTemporala.cpp" HANDLE hMailSlot;

void mailslotScriitor() { DWORD dwOctetiDeScris, dwOctetiScrisi; char buf[SMAX],b[2*SMAX]; Mesaj mesg;

Windows Compendiu

35

for (;;) { gets(buf); strcpy(mesg.semn,semnaturaTemporala()); strcpy(mesg.linie,buf); dwOctetiDeScris=DSEMN+strlen(buf); //trimite mesajul utilizatorului la server if (! WriteFile ( hMailSlot, //handle mailslot &mesg, //buferul ce contine mesajul dwOctetiDeScris, //nr. octeti de scris &dwOctetiScrisi, //nr octeti scrisi efectiv NULL)) err_sys("Eroare la scrierea in Mailslot.\n"); if (!strcmp(mesg.linie,"STOP")) { printf("Inchidere mailslot.\n"); CloseHandle (hMailSlot); } Sleep(1000*(rand()%10)); } } int main ( char argc, char ** argv){ printf("Connectare la %s\n", NUME_MAILSLOT); //deschidere mailslot if((hMailSlot = CreateFile(NUME_MAILSLOT, //nume mailslot GENERIC_WRITE, //mod de acces FILE_SHARE_READ, // share mod NULL, //securitate implicita OPEN_EXISTING, //desch. maislot existent FILE_ATTRIBUTE_NORMAL, //atribute normale NULL ))== INVALID_HANDLE_VALUE) err_sys("Eroare la deschiderea maislotului.\n"); mailslotScriitor(); }

Programul 3.9 Sursa mailslotScriitor.cpp Lsm pe seama cititorului s experimenteze cu aceste dou programe lucrul multiserver ntro reea LAN. Evident, pentru aceasta trebuie s ia legtura cu administratorul reelei LAN la care are acces, pentru a putea implementa sistemul.

7.5 Thread-uri pe platforme Microsoft: Windows NT, 2000

7.5.1

Caracteristici ale thread-urilor sub Windows NT

In 4.1.2 am precizat o clasificare a thread-urilor, n funcie de implementarea acestora n: thread-uri nucleu i thread-uri user. Aceste dou tipuri de thread-uri se regsesc i pe platformele Windows, sub denumirea de thread-uri, respectiv fibre sau fibers. Din punct de vedere al sarcinilor de executat, Win32 [63, 67] mai definete dou tipuri de thread-uri: user-interface(UI) i worker. Thread-urile user-interface au asociate una sau mai multe ferestre, pentru care sistemul ateapt evenimente ntr-o bucl de mesaje. La sosirea unui eveniment, este apelat metoda care deservete evenimentul / fereastra corespunztoare. Sincronizarea thread-urilor UI este realizat de sistem, iar pentru thread-urile worker sincronizarea este sarcina programatorului. In continuare vom trata numai thread-urile worker.

Windows Compendiu

36

Sub Windows NT, threadul este cea mai mic entitate executabil la nivel nucleu. Fiecare proces conine unul sau mai multe thread-uri. In momentul crerii procesului (vezi 3.1.3.3), odat cu el se creaz threadul primar al procesului. Threadul primar poate crea la rndul su alte thread-uri cu care va partaja spaiul de adrese al procesului comun. De asemenea, ele mai partajeaz i alte resurse sistem: descriptori de fiiere, etc. Fiecare thread are ns un context propriu, inclusiv stive de execuie i date specifice. In cele ce urmeaz, sunt prezentate cteva dintre componentele eseniale ale unui thread n executivul NT: Un identificator unic, numit client ID. Contextul threadului, care const din: -Coninutul unui set de regitri volatili, reprezentnd starea procesorului. -Dou stive, una pentru utilizarea n mod utilizator i cealalt pentru utilizarea n mod nucleu. -O zon de memorie privat folosit de ctre subsisteme i diversele biblioteci dinamice (DLL-uri). In documentaia interfeei Win32 API, se precizeaz c un thread are asociate una sau mai multe fibre (fibers). Fibra este cea mai mic entitate executabil care este creat i executat la nivel utilizator. Gestiunea este realizat exclusiv de programator pentru toate operaiile: creare, planficare, distrugere, etc. Planificarea thread-urilor este sarcina exclusiv a nucleului sistemului de operare i se bazeaz pe prioritatea de baz a thread-urilor. In cadrul unui proces, thread-urile se execut independent unele de celelalte i implicit nici unul nu are cunotin de cellalt. In funcie de aplicaia concret, programatorul poate decide dac e nevoie ca thread-urile s se vad ntre ele.

7.5.2

Operaii asupra thread-urilor: creare, terminare

7.5.2.1 Crearea unui thread Prototipul funciei de creare este:


HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );

lpThreadAttributes - pointer la atributele de securitate dwStackSize - dimensiunea iniial a stivei threadului, n octei lpStartAddress - pointer la funcia ce dirijeaz threadul lpParameter - argumentul funciei dwCreationFlags - flaguri de creare lpThreadId - pointer la identificatorul threadului

Windows Compendiu

37

La crearea threadului, este generat un descriptor care identific n mod unic threadul n sistem. Dup creare, se lanseaz n execuie funcia specificat prin parametrul lpStartAddress. Aceast funcie are parametrii specificai prin lpParameter i ntoarce o valoare de tip DWORD. Pentru a determina valoarea ntoars de aceast funcie, se poate folosi funcia GetExistCodeThread(). Dac parametrul dwCreationFlags are valoarea 0, atunci threadul se va executa imediat, altfel el va fi trecut n starea CREATE_SUSPENDED. Dac funcia se execut cu succes, valoarea ntoars este un handler la threadul nou creat, altfel funcia ntoarce NULL. Dac nu se precizeaz un descriptor de securitate, handler-ul dispune de drepturi absolute de acces la threadul nou creat si poate fi folosit n orice funcie care cere un obiect de tipul descriptor de thread (de exemplu functiile: suspend(), resume(), terminate()). Pe de alt parte, dac este furnizat un parametru de securitate, nainte ca un proces s obin acces la un anumit thread prin intermediul descriptorului de thread, se verific dac acest acces este autorizat. Dac parametrul lpStartAddress reprezint o adres invalid se genereaz o excepie i threadul se termin cu eroare. 7.5.2.2 Terminarea unui thread Un thread i ncheie execuia n urmtoarele condiii: la ieirea din procedura asociat threadului. la apelul funciilor ExitProcess(), ExitThread() apelate din threadul curent. dac se apeleaz ExitProcess() sau TerminateThread() din alte procese, cu argument handler-ul threadului care urmeaz a fi distrus sau din alte thread-uri, folosind, de asemenea, funcia TerminateThread(). Prototipurile unora dintre funciile de terminare a unui thread sunt:
void ExitThread(UINT exitcode); BOOL TerminateThread(HANDLE hThread, DWORD exitcode); BOOL GetExitCodeThread(HANDLE hThread, LPDWORD exitcode);

Parametrul hThread identific threadul care se va termina. Apelul ExitThread provoac terminarea threadului curent cu ntoarcerea codului de ieire exitcode. Dup apelul funciei, stiva asociat threadului este eliberat, iar starea obiectului thread devine semnalat. Funcia GetExitCodeThread primete n zona punctat de exitcode codul de ieire al threadului indentificat prin hThread. Dup terminare, obiectul thread rmne n memorie pn cnd sunt nchii i toi descriptorii (handle) asociai threadului. Inchiderea se face cu apelul CloseHandle, descris n 3.2.3.1.

Windows Compendiu

38

7.5.3

Instrumente

standard

de

sincronizare

Mecanismele de comunicare i sincronizare ntre thread-uri sunt furnizate de interfaa Win32API, care furnizeaz primitive de lucru cu evenimente, semafoare, variabile mutex, seciuni critice. Aceste obiecte de sincronizare au, aa cum am mai spus n cazul semafoarelor (3.4.2.1) dou stri: semnalat i nesemnalat. Starea semnalat presupune de obicei ndeplinirea unei condiii i semnalarea acestui fapt unor thread-uri interesate. 7.5.3.1 Funcii de ateptare Cea mai simpl modalitate de comunicare este ateptarea terminrii unui thread. Pentru sincronizare se ateapt, de asemenea i dup alte obiecte de sincronizare: evenimente, semafoare, variabile mutex, etc. Pentru realizarea operaiilor de ateptare, att thread-urile, ct i celelalte obiecte de sincronizare sunt privite de NT ca i obiecte care urmeaz s ajung n starea semnalat. Funciile de ateptare modific starea unui obiect de sincronizare astfel: Dac obiectul de sincronizare este un semafor, atunci valoarea acestuia este micorat cu o unitate i starea lui este setat ca nesemnalat. Dac obiectul este o variabil mutex, atunci starea ei este setat ca nesemnalat.

Funciile Win32 API prin care se realizeaz ateptarea au fost deja prezentate n 3.4.2.1. Pentru scopurile de sincronizare a thread-urilor sunt utile urmtoarele funcii: WaitForSingleObject i WaitForSingleObjectEx, ambele primind ca parametru un handle al unui obiect de sincronizare. Ca efect, blocheaz execuia threadului curent pn cnd obiectul dat ca parametru ajunge n starea semnalat. Eventual limiteaz timpul de ateptare la un numr maxim de milisecunde (dac parametrul este diferit de INFINITE). SignalObjectAndWait permite threadului apelant ca, n mod atomic, s stabileasc starea setat a unui obiect de sincronizare i apoi s atepte ca un alt obiect de sincronizare s fie semnalat. WaitForMultipleObjects i WaitForMultipleObjectsEx permit threadului s specifice un tablou de handle la obiecte de sincronizare. Funciile returneaz starea de semnalat a unuia dintre obiecte sau a tuturora, sau expirarea intervalului de timp destinat ateptrii.

Pentru detalii asupra acestor funcii se va revedea 3.4.2.1 sau [63, 83] 7.5.3.2 Variabile mutex Reamintim c variabilele mutex permit implementarea accesului exclusiv la o resurs partajat ntre mai multe thread-uri. Semantica obiectelor de sincronizare mutex este similar cu cea ntlnit la implementarea thread-urilor de pe platformele Unix. Crearea unei astfel de variabile se face cu funcia:
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL binitialOwner, LPCTSTR lpName );

Windows Compendiu

39

lpMutexAttributes - pointer la atributele de securitate bInitialOwner - flag care specific dac variabila mutex este sau nu proprietatea threadului lpName - pointer la numele variabilei mutex In caz de succes, funcia returneaz un handler la variabila mutex, altfel ntoarce NULL. Acest handler poate fi specificat ca parametru n funciile de blocare a variabilei mutex, de eliberare a ei sau de distrugere. Ocuparea unei variabile mutex se face prin funciile de ateptare prezentate n seciunea precedent. De exemplu apelul WaitForSingleObject(g_hMutex, INFINITE) se va termina doar cnd starea variabilei mutex indentificat prin handler-ul g_hMutex devine semnalat. Eliberarea unei variabile mutex se realizeaza cu funcia: BOOL ReleaseMutex(HANDLE hMutex); hMutex - handler la obiectul mutex Distrugerea unei variabile mutex se face fie prin invocnd CloseHandle(). Dac acest apel lipsete, variabilele mutex sunt eliminate de sistem. 7.5.3.3 Semafoare fr nume In 3.4.2 am prezentat utilizarea semafoarelor NT pentru sincronizarea proceselor. Aceleai semafoare pot fi, natural, utilizate pentru sincronizarea thread-urilor. Pentru sincronizarea thread-urilor din cadrul aceluiai proces, este de preferat utilizarea semafoarelor fr nume, deoarece nucleul sistemului este scutit de gestiunea lor. Crearea unui astfel de semafor trebuie fcut folosind apelul CreateSemaphore, descris n 3.4.2.1. Pentru a fi semafor anonim trebuie ca ultimul parametru, pointer la numele semaforului, s aib valoarea NULL. Handle-ul ntors de funcia de creare trebuie salvat ntr-o variabil care s fie vizibil de ctre toate thread-urile interesate. Acest handle trebuie citat n toate funciile de ateptare Valoarea semaforului poate fi mrit cu o cantitate pozitiv apelnd funcia ReleaseSemaphore() descris n 3.4.2.1. Ea are ca prim argument handle-ul semaforului i cantitatea cu care se mrete valoarea. Ateptarea la semafor se face folosind funciile de ateptare descrise n seciunea 3.4.2.1. 7.5.3.4 Seciuni critice

O variabil de tip seciune critic se declar astfel:


CRITICAL_SECTION numeSectiuneCritica;

Utilizarea seciunii critice se face astfel:

Windows Compendiu

40

EnterCriticalSection(&numeSectiuneCritica); - - - Corpul sectiunii critice - - LeaveCriticalSection(&numeSectiuneCritica);

7.5.3.5 Alte obiecte de sincronizare In continuare, vom descrie pe scurt alte obiecte de sincronizare, ale cror stri: semnalat, respectiv nesemnalat, permit s fie folosite ca parametri n apelurile unor funcii de ateptare, respectiv funcii de semnalizare. Event Acest obiect are asociat o anumit condiie, numit eveniment. La apelul funciei SetEvent() pentru un obiect event, starea acestuia devine semnalat i thread-urile care ateapt condiia, sunt notificate. Timer Un obiect timer se construiete folosind funcia CreateTimer() i devine semnalat cnd intervalul de timp, specificat la creare, expir. Change notification Un astfel de obiect se creeaz cu ajutorul funciei FindFirstChangeNotification() i starea obiectului devine semnalat cnd modificarea specificat la creare a aprut n structura de directoare. Console input Obiectul este construit cnd se creeaz o consol, folosind apelurile CreateFile() i GetStdHandle(). Aceste funcii ntorc un handler la un obiect console input. Starea obiectului devine semnalat cnd exist octei necitii n bufferul consolei i nesemnalat cnd bufferul este gol. Process Acest obiect se creeaz cu funcia CreateProcess. Starea sa este setat ca nesemnalat cnd procesul se execut i semnalat, cnd procesul s-a terminat. Pentru informaii suplimentare legate de operaiile de creare i semantica acestor obiecte de sincronizare, se recomand studiul documentaiei MSDN [83].

7.5.4

Atributele i planificarea thread-urilor NT

7.5.4.1 Atributele thread-urilor Atributele de securitate sunt precizate printr-un obiect de tip SECURITY_ATTRIBUTES n funcia de creare a unui thread. Structura acestui tip este:
typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES;

Windows Compendiu

41

unde nLength reprezint dimensiunea n octei a structurii, lpSecurityDescriptor este descriptor de securitate i precizeaz cum va fi folosit handler-ul la obiectul creat (n acest caz obiect thread) n alte funcii care cer argument handler. Dac este NULL, se va folosi descriptorul de securitate al procesului apelant. Parametrul boolean bInheritHandle specific dac un proces fiu va moteni handler-ul obiectului nou creat. Un alt atribut este dimensiunea stivei de executie. Stiva specific fiecrui thread este alocat automat la crearea threadului n spaiul de adrese a procesului. Dac pentru dimensiune se specific valoarea 0, stiva noului thread va avea aceeai dimensiune cu cea a threadului primar 7.5.4.2 Prioritile thread-urilor Fiecare thread are asociat o anumit prioritate de baz, care combin prioritatea clasei procesului i nivelul de prioritate al threadului n cadrul clasei respective. Pentru a permite i altor thread-uri s ruleze, sistemul poate mri sau micora prioritatea unui thread, folosind funciile SetPriorityClass() i SetThreadPriority(). Prototipurile acestora sunt:
BOOL SetPriorityClass( HANDLE hProcess, DWORD dwPriorityClass ); BOOL SetThreadPriority( HANDLE hThread, int nPriority ); //handler-ul procesului //prioritatea procesului

//handler-ul threadului //prioritatea threadului

Funcia care ntoarce prioritatea clasei este getPriorityClass() cu prototipul:


DWORD GetPriorityClass(HANDLE hProcess);

In caz de eroare, funcia ntoarce valoarea 0. Prioritatea threadului se obine prin apelul GetThreadPriority() cu prototipul:
int GetThreadPriority(HANDLE hThread);

In caz de eroare este ntors codul THREAD_PRIORITY_ERROR_RETURN. Pentru procese, exist patru clase de prioriti: HIGH_PRIORITY_CLASS indic un proces care execut operaii critice din punct de vedere al timpului. Aceste operaii trebuie s ruleze imediat pentru ca procesul s se execute corect. Un exemplu de astfel de operaie este obinerea listei de procese din sistem, operaie care trebuie s furnizeze rspunsul imediat, indiferent de ncrcarea procesorului (sau a procesoarelor). IDLE_PRIORITY_CLASS pentru procese care pot s ruleze doar cnd sistemul este disponibil. Exemple de procese cu aceast prioritate sunt programele de tip screen saver. NORMAL_PRIORITY_CLASS indic procesare normal fr reguli speciale de planificare.

Windows Compendiu

42

REALTIME_PRIORITY_CLASS cea mai mare prioritate posibil. Un proces cu aceast prioritate ruleaz naintea tuturor proceselor din sistem.

Componenta de planificare a sistemului menine o coada de thread-uri pentru fiecare nivel de prioritate. Thread-urile cu prioritate superioar ruleaz naintea celor cu prioritate mai mic, iar pentru acelai nivel, thread-urile sunt planificate dup o politic de tip round-robin. Exist nivele de prioritate de la 0 (cea mai mic) la 31 (cea mai mare). Prioritatea relativ a unui thread poate avea urmtoarele valori: THREAD_PRIORITY_ABOVE_NORMAL indic o valoare cu 1 mai mare dect prioritatea normal pentru prioritatea clasei. THREAD_PRIORITY_BELOW_NORMAL indic o valoare cu 1 mai mic dect prioritatea normal pentru prioritatea clasei. THREAD_PRIORITY_HIGHEST indic o valoare cu 2 mai mare dect prioritatea normal pentru prioritatea clasei. THREAD_PRIORITY_IDLE indic o prioritate de nivel 1 pentru clasele IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS i HIGH_PRIORITY_CLASS i o prioritate de nivel 16 pentru REALTIME_PRIORITY_CLASS. THREAD_PRIORITY_LOWEST -indic o valoare cu 2 mai mic decat prioritatea normal pentru prioritatea clasei. THREAD_PRIORITY_NORMAL indic o prioritate normal pentru prioritatea clasei. THREAD_PRIORITY_TIME_CRITICAL indic o prioritate de nivel 15 pentru clasele IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS i HIGH_PRIORITY_CLASS i o prioritate de nivel 31 pentru REALTIME_PRIORITY_CLASS.

Implicit, threadul este creat cu prioritatea THREAD_PRIORITY_NORMAL.

7.5.5

Faciliti speciale ale lucrului cu thread-uri NT

7.5.5.1 Date specifice thread-urilor Thread Local Storage (pe scurt TLS) este o metod prin care fiecare thread ntr-un proces multi-thread poate aloca locaii n care s fie memorate date specifice threadului. Ca suport pentru TLS, n C i C++ a fost adugat un nou atribut numit thread care se declar folosind identificatorul __declspec. De exemplu, declararea i iniializarea unei variabile locale de tip ntreg, cu atributul thread, se face astfel:
__declspec( thread ) int tls_i = 1;

Prezentm n continuare scenariul de creare i folosire a unor date specifice thread-urilor: 1. Un thread aloc un buffer i obine un index TLS returnat de funcia TlsAlloc care are prototipul:
DWORD TlsAlloc(VOID);

2. Se memoreaz un pointer la buffer n locaia indicat de index prin apelul TlsSetValue:

Windows Compendiu BOOL TlsSetValue( DWORD dwTlsIndex, LPVOID lpTlsValue );

43 // indexul TLS pentru care se seteaza valoarea // valoarea care se va memora

3. Celelalte thread-uri folosesc indexul TLS pentru a obine pointerul, cu funcia TlsGetValue:
LPVOID TlsGetValue(DWORD dwTlsIndex //indexul TLS de unde se va extrage );

4. Funcia TlsFree elibereaz indexul TLS, fiind posibil astfel reutilizarea lui, folosind TlsAlloc: BOOL TlsFree(DWORD dwTlsIndex //indexul TLS ); Cnd un thread se termin, starea handler-ului corespunztor devine semnalat i celelalte thread-uri care ateptau acest eveniment sunt notificate. 7.5.5.2 Thread-uri utilizator: fibre NT O fibr este un fir de execuie care trebuie planificat manual n cadrul aplicaiei. Fibrele ruleaz n contextul unor thread-uri, care le planific pentru execuie. Un thread poate planifica mai multe fibre. Folosirea fibrelor nu ofer avantaje suplimentare fa de thread-urile Win32, dar pot fi utile n cazul unui program n care planificarea fibrelor are loc n spaiul utilizator. Exist o structur de date specific fiecrei fibre. La crearea unei fibre, se creaz aceast structur i se ntoarce un pointer la ea. Crearea unei fibre se face cu funcia CreateFiber. Prototipul ei este:
LPVOID CreateFiber( DWORD dwStackSize, //dimesiunea iniial a stivei threadului LPFIBER_START_ROUTINE lpStartAddress, //pointer la funcia fibrei LPVOID lpParameter //argumentul funciei fibrei );

Funcia aloc un obiect fibr, Ii asociaz o stiv i specific funcia care va fi executat de fibr. Aceast funcie nu realizeaz planificarea fibrei. Dac pentru dimensiunea stivei se specific valoarea 0, se va fixa o dimensiune implicit. Dac este necesar, sistemul mrete dimensiunea stivei n mod dinamic. Funcia specficat prin lpStartAddress i implicit fibra, se va executa doar cnd o alt fibr apeleaz SwitchToFiber, cu argument, adresa funciei. Unicul argument al funciei fibrei poate fi obinut cu apelul:
PVOID GetFiberData(VOID);

In caz de succes, funcia CreateFiber returneaz adresa fibrei nou create, altfel NULL.

Windows Compendiu

44

In threadul primar al procesului, se apeleaz funcia ConvertThreadToFiber pentru a permite fibrelor s fie planificate de thread-uri, explicit prin program. Apelul convertete threadul curent ntr-o fibr. Prototipul funciei este:
LPVOID ConvertThreadToFiber(LPVOID lpParameter);

Funcia are un singurl argument, care reprezint pointerul la fibra care se va planifica. Numai o fibr poate s execute alte fibre. Dac un thread are nevoie s execute o fibr, mai nti apeleaz ConvertThreadToFiber pentru a crea o zon de memorie n care s se memoreze informaiile de stare ale fibrei. Aceste informaii includ datele specifice fibrei i care sunt specificate prin lpParameter. Planificarea manual spre execuie a unei fibre se realizeaz cu funcia SwitchToFiber:
VOID SwitchToFiber(LPVOID lpFiber);

Funcia salveaz informaiile de stare ale fibrei curente i le restaureaz pe cele ale fibrei specificate. Argumentul funciei este un pointer la fibra care se va planfica. Pentru a terge structura de date asociate unei fibre, se apeleaz funcia DeleteFiber:
VOID DeleteFiber(LPVOID lpFiber);

Dac aceast funcie este apelat pentru o fibr creat n alt thread, threadul respectiv se va termina forat, cu eroare. Dac fibra activ n mod curent apeleaz funcia de tergere, threadul su apeleaz implicit funia ExitThread i i ncheie activitatea. 7.5.5.3 Utilizarea thread-urilor n regim multiprocesor Aa cum am punctat n 2.1.2, un program multi-thread nregistreaz performane sporite, dac sistemul gazd este multiprocesor. Prezentm structura tipului SYSTEM_INFO care conine diverse informaii despre sistem, printre care tipul i numrul procesoarelor:
typedef struct _SYSTEM_INFO { union { DWORD dwOemId; struct { WORD wProcessorArchitecture; WORD wReserved; }; }; DWORD dwPageSize; LPVOID lpMinimumApplicationAddress; LPVOID lpMaximumApplicationAddress; DWORD dwActiveProcessorMask; DWORD dwNumberOfProcessors; DWORD dwProcessorType; DWORD dwAllocationGranularity; WORD wProcessorLevel; WORD wProcessorRevision; } SYSTEM_INFO;

Pentru NT, numrul de procesoare se poate determina prin apelul GetSystemInfo(), al crei prototip este:

Windows Compendiu 45 VOID GetSystemInfo(LPSYSTEM_INFO lpSystemInfo);

Acest apel depune la adresa lpSystemInfo o copie a structurii SYSTEM_INFO, din care se poate extrage componenta lpSystemInfo->dwNumberOfProcessors, care reprezint numrul de procesoare. Win32 furnizeaz funcia CreateRemoteThread care permite crearea unui thread care se execut n spaiul de adrese al unui alt proces. Sintaxa acestei funcii este: HANDLE CreateRemoteThread( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); hProcess - handlerul procesului care va gazdui noul thread lpThreadAttributes - descriptor de securitate dwStackSize - dimensiunea iniial a stivei lpStartAddress - funcia threadului lpParameter - argumentul funciei threadului dwCreationFlags - flaguri de creare lpThreadId - pointer la identificatorul threadului Pentru a permite crearea unui thread n spaiul de adrese al procesului, handler-ul hProcess trebuie sa aib atributul de acces PROCESS_CREATE_THREAD.

7.5.6

Exemple clasice de lucru cu thread-uri

Relum cele trei probleme enunate n 4.2, i pe care le vom implementa, de data aceasta, folosind thread-uri NT. Comparativ cu implementarea acestor probleme, folosind thread-uri Posix, sub Windows vom pstra aceeai structur a programelor, inclusiv aceeai denumire a funciilor. Diferena semnificativ n aceast variant de rezolvare a problemelor o reprezint mecanismele de gestiune a thread-urilor participante. 7.5.6.1 Adunarea n paralel a n numere Implementarea sub Windows a sumei paralele a n numere este prezentat n programul 4.13. Ca i mai nainte (4.3.8.1), se va nsuma 1 de n ori.
#include <windows.h> #include <winbase.h> #include <string.h> #include <tchar.h> #include <stdlib.h> #include <time.h> #include <stdio.h> #define nrElem 10 //tip cu informatii despre rutinei threadului typedef

Windows Compendiu struct { int i,st,dr,astept,sa,da; } Thr_info; // variabile globale static int* a; DWORD *tid; HANDLE *doneEv;

46

//rutina threadului, care aduna 2 numere din arbore DWORD WINAPI aduna(LPVOID *inf) { int i, sa, da, st = 0, dr = 0, status, astept; i=((Thr_info*)inf)->i; astept=((Thr_info*)inf)->astept; if (astept) { st=((Thr_info*)inf)->st; dr=((Thr_info*)inf)->dr; WaitForSingleObject(doneEv[st],INFINITE); WaitForSingleObject(doneEv[dr],INFINITE); } sa=((Thr_info*)inf)->sa; da=((Thr_info*)inf)->da; a[sa]=a[sa]+a[da]; printf("Thread %d cu ascendenti %d %d.\n",i,st,dr); free(inf); SetEvent(doneEv[i]); return 0; } //calculeaza in paralel 1 + 1 +1 .. 1 (n de 1) paralel(int n) { HANDLE h; int l, m, k, i, j, dk, dlmk; Thr_info *inf; char buf[30]; for (l = 0, m = 1; n > m; l++, m *= 2); a=(int*) malloc(m*sizeof(int)); tid=(DWORD*) malloc(m*sizeof(DWORD)); doneEv=(HANDLE*) malloc(m*sizeof(HANDLE)); for (i=0;i<m;i++) doneEv[i]= CreateEvent(0, false, false, 0); for (i = 0; i < n; i++) a[i] = 1; for (i = n; i < m; i++) a[i] = 0; for (k = 0, i = 1, n = 1, dk = 1, dlmk = m; k < l; k++, dk *= 2, dlmk /= 2, n *= 2) for (j = 0; j < n; j++, i++) { inf=(Thr_info*)malloc(sizeof(Thr_info)); inf->i=i; inf->st=2*i; inf->dr=2*i+1; inf->astept=(k<l-1)?1:0; inf->sa=dlmk*(i-dk); inf->da=dlmk/2*(2*i+1-dk*2); h=CreateThread(NULL, 16384, (LPTHREAD_START_ROUTINE)aduna, (LPVOID)inf,0, &tid[i]); } WaitForSingleObject(doneEv[1],INFINITE);

Windows Compendiu

47

sprintf(buf,"...Pentru n=%d, totalul=%d...",nrElem,a[0]); printf("%s\n",buf); free(a); free(tid); free(doneEv); return 1; } //main int main(int argc, char* argv[]) { if (argc>1) paralel(atoi(argv[1])); else paralel(nrElem); return 0; }

Programul 4.10 Sursa SumaN.cpp 7.5.6.2 Problema productorilor i consumatorilor Implementarea sub Windows a problemei productorilor i a consumatorilor este prezentat n programul 4.14. Sincronizarea se va realiza prin intermediul seciunii critice.
#include #include #include #include #include #include #include <windows.h> <winbase.h> <string.h> <tchar.h> <stdlib.h> <time.h> <stdio.h>

#define MAX 10 #define P 5 #define C 5 // variabile globale int recip[MAX]; int art=0; int p[P],c[C]; int pozPut,pozGet; CRITICAL_SECTION crit; HANDLE condGet,condPut; DWORD tid[20]; HANDLE doneEv; void scrie() { int i; //printf("scr"); for (i=0;i<P;i++) printf("P%d_%d ",i,p[i]); for (i=0;i<C;i++) printf("C%d_%d ",i,c[i]); printf("B: "); if (pozPut>pozGet) { for (i=pozGet;i<pozPut;i++) if (recip[i]!=0) printf("%d ",recip[i]); } else {

Windows Compendiu 48 for (i=pozGet;i<MAX;i++) if (recip[i]!=0) printf("%d ",recip[i]); for (i=0;i<pozPut;i++) if (recip[i]!=0) printf("%d ",recip[i]); } printf("\n"); } //verifica daca buferul este plin int plin() { if (recip[pozPut]!=0) return 1; else return 0; } //verifica daca buferul este gol int gol() { if (recip[pozGet]==0) return 1; else return 0; } //pune un articol in buffer int put(int art,char* sind) { int i=atoi(sind); EnterCriticalSection(&crit); while(plin()) { LeaveCriticalSection(&crit); p[i]=-art; WaitForSingleObject(condPut,INFINITE); EnterCriticalSection(&crit); } recip[pozPut]=art; pozPut=(pozPut+1)%MAX; p[i]=art; scrie(); p[i]=0; LeaveCriticalSection(&crit); SetEvent(condGet); return 1; } //extrage un articol din buffer int get (char *sind) { int i=atoi(sind); EnterCriticalSection(&crit); //ResetEvent(condGet); while(gol()) { LeaveCriticalSection(&crit); c[i]=-1; WaitForSingleObject(condGet,INFINITE); EnterCriticalSection(&crit); } c[i]=recip[pozGet]; recip[pozGet]=0; pozGet=(pozGet+1)%MAX; //printf("%d\n",pozGet); scrie(); c[i]=0; LeaveCriticalSection(&crit); SetEvent(condPut); return 1;

Windows Compendiu }

49

//rutina thread-urilor producator DWORD produc(void* sind) { int sl; while (1) { art++; put(art,(char*)sind); sl=1+(int) (3.0*rand()/(RAND_MAX+1.0)); Sleep(sl*1000); } return 1; } //rutina thread-urilor consumator DWORD consum (LPVOID sind) { int sl; while (1) { get((char*)sind); sl=1+(int) (3.0*rand()/(RAND_MAX+1.0)); Sleep(sl*1000); } return 1; } //creeaza P+C thread-uri int creaThreads() { int i; char* sind; srand(0); HANDLE h; InitializeCriticalSection(&crit); pozPut=0;pozGet=0; for (i=0;i<MAX;i++) recip[i]=0; for (i=0;i<P;i++) { sind=(char*)malloc(3*sizeof(char)); sprintf(sind,"%d",i); CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)produc, (void*)sind, 0, &tid[i]); } for (i=0;i<C;i++) { sind=(char*)malloc(3*sizeof(char)); sprintf(sind,"%d",i); CreateThread(NULL, 16384, (LPTHREAD_START_ROUTINE)consum, (void*)sind, 0, &tid[i+P]); } return 1; } int main(int argc, char* argv[]) { condGet=CreateEvent(0, false, false, 0); condPut=CreateEvent(0, false, false, 0); doneEv=CreateEvent(0, false, false, 0); creaThreads(); WaitForSingleObject(doneEv,INFINITE); } return 0;

Programul 4.11 Sursa ProducatorConsumator.cpp

Windows Compendiu

50

7.5.6.3 Problema cititorilor i scriitorilor Implementarea sub Windows a problemei cititorilor i scriitorilor este prezentat n programul 4.15. Sincronizarea se va realiza prin intermediul seciunii critice.
#include #include #include #include #include #include #include <windows.h> <winbase.h> <string.h> <tchar.h> <stdlib.h> <time.h> <stdio.h>

#define MAX 10 #define S 5 #define C 5 // variabile globale int s[S],c[C]; int cititori=0; CRITICAL_SECTION crit; HANDLE condSc; DWORD tid[S+C]; HANDLE doneEv; //afiseaza starea curenta a citittorilor si scriitorilor void afiseaza() { int i; int z=0; for (i=0;i<S;i++) printf("S%d %d ",i,s[i]); for (i=0;i<C;i++) printf("C%d %d ",i,c[i]); printf("\n"); } //scrie un articol in buffer scrie(char sind[]) { int i=atoi(sind); EnterCriticalSection(&crit); while(cititori>0) { s[i]=-1; LeaveCriticalSection(&crit); WaitForSingleObject(condSc,INFINITE); EnterCriticalSection(&crit); } s[i]=1; afiseaza(); s[i]=0; LeaveCriticalSection(&crit); return 1;

//citeste un articol din buffer citeste (char sind[]) { int i=atoi(sind); c[i]=-1; EnterCriticalSection(&crit);

Windows Compendiu cititori++; LeaveCriticalSection(&crit); c[i]=1; EnterCriticalSection(&crit); afiseaza(); c[i]=0; cititori--; LeaveCriticalSection(&crit); SetEvent(condSc); } return 1;

51

//rutina thread-urilor cititor DWORD WINAPI cititor(LPVOID sind) { int sl; while (1) { citeste((char*)sind); sl=1+(int) (3.0*rand()/(RAND_MAX+1.0)); Sleep(sl); } return 1; } //rutina thread-urilor scriitor DWORD WINAPI scriitor (LPVOID sind) { int sl; while (1) { scrie((char*)sind); sl=1+(int) (3.0*rand()/(RAND_MAX+1.0)); Sleep(sl); } return 1; } //creeaza S+C thread-uri int creaThreads() { int i; char* sind; HANDLE h; srand(0); InitializeCriticalSection(&crit); for (i=0;i<S;i++) { sind=(char*)malloc(3*sizeof(char)); sprintf(sind,"%d",i); h=CreateThread(NULL, 16384, (LPTHREAD_START_ROUTINE)cititor, (LPVOID)sind, 0, &tid[i]); SetThreadPriority(h,THREAD_PRIORITY_LOWEST); } for (i=0;i<C;i++) { sind=(char*)malloc(3*sizeof(char)); sprintf(sind,"%d",i); h=CreateThread(NULL, 16384, (LPTHREAD_START_ROUTINE)scriitor, (LPVOID)sind, 0, &tid[i+S]); SetThreadPriority(h,THREAD_PRIORITY_HIGHEST); } return 1;

//main int main(int argc, char* argv[]) {

Windows Compendiu 52 condSc=CreateEvent(0, false, false, 0); doneEv=CreateEvent(0, false, false, 0); creaThreads(); WaitForSingleObject(doneEv,INFINITE); return 0; }

Programul 4.12 Sursa CititorScriitor.cpp Rezultatele execuiilor sub Windows ale celor trei probleme sunt similare rezultatelor obinute folosind thread-uri Posix.

7.5.7

Particulariti de utilizare WINDOWS (WINSOCK)

Utilizarea socket a fost muli ani apanajul sistemelor de operare de tip Unix operaionale pe mainframe-uri i pe minicalculatoare [62,12,] Rspndirea colosal a calculatoarelor personale n ultimii zece ani a ridicat, firesc, problema comunicrii i ntre aceste tipuri de calculatoare. Primul pas a fost fcut cu raelele LAN, ntre care reelele Novell au fost lideri ai pieii nainte de 1990. Apariia mediilor grafice WINDOWS a avut un impact deosebit asupra utilizatorilor acestui tip de calculatoare. Incepnd cu WINDOWS 3.11 (for workgroups), apoi WINDOWS 95i n prezent WINDOWS NT, s-au implemenat mecanisme de comunicaii n reele LAN la fel de puternice ca i cele Novell, dar mult mai uor i mai elegant de utilizat datorit interfeelor grafice oferite. Desigur, larga rspndire a protocoalelor TCP/IP nu a lsat indifereni pe productorii din marile case de software. Mai nti, imediat dup primele versiuni de Novell au aprut versiuni care s suporte, pe lng protocolul propriu IPX/SPX i protocolul TCP/IP. Dar un mult mai mare impact n acest sens l-a avut apariia facilitilor de comunicaie TCP/IP sub WINDOWS. In prezent, cel mai rspndit pachet de acest gen este WINSOCK. Acesta ofer, ncepnd cu WINDOWS 3.1, toate facilitile de lucru cu socket BSD, folosind ca limbaj gazd orice implementare C sau C++ operaionale pe platforme WINDOWS (ncepnd cu WINDOWS 95, pachetul WINSOCK este integrat direct n pachetul de instalare). De pild, programele din exemplele care urmeaz au fost testate pentru WINDOWS cu pachetul Visual C++ 4.2. Deoarece funciile de utilizare socket coincid pn la identificare msur cu cele deja prezentate n seciunile precedente, ne vom mrgini s expunem diferenele fa de acestea. Pentru implementarea WINSOCK sunt necesare urmtoarele trei fiiere: 1. Fiierul header <winsock.h> ; 2. Fiierul WINSOCK.DLL, de interfa WINDOWS - socket; 3. Fiierul WINSOCK32.LIB, necesar i obligatoriu numai pentru WINDOWS 95 i WINDOWS NT. Ele pot fi procurate, din mai multe locuri din Internet, printre care i ftp.ubbcluj.ro Pentru a scrie aplicaii care s fie rapid portate pe aceste platforme se recomand utilizarea strict a apelurilor standard socket BSD. Evident c unele apeluri sistem Unix nu sunt

Windows Compendiu

53

suportate. Unele dintre acestea pot sunt de nefolosit pe astfel de platforme.

fi nlocuite, iar altele, ca de exemplu fork(),

Cteva funcii suportate pe platformele Unix i cu argumente descriptori de socket, sub WINDOWS acestea nu sunt valide. In locul acestora sunt furnizate alte apeluri echivalente. De asemenea, unele fiiere header au fost nlocuite prin altete. Concret, este vorba de urmtoarele cinci situaii: 1. Apelul read ( sd , ... ) cu sd descriptor de socket, nu este suportat; el trebuie nlocuit cu recv ( sd , ... , 0 ). 2. Apelul write ( sd , ... ) cu sd descriptor de socket, nu este suportat; el trebuie nlocuit cu send ( sd , ... , 0 ). 3. 4. Apelul close ( sd ) trebuie nlocuit cu apelul closesocket ( sd ). Apelul ioctl nu exist; n locul lui se folosete, dup caz, una dintre funciile sau setsockopt.

getsockopt

5. Fiierele header: <netinet/in.h>, <fcntl.h>, <sys/socket.h> i <netb.h> au fost comasate i n locul lor trebuie s apar fiierul header <winsock.h>. Pentru portarea Unix -> WINDOWS a unui program care comunic prin socket sunt necesari i obligatorii urmtorii doi pai: 1. Se vor nlocui liniile cu citarea fiierelor header citate mai sus. Deci: # include <netinet/in.h> # include <fcntl.h> # include < sys/socket.h> # include <netb.h> se va nlocui cu: # include <winsock.h> 2. Orice aplicaie WINDOWS care folosete WINSOCK trebuie s iniializeze biblioteca socket. Aceasta se realizeaz declarnd trei variabile i apelnd dou funcii. Apelurile trebuie s fie primele instruciuni n cadrul funciei main a programului. Secvena de iniializare este:
WORD wVersiune; WSADATA wDate; int wEroare; wVersiune = MAKEWORD(1,1); /* Se cere versiunea 1.1 */ wEroare = WSAStartup( wVersiune, &wDate); /* Initbiblioteca */

In funcie de versiunea de WINDOWS se pot alege variante de creare a aplicaiilor:

Windows Compendiu

54

1. Sub WINDOWS 3.1 se pot crea doar aplicaii standard (cu procedur fereastr, o clas de ferestre etc.). In fiierul de definire a proiectului (.DEF), la seciunea IMPORTS, trebuie specificate funciile socket folosite, astfel:
IMPORTS Winsock.bind Winsock.send

Dealtfel aceasta este procedura standard de folosire a funciilor dintr-un DLL. 2. Incepnd cu WINDOWS 95 se pot genera aplicaii consol la care s fie legat biblioteca pe 32 de bii wsock32.lib. Sub Visual C++ 4.2, opiunea de includere se afl n ierarhia de meniuri Build/Settings/Link. In ncheierea acestei seciuni vom enumera paii necesari portrii unei aplicaii folosind Visual C++ 4.2. Evident, aceast niruire presupune c cititorul este deja familiar cu acest mediu: Se fixeaz numele noului proiect. Se ataeaz la proiect fiierul surs principal al aplicaiei. Se adaug la modulele obiect folosite biblioteca wsock32.lib. Se fac modificrile necesare n fiierele surs i se salveaz aceste modificri. Se construiete (build) fiierul proiect. Se lanseaz aplicaia, eventual la prompter DOS.

7.5.7.1

RPC sub Windows NT

Mecanismul RPC este integrat i pe platformele Windows. Aa cum am mai spus, cel mai folosit pe astfel de platforme este DCE RPC [56,29]. Cum ns noi tratm aici ONC RPC, vom descrie modul de folosire al acestuia. n 3.3.5 am descris folosirea socket pe platformele Windows, prin intermediul pachetului Winsock. Incepnd cu Windows 95, acest pachet a fost integrat n pachetul de instalare. Mecanismul ONC RPC a fost implementat pe platforma Windows NT i este dezvoltat peste pachetul Winsock. Din punctul de vedere al utilizatorului, folosirea ONC RPC pe Windows NT este aproape identic cu ONC RPC standard. Sunt deci posibile cele patru variante de dezvoltare de aplicaii: Client Windows NT - server Windows NT; Client Windows NT - server Unix; Client Unix - server Windows NT; Client Unix - server Unix.

Pentru programele operaionale pe Windows NT trebuie inut cont de urmtoarele ase chestiuni: 1. Textele surs C, conin n headreul <rpc/rpc.h> toate specificrile proprii Windows NT. 2. Inainte de prima instruciune referitoare la un apel RPC, trebuie apelat funcia:
rpc_nt_init();

Windows Compendiu

55

Aceasta realizeaz RPC.

iniializrile

necesare pentru Winsock i pentru partea specific

3. Inainte de terminarea programului (la sfritul apelului), trebuie apelat funcia:


rpc_nt_exit();

care realizeaz operaia de nchidere RPC i Winsock. n programul 4.6 dintr-un exemplu care va urma ntr-o seciune urmtoare am indicat prin comentarii locurile acestor dou apeluri. 4. Arhiva cu pachetul ONC RPC se gsete la adresa:
ftp://ftp.gmd.de/gmd/I5.RS/SRPC112.ZIP

Aceeai arhiv se afl n momentul de fa i pe serverul ftp.ubbcluj.ro. Instalarea se face foarte uor dup dezarhivare, urmnd cu atenie paii recomandai acolo. Este necesar numai copierea unor fiiere n directorul %SystemRoot%\system32\ i lansarea unui program care instaleaz serviciul Portmapper (despre care vom vorbi ntr-o seciune urmtoare). 5. n vederea compilrii programului, mai trebuie inclus n path un director rpc (extras din arhiva SRPC112.ZIP). 6. n vederea link-editrii, trebuiespecificat biblioteca specific:
Calea_unde_s_a_despachetat_arhiva\bin\oncrpc.lib

Specific i diferene: Descrierea protocolului se face ntr-un fiier N.x, exact sub forma prezentat n seciunile precedente. Sunt admise numai apelurile sistem standard ONC RPC. Dac se respect specificaiile POSIX [87,17,40] atunci apelurile sunt posibile att pentru client, ct i pentru server. De exemplu, n aplicaia rls pe care o vom prezenta (i care este adaptat dup aplicaia rdir prezentat n 3.4.1), clientul se obine doar adugnd cele dou apeluri indicate mai sus. Pentru server pe Windows NT, trebuie nlocuit procedura read_dir din programul 3.3, cu cea din programul 3.5. ONC RPC sub Windows NT nu are pe moment implementat dect varianta generrii cu RPCGEN. Variantele ONC_high_level i ONC_low_level, despre care vom vorbi n seciunile urmtoare, nu sunt nc accesibile pe platforma Windows NT.

S-ar putea să vă placă și