Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
vuoi
o PayPal
tutte le volte che vuoi
NULL,
NULL,
FALSE,
NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&si,
&pi);
if (newprocess == FALSE)
{ printf(“Chiamata fallita\n") };
}
Le strutture dati STARTUPINFO e PROCESS_INFORMATION sono incluse nell’header
“windows.h”. In questo esempio si lancia un eseguibile denominato figlio.exe, con argomenti pippo
e pluto. NORMAL_PRIORITY_CLASS mappa su informazioni basiche per il time-sharing di CPU.
L’environment passato è NULL, cioè non sto specificando l’environment, perciò viene ereditato
“”
l’environment del chiamante, se io voglio azzerare l’environment dovevo passare una stringa
nulla. Attenzione: il kernel Windows utilizza la codifica UNICODE per rappresentare le
stringhe a differenza di UNIX che usa l’ASCII. Perciò in Windows esistono system
call diverse a seconda di quale codifica adottiamo, infatti le stringhe ASCII vanno
tradotte prima di darle in pasto al kernel. Ad esempio per createProcess esiste
createProcessA (ASCII) e createProcessW (UNICODE). Poi se specifichiamo una
macro a tempo di compilazione la chiamata a createProcess viene mappata
automaticamente in una delle due, ma comunque il kernel lavora con caratteri
UNICODE.
21/10/2016
Data questa differenza fra le system call che usano ASCII oppure UNICODE, chi sviluppa driver
per Windows utilizza la forma UNICODE, chi sviluppa software applicativo di solito sceglie
ASCII. Quando si parla di rappresentazione di carattere si possono utilizzare tutte le facility messe a
disposizione in ambiente windows, ad esempio TCHAR è un carattere anch’esso anonimo; può
essere wchar_t (UNICODE) oppure semplicemente char (ASCII). La macro che regola questa ed
altre trasformazioni è la macro _UNICODE, definita a tempo di compilazione. Usando la macro
TEXT (Stringa s) trasforma la stringa da ASCII a UNICODE e viceversa sempre secondo
_UNICODE. Un altro aspetto importante è il punto di vista della sicurezza, infatti scanf in windows
è inutilizzabile per la sicurezza con settaggio basico (viene restituito un errore, non un warning
come in UNIX), ma definendo CRT_SECURE_NO_WARNINGS si possono utilizzare queste
funzioni deprecate.
Altri servizi per la gestione dei processi in ambiente Windows sono:
DWORD WINAPI GetCurrentProcessId(void)
DWORD WINAPI GetProcessId( _In_ HANDLE Process )
Queste due system call chiamano il Sistema per avere l’identificativo del processo corrente o di un
certo oggetto di tipo processo riferito tramite l’handle. Come terminare on-demand un processo in
windows? La system call è: VOID ExitProcess(UINT uExitCode)
ExitProcess prende il codice di terminazione del processo come accadeva in exit in UNIX. Una
system call importantissima di uso generale che si può utilizzare per i tipi di oggetti più disparati:
DWORD WaitForSingleObject (HANDLE hHandle, DWORD dwMilliseconds)
Questa system call è un servizio bloccante che mette il thread chiamante in attesa che un certo
oggetto sia disponibile per colui che l’ha chiamata. Significa dire che voglio attendere il codice di
terminazione di quel processo. Posso specificare un tempo di attesa al termine del quale viene
restituito errore, cioò WAIT_FAILED. Poi il codice di terminazione lo prendo con il servizio
int GetExitCodeProcess( HANDLE hProcess, LPDWORD lpExitCode )
Che prende in input l’handle del processo e il puntatore all’area di memoria dove verrà scritto il
codice di terminazione del processo target. All’atto della consegna il codice di terminazione non ha
più senso di essere tenuto dal kernel. Se il processo non è terminato la system call ritorna errore,
ovviamente viene utilizzata in combinazione con waitForSingleObject.
Un processo attivo può chiedere la terminazione di un altro processo tramite la system call
BOOL TerminateProcess (HANDLE hProcess, UINT uExitCode)
Al quale va passato l’handle del processo da terminare con il relative codice di terminazione da
associare alla chiusura del processo. Ovviamente il processo che chiama questa system call deve
avere le capability per farlo, cioè deve avere i corretti permessi.
Le variabili d’ambiente su Windows sono simili a quelle su UNIX, per far restituire al kernel il
pointer all’array di stringhe d’ambiente si utilizza la system call:
LPTCH GetEnvironmentStrings(void)
Oppure posso prendere una variabile d’ambiente specifica con
DWORD GetEnvironmentVariable (LPCTSTR lpName,LPTSTR lpBuffer,DWORD
nSize)
Questa system call prende in input il nome della variabile, il puntatore al buffer di memoria dove
voglio far scrivere il valore e la dimensione del buffer, il valore di ritorno rappresenta il numero di
caratteri effettivamente scritti all’interno del buffer (se il buffer è stato riempito si chiama la stessa
system call con un buffer più grande). O, se ne voglio resettare una:
BOOL SetEnvironmentVariable (LPCTSTR lpName, LPCTSTR lpValue)
Che prende la stringa che identifica la variabile ed il nuovo valore. Se voglio invece resettare tutte
le variabili d’ambiente, quindi distruggerlo:
BOOL FreeEnvironmentStrings (LPTCH lpszEnvironmentBlock)
Per ulteriori informazioni e chiarimenti su queste system call o altre, visitare il manuale online delle
WINAPI postato da Microsoft, msdn.com.
Vediamo un esempio di gestione processi in Windows:
int main(int argc, char *argv[]) {
BOOL newprocess; STARTUPINFO si; PROCESS_INFORMATION pi;
DWORD exitcode; char buff[128];
memset(&si, 0, sizeof(si)); memset(&pi, 0, sizeof(pi)); si.cb = sizeof(si);
if (argc == 2){
printf("Starting up echoing process ....\n");
fflush(stdout);
newprocess = CreateProcessA((LPCSTR)argv[1], (LPSTR)argv[1], NULL, NULL, FALSE,
NORMAL_PRIORITY_CLASS, NULL, NULL, (LPSTARTUPINFO)&si, &pi);
if (newprocess == FALSE) {printf(" failure\n\n");ExitProcess(-1); };
printf(" done\n\n");
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &exitcode);
printf("\nEchoing process exited with code %d\n", exitcode);
fflush(stdout); }
else{ printf("\tHere I'm for echoing\n\t");
fflush(stdout);
while (1) {scanf("%s", buff);
if (strcmp(buff, "bye") == 0) ExitProcess(ECHOING_EXIT_CODE);
printf("\t%s\n\t", buff);
}}}
fflush(stdout);
In questo esempio ci sono le variabili si ed pi che sono particolarmente interessanti, perché sono
variabili strutturate secondo lo schema STARTUPINFO e PROCESS_INFORMATION viste prima,
queste vengono azzerate prima di chiamare il kernel così da osservare l’output senza creare rumore.
Il campo cb della variabile si indica la taglia della tabella (control block), che sostanzialmente
indica la versione dell’informazione di startup. Se poi argc (cioè il numero di argomenti) è pari a 2
il codice va a fare la creazione vera del processo, fatto direttamente con la versione ASCII della
system call createProcess, dando per scontato che si stanno usando stringhe ASCII.
Se la chiamata a createProcess non va a buon fine terminerà l’esecuzione del processo corrente,
altrimenti sappiamo che l’altro processo esiste e abbiamo la maniglia per quel processo, infatti nel
campo di pi chiamato hProcess ho l’handle per il processo creato. Avendo l’handle il processo si
mette in attesa della terminazione per un tempo infinito, dopodiché vado a chiedere il codice di
terminazione al kernel (dovrei controllare il valore di ritorno della wait).
Se argc è invece uguale a 1 il codice fa un lavoro di echo sull’input fino alla ricezione della stringa
“bye”. Ma quand’è che argc è uguale a 1? Per esempio quando il processo padre lancia un'altra
istanza di quest’applicazione, nel processo figlio infatti verrà passato come argomento solo il nome
dell’applicazione stessa, il processo figlio trapperà quindi nel ramo else.
Se volevo passare al processo figlio un ambiente diverso dal processo padre, dovevo inserire come
parametro dopo NORMAL_PRIORITY_CLASS ad esempio la stringa "A=10\0B=20\0\0", in cui si
specifica una variabile A = 10 e una variabile B = 20, separate dal terminatore di stringa.
Gestione dei Thread
Un thread è caratterizzato da uno spazio d’indirizzamento e una traccia di istruzioni. Può usare al
più una CPU per l’esecuzione. Nei SO moderni c’è il multithreading, quindi si effettua dispatching
di thread non più processi. Per avere un thread attivo deve essere attivo anche un processo e la sua
immagine deve essere caricata in memoria. Ogni traccia d’esecuzione deve essere però controllata,
perciò serve un’area di stack per ogni thread. Ciascuno dei thread può poi chiamare una system call,
anche contemporaneamente se la CPU ha più di un core, perciò deve esserci una system stack per
ogni thread. I thread comunque possono accedere alla stessa sezione dati, stesso heap, pur avendo
immagini di CPU da restorare in memoria differenti. Quindi un thread di un processo può essere
bloccato su un dispositivo di I/O mentre il resto del processo continua a lavorare. La funzione di
sistema per creare un thread la prima cosa che fa è allocare memoria per lo stack, quindi
pthread_create in UNIX usa malloc. Nel modello multithread quindi rimangono i metadati di
gestione dei processi con il relativo address space e vengono inseriti un TCB e uno stack per ogni
thread. I Thread Control Block (TCB) sono i metadati del thread e contengono ad esempio lo
snapshot di CPU per ogni thread. Ogni thread ha il suo state diagram.
Thread in Windows
Essendo un ambiente nativamente multithread, windows è il riferimento in termini di thread, i
thread in windows sono oggetti, e come ogni oggetto windows hanno attribute e servizi, vediamoli
Attributi
o ID del thread
o Contesto del thread, CPU state che verrà restorato quando il thread riprenderà il
controllo
o Priorità base del thread, quanto è importante questo thread all’interno della
schedulazione (può essere diverso fra i thread appartenenti allo stesso processo)
o Priorità dinamica del thread
o Affinità di processore (insieme di processori utili all’esecuzione, si può assegnare ad
un thread la facoltà di eseguire soltanto su una CPU)
o Tempo di esecuzione
o Contatore dei blocchi
o Stato di uscita
Servizi
o Creazione di un thread
o Richiesta o modifica di informazioni di thread
o Terminazione di thread
o Lettura di contesto, cioè i metadati di gestione del thread
o Modifica di contesto
o Blocco di un thread
Andiamo a vedere un API per creare un thread in Windows
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
S