Sunteți pe pagina 1din 55

Windows Compendiu 1

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 .............................................................................................. 32
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, s-
au 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 3
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 ntr-un
Windows Compendiu 4
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 5

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.

E
x
e
c
u
t
i
v
u
l

N
T
Micronucleu
(microkernel)
Drivere
Hardware Abstraction Layer (HAL)
Manager de obiecte (Object Manager)
I
/
O
M
a
n
a
g
e
r
P
r
o
c
e
s
s
M
a
n
a
g
e
r
I
P
C
M
a
n
a
g
e
r
W
i
n
d
o
w
M
a
n
a
g
e
r
GDI

K
e
r
n
e
l

S
p
a
c
e
O
S
/
2
S
u
b
s
y
s
t
e
m
P
O
S
I
X
S
u
b
s
y
s
t
e
m
W
i
n
3
2
S
u
b
s
y
s
t
e
m
Procese de baza ale
sistemului de operare
(System support
processes)
S
e
r
v
i
c
i
i
(
S
e
r
v
i
c
e
p
r
o
c
e
s
s
e
s
)
Aplicaii utilizator
U
s
e
r

s
p
a
c
e
Windows Compendiu 6
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.

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
Executivul
Windows NT
...
Object Manager
Process
Manager
I/O
Manager
IPC
Manager
Windows API (kernel32.dll)
Aplicaii
(att utilizator cat si sistem) s
Windows Compendiu 7
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 8
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 9
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
Desktop este diferit de cel asociat sesiunii de lucru curente la consola staiei Windows; din
Windows Compendiu 10
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;
nNrOctetiDeScris - numr de octei de scris;
Windows Compendiu 11
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,
DWORD nNumberOfBytesToUnlockLow,
Windows Compendiu 12
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 "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

int main(int argc, char* argv[])
{
Windows Compendiu 13
HANDLE fd;

// 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.

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);

1. CreateNamedPipe
2. ConnectNamedPipe
3. ReadFile
4. WriteFile
5. DisconnectNamedPipe
6. CloseHandle
Server
1. CreateFile
2. ReadFile
3. WriteFile
4. CloseHandle
Client
Pipe cu nume
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 20
BOOL code;

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 file-
mapping), 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 --> shmget
MapViewOfFile --> shmat
UnmapViewOfFile --> shmdt
CloseHandle --> 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

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

hFileMap handle la obiectul file-mapping (handle returnat de
CreateFileMapping)
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 7.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

int main () {

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

//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 7.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++) {
copie->v[j]=mp->v[j];
strcpy(copie->s[j],mp->s[j]);
}
Windows Compendiu 25

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 7.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 thread-
urilor 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) single-object
ii) multiple-object
iii) 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 alertable: MsgWaitForMultipleObjectsEx,
SignalObjectAndWait, WaitForMultipleObjectsEx i
WaitForSingleObjectEx

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 Descriere
WaitForSingleObject() Ateapt dup un anumit obiect ca acesta s ajung
n starea setat (valoarea semaforului > 0).
WaitForSingleObjectEx() Ca i precedentul, plus ateptarea a altor dou
evenimente: terminarea unei operaii de intrare
ieire, sau sosirea unui apel asincron n threadul
curent.
WaitForMultipleObjects() 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.
Windows Compendiu 29
WaitForMultipleObjectsEx() Ca i precedentul, plus ateptarea celor dou
evenimente specificate n cazul funciei
WaitForSingleObjectEx.


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 7.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 7.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 7.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 7.7 Arhitectura mailslot

Sintaxa numelor folosite n mailslot este prezentat n tabelul din fig. 3.17:

Sintaxa Descriere
\\.\mailslot\numeMailslot Serverul mailslot de pe maina local
\\adresaMasina\mailslot\numeMailslot 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 7.8 Sintaxa numelor folosite n mailslot


Server NT
Server
mai l sl ot
NetBIOS IPX TCP/IP

Windows Compendiu 32
7.4.5.2 Programarea folosind mailslot

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, ReadFile, WriteFile i
CloseHandle au fost prezentate n 3.2.3.2.

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.

Windows Compendiu 33
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.

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 7.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.
Windows Compendiu 34

#include "antetMailslot.h"
#include "semnaturaTemporala.cpp"

HANDLE hMailSlot;

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

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 7.8 Sursa mailslotCititor.cpp

#include "antetMailslot.h"
#include "semnaturaTemporala.cpp"

HANDLE hMailSlot;
Windows Compendiu 35

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

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 7.9 Sursa mailslotScriitor.cpp

