Category: sistemas operativos 2

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 : ,

SO2 – Caracteres e suporte para unicode em Windows/Visual Studio

Bibliografia:
What are TCHAR, WCHAR, LPSTR, LPWSTR, LPCTSTR (etc.)? – +infos(LINK)
Windows System Programming, 4t edition, Johnson M. Hart, Addison Wesley, 2010 – capitulo 2

Os caracteres e o unicode em Windows  com Visual Studio:

O unicode permite que:
tem um formato de caracteres multi-byte -> wchar_t
é adequado para todas as línguas
existem vários encondings possíveis: Windows UTF-16 (caracteres de 16 bits), UTF-32 (4 bytes), UTF-8,..

É o tipo de encoding que está a ser usado que permite saber o sizeof(qualquer coisa) , e isto vai afectar a portabilidade do código fonte.

No visual studio podemos fazer uso da macro TCHAR, em que char pode ser usado (se estiver configurado para ANSI/Multi-byte charset)) ou wchar_t (se estiver configurado para unicode)

#include <windows.h>
#include <tchar.h>
#include <fcntl.h>
#include <io.h>
#include <stdio.h>

#define MAX 256

int _tmain(int argc, LPTSTR argv[]) {
	TCHAR str[MAX], result[MAX] = TEXT("Olá! Este programa é para aceitar UNICODE. Insira \'fim\' para sair\n");
	unsigned int i;

#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
#endif

	do {
		_tprintf(result);
		fflush(stdin);
		_fgetts(str, MAX, stdin);
		str[_tcslen(str) - 1] = '\0';

		for (i = 0; i < _tcslen(str); i++)
			str[i] = _totupper(str[i]);
		_stprintf_s(result, MAX, TEXT("Frase:%s, Tamanho:%d\n"), str, _tcslen(str));
	} while (_tcsicmp(TEXT("FIM"), str));
	return 0;
}

Para se manter o máximo de portabilidade do código fonte deve-se:
evitar menções e uso explicito de tamanho de caracteres
usar as macro que são convertidas para as funções adequadas consoante o tamanho de carácter em uso

Aquando do uso das funções de biblioteca em C:
deve fazer-se uso ds funções _tcs ou equivalente
_tcslen em vez de strlen ou _wcslen

Nas strings para se fazer uso de compatibilidade ou portabilidade

Tags : , ,

SO2 – Visão geral do sistemas Windows (“família NT”)

Windows família NT, principais características:
sistema de 32 bits / 64 bits
multiprogramado preemptivo reentrante
multi-thread
suporta uniprocessador e multiprocessador SMP

Os principais objectivos e impacto no desenvolvimento do SO:
extensibilidade
portabilidade
fiabilidade
compatibilidade
performance

A arquitectura do Windows NT é do tipo: cliente servidor (micro-kernel) e que tem como principais vantagens:
núcleo mais simples
maior fiabilidade
maior extensibilidade
maior adaptabilidade a computação distribuída (vários processadores)

Componentes do sistema:
parte em modo de utilizador (modo não privilegiado: subsistemas / servidores: proporcionam um ambiente de execução, de API, e integrais (por exemplo de segurança)
parte em modo de núcleo (modo de execução privilegiado)

Surge assim a estrutura do Windows NT:

As tarefas dos componentes do Executivo NT são:
gestão de processo e threads (escalonamento e despacho)
gestão das LPC (local procedures call)
gestão da memória do sistema /memória virtual
kernel, tratamento de interrupções e excepções
I/O, rede, sistemas de ficheiros, device drivers, gestor de cache
HAL, adaptação às especificidades das várias plataformas

A evolução na família NT teve em conta:
suporte para novo hardware
modelos dos device drivers
espaço de memória suportado
optimizações na comunicação com periféricos
interface com o utilizador
optimizações do processo de boot
modificações de subsistemas / modelos de programação

 

Tags : , ,

SO2 – apresentação

Bibliografia recomendada:
Windows System Programming, 4t edition, Johnson M. Hart, Addison Wesley, 2010
Windows NT4 Advanced Programming, Raj RajaGopal & Subodh Monica, Osborne McGraw-Hill
Windows NT programming, Herbert Schildt, Osborne McGraw Hill, 1997
Operating Systems Concepts, 6th edition, Silberschatz and Galvin, Addison-Wesley
Fundamentos de Sistemas Oeerativos, 3 edição, José Alves Marques, Paulo Guedes, Editorial Presença
Inside Windows NT, 2 edition, Microsoft PRess, 1998
Operating Systems: Internal and Design Principles, 3rd edition, William Stallings, Prentice-Hall, 1998
Structured Computer Organization, 4th edition, Andrew S, Tanenbaum, Prentice-Hall Internacional
The Design of the Unix Operating System, Maurice Bach, Prentice- Hall, 1999

Tags : , , ,