Számítógép-hálózatok: Labor 6
Konkurens TCP Szerver A gyakorlat célja: Megismerkedni a párhuzamos programozás és a konkurens TCP szerver készítésének az elméleti és gyakorlati alapjaival és egy egyidejűleg több klienst is kiszolgáló visszhang szerver elkészítése. Elméleti bevezető: Az előző laboron bemutatott TCP szerver egyidejüleg csak egyetlen egy klienset képes kiszolgálni. Meghatározás: A szál egy független vezérfonal egy folyamaton belül, amely egy kontextusból és egy utasítás szekvenciából áll. A szálak (threadek) a legkisebb futatható egységek rendszermag szinten. A folyamat létrejöttének pillanatában létrejön az elsődleges-szál is (fő szál ami egy rendszermag típusú szál). Ebben létre lehet hozni a továbbiakban más szálakat is. Egy folyamat szálai osztoznak a folyamat közös címtartományán és más erőforrásain. Ezen kívül minden szál rendelkezik saját kontextussal és veremmel. Kontextus és a szálak állapotai: Szál: Sajátos tulajdonságokkal rendelkező kontextus o Azonosító \ név o Ütemezési politika o Prioritás o Mi vezérli a szálat: rendszer magja vagy a folyamat Felhasználói struktúra o PC program számláló o SP Verem Mutató Verem Saját adat terület Végrehajtandó utasítások A szálak kezdeti állapota a Created amikor a szál objektum létre van hozva de meg nincs elindítva. Ebből az állapotból kerül a futatható (Runnable) állapotba, amikor várakozási listában várja a beütemezését. A futó állapot (Running) amikor effektív a fut szál a processzoron. Ez lehet felhasználói vagy rendszermag típusú futási állapot annak függvényében, hogy milyen típusú a szál és milyen műveletet hajt végre. Abban az esetben, ha egy rendszer hívás eredményeként várakoznia kell egy IO műveletre vagy egy eseményre, akkor alvó állapotba található (Sleeping), ebben az esetben nem ütemeződik újra a válasz megérkezéséig. Ha az utasítások, végrehajtását felfüggeszti egy
Számítógép-hálózatok: Labor 6 időre önmagának vagy egy külsőszál akkor a felfüggesztett állapotba (Stopped) kerül. Abban az esetben, ha a futást véglegesen befejezi, akkor a befejezett (Terminated) állapotban kerül.
1.Ábra A szálak lehetséges állapotai .
Szálak osztályzása: A szálakat implementációja történhet rendszerhívásokkal vagy egy speciális könyvtár API-ján keresztül a felhasználói program szintjén. Ennek alapján a szálakat három csoportba soroljuk: Rendszer szálak Rendszerhívások segítségével van implementálva. Láthatók az operációs rendszer magjában. Nagyobb erőforrást használnak mert a operációs rendszer kell kezeljen adatstruktúrákat, ütemezést. Előnyei közé tartozik, hogy a rendszer párhuzamos ütemezheti végrehajtásra egy folyamat több szálát is, ha a rendszer több processzorral rendelkezik. Valamint ha a folyamat egyik szálának várakoznia kell egy IO műveletre a többi szálat azért beütemezhető. Felhasználói szálak Speciális könyvtár segítségével vannak implementálva. Az operációs rendszernek nincs tudomása róluk. Ha rendszerhívásra van szükség azt csak a folyamaton vagy a folyamathoz rendelt rendszerszálon keresztül, lehet elvégezni. A rendszernek mivel nincs tudomása a felhasználói szálakról, csak a folyamatokat beütemezésével foglakozik, a szálak ütemezését a felhasználó kell elvégezze. A szálak létrehozása, szinkronizálás, befejezése a speciális könyvtár API-án keresztül történik. A szálak egy jelzés hatására felszabadítják a processzor erőforrást a következő szál részére. Abban az esetben, ha egy szálnak rendszerhívása van akkor az többi szál meg kell várja amíg a rendszerhívás befejeződik. Nem ajánlott az olyan feladatok alkalmazásánál ahol gyakoriak a rendszerhívások.
Számítógép-hálózatok: Labor 6 Az előnyei közé tartozik, hogy az ütemezés független a rendszertől ezért könnyen kialakítható az alkalmazás szükségleteinek megfelelő ütemezőt. A kontextus váltáskor nincs szükség a rendszerre. Hordozható az alkalmazás, ha a megfelelő speciális könyvtár a másik típusú operációs rendszeren is megtalálható.
Kombinált szálak Felhasználói szálak képezik, amelyek multiplikálva vannak rendszermag alapú szálra. A két típusú szál előnyeit próbálja ötvözni Több típusú van: Mx1 modell (many to one) 1x1 modell (one to one) MxN modell (many to many)
egy
Szálak a Microsoft Windows platformon A Windowsban is megtalálhatók a: felhasználói szálak, ami a FIBER vagy FIBERS név alatt szerepelnek rendszerszálak THREADS név alatt. Ebből két típusú létezik: o Felhasználó interface (User-Interface UI) szálak ezekhez a szálakhoz egy vagy több ablak rendelhető. Ebben az esetben létezik egy hurok ami várja az események érkezését és meghívja azt metódust ami kiszolgálja az eseményt. o Feldolgozói WORKER szálak Szálak létrehozása: HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,
// Egy biztonsági beállításokat tartalmazó struktúra ami megmondja, hogy a HANDLE azonosító örökölheti-e a gyerek szál. Ha NULL érték van akkor nem. SIZE_T dwStackSize,
// szál vermének a kezdeti nagysága LPTHREAD_START_ROUTINE lpStartAddress,
// szál függvénye ami vezérli a működését. Ha ez egy nem létező (nem megfelelő) függvényre mutat akkor a szál futása azonnal befejeződik és hibát térít vissza. LPVOID lpParameter,
// szál argumentuma DWORD dwCreationFlags,
// Létrehozási opciók
egyelőre csak a 0 van implementálva- a szál létrejöttekor egyből a fut. LPDWORD lpThreadId
// Egy mutató amiben tároljuk a szál azonosítóját );
Számítógép-hálózatok: Labor 6 Ha sikeres a szál létrehozása, akkor egy operációs rendszer szinten egyedi azonosítót (HANDLE) térít vissza. Miután létrejött a szál az lpStartAddress-ben megadott függvényt kezdi végrehajtani. A visszatérítési értéket a GetExitCodeThread() metódus segítségével kapom meg. Szál befejezése Egy szál a futását a következő esetekben fejezi be: Befejeződik a hozzá rendelt metódus futása A konkurens szálból meghívjuk a ExitPrecess() vagy az ExitThread()é Egy másik szálból meghívódik a ExitPrecess() vagy az ExitThread() argumentumként megadva a megfelelő szál azonosítóját (HANDLE), ha a biztonsági beállítások ezt megengedik. A szál befejezése után a hozzá rendelt verem felszabadul és a szál objektum jelzett állapotba kerül. A szál mindaddig a memóriában marad, amíg az összes hozzárendelt HANDLE nincs felszabadítva a CloseHandle segítségével. Feladat: 1. Valósítsatok meg egy visszhang szervert amely egyidejüleg több klienst is kiszolgál. Megvalósítás: objektum orientáltan szekvenciálisan Objektum orientált implementálása a szálaknak Cél a szálak használatát megkönnyíteni és a szál által használt változókat és metódusokat egy objektumba zárni. Példaprogram: class SysThread { public: SysThread( void ); virtual ~SysThread(); virtual bool start( void ); virtual bool stop( unsigned int timeout = 0 ); inline volatile bool& isRunning( void ) { return m_bRunning; } inline volatile bool& isExited( void ) { return m_bExited; }
Számítógép-hálózatok: Labor 6 protected: virtual void run( void ); //Ezt a metodust a származtatott osztályban felül kell írni. Ide kell beírni az utasítás szekvenciát amit a szálunk végre kell hajtson private: friend DWORD WINAPI runStub( LPVOID mthread ); public: static const unsigned int INFINIT_WAIT; private: volatile bool m_bRunning; volatile bool m_bExited; HANDLE m_thread; };
#include "SysThread.h" #include
#include <windows.h> #define
INVALID_HANDLE_VALUE
0
const unsigned int SysThread::INFINIT_WAIT = UINT_MAX; SysThread::SysThread( void ) { m_bRunning = false; m_bExited = true; m_thread = INVALID_HANDLE_VALUE; } SysThread::~SysThread() { } bool SysThread::start( void ) { if( m_bExited ) { m_bExited = false; DWORD dw; if( (m_thread = CreateThread( NULL, 4096, runStub, this, 0, &dw )) == INVALID_HANDLE_VALUE ) { m_bRunning = false; m_bExited = true; return false; } } return true; } bool SysThread::stop( unsigned int timeout ) {
Számítógép-hálózatok: Labor 6 m_bRunning = false; if( !m_bExited ) { for( unsigned int i = 0; (i <= timeout/100) || (timeout == INFINIT_WAIT); i++) { m_bRunning = false; if( m_bExited ) { break; } Sleep( 10 ); } } if( m_thread != INVALID_HANDLE_VALUE ) { CloseHandle( m_thread ); m_thread = INVALID_HANDLE_VALUE; } return m_bExited; } void SysThread::run( void ) { } DWORD WINAPI runStub( LPVOID mthread ) { SysThread* pThread = static_cast<SysThread* >(mthread); pThread->m_bRunning = true; pThread->run(); pThread->m_bRunning = false; pThread->m_bExited = true; return 0; }
//Itt meghívom a leszármaztatott osztály run függvényt, ami tartalmazza a szál utasításait.
Kérdések: 1. MI a szerepe a friend és a virtual kulcsszónak? 2. Mikor hozok létre új szálat a szerverben? Könyvészet: [1] Florian Mircea Boian …: Programare concurenta pe platforme Unix, Windows, Java Albastra kiadó 2002