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

Um exemplo ilustrado:

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

Tags : ,

0 thoughts on “S02 – F6 – Memória partilhada/sincronização – Memória partilhada e semáforos”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.