S02 – F6 – Memória partilhada/sincronização – Memória partilhada e semáforos
Windows System Programming, Fourth Edition, de Johnson M. Hart
Resumo do capitulo 6 (página 181 a 195)
Um processos contem o seu espaço virtual e independente de endereçamento, que contém código e dados, protegidos de outros processos.
Cada processo têm um ou mais threads a serem executadas.
Uma thread num processo pode executar código de aplicações, criar novas threads, crias novos e independentes processos, e gerir a comunicação e sincronização entre threads.
Assim criar a gerir processos, permite que as aplicações executem tarefas concorrentes, que trabalham ficheiros, que façam computação ou comunicação.
Do ponto de vista do Windows um processo inclui os seguintes recursos:
uma ou mais threads
um espaço de endereço virtual, distinto de outros processos (ficheiros partilhados mapeados em memória partilham um endereço físico em memoria, mas a partilha entre processos vai fazer uso de um outro endereço virtual para aceder a esse ficheiro mapeado)
um ou mais segmentos de código, onde se incluem DLLs
um ou mais segmentos de dados que contêm variáveis globais
variáveis de ambiente que contêm informação
o heap do processo
recursos, como é o caso de handles abertos ou outras heaps
uma thread dentro de um processo partilha código, variáveis globais, variáveis de ambiente e recursos. assim uma thread, tem de forma independente:
chamadas, interrupções, excepções de handlers e armazenamento automático;
Thread local storage (TLS), que são um array de ponteiros, que permitem à thread de alocar o seu espaço de armazenamento, que é único
de ter um argumento na stack
um contexto relacionado com a estrutura, e que é mantido pelo kernel
BOOL CreateProcessA( LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation );
+infos(LINK)
surge também a estrutura relacionado com o processo e que:
typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
+infos(LINK)
Porque é que um processo e as threads precisam de hanndle e dos IDs? Sendo o ID único durante o período de vida do objecto, e fica inválido quando o processo ou a thread deixam de existir. Mas um processo pode ter vários handles, com as devidas restrições de segurança e por esse motivo é necessário que na gestão dos processos as funções necessitem do ID e outros necessitam dos handles.
Um processo filho pode requerer acesso a um objecto referenciado por um handle no pai, se esse handle for herdado o filho pode então receber um copia do handle aberto do pai. Isso é feito através de um mecanismos de comunicação inter-processos ou através da atribuição de um handle de I/O na estrutura do STARTUPINFO (opção ideal)
A estrutura do PROCESS_INFORMATION permite ao processo obter a identidade e o handle de um novo filho. Fechar o handle do filho, não destro o filho, mas a habilidade do pai aceder ao filho.
Surgem duas funções para aceder a identificação do processo:
HANDLE GetCurrentProcess();
+infos(LINK)
DWORD GetCurrentProcessId();
+infos(LINK)
Assim que um processo terminar o seu trabalho, o processo pode invocar a função ExitProcess com um determinado código:
void ExitProcess( UINT uExitCode );
+infos(LINK)
Complementar a esta função surge o GetExitCodeProcess
BOOL GetExitCodeProcess( HANDLE hProcess, LPDWORD lpExitCode );
+infos(LINK)
e o TerminateProcess, onde um processo pode terminar outro processo caso o handle tenha essa referência
BOOL TerminateProcess( HANDLE hProcess, UINT uExitCode );
+infos(LINK)
Tem que existir um cuidado por parte do programador quando ele faz uso destas funções, já que antes de terminar um processo ele deve libertar os recursos que está a partilhar com outros processos, e também o terminar um processo deve ser feito enviando um sinal para os outros processos, para que possam terminar em segurança.
Para evitar estes problemas, vai ser feito uso de um tempo de espera. Esse método é sincronizado e fica à espera que o processo termine. Estas funções têm como interesse:
podem esperar por diferentes tipos de objectos; como é o caso do handles de processos
podem esperar por um único processo, ou pelo primeiro processo de um conjunto de processos ou por uma colecção de processos
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
+infos(LINK)
e
DWORD WaitForMultipleObjects( DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds );
+infos(LINK)
Resumo do capitulo 7 (página 223 a XXX)
Uma thread é uma unidade independente de execução dentro de um processo. O uso de multithreads permite tirar vantagem do paralelismo de um programa e de um computador.
O uso de threads independentes e concorrentes tem várias desvantagens:
é demasiado dispendioso e consumidor de tempo para o SO, efectuar trocas entre processos, bem como na pesquisa das threads
é difícil e ineficiente para uma unica thread fazer a gestão de diversas actividades concorrentes, como é o caso das tarefas de interactividade, como é o caso do input do utilizador..
As threads dentro de um processo partilham informações, código, mas as threads individuais também têm o seu código e partilha de dados. Tem que ser o programador a ter cuidado com a protecção indevida por parte de outras threads num determinado processo. As threads não podem aceder a informações que pertencem a outras threads.
Surgem algumas observações:
cada thread têm a sua própria stack para a chamada de funções e outros processamentos
a chamada de um processo pode passar por argumento, normalmente um ponteiro, para uma thread
cada thread pode pode alocar o indexa-mente da sua própria thread local storage, e pode ler e escrever os valores nessa thread local storage ( são arrays de ponteiros para threads, e uma thread acede ao seu próprio array de thread local storage)
A gestão das threads passa pelo uso de handles específicos para threads.
CreateThread
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, __drv_aliasesMem LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );
+infos(LINK)
ExitThread
void ExitThread( DWORD dwExitCode );
+infos(LINK)
GetExitCodeThread
BOOL GetExitCodeThread( HANDLE hThread, LPDWORD lpExitCode );
+infos(LINK)
Podemos obter o ID de uma thread e o handle através das seguintes funções:
getcurrentthread
HANDLE GetCurrentThread();
+infos(LINK)
getcurrentthreadID
DWORD GetCurrentThreadId();
+infos(LINK)
getthreadid
DWORD GetThreadId( HANDLE Thread );
+infos(LINK)
Uma thread pode esperar que outra thread termine, da mesma forma que um processo pode esperar que uma thread termine.
Terminei esta secção na pagina 236 do livro e saltei para a 251 até 253!
Erros mais comuns e observações, relacionados com o uso das threads:
as threads são executadas de forma assíncrona
é possivel que a thread filho termine antes do pai retornar do CreateThread
Todas as inicializações que são necessárias no filho têm que ser feitas antes do CreateThread (o pai pode falhar qualquer inicialização que o filho necessite)
tem a certeza que cada filho tem a sua própria estrutura de dados, que lhe é transmitida através de um parâmetro da função da thread.
Uma qualquer thread, em qualquer momento, pode ser preemptiva, e uma qualquer thread, em qualquer momento, pode resumir a execução
não se deve usar as prioridades das threads para substituir a sincronização
A função Sleep, permite indicar à thread para deixar o processador e, sair do estado de running para o estado wait durante um determinado período de tempo especifico.
void Sleep( DWORD dwMilliseconds );
+infos(LINK)
Resumo do capitulo 8 (página 259 a XXX)
As threads podem simplificar o desenho e implementação de um programa e melhorar a performance, mas é necessário proteger os recursos que são usados pelas threads, quando ocorrem várias modificações em simultâneo.
Esta secção do livro vai apresentar: sincronização de objectos, secções criticas, mutexes, semáforos e eventos. Também vão ser observados os deadlocks e as condições de corrida (race conditions), exclusão mutua.
Um exemplo de quando duas threads estão a mexer no mesmo recurso:
Assim é necessário usar um mecanismos que permita corrigir o erro da corrida, e para que apenas uma thread execute a operação na região critica, de cada vez.
267
1 |
0 thoughts on “S02 – F6 – Memória partilhada/sincronização – Memória partilhada e semáforos”