Lsm pe seama cititorului s experimenteze cu aceste dou programe lucrul multiserver ntr-
o 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
Windows Compendiu 36
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.

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
Windows Compendiu 37
lpStartAddress - pointer la funcia ce dirijeaz threadul
lpParameter - argumentul funciei
dwCreationFlags - flaguri de creare
lpThreadId - pointer la identificatorul threadului

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.

Windows Compendiu 38
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.


7.5.3 I nstrumente 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.

Windows Compendiu 39
Crearea unei astfel de variabile se face cu funcia:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL binitialOwner,
LPCTSTR lpName
);

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


Windows Compendiu 40
O variabil de tip seciune critic se declar astfel:

CRITICAL_SECTION numeSectiuneCritica;

Utilizarea seciunii critice se face astfel:

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:
Windows Compendiu 41

typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES;

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, //handler-ul procesului
DWORD dwPriorityClass //prioritatea procesului
);

BOOL SetThreadPriority(
HANDLE hThread, //handler-ul threadului
int nPriority //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
Windows Compendiu 42
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.
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:

Windows Compendiu 43
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:

BOOL TlsSetValue(
DWORD dwTlsIndex, // indexul TLS pentru care se seteaza valoarea
LPVOID lpTlsValue // 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.

Windows Compendiu 44
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.

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;
Windows Compendiu 45
WORD wProcessorLevel;
WORD wProcessorRevision;
} SYSTEM_INFO;

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

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>
Windows Compendiu 46
#include <tchar.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#define nrElem 10

//tip cu informatii despre rutinei threadului
typedef
struct {
int i,st,dr,astept,sa,da;
} Thr_info;

// variabile globale
static int* a;
DWORD *tid;
HANDLE *doneEv;

//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;
Windows Compendiu 47
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);

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 7.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 <windows.h>
#include <winbase.h>
#include <string.h>
#include <tchar.h>
#include <stdlib.h>
#include <time.h>
#include <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]);
Windows Compendiu 48
printf("B: ");

if (pozPut>pozGet) {
for (i=pozGet;i<pozPut;i++)
if (recip[i]!=0)
printf("%d ",recip[i]);
}
else {
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];
Windows Compendiu 49
recip[pozGet]=0;
pozGet=(pozGet+1)%MAX;
//printf("%d\n",pozGet);
scrie();
c[i]=0;
LeaveCriticalSection(&crit);
SetEvent(condPut);
return 1;
}

//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);
Windows Compendiu 50

return 0;
}

Programul 7.11 Sursa ProducatorConsumator.cpp

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 <windows.h>
#include <winbase.h>
#include <string.h>
#include <tchar.h>
#include <stdlib.h>
#include <time.h>
#include <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;
Windows Compendiu 51
}

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

EnterCriticalSection(&crit);
cititori++;
LeaveCriticalSection(&crit);

c[i]=1;

EnterCriticalSection(&crit);
afiseaza();
c[i]=0;
cititori--;
LeaveCriticalSection(&crit);

SetEvent(condSc);

return 1;
}

//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,
Windows Compendiu 52
(LPVOID)sind, 0, &tid[i+S]);
SetThreadPriority(h,THREAD_PRIORITY_HIGHEST);
}
return 1;
}

//main
int main(int argc, char* argv[]) {
condSc=CreateEvent(0, false, false, 0);
doneEv=CreateEvent(0, false, false, 0);
creaThreads();
WaitForSingleObject(doneEv,INFINITE);
return 0;
}

Programul 7.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:

Fiierul header <winsock.h> ;
Fiierul WINSOCK.DLL, de interfa WINDOWS - socket;
Windows Compendiu 53
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
suportate. Unele dintre acestea pot fi nlocuite, iar altele, ca de exemplu fork(), sunt de
nefolosit pe astfel de platforme.

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:

Apelul read ( sd , ... ) cu sd descriptor de socket, nu este suportat; el trebuie
nlocuit cu recv ( sd , ... , 0 ).

Apelul write ( sd , ... ) cu sd descriptor de socket, nu este suportat; el trebuie
nlocuit cu send ( sd , ... , 0 ).

Apelul close ( sd ) trebuie nlocuit cu apelul closesocket ( sd ).

Apelul ioctl nu exist; n locul lui se folosete, dup caz, una dintre funciile
getsockopt sau setsockopt.

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;
Windows Compendiu 54

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:

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.

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:

Windows Compendiu 55
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();

Aceasta realizeaz iniializrile necesare pentru Winsock i pentru partea specific RPC.

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