Comments
Description
Transcript
Processi - Microsoft
Windows internals http://www.microsoft.com/italy/msdn/studenti [email protected] Benvenuti Un giorno di full immersion nel kernel di Windows Architettura del kernel Come funziona Come sfruttarlo al meglio Come analizzare i problemi Agenda Architettura kernel di Windows Gestione della memoria Processi Thread Thread pool Jobs Interprocess communication Overlapped I/O Domande & Risposte Architettura kernel di Windows MS Windows NT: obiettivi del progetto Compatibilità Portabilità applicazioni Win32, Win16, MS-DOS, OS/2, POSIX Ieri: piattaforma Intel, RISC Oggi: IA-32 (Pentium), IA-64 (Itanium) A breve: AMD-64 (Opteron) Robustezza Estendibilità Performance Compatibilità binaria, sorgente, file system Portabilità Symmetric Multiprocessors Server Laptop Computer Personal Computer Architettura di MS Windows NT Applications and Subsystems Software (User-Mode Instruction Set) Process Security Local Object Virtual Manager Monitor IPC Manager Memory Manager Kernel I/O Manager GDI USER Device Drivers HAL Hardware I/O Devices DMA/Bus Controller Timers Caches, Interrupts CPU Privileged Architecture Robustezza: Spazi di indirizzamento separati System Memory (2 GB) Non-paged Paged Per-Process Memory (2 GB) Physical Addressing Range Paged Robustezza: User Mode e Privileged-Processor Mode User Mode applicazioni in esecuzione Accesso esclusivamente allo spazio di indirizzamento dei processi Divieto di accesso diretto all’hardware Privileged-Processor Mode Contiene il codice di sistema: executive, drivers, kernel e HAL “Trusted” Può eseguire qualsiasi istruzione Accesso all’intero spazio di indirizzamento Robustezza: Security Per-User Permissions Access Control List (ACL) Auditing Access Quotas (sempre presente, non implementato fino a Windows 2000) Robustezza: i sottosistemi User Mode Win32-Based Application POSIX OS/2 Subsystem Local Procedure Call Executive Send Reply Privileged-Processor Mode Win32 Subsystem Estendibilità Applications and Subsystems Software (User-Mode Instruction Set) Process Security Local Object Virtual Manager Monitor IPC Manager Memory Manager Jobs Kernel Device Drivers I/O Manager NTFS GDI USER FAT Timer Queue Encryption HAL Hardware I/O Devices DMA/Bus Controller Timers NTFS-5 Caches, Interrupts CPU Privileged Architecture Gli oggetti di NT (kernel objects) Process Access Token Mutex Thread Event Semaphore Section File Registry Object model L’object model consente di: monitorare le risorse implementare la sicurezza condividere le risorse Object: struttura interna Object Attributes Object Header Object Name Object Directory Security Descriptor Quota Charges Open Handle Counter Open Handle Database Permanent/Temporary Kernel/User Mode Type Object Pointer Object Body Type Object Object: interfaccia esterna Funzioni Parametri comuni Createxxx Security attributes, inheritance, name Openxxx Security attributes, inheritance, name BOOL CloseHandle(hObject) BOOL DuplicateHandle(hSourceProcess, hSource, hTargetProcess, lphTarget, fdwAccess, fInherit, fdwOptions) Lo scope di un Object Handle è relativo a ogni singolo processo Durata di un oggetto Struttura SECURITY_ATTRIBUTES typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES; Access Token Security Detail Security ID: LEES Group IDs: TEAM1 TEAM2 LOCAL INTERACTIVE WORLD Privileges: None . . . Event Object . . Security Descriptor . . Access Control List Allow LEES Synchronize Modify State Allow TEAM1 Synchronize Allow TEAM2 Synchronize Modify State Architettura Windows NT/2000 Radici nel passato (VMS) Disegno modulare ed estendibile Pochi cambiamenti e molte ottimizzazioni dal kernel di Windows NT 3.1 a quello di Windows 2000 Modello ad oggetti, ma non object-oriented Gestione della memoria (Memory Manager) Gestione della memoria I processori iAPX86 Indirizzamenti real-mode e protected-mode Funzioni API Condivisione della memoria VLM – Very Large Memory AWE – Address Windowing Extension I Processori Intel iAPX86 Tutti eseguono il boot in Real Mode Su 8088/8086 è l’unica possibilità Su 286 e successivi è necessario per compatibilità Dopo il boot 286 e successivi possono passare al protected mode Tutti hanno un’architettura segmentata Indirizzamento in real-mode 15 0 15 segment 0 offset : (shift left by 4 bits) 19 43 0 19 segment’s base address 0 0 0 0 16 15 0000 0 effective address 1 MB + RAM 19 0 linear address 220 = 1MB 0 Real-mode Nessun meccanismo di protezione della memoria Diverse combinazioni segment:offset indirizzano la stessa locazione di memoria fisica Nessun supporto hardware nativo per una efficacie implementazione del multitasking Microsoft ha inventato una implementazione software del multitasking per Windows 1.x 15 Protected-mode (32 bit) selector 2 1 0 T RPL I 31 : offset 15 BASE ACCESS 0 ? 63 15 1 TI = Table Index RPL = Requestor Privilege Level GDT = Global Descriptors Table (64KB) LDT = Local Descriptors Table (64KB) BASE = BA = Base Address (32 bit) LIMIT = Limit (20 bit) = 1 MB ACC. = ACCESS = Access Byte (12 bit) SS = Segment Size 0 BASE ACCESS GDT 0 ACC. LIMIT BASE BASE LIMIT x 8192 48 LDT SEGMENT 0 ACC. LIMIT BASE BASE LIMIT 63 48 GD 0 16GB/ 64TB A V L P DPL S x 8192 TYPE RAM BA+SS BA 0 Struttura Interna Access Byte G = 0 SS = Limit x 1 Byte = 1 MB G = 1 SS = Limit x 1 Page = Limit x 4KB = 4GB (G = Granularity Bit) Protected-mode (32 bit) Se la granularità è una pagina (4KB), ciascun segmento è da 4GB e lo spazio di indirizzamento virtuale è di 64TB! Il descriptor è comunque compatibile con 16 bit PM L’architettura è comunque segmentata e limita la portabilità Tipicamente le piattaforme RISC non hanno architettura segmentata… Eliminare la Segmentazione In Win95 e WinNT, il sistema operativo utilizza un solo segmento da 4GB In pratica, l’indirizzamento in Win32 è basato esclusivamente sull’offset (32 bit) L’indirizzamento appare lineare (o flat) Questo assicura la portabilità ad architetture non segmentate (es. RISC) Win32 indirizza fino a 4GB RAM fisica Tuttavia la strategia di implementazione è basata sulle Pagine La quantità minima di memoria allocabile è la pagina = 4KB Perchè Paginare la Memoria Se l’offset fosse usato come un riferimento diretto alla locazione di memoria, lo spazio di indirizzamento virtuale coinciderebbe con la RAM fisica installata Per indirizzare 4GB virtuali, sarebbe necessario disporre di 4GB di RAM fisica in ogni sistema! La paginazione consente di simulare uno spazio di indirizzamento fisico da 4GB anche se la RAM fisica installata è <4GB Remapping della memoria fisica sulla memoria virtuale Paginazione su disco Exceptions generate dal processore e gestite dal sistema operativo 32 bit Offset in Win32 31 22 21 PDE (10 bit) 20 bits 12 11 PTE (10 bit) 12 bits 1023 20 bits OFFSET (12 bit) 12 bits 0 Page Directory CR#3 i386 0 4GB 1023 0 PAGE 4KB Page Table Win32 Process Context RAM PDE = Page Directory Entry PTE = Page Table Entry CR#3 = Control Register #3 0 Spazio di indirizzamento System Memory (2 GB) Non-paged Paged Per-Process Memory (2 GB) Physical Addressing Range Paged Memoria virtuale Process Address Space Physical Memory 2 GB Page Tables R e s e r v e d 0 Committed Pages Invalid Committed Pages Invalid 2 GB Page Directory, Page Table, Page Frame Virtual Physical Address Space Directory Table 1024 Entries 1024 Entries Memory 4K 12 MB Invalid Frame Invalid 8 MB 4 MB 0 4K 4K Valid Paged Paging File 10 Bits Directory Index 10 Bits Table Index 12 Bits Byte Within Page Paging File Contiene Pagine scambiate dalla Memoria RAM al Disco Dimensione pagina: 4K o 8K Paging Policy Page Commitment Process Address Space 2 GB R e s e r v e d Committed Pages Committed Pages Copy-on-Write e Guard Pages Process 1 Virtual Memory Page 1 Committed Memory Page 2 Page 1 Page 2 Section Page 1 Process 2 Page 2 Copy Virtual Memory Page 2 Copy (Read-Write) Funzioni API: VirtualAlloc Manipolazione pagine virtuali o spazi di indirizzamento Lettura stato pagine virtuali o spazi di indirizzamento LPVOID VirtualAlloc(lpvAddress, cbSize, fdwAllocationType, fdwProtect) BOOL VirtualFree(lpAddress, cbSize, fdwFreeType) BOOL VirtualProtect(lpvAddress, cbSize, fdwNewProtect, pfdwOldProtect) DWORD VirtualQuery(lpAddress, pmbiBuffer,cbLength) BOOL VirtualLock(lpvAddress, cbSize) BOOL VirtualUnlock(lpvAddress, cbSize) Esempio: Uso di Funzioni VirtualXXX ... lpBase = VirtualAlloc (NULL, my_size, MEM_RESERVE, PAGE_NOACCESS); ... __try{ // an EXCEPTION_ACCESS_VIOLATION here // will be fixed by MyFilter if possible } __except (MyFilter (GetExceptionInformation())) { } ... VirtualFree (lpBase, 0, MEM_RELEASE); ... Gestione Heap Lo Heap è ottimizzato per allocare blocchi di memoria più piccoli senza l’overhead di pagina (4k o 8k) Ogni processo ha uno Heap di default E’ possibile allocare Heap privati La memoria allocata in uno heap può portare a fenomeni di frammentazione Sono Thread-Safe per default Funzioni API: HeapAlloc Allocazione di un Heap privato HANDLE HeapCreate(flOptions, dwInitialSize, dwMaximumSize) BOOL HeapDestroy(hHeap) LPVOID HeapAlloc(hHeap, dwFlags, dwBytes) BOOL HeapFree(hHeap, dwFlags, lpMem) DWORD HeapSize(hHeap, dwFlags, lpMem) LPVOID HeapReAlloc(hHeap, dwFlags, lpMem, dwBytes) Sono Thread-Safe per default Funzioni API: LocalAlloc/GlobalAlloc Tutti i puntatori sono valori a 32 bit LocalAlloc e GlobalAlloc allocano memoria dallo stesso Heap GlobalAlloc garantisce allocazioni con allineamento a 16 bit HGLOBAL GlobalAlloc(fuFlags, cbBytes) HLOCAL LocalAlloc(fuFlags, cbBytes) Presenti solo per ragioni di compatibilità: richiamano HeapAlloc Funzioni API: C Run-Time Library Le funzioni delle Standard C Run-Time Libraries possono essere usate (malloc, calloc, free, realloc, ...) Bilanciare chiamate malloc() con free(), senza mischiare con GlobalFree(), HeapFree() o altro Memory-Mapped File I Memory-Mapped Files consentono di condividere la memoria tra processi diversi Sono l’equivalente Win32 dell’oggetto kernel Section Viste di Memory-Mapped File Physical Memory PG.SYS Sections Views File FileMapping: le cose ovvie Il file è mappato in memoria come se fosse effettivamente caricato Gli indirizzi di memoria necessari ad effettuare il mapping devono essere riservati Solo le pagine effettivamente utilizzate (a cui si accede in lettura o in scrittura attraverso puntatori di memoria) vengono effettivamente lette/scritte su disco Il Paging File usato come Memory Mapped File non rende persistenti le operazioni effettuate in memoria File Mapping: le cose meno ovvie Tutte le operazioni di I/O su file avvengono come operazioni di file-mapping La cache dell’I/O Manager è integrata con il Memory Manager Tutte le volte che si apre un eseguibile (.EXE, .DLL, .OCX), il codice e le risorse non vengono caricate in memoria fino a che non sono realmente utilizzate (anche queste sono operazioni di file-mapping) Caricando più volte la stesso file EXE/DLL da processi diversi, la memoria fisica impegnata è sempre la stessa CreateFileMapping e OpenFileMapping Crea un oggetto Section Apre un oggetto Section HANDLE CreateFileMapping(hFile, lpsa, fdwProtect, dwMaximumSizeHigh, dwMaximumSizeLow, lpszMapName) HANDLE OpenFileMapping(dwDesiredAccess, bInheritHandle, lpszMapName) Assegnando a hFile il valore –1 si usa il PagingFile MapViewOfFile e UnmapViewOfFile LPVOID MapViewOfFile(hMapObject, fdwAccess, dwOffsetHigh, dwOffsetLow, cbMap) BOOL UnmapViewOfFile(lpBaseAddress) BOOL FlushViewOfFile(lpvBase, cbFlush) UnmapViewOfFile scrive tutti i cambiamenti su disco Lazy Writes su disco delle scritture in memoria E’ garantita la coerenza di viste differenti di un singolo oggetto Section Usare Memory-Mapped File come Shared Memory Process 1 Virtual Memory View Committed Memory Section Process 2 Virtual Memory View Gestione della memoria Superare il muro dei 4Gb con Win32 Solo per i dati, non per il codice VLM – Very Large Memory Solo su Windows 2000 Advanced Server Solo per processori a 64 bit, ad oggi solo Alpha, che non supporta più Windows 2000 AWE – Address Windowing Extension Su tutte le versioni di Windows 2000 Per tutti i processori a 32 e 64 bit (Win64) Meno comodo da usare... AWE – Address Windowing Extension Per tutti i processori e per tutte le versioni di Windows 2000 Memoria mappata nello spazio virtuale disponibile al processo (2Gb/3Gb) Non funziona con funzioni grafiche/video Granularità 4K/8K Il processo deve essere abilitato (diritto Lock Pages in Memory) Una zona allocata con AWE non può essere condivisa con altri processi AWE – Funzioni AllocateUserPhysicalPages VirtualAlloc MapUserPhysicalPages FreeUserPhysicalPages Gestione della memoria Introduzione architetturale Funzioni API Memory mapped file Condivisione della memoria VLM & AWE Processi Cosa è un Processo? Un istanza di un programma in esecuzione Un processo possiede degli Oggetti Cosa distingue un processo da un altro? Gli oggetti sono rappresentati dagli Handle Handle table Memoria privata Windows Il processo è un insieme di thread, ed esiste fino a che contiene almeno un thread Win32-Based Process Model Access Token Virtual Address Space Description ... Process Object Table Available Objects Handle 1 Thread x Handle 2 File y Handle 3 Section z Object Handle Ogni Object Handle E’ valido solo nel contesto del proprio processo Può essere ereditato Mantiene un oggetto in vita nel sistema fino a che tutti gli handle allo stesso oggetto non sono chiusi Tipi di Object Handle Private: CreateMutex, OpenSemaphore, ... Duplicated: BOOL DuplicateHandle(...) Inherited: fInheritHandles a TRUE in CreateProcess Pseudo: GetCurrentProcess(), GetCurrentThread(), ... Ciclo di vita di un Processo Un processo viene creato associando sempre un file eseguibile (.EXE), che è mappato in memoria ed eseguito Creando un processo viene creato un thread Il primo thread di un processo può essere chiamato thread principale, anche se la sua chiusura non determina necessariamente la fine del processo La run-time library del C termina il processo corrente se si esce dalla funzione main() con return, indipendentemente dalla presenza di altri thread Creazione di un Processo BOOL CreateProcess(lpszImageName, lpszCommandLine, lpsaProcess, lpsaThread, fInheritHandles, fdwCreate, lpvEnvironment, lpszCurDir, lpsiStartInfo, lppiProcInfo) HANDLE OpenProcess(fdwAccess, fInherit, IDProcess) Informazioni ritornate dalla creazione di un Processo typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION; Gli handle hProcess e hThread vanno sempre chiusi con CloseHandle(hObject) quando non sono più utilizzati E’ un errore frequente dimenticare questa operazione su entrambi gli handle restituiti! Chiudere un Processo Chiusura normale Chiudere l’handle ad un processo BOOL CloseHandle(hObject) Chiusura immediata (e anomala) di un processo VOID ExitProcess(uExitCode) BOOL TerminateProcess(hProcess, uExitCode) La chiusura dell’ultimo thread di un processo implica una chiamata ad ExitProcess() Funzioni API LPTSTR GetCommandLine(VOID) HANDLE GetCurrentProcess(VOID) DWORD GetCurrentProcessId(VOID) VOID GetStartupInfo(lpsi) HANDLE OpenProcess(fdwAccess, fInherit, IDProcess) DWORD GetEnvironmentVariable(lpszName, lpszValue, cchValue) BOOL SetEnvironmentVariable(lpszName, lpszValue) LPVOID GetEnvironmentStrings(VOID) Interprocess Communication (IPC) IPC viene realizzata quando due o più processi condividono un oggetto Gli oggetti usati per IPC includono: Shared memory (section object) Files Semaphores Pipes / Mailslot Windows sockets I metodi di accesso condiviso sono gli stessi per molti oggetti di tipo diverso Condivisione di oggetti Può avvenire in tre modi: BOOL DuplicateHandle(hSourceProcess, hSource, hTargetProcess, lphTarget, fdwAccess, fInherit, fdwOptions) Creando o aprendo un “Named Object” Ereditarietà Condivisione di oggetti Gli oggetti condivisi sono handle diversi che puntano allo stesso oggetto kernel Process A Process B Kernel Objects Object Table Thread x Object Table Handle 1 Thread y Handle 1 Handle 2 File k Handle 2 Handle 3 Section z File w Handle 3 Ereditarietà Access Token Granted Access Process A Inheritance Available Objects Handle 1 Thread x Handle 2 File y Handle 3 Section z Object Table Ereditarietà Gli oggetti condivisi attraverso ereditarietà assumono lo stesso valore di Handle CreateProcess(…) Process A Process B Kernel Objects Object Table Thread x Object Table Handle 1 Thread y Handle 1 Handle 2 File k Handle 2 Handle 3 Section z Handle 3 Controllare l’ereditarietà typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES; BOOL bInheritHandle; Processi Achitettura Object Handle Ciclo di vita di un processo Condivisione di oggetti Ereditarietà Thread Cosa è un Thread? Un thread è un percorso di esecuzione all’interno di un processo Un thread accede a tutte le risorse del processo in cui è contenuto Il ThreadID contraddistingue un thread nel sistema operativo, indipendentemente dal processo ospitante Un thread esiste fino a che: il percorso di esecuzione termina viene chiuso con ExitThread viene chiuso/distrutto il processo ospitante Preemptive vs. Cooperative Preemptive (Windows NT) Thread 1 Processor Done Thread 2 Time Cooperative (Windows 3.1) Task 1 Processor Task 2 I/O or Idle Executing Involuntary Voluntary Win32 Preemptive Multitasking Process A Thread 1 System Scheduler Thread 2 Process B Process C Thread 1 Thread 1 i386 Process D Thread 1 Thread 2 Process E Thread 3 Thread 1 Perché usare thread multipli? I thread sono più economici dei processi Per dividere task paralleli Per supportare la concorrenza Perché i thread comunicano più velocemente Per sfruttare Symmetric Multiprocessing Thread Overhead Ogni thread Win32 ha un “gemello” nel sottosistema Host Ci sono due stack per ogni thread C’è un Hardware Context per thread C’è un insieme di variabili statiche delle C RunTime Library per thread Schedulazione dei Thread Lo Scheduler Win32 : Schedula soltanto thread E’ preemptive E’ basato sulle priorità Scheduler Lo scheduler conosce solo i Thread Ogni Thread ha due livelli di priorità Priorità di base Priorità corrente La priorità corrente è analizzata per decidere quale thread eseguire Scheduler La priorità di base è funzione di due valori: Classe di priorità del processo Valore di priorità del thread La priorità corrente è dinamica (viene variata dallo scheduler), ma non scende mai al di sotto della priorità di base Priorità di processi e thread Processi Thread IDLE IDLE BELOW_NORMA L NORMAL LOWEST ABOVE_NORMA L HIGH REALTIME BELOW_NORMA L NORMAL ABOVE_NORMA L HIGHEST TIME_CRITICAL Calcolo priorità di base dei thread Realtime Time Critical 31 Realtime Realtime Livelli 16-31 Realtime Idle Dynamic Time Critical 24 High 16 15 Above Normal 13 Normal Dynamic 10 Livelli 1-15 Below Normal 8 Idle 6 4 Dynamic Idle System Idle 0 Scheduler Logica di funzionamento dello scheduler Trova il thread attivo con priorità corrente più alta e lo esegue per un quantum Solo il thread non sospeso con la priorità più alta viene eseguito Se ci sono più thread con lo stesso livello, effettua un roundrobin tra questi L’unico modo per eseguire thread a priorità più bassa è che i thread a priorità più alta vadano in stato di Wait (ad es. per operazioni di I/O) Scheduler Impatto dello scheduler sui tempi di risposta Appena un thread a priorità più alta non è più sospeso, lo scheduler interrompe il thread in esecuzione e passa il controllo al thread con la priorità più alta La priorità di un thread non agisce quindi direttamente sulla percentuale di tempo di CPU assegnata Priorità di processi e thread Variable Real-Time Real-Time Class High Class Normal Class Idle Class 1 5 10 15 16 20 25 31 Scheduler Come fa Windows a funzionare? Il trucco è che la priorità corrente del thread è modificata continuamente dallo scheduler: I thread in attesa vengono premiati I thread che usano più CPU sono penalizzati L’applicazione in primo piano riceve un quantum più lungo Scheduler Andamento del livello di priorità corrente nel tempo Priorità Priority Boost Quantum Decay Base Run Wait Run Preempt Tempo Round-Robin at Base Run Scheduler Non voglio che lo scheduler cambi il livello di priorità corrente di un thread. Come faccio? Il livello di priorità corrente non scende mai al di sotto della priorità base. Un thread con priorità TIME_CRITICAL ha sempre il valore di priorità corrente 15 Tutti i valori superiori a 15 non sono mai modificati dinamicamente (processi in classe di priorità REALTIME) Scheduler Priorità dinamiche Priorità statiche High Class Real-Time Class Above Normal Normal Class Non sono solo driver Below Normal Idle Class 1 5 10 15 16 20 25 31 Controllare le priorità Un’applicazione può controllare la priorità dei suoi thread: CreateProcess parametro fdwCreate BOOL SetPriorityClass(hProcess, fdwPriority) BOOL SetThreadPriority(hThread, nPriority) Creazione di un Thread La funzione Win32 per creare un thread è CreateThread Ogni libreria o ambiente di sviluppo ha delle funzioni specifiche per creare un thread: C/C++ RTL MFC _beginthread(...) AfxBeginThread(...) Se si usano le C-Run Time Libraries, specificare la versione multi-thread delle librerie nelle opzioni del progetto C Run-Time Library Funzioni di creazione dei Thread Win32 API HANDLE CreateThread( lpsa, cbStack, lpStartAddr, lpvThreadParm, fdwCreate, lpIDThread) C Run-Time API unsigned long _beginthread( void (*start_address) (void *), unsigned stack_size, void *arglist) Esempio CreateThread DWORD WINAPI CalculationThreadProc (LPVOID lpv) { printf(“CalculationThreadProc: Param=%d\n”, *((lpint)lpv); return 0; } DWORD main (void) { DWORD dwThreadId, dwThrdParam = 1; HANDLE hThread; hThread = CreateThread (NULL, 0, CalculationThreadProc, &dwThrdParam, 0, &dwThreadId); if (hThread == INVALID_HANDLE_VALUE) return 1; ... } //no security attributes //default stack size //thread function //thread function argument //default creation flags //returns thread ID Esempio CalculationThreadProc typedef struct _threadargs { ... } THREADARGS, * LPTHREADARGS; DWORD WINAPI CalculationThreadProc ( LPVOID lpv) { LPTHREADARGS lpta = lpv; BOOL bRet; /*Do Calculation*/ return bRet; } Thread IDs e Handles DWORD GetCurrentThreadId(VOID) DWORD GetCurrentProcessId(VOID) HANDLE CreateThread(lpsa, cbStack, lpStartAddr, lpvThreadParm, fdwCreate, lpIDThread) HANDLE CreateRemoteThread(hProcess, lpsa, cbStack, lpStartAddr, lpvThreadParm, fdwCreate, lpIDThread) HANDLE GetCurrentThread(VOID) HANDLE GetCurrentProcess(VOID) Uscita da un Thread Funzioni API Win32 VOID ExitThread(dwExitCode) BOOL TerminateThread(hThread, dwExitCode) Funzioni API C Run-Time Libraries void _endthread(void) Valori di uscita da un thread BOOL GetExitCodeProcess (hProcess, lpdwExitCode) BOOL GetExitCodeThread (hThread, lpdwExitCode) DWORD WaitForSingleObject(hObject, dwTimeout) DWORD WaitForMultipleObjects(cObjects, lphObjects, fWaitAll, dwTimeout) Sincronizzazione Per scrivere codice efficiente è indispensabile... evitare tecniche di polling Sincronizzazione Un thread che non deve aspettare un evento esterno deve andare in stato di Wait Se un thread deve aspettare che un altro thread abbia compiuto una certa operazione, si sfruttano gli oggetti kernel per la Sincronizzazione: Mutex Semaphore Event Critical Section Attesa su Oggetti kernel Per sospendere un thread fino a che uno o più oggetti kernel non diventano “segnalati”, usare le funzioni di Wait: DWORD WaitForSingleObject( hObject, dwTimeout) DWORD WaitForMultipleObjects( cObjects, lphObjects, fWaitAll, dwTimeout) DWORD MsgWaitForMultipleObjects( lphObjects, fWaitAll, dwTimeout, fdwWakeMask ) cObjects, Scenario di sincronizzazione 1 Gestire in maniera efficiente l’input da una porta seriale Attende il prossimo carattere o il riempimento del buffer di input Legge e processa i caratteri Attende il prossimo carattere o il buffer Si usa l’oggetto Event, che è funzionalmente simile ad una variabile booleana Event Objects Interprocess or Intraprocess Named or Unnamed HANDLE CreateEvent(lpsa, fManualReset, fInitialState, lpszEventName) HANDLE OpenEvent(fdwAccess, fInherit, lpszEventName) BOOL SetEvent(hEvent) BOOL ResetEvent(hEvent) Uso di Event HANDLE hEvent = CreateEvent ( NULL, // no security TRUE, // event must be reset // manually FALSE, // initially nonsignaled NULL); // anonymous event StartTaskThatSignalsEventWhenDone (hEvent); // Do other processing. // Wait for event to be signaled; then reset it. WaitForSingleObject (hEvent, INFINITE); ResetEvent(hEvent); ... CloseHandle (hEvent); Scenario di sincronizzazione 2 Proteggere strutture dati condivise, come code e liste in memoria condivisa, dal danneggiamento dovuto ad accessi concorrenti Si usa l’oggetto Mutex, che è un flag che coordina l’esecuzione di operazioni mutualmente esclusive Solo un thread può detenere un Mutex, gli altri thread vengono sospesi sulla Wait fino a che il Mutex non viene rilasciato Mutex Objects Interprocess or Intraprocess Named or Unnamed Noncounting Mutual Exclusion HANDLE CreateMutex( lpsa, fInitialOwner, lpszMutexName) HANDLE OpenMutex( fdwAccess, fInherit, lpszMutexName) BOOL ReleaseMutex(hMutex) Uso di Mutex if (!hMutex) // if we have not yet created the mutex... { // create mutex with creator having initial ownership hMutex = CreateMutex (NULL, TRUE, "Bob"); if (GetLastError() == ERROR_ALREADY_EXISTS) WaitForSingleObject (hMutex, INFINITE); } else WaitForSingleObject (hMutex, INFINITE); __try{ // use the resource protected by the mutex } __finally { ReleaseMutex (hMutex); } ... CloseHandle (hMutex); Scenario di sincronizzazione 3 Porta per n-way Mutex Acquisisce una delle n risorse identiche da un pool di risorse disponibili Si usa l’oggetto Semaphore, che è simile ad un Mutex con un contatore Un Mutex è come un Semaphore che assume solo i valori 0 e 1; quando il Semaphore diventa 0, le chiamate a WaitFor... diventano sospensive, fino a che qualche thread non incrementa il valore del Semaphore Semaphore Objects Interprocess or Intraprocess Named or Unnamed Counting HANDLE CreateSemaphore( lpsa, cSemInitial, cSemMax, lpszSemName) HANDLE OpenSemaphore( fdwAccess, fInherit, lpszSemName) BOOL ReleaseSemaphore( hSemaphore, cReleaseCount, lplPreviousCount) Uso di Semaphore HANDLE hSem = CreateSemaphore ( NULL, // security 4, // initial count 4, // maximum count "Sally"); // global object name ... // Wait for any 1 of 4 resources to be available. WaitForSingleObject (hSem, INFINITE); __try{ // Use the semaphore. } __finally { ReleaseSemaphore (hSem, 1, &lPrevCount) }; ... CloseHandle (hSem); Scenario di sincronizzazione 4 Sincronizzazione di diversi thread nello stesso processo con un oggetto Mutex il più velocemente possibile, con il minimo overhead (es. inserimento di un oggetto in una coda) Si usa l’oggetto Critical Section, che è un Mutex utilizzabile solo da thread di uno stesso processo Critical Section Meccanismo veloce di condizione mutualmente esclusiva Memoria allocata dall’utente VOID InitializeCriticalSection(lpcsCriticalSection) VOID DeleteCriticalSection(lpcsCriticalSection) VOID EnterCriticalSection(lpcsCriticalSection) VOID LeaveCriticalSection(lpcsCriticalSection) Protegge l’accesso a dati condivisi da thread dello stesso processo Uso di Critical Section CRITICAL_SECTION cs; InitializeCriticalSection (&cs); . . EnterCriticalSection (&cs); __try { // initialize the thread arguments. ta.hWnd = hWnd; ta.lpDIB = lpDIB; ta.rc = *lprc; ta.bFlags = bFlags; ta.pMP = pMP; } __finally { LeaveCriticalSection (&cs); } DeleteCriticalSection (&cs); Thread e code mesaggi: Modello Win32 Mouse Device Driver Raw Input Thread Keyboard Device Driver RIT Event Queues Thread Thread Application Thread Application Funzioni API per messaggi e thread BOOL PostMessage(hWnd, uMsg, wParam, lParam) BOOL GetMessage(lpmsg, hWnd, uMsgFilterMin, uMsgFilterMax) BOOL PostThreadMessage( dwThreadId, uMsg, wParam,lParam) BOOL PeekMessage(lpmsg, hWnd, uMsgFilterMin, uMsgFilterMax, fuRemoveMsg) LRESULT SendMessage(hWnd, uMsg, wParam, lParam) BOOL SendNotifyMessage(hWnd, uMsg, wParam, lParam) BOOL AttachThreadInput(idAttach, idAttachTo, fAttach) DWORD MsgWaitForMultipleObjects(cObjects, lphObjects, fWaitAll, dwTimeout, fdwWakeMask ) Thread Definizione di thread Cooperative e preemptive multitasking Perchè usare thread multipli? Thread overhead Scheduler Creazione di un thread C Run-Time Library Oggetti per la sincronizzazione Thread e code di messaggi Thread Pool Thread Pool Insieme di Thread gestiti dal sistema Disponibili solo da Windows 2000 in poi Scenari affrontati: 1. 2. 3. 4. Accodare l’esecuzione asincrona di diverse funzioni Chiamare diverse funzioni periodicamente (senza WM_TIMER) Chiamare funzioni quando un oggetto kernel va in stato “segnalato” Chiamare una funzione quando una richiesta di I/O asincrono viene completata Thread Pool – Scenario 1 Scenario: accodare l’esecuzione asincrona di diverse funzioni (ad es. una scrittura “posticipata”) Senza Thread Pool Creazione di un Thread, esecuzione della funzione, distruzione del Thread Con Thread Pool Una chiamata a QueueUserWorkItem Thread Pool – Scenario 1 BOOL QueueUserWorkItem( LPTHREAD_START_ROUTINE pfnCallback, PVOID pvContext, ULONG Flags ); Valori di Flags: WT_EXECUTEDEFAULT WT_EXECUTEINIOTHREAD WT_EXECUTEINPERSISTENTIOTHREAD WT_EXECUTELONGFUNCTION Thread Pool – Scenario 2 Scenario: chiamare diverse funzioni periodicamente Senza Thread Pool Un thread per ogni funzione, Sleep(x) tra una chiamata e l’altra WM_TIMER per ogni funzione Con Thread Pool Una chiamata a CreateTimerQueueTimer Thread Pool – Scenario 2 CreateTimerQueue CreateTimerQueueTimer ChangeTimerQueueTimer DeleteTimerQueueTimer DeleteTimerQueueEx Thread Pool – Scenario 2 BOOL CreateTimerQueueTimer( PHANDLE phNewTimer, HANDLE TimerQueue, WAITORTIMERCALLBACK Callback, PVOID Parameter, WORD DueTime, WORD Period, LONG Flags ); Valori di Flags: WT_EXECUTEINTIMERTHREAD WT_EXECUTEINIOTHREAD WT_EXECUTEINPERSISTENTIOTHREAD WT_EXECUTELONGFUNCTION Thread Pool – Scenario 3 Scenario: eseguire una funzione quando un oggetto kernel diventa segnalato Senza Thread Pool Un thread per ogni funzione/oggetto, WaitForSingleObject prima della chiamata Un thread controlla più oggetti kernel con WaitForMultipleObjects... Con Thread Pool Chiamare RegisterWaitForSingleObject Thread Pool – Scenario 3 RegisterWaitForSingleObject UnregisterWaitEx Thread Pool – Scenario 3 BOOL RegisterWaitForSingleObject( PHANDLE phNewObject, HANDLE hObject, WAITORTIMERCALLBACK Callback, PVOID pvContext, ULONG dwMillisecs, ULONG dwFlags ); Valori di Flags: WT_EXECUTEDEFAULT WT_EXECUTEINWAITTHREAD WT_EXECUTEINIOTHREAD WT_EXECUTEINPERSISTENTIOTHREAD WT_EXECUTELONGFUNCTION WT_EXECUTEONLYONCE Thread Pool – Scenario 4 Scenario: chiamare una funzione quando termina un’operazione di I/O asincrono Senza Thread Pool Creazione di un I/O completion port e gestione manuale di un thread pool Con Thread Pool Una chiamata a BindIoCompletionCallback Thread Pool – Scenario 4 BOOL BindIoCompletionCallback( HANDLE FileHandle, LPOVERLAPPED_COMPLETION_ROUTINE Function, ULONG Flags ); Flags è riservato, deve valere sempre 0 Prototipo LPOVERLAPPED_COMPLETION_ROUTINE: VOID WINAPI OverlappedCompletionRoutine( DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ); Thread Pool Esecuzione di funzioni asincrone Esecuzione periodica di funzioni Operazioni da eseguire quando un oggetto kernel diventa “segnalato” Operazioni da eseguire al termine di operazioni di I/O Jobs Job Gruppo di processi E’ un oggetto kernel Disponibile solo da Windows 2000 in poi Crea delle regole per i processi che contiene Può definire dei limiti su: Risorse del sistema Interfaccia utente Sicurezza Può monitorare l’attività dei processi Job – Ciclo di vita Il Job viene creato “vuoto” Impostare proprietà del Job Un processo viene assegnato ad un Job La relazione Processo-Job è irreversibile Un processo figlio può nascere fuori dal Job del processo padre Se un Job viene terminato, tutti i suoi processi vengono terminati Il Job comunica con l’esterno con le I/O completion port (non segnala su Handle) Job – Ciclo di vita Il nome associato al Job è accessibile fino a che ci sono handle aperti al Job Se tutti gli handle al Job vengono chiusi, il Job resta in vita ma non è più accessibile (verrà distrutto quando tutti i processi all’interno si saranno chiusi) Job - Limiti Limiti sulle risorse di sistema: Process Priority Consumo CPU Working Set Size Consumo memoria Processor Affinity Job - Limiti Limiti su interfaccia utente Accesso USER Handle (HWND, HMENU, ...) Creazione/Switch Desktop Modifica impostazioni video Modifica parametri di sistema Clipboard ExitWindows Job - Limiti Limiti sulla security – Token utilizzabili: No Administrator Tutti i processi con un token uguale Solo token ristretti rispetto a quello del processo Filtro sui token (disabilitazione di alcuni diritti indipendentemente dall’utente impersonato) Job - Monitoraggio Tempo CPU (kernel/user) Page Fault Numero processi (attivi/terminati) Attività di I/O (operazioni/byte) Elenco processi Job - Funzioni CreateJobObject OpenJobObject AssignProcessToJobObject TerminateJobObject QueryInformationJobObject SetInformationJobObject UserHandleGrantAccess Jobs Definizione di Job Ciclo di vita Limiti definibili Monitoraggio Comunicazione tra processi (IPC – Interprocess Communication) Comunicazione tra processi Per comunicare tra loro, due processi devono richiedere dei servizi al sistema operativo: Memory Mapped File (Shared Memory) Object Handles File su disco Socket, Named Pipe e Mailslot RPC (Remote Procedure Call) Memory Mapped File Consente di condividere memoria tra processi diversi E’ una scelta adeguata quando più processi devono accedere contemporaneamente ad informazioni contenute in strutture dati di vaste dimensioni L’accesso simultaneo (almeno in scrittura) va protetto tramite meccanismi di sincronizzazione tra thread Non vi è una notifica istantanea della variazione di un dato da parte di un processo Object Handle Più processi possono accedere ad uno stesso oggetto kernel E’ una tecnica indispensabile per condividere oggetti di sincronizzazione tra processi diversi File su disco E’ un meccanismo che consente la condivisione di informazioni tra programmi in esecuzione anche su macchine diverse e con sistemi operativi diversi Le prestazioni e la scalabilità sono le più basse rispetto alle tecniche esaminate Socket, Named Pipe, Mailslot Sono dei “canali” di comunicazione tra processi Si possono usare anche tra processi residenti su elaboratori diversi La trasmissione delle informazioni è sequenziale I dati inviati generano delle notifiche ai processi in ascolto: questo consente di evitare tecniche di polling anche su comunicazioni in rete Socket Windows Sockets 2 è l’implementazione Microsoft dell’interfaccia di programmazione definita in “Berkely Sockets” Conosciuta come interfaccia di programmazione per TCP/IP, è utilizzabile anche per altri protocolli E’ il sistema ideale per implementare meccanismi di comunicazione su rete tra sistemi operativi diversi Supporta una comunicazione bidirezionale E’ un meccanismo client/server Socket Il client può risiedere sulla stessa macchina del server, o su una macchina diversa Processo S1 socket Port 80 Processo C2 Port 1198 Port 80 socket Computer A Port 1219 Computer B Processo C1 S1: Processo server C1, C2: Processi client Named Pipe Meccanismo di comunicazione client-server Simile ad un socket, ma indipendente dal protocollo fisico Pipe Server: processo che crea una named pipe Pipe Client: processo che si connette ad una istanza di named pipe La named pipe ha un nome univoco per la macchina in cui è definita, ma possono esistere più istanze della stessa named-pipe Integra la “security” Modalità di funzionamento “message-mode” Server solo su NT/2000, Client su Win9x/NT/2000 Named Pipe Il client può risiedere sulla stessa macchina del server, o su una macchina diversa named pipe Processo S1 Processo C2 \\S1\pipe\Test \\S1\pipe\Test named pipe Computer A Computer B Processo C1 S1: Processo server C1, C2: Processi client Pipe Server Sequenza di funzioni API da chiamare: CreateNamedPipe ConnectNamedPipe ReadFile / WriteFile DisconnectNamedPipe CloseHandle Pipe Client Sequenza di funzioni API da chiamare: CreateFile WriteNamedPipe SetNamedPipeHandleState ReadFile / WriteFile CloseHandle Named Pipe: API speciali BOOL TransactNamedPipe(…) Esegue sequenzialmente WriteFile seguita da ReadFile Si usa sul client per semplificare il codice di esecuzione di transazioni sincrone BOOL CallNamedPipe(…) Esegue sequenzialmente WaitNamedPipe, CreateFile, TransactNamedPipe e CloseHandle Si usa sul client per semplificare il codice nel caso in cui per ogni transazione si vuole aprire e chiudere una connessione Mailslot Meccanismo di comunicazione broadcast senza connessione Mailslot Server: processo che crea una mailslot e legge i dati ad essa inviati Mailslot Client: processo che scrive un messaggio su una mailslot Il nome di una mailslot è univoco per una macchina Nessuna gestione di “security” Modalità di funzionamento “message-mode” Server e Client su Win9x / NT / 2000 Mailslot Una macchina può ospitare solo una mailslot con lo stesso nome Processo C1 mailslot Processo S2 \\.\mailslot\Test mailslot Computer A \\.\mailslot\Test Computer B Processo S1 S1, S2: Processi server C1: Processo client Nomi delle Mailslot Creazione mailslot (server): \\.\mailslot\mailslotname Apertura mailslot per scrittura (client): \\.\mailslot\mailslotname \\ComputerName\mailslot\mailslotname \\DomainName\mailslot\mailslotname \\*\mailslot\mailslotname Mailslot locali Il Mailslot server deve esistere quando il Mailslot client viene creato Un messaggio può essere lungo fino a 64K I messaggi inviati al server vengono ricevuti una volta I messaggi inviati vengono sicuramente ricevuti, ma non si sa se e quando vengono letti Possono esistere più client che scrivono sullo stesso server, se i client usano gli attributi FILE_SHARE_WRITE | FILE_SHARE_READ nella CreateFile() Mailslot remote Deve esserci almeno un protocollo di rete attivo Il Mailslot client può essere creato in qualsiasi momento (anche se non esiste un Mailslot server) La scrittura da parte del Mailslot client non genera mai errori Un messaggio inviato ad un dominio può essere letto da più server Compatibilità garantita per messaggi fino a 425 byte Viene inviata una copia del messaggio per ogni protocollo di rete installato (il Mailslot server può ricevere più copie dello stesso messaggio) Mailslot Server Sequenza di funzioni API da chiamare: CreateMailslot ReadFile CloseHandle Mailslot Client Sequenza di funzioni API da chiamare: CreateFile WriteFile CloseHandle Remote Procedure Call Consente di eseguire chiamate a funzioni presenti in processi diversi, anche su elaboratori diversi E’ basato sugli standard definiti da OSF (Open Software Foundation) DCE (Distributed Computing Environment) E’ indipendente dal protocollo di rete E’ la base di DCOM Remote Procedure Call Per ogni chiamata, avviene una transazione coordinata dal sistema operativo, per trasferire al “server” i parametri della funzione, e ricevere da esso i risultati Client Server Application 1 14 Client Stub 2 13 Client Run-Time Library 3 12 Application 8 7 Client Stub 9 6 Client Run-Time Library 10 5 Transport Transport 11 4 Comunicazione tra processi Shared Memory Sockets Named Pipe Mailslot Remote Procedure Call Operazioni di I/O (I/O Manager) Windows NT I/O System Architecture Applications and Subsystems Software (User-Mode Instruction Set) Process Security Local Object Virtual Manager Monitor IPC Manager Memory Manager Kernel I/O Manager GDI USER Device Drivers HAL Hardware I/O Devices DMA/Bus Controller Timers Caches, Interrupts CPU Privileged Architecture Name Space unico per tutti i Device Environment Subsystem or DLL I/O System Services File System and Network Drivers I/O Manager Device Drivers User Mode Video Monitor and Keyboard Mouse Kernel Mode Printer Network Device Disk Drive CD-ROM Drive Tape Link simbolici Usati per mappare nomi di Device MS-DOS al Name Space di Windows NT DWORD QueryDosDevice( lpDeviceName, lpTargetPath, ucchMax) BOOL DefineDosDevice( dwFlags, lpDeviceName, lpTargetPath) Esempio D: -> \DEVICE\HARDDISC0\PARTITION2 Cache Manager Cache di tutto l’I/O per default Incrementa le performance dei programmi che effettuano molte operazioni di I/O La dimensione della Cache è dinamica E’ un insieme di oggetti Section Ha il proprio spazio di indirizzamento (di sistema) Usa il Virtual Memory Manager per effettuare la paginazione API per File I/O Si possono usare le seguenti API per accedere ai file: C Run-Time Library Windows 3.1 API (solo per compatibilità) Win32 API Win95-Based Synchronous I/O Processing Application WriteFile(file_handle data, ...) Returns WriteFile(file_handle data, ...) Win32 Subsystem Call Windows NT Write File Service Return Data User Mode Kernel Mode Check Parameters Create IRP Call Device Driver I/O Manager Complete IRP Return Queue I/O to Device Device Driver Device Time Perform I/O Transfer Wait for Completion Application Windows NT–Based . . . Processing Synchronous Win32 Subsystem WriteFile(file_handle data, ..., overlapped) <Wait ends> Return I/O Status Call Windows NT Write File Service User Mode Kernel Mode I/O Manager Check Parameters Create IRP Call Device Driver Queue I/O to Device Device Driver Device ... Time Wait(...) Set File Handle to Signaled State Return Handle Interrupt Perform I/O Transfer Interrupt for Service Asynchronous (Overlapped) I/O Processing . . . Application WriteFile(file_handle data, ..., overlapped) Win32 Subsystem Call Windows NT Write File Service <Perform other work> Wait(file_handle) <Wait ends> Return I/O Pending Status User Mode Kernel Mode I/O Manager Check Parameters Create IRP Call Device Driver Queue I/O to Device Device Driver Device ... Time Return Set File Handle to Signaled State Return Handle Interrupt Perform I/O Transfer Interrupt for Service Effettuare Overlapped I/O con Win32 Usare il flag FILE_FLAG_OVERLAPPED quando si apre un file con CreateFile() Effettuare un’operazione di I/O, e mettersi in Wait su: File handle, oppure Event object, oppure Una I/O completion callback Esempio: Overlapped I/O HANDLE usando hFile; Event Object LPOVERLAPPED lpo; // create and initialize an OVERLAPPED structure lpo = calloc (sizeof(OVERLAPPED), 1); lpo->Offset = 0; lpo->hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); hFile = CreateFile ("filename", GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); // begin async io WriteFile(hFile, pBuffer, cbBuffer, &nBytesWritten, lpo); // wait for io to complete and get result GetOverlappedResult (hFile, lpo, &nBytesWritten, TRUE); // cleanup CloseHandle (lpo->hEvent); free (lpo); I/O Completion Routine User-Mode Asynchronous Callback Non è veramente asincrona, perché può essere eseguita solo in corrispondenza di punti di controllo definiti (Control Points) Control Points ReadFileEx, WriteFileEx, WaitForSingleObjectEx, WaitForMultipleObjectsEx, SleepEx Esempio: I/O Completion Routine VOID WINAPI lpfnIOCompletion ( DWORD dwError, DWORD cbWritten, LPOVERLAPPED lpo) { if (!dwError) lpo->Offset += cbWritten; } ... hFile = CreateFile ("filename", GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); // create and initialize an OVERLAPPED structure lpo = calloc (sizeof(OVERLAPPED), 1); lpo->Offset = GetFileSize (hFile, NULL); // begin async io with callback WriteFileEx (hFile, pBuffer, cbBuffer, lpo, lpfnIOCompletion); ... // wait for callback to be called on i/o completion SleepEx (INFINITE, TRUE); ... I/O Completion Port Basato su una coda (I/O Completion Port) di messaggi relativi alle operazioni di I/O asincrone completate (I/O Completion Packet) Uno o più thread (generalmente worker thread) possono prelevare messaggi dalla coda ed eseguire operazioni appropriate Si può controllare il numero massimo di thread attivi che possono processare I/O Completion Packet (eventuali altre richieste concorrenti sospendono il thread richiedente fino a che uno dei thread in esecuzione non completa l’elaborazione del Completion Packet prelevato) Scatter-Gather I/O Meccanismo ad alte prestazioni per lettura/scrittura dati su file Disponibile su NT4.0 SP2 e successivi Usato da SQL Server Legge/scrive dati presenti in zone non contigue di memoria su zone di file contigue, sfruttando il DMA Granularità di pagina (4K / 8K) Funzioni API: ReadFileScatter(…) WriteFileGather(…) Change Notification Oggetto kernel utilizzabile in funzioni di Wait per monitorare il cambiamento del contenuto di una directory FindFirstChangeNotification(…) FindNextChangeNotification(…) FindCloseChangeNotification(…) Nota bene: non si usa CloseHandle(…) per la chiusura dell’handle ottenuto con FindFirstChangeNotification(…) I/O Manager Architettura I/O Manager Elaborazione sincrona dell’I/O Elaborazione asincrona dell’I/O I/O Completion Routine I/O Completion Port Change Notification Riferimenti http://msdn.microsoft.com http://www.sysinternals.com Altre Informazioni Dove posso ottenere maggiori informazioni www.sysinternals.com www.microsoft.com/msdn/italy/studenti www.ugidotnet.org Developer resources Microsoft Developer Network Windows internals I vostri feedback sono importanti Compilate il modulo di valutazione Grazie della partecipazione – [email protected]