• Nenhum resultado encontrado

Sistemas Operativos. Processos e threads no Windows. Jorge Martins baseado parcialmente em slides do Eng.

N/A
N/A
Protected

Academic year: 2021

Share "Sistemas Operativos. Processos e threads no Windows. Jorge Martins baseado parcialmente em slides do Eng."

Copied!
30
0
0

Texto

(1)

Sistemas Operativos

Processos e threads no Windows

Jorge Martins

(jmartins@isel.pt)

baseado parcialmente em slides do Eng. João Patriarca

Centro de Cálculo

(2)

Handles de objectos kernel

Os objectos kernel (processos, sections, files, mutexes, eventos, semáforos, etc.) partilham

uma série de características:

A maior parte pode ter um nome associado. No caso de processos um número (process id), nos outros casos um valor alfanúmerico.

• As permissões de acesso dependem do processo em que são usados.

• Contador de referências.

– O objecto só é destruído quando o seu contador de referências atingir o valor 0. Logo podem permanecer activos para além do processo que os criou.

Os objectos kernel são acedidos pelos processos que para eles têm referências. Um processo pode partilhar o acesso aos objectos com os processos criados por si (herança).

O acesso aos objectos e efectuado através de handles. Em geral, um handle é obtido como

resultado de uma operação de criação (

Create...

) ou abertura (

Open...

) de objectos

kernel. O que designamos por handle não é mais que um ponteiro (índice) para uma tabela

(tabela de handles), incluída no objecto que representa o processo, que contém os handles

que referem os objectos usados pelo processo. Cada entrada da tabela de handles contém:

Access Mask

Flags

Reference

define as permissões

de acesso ao objecto

flags adicionais, por

exemplo se o handle é

herdável

ponteiro para o objecto

referido

(3)

Criação/Abertura de objectos kernel

HANDLE CreateThread( ... ); HANDLE CreateFile( ... ); HANDLE CreateFileMapping( ... ); HANDLE CreateEvent( ... ); HANDLE CreateSemaphore( ... );

Nas funções de criação, se o objecto ainda não existe, o kernel aloca e inicia um bloco de

memória para alojar o objecto a criar. Se o objecto existe, é retornando um handle para o objecto, mas a chamada à função GetLastError() retorna ERROR_ALREADY_EXISTS.

O kernel procura por uma entrada livre na tabela de handles do processo onde teve origem a criação (ou abertura) do objecto.

Preenchimento de uma entrada livre na tabela de handles:

– O campo Reference é afectado com o endereço do bloco alocado em kernel space para alojar o objecto criado.

Os campos Access mask e Flags são afectados de acordo com o valor do parâmetro psa do tipo PSECURITY_ATTRIBUTES.

• Em caso de erro durante a criação do objecto, a maioria das funções retornam NULL, algumas retornam INVALID_HANDLE_VALUE (-1).

• Em caso de sucesso retornam o índice (HANDLE) da entrada preenchida.

– O valor retornado é válido apenas no âmbito do processo que efectuou a criação/abertura

11/05/2016 Sistemas Operativos 4 HANDLE OpenThread( ... ); HANDLE OpenFile( ... ); HANDLE OpenFileMapping( ... ); HANDLE OpenEvent( ... ); HANDLE OPenSemaphore( ... );

(4)

Partilha de objectos entre processos

Cenários onde há necessidade de partilha:

Memória partilhada entre processos (através da função

CreateFileMapping)

Sincronização de threads pertencentes a diferentes processos.

Formas de partilha (detalhadas nos slides seguintes):

Por herança entre processo pai e processos filhos

Por nome do objecto (parâmetro pszName do tipo PCTSTR da função

de criação ou abertura).

Por duplicação de handles (função DuplicateHandle ).

(5)

Processos e partilha de objectos – partilha por nome

11/05/2016 Sistemas Operativos 6 Kernel memory User memory Processo A tabela de handles Processo B tabela de handles

código user de Processo A

código user de Processo B

...

handle1=CreateFileMapping(..., _T(“map1”)) ...

...

handle2 = CreateFileMapping(..., _T(“map1”)) ... 0x30 0x40 Name:“map1” Ref Count: 2 Section object outras informações outras informações

(6)

Processos e partilha de objectos – partilha por herança

11/05/2016 Sistemas Operativos 7 Kernel memory User memory Processo A tabela de handles Processo B (filho) tabela de handles

código user de Processo A código user de Processo B

(execução de “Program B”)

...

handle1 = CreateFileMapping(&sa,..., NULL); ...

handle2 = CreateProcess(“Program B”, ..., TRUE,...);

... ... 0x30 0x30 Name: NULL Ref Count: 2 Section object outras informações outras informações outras informações 0x20 sa.bInherithandle = TRUE

Indica que os handles (herdáveis) são herdados. Note-se que o handle para a section tem o mesmo valor no filho (0x30). Tem de arranjar forma de passar essa informação ao filho. Por exemplo através da linha de comandos...

não necessita de nome

(7)

Processos e partilha de objectos –

partilha por duplicação de handles

11/05/2016 Sistemas Operativos 8 Kernel memory User memory Processo A tabela de handles Processo B (pid = 125) tabela de handles

código user de Processo A

...

handle1 = CreateFileMapping(NULL,..., NULL) hprocB = OpenProcess(..., 125) DuplicateHandle(GetCurrentProcess(),handle1,hprocB,&hObj,0,TRUE, DUPLICATE_SAME_ACCESS); ... 0x30 0x40 Name: NULL Ref Count: 2 Section object outras informações outras informações outras informações 0x20

hObj vale 0x40. É preciso agora informar o processo B de que o handle para a section vale 0x40. Por exemplo, através de

memória partilhada...

(8)

A função

CloseHandle

Deverá ser chamada quando o objecto não for mais necessário

Decrementa o contador de utilizadores do objecto referenciado pelo

handle

Se o contador atingir o valor 0, o objecto é destruído

Limpa a entrada da tabela de handles correspondente ao handle

Na terminação de um processo, o sistema percorre a sua tabela de

handles fechando todos os handles ainda presentes na tabela

(9)

Função

CreateProcess

BOOL CreateProcess(

PCTSTR pszApplicationName,

PTSTR pszCommandLine,

PSECURITY_ATTRIBUTES psaProcess,

PSECURITY_ATTRIBUTES psaThread,

BOOL bInheritHandles,

DWORD fdwCreate,

PVOID pvEnvironment,

PCTSTR pszCurDir,

PSTARTUPINFO psiStartInfo,

PPROCESS_INFORMATION ppiProcInfo);

Pode ter o valor NULL (o primeiro token do próximo parâmetro deverá indicar o nome da aplicação)

Pode ser NULL; usado em alternativa com pszApplicationName

Indica se o processo filho herdará os

handles herdáveis do processo pai

Flags que controlam a classe de prioridade

e a criação do processo

Pode ter o valor NULL, herdando as variáveis de ambiente do pai

A NULL, a directoria é a mesma do processo pai; caso contrário, deverá incluir a drive e directoria

Inicialização mínima (consultar bib):

STARTUPINFO si = { sizeof(si) }; CreateProcess( ..., &si, ...);

typedef struct _PROCESS_INFORMATION { HANDLE hProcess;

HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION;

(10)

Parâmetro

fdwCreateFlags

Controlo da criação do processo

CREATE_DEBUG, CREATE_SUSPENDED, CREATE_NEW_CONSOLE,

CREATE_NO_WINDOW, DETACHED_PROCESS

Classes de prioridade (a ver mais tarde).

REALTIME_PRIORITY_CLASS, HIGH_PRIORITY_CLASS,

ABOVE_NORMAL_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS,

BELOW_NORMAL_PRIORITY_CLASS, IDLE_NORMAL_PRIORITY_CLASS

Normalmente não é especificado: processos criados com a prioridade

(11)

STARTUPINFO

11/05/2016 Sistemas Operativos 12

typedef struct _STARTUPINFO {

DWORD cb;

LPTSTR lpReserved;

LPTSTR lpDesktop;

LPTSTR lpTitle;

DWORD dwX; DWORD dwY;

DWORD dwXSize; DWORD dwYSize;

DWORD dwXCountChars;

DWORD dwYCountChars;

DWORD dwFillAttribute;

DWORD dwFlags;

WORD wShowWindow;

WORD cbReserved2;

LPBYTE lpReserved2;

HANDLE hStdInput;

HANDLE hStdOutput;

HANDLE hStdError;

} STARTUPINFO, *LPSTARTUPINFO;

(12)

Criação de processo – passos envolvidos

Convert and validate

parameters and flags

Perform Windows-subsystem specific process initialization

Open EXE and create section object

Create Windows process object

Create Windows thread object

Start execution of the initial thread

Return to caller

Set up for new process and thread Final process/image initialization Start execution at entry point to image

Stage 1

Stage 2

Stage 3

Stage 4

Stage 5

Stage 6

Stage 7

Creating process

Windows subsystem (process)

(13)

Terminação de um processo

Diferentes formas de terminar um processo:

A função de entrada da thread primária (

main

) retorna. É a forma

adequada de terminar o processo, supondo que todas as outras threads

já terminaram. Se outras threads ainda não terminaram, a sua

terminação é forçada pelo Windows (via

TerminateThread).

Uma thread do processo chama a função ExitProcess (evitar este

método). As outras threads do processo são forçadas a terminar sem

libertação controlada de recursos.

Uma thread noutro processo chama a função TerminateProcess (evitar

este método). As outras threads do processo são forçadas a terminar

sem libertação controlada de recursos.

(14)

Terminação de um processo -

Função TerminateProcess

BOOL TerminateProcess(HANDLE hProcess, UINT fuExitCode);

Distingue-se da função ExitProcess por poder ser chamada por

qualquer thread dentro ou fora do processo (desde que tenha um

handle válido para o processo, é claro!),

Usar apenas na condição de não se conseguir terminar o processo

por qualquer outro método.

O sistema garante que os recursos associados ao processo são

completamente eliminados, mas o estado aplicacional (persistente)

pode perder informação ou ficar inconsistente.

Acção de terminação assíncrona o que justifica a chamada da função

WaitForSingleObject para determinar a conclusão do processo.

(15)

Terminação de um processo -

Acções realizadas

Termina as threads ainda existentes no processo.

Todos os objectos kernel são fechados (

CloseHandle

) e eliminados se

os contadores de referências dos objectos atingiram o valor 0.

O código de saída do processo altera de STILL_ACTIVE (0x103) para

o valor passado na função

ExitProcess ou TerminateProcess,

obtido por:

O objecto kernel processo é sinalizado (para efeitos de

sincronização).

O contador de referências do objecto kernel processo é

decrementado

(16)

PROCESS_INFORMATION pi;

DWORD dwExitCode;

// Waiting for child process termination

BOOL fsuccess = CreateProcess(..., &pi);

if (fsuccess) {

// Close the thread handle as soon as it is no longer needed!

CloseHandle(pi.hThread);

// Suspend our execution until the child has terminated.

WaitForSingleObject(pi.hProcess, INFINITE);

// The child process terminated; get its exit code.

GetExitCodeProcess(pi.hProcess, &dwExitCode);

// Close the process ahndle as soon as it is no longer needed.

CloseHandle(pi.hProcess);

}

Criação de processo filho com e sem espera

// Without wait for child ending

PROCESS_INFORMATION pi;

BOOL fsuccess =

CreateProcess(..., &pi);

if (fsuccess) {

CloseHandle(pi.hThread);

CloseHandle(pi.hProcess);

}

(17)

Função

CreateThread

HANDLE CreateThread(

PSECURITY_ATTRIBUTES psa,

DWORD cbStackSize,

PTHREAD_START_ROUTINE pfnStartAddr,

PVOID pvParam,

DWORD dwCreateFlags,

PDWORD pdwThreadID);

Na escrita de programas em C/C++ dever-se-á usar a função

_beginthreadex

em vez de

CreateThread

Pode ter o valor NULL (atributos de segurança

por omissão). Pode ser usado para determinar

se um objecto processo filho tem acesso ao

objecto kernel thread criado por esta função

Memória que ficará committed

inicialmente. O valor 0 usa os valores

presentes na imagem do executável.

DWORD WINAPI <threadFuncName>(

PVOID param);

O retorno corresponde ao valor de saída

(idêntico ao valor de retorno da função main)

Pode ter o valor

NULL

Valor 0: a thread pode ser colocada de

imediato em execução

Valor CREATE_SUSPENDED: a thread sai do

estado Suspended quando for

(18)

Terminação de uma thread

Cenários em que uma thread pode terminar

A função correspondente ao ponto de entrada da thread retorna (método

adequado).

A thread mata-se a si própria chamando a função ExitThread (evitar

este método).

Uma qualquer thread deste processo chama a função TerminateThread

(evitar este método)

(19)

Terminação de uma thread

- Acções realizadas

O código de saída da thread, obtido por:

BOOL GetExitCodeThread(HANDLE hThread, PDWORD pdwExitCode);

altera de

STILL_ACTIVE (0x103) para o valor passado na função

ExitThread ou TerminateThread

O objecto kernel thread é sinalizado (para efeitos de sincronização)

Se a thread for a última thread activa do processo, o sistema considera o

processo terminado também

(20)

Thread Local Storage

Por vezes há necessidade de manter estado global por cada thread e não estado global ao nível do processo. O Windows suporta esta necessidade através da funcionalidade designada de Thread Local Storage.

A TLS é simplesmente um array de ponteiros que pode ser acedido unicamente pela thread a que está associado. A TLS é usada tipicamente por bibliotecas ou serviços que necessitam de manter estado global por thread e não por processo . Na sua iniciação, tipicamente em DllMain, a biblioteca reserva uma entrada nas TLS de cada thread via TlsAlloc e armazena o índice devolvido numa variável global ao processo. O código executado em cada thread usa TlsSetValue e TlsGetValue para escrever e ler informação na posição reservada previamente (a posição é a mesma em todas as threads, a informação é naturalmente distinta). No fecho da biblioteca a entrada reservada é libertada via TlsFree.

Como é que o Windows sabe o endereço do bloco de memória que contém a TLS da thread corrente?

11/05/2016 Sistemas Operativos 21 TLS thread 1 thread 1 ... TLS thread 2 thread 2 ... TLS thread 3 thread 3 ...

(21)

Biblioteca runtime C/C++ - uso de Thread Local Storage (TLS)

Preocupações com a biblioteca standard do C num cenário de programação

concorrente:

A biblioteca na sua génese não previa execução concorrente

Muitas funções usam variáveis com alocação estática: errno, strtok, strerror,

...

Solução:

Cada thread tem as variáveis globais da biblioteca reunidas num bloco

O bloco fica alojado num espaço visível apenas pela thread (TLS – Thread Local

Storage)

• É por esta razão que se deve utilizar _beginthreadex em vez da função CreateThread. A função, antes de criar a thread, cria o bloco com as variáveis globais da biblioteca, sendo o bloco

associado à nova thread no início da sua execução, via TlsSetValue. Quando uma função de biblioteca necessita de aceder a estado global, obtém o bloco da thread em que está a executar (via TlsGetvalue) e acede ao campo do bloco que representa o estado global. Como é que

beginthreadex garante a iniciação TLS da nova thread antes de começar a execução do códifo especificado pelo chamador? Mais detalhes sobre a implementação de beginthreadex nos

(22)

c/c++ run time library data block in TLS

11/05/2016 Sistemas Operativos 23

struct _tiddata {

unsigned long _tid; /* thread ID */ uintptr_t _thandle; /* thread handle */ int _terrno; /* errno value */

unsigned long _holdrand; /* rand() seed value */ char * _token; /* ptr to strtok() token */ wchar_t * _wtoken; /* ptr to wcstok() token */ unsigned char * _mtoken; /* ptr to _mbstok() token */

char * _errmsg; /* ptr to strerror()/_strerror() buff */ wchar_t * _werrmsg; /* ptr to _wcserror()/__wcserror() buff */ char * _namebuf0; /* ptr to tmpnam() buffer */

wchar_t * _wnamebuf0; /* ptr to _wtmpnam() buffer */ char * _namebuf1; /* ptr to tmpfile() buffer */ wchar_t * _wnamebuf1; /* ptr to _wtmpfile() buffer */ char * _asctimebuf; /* ptr to asctime() buffer */ wchar_t * _wasctimebuf; /* ptr to _wasctime() buffer */ void * _gmtimebuf; /* ptr to gmtime() structure */ char * _cvtbuf; /* ptr to ecvt()/fcvt buffer */

/* following fields are needed by _beginthread code */

void * _initaddr; /* initial user thread address */ void * _initarg; /* initial user thread argument */

// ...

};

• Para já um excerto do estrutura que define o bloco de memória a armazenar por thread.

(23)

Implementação de

_beginthreadex

11/05/2016 Sistemas Operativos 24

uintptr_t _beginthreadex( void* psa, unsigned cbStackSize,

unsigned (__stdcall * pfnStartAddr) (void*), void* pvParam, unsigned dwCreateFlags, unsigned * pdwThreadID) {

_ptiddata ptd; // Pointer to thread’s data block

uintptr_t thdl; // Thread handles // Allocate data block for the new thread

if ((ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata))) ==NULL) goto error_return;

initptd(ptd); // Initialize data block

// Save desired thread function and parameter in threads’s data block

ptd->_initaddr = (void*)pfnStartAddr; ptd->_initarg = pvParam;

ptd->handle = (uintptr_t)(-1);

// Create the new thread

thdl = (uintptr_t) CreateThread((LPSECURITY_ATTRIBUTES)psa, cbStackSize,

_threadstartex, (PVOID)ptd, dwCreateFlags, pdwThreadID);

if (thdl == 0) goto error_return; return (thdl); error_return: _free_crt(ptd); return ((uintptr_t)0L); }

(24)

Função que executa o código da thread criada via

_beginthreadex

11/05/2016 Sistemas Operativos 25

static unsigned long WINAPI _threadstartex( void* _ptd) { _ptiddata ptd = (_ptiddata ) _ptd;

// Associate the tiddata block with this thread

TlsSetValue(__tlsindex, // global telling the TLS index for c/c++ run time library

ptd);

ptd->_tid = GetCurrentThreadId(); // save this thread ID

// Using Windows exceptions to catch any fatal error on thread execution….

__try {

// Call desired thread function, passing it he desired parameter // Pass thread’s exit code value to _endthreadex

_endthreadex(

(unsigned (WINAPI*)(void*))(ptd->_initaddr))(ptd->_initarg) );

}

__except(_XcptFilter(GetExceptionCode(), GetExceptionInformation())) {

// The execution code should never reach here

_exit(GetExceptionCode()); }

// The execution code never reach here (the thread dies in _callthreadstartex)

return (0L); }

(25)

Bloco PEB (Process Environment Block)

Image base address Module list

Thread-local storage management data Code page data

Number of heaps Heap size information

Operating system version number information Image version information

Image process affinity mask

Process heap

Bloco de informação associada a cada processo, disponível em user mode. A figura apresenta alguns dos campos mais importantes.

(26)

Bloco TEB (Thread environment block)

Exception list Stack base Stack limit Thread ID Active RPC handle LastError value

Count of owned critical sections

Subsystem thread information block (TIB) Fiber information

PEB

Current locale

User32 client information GDI32 information

OpenGL information TLS array

Winstock data

bib: Windows internals, Russinovich, Solomon, fig. 5-9 Bloco de informação associada a cada thread do processo, disponível em user mode. A figura apresenta alguns dos campos mais importantes.

A instrução mov eax, fs[0] coloca em eax o endereço da estrutura TEB para a thread corrente

(27)

Contabilização de tempos associados à execução de threads

ULONGLONG GetTickCount64();

// Não considera a preempção

DWORD GetTickCount();

// variante de 32 bits

BOOL GetThreadTimes(HANDLE hThread,

PFILETIME pftCreationTime,

// valor absoluto desde 1 de Janeiro de 1601

PFILETIME pftExitTime,

// valor absoluto desde 1 de Janeiro de 1601

PFILETIME pftKernelTime,

// valor relativo gasto em kernel mode

PFILETIME pftUserTime);

// valor relativo gasto em user mode

• PFILETIME expresso no número de intervalos de 100 nano-segundos

• Funções adequadas para medir intervalos de tempo longos (fraca precisão)

O tempo de CPU de uma thread é contabilizado com base num temporizador que gera ciclos entre os 10 e 15 ms

BOOL GetProcessTimes(<same as GetThreadTimes>); // Tempo gasto por todas as threads

BOOL QueryPerformanceFrequency(LARGE_INTEGER* pliFrequency);

BOOL QueryPerformanceCounter(LARGE_INTEGER* pliCount);

Funções medidoras de tempo de alta resolução

– Não consideram a preempção

(28)

Linhas de cache num contexto multiprocessador

Cenário:

CPU1 lê 1 byte. O byte solicitado e adjacentes são carregados na cache do CPU1

CPU2 lê o mesmo byte. Os mesmos bytes são carregados na cache do CPU2

CPU1 altera o byte lido

O respectivo byte na cache do CPU2 fica inválido; a arquitectura garante a

marcação como inválida da linha de cache do CPU2

Boas práticas:

Um CPU deverá ter as suas variáveis em memória agrupadas numa linha de

cache: as variáveis de um programa que sejam acedidas num intervalo de tempo

curto deverão ser declaradas próximas (localidade espacial)

Evitar que duas ou mais threads em diferentes

CPU’s partilhem variáveis

independentes na mesma linhas de cache. A alteração de uma das variáveis tem

como efeito invalidar a linha de cache no outro CPU, mesmo que não se utilize a

variável alterada.

Separar dados de leitura apenas dos dados que serão lidos e escritos

Usar a directiva para o compilador C/C++ __declspec(align(#))

(29)

Objectivos de aprendizagem

Identificar os campos essenciais presentes no objecto descritor de processos.

Explicar o que é a tabela de handles de um processo e como é constituída cada entrada da

tabela

Explicar o que significa o valor retornado (HANDLE) nas operações de criação e abertura de

objectos de kernel e qual a razão de um handle só ser válido no processo a que pertence

Explicar os mecanismos que suportam a partilha de objectos de kernel entre processos

distintos- Conhecimento de nome, herança e duplicação de handles.

Realizar pequenos programas que envolvam criação de processos e/ou threads.

Identificar cenários que determinem a necessidade de criação de threads adicionais o

Windows.

Sincronizar a execução com a terminação de outras threads e/ou processos (via

WaitForSingleObject, WaitForMultipleObjects

(30)

Bibliografia

• “Windows internals”, Russinovich e Solomon, cap. 5, 5ª edição

• “Windows via C/C++”, Richter e Nasarre, cap. 4, 6 e 7, 5ª edição

• MSDN - windows development

Referências

Documentos relacionados

Essas operações, com e sem risco de crédito, são contabilizadas em conta do ativo “Outros créditos de operações com planos de assistência à saúde”

Organizado pela Abimaq, reuniu palestras sobre contexto e perspectivas para indústria de máquinas e equipamentos diante das alterações na política de conteúdo local para

POSTO TIPO &#34;D&#34; - NOTURNO, COM COBERTURA ININTERRUPTA AOS SÁBADOS, DOMINGOS E FERIADOS - Número de Vigilantes: 02 (dois) por posto de serviço - Escala de Trabalho: 12 X 36

O Presidente turco desafiou também a posição de Washington quando, após o Presidente Trump ter imposto tarifas à importação do aço turco, o que provocou

Esta última tendência no Cinema Alemão Contemporâneo está intimamente associada ao modo como se estabelecem as relações interculturais na sociedade alemã, que podem

Este artigo ressalta a importância do tempo como fator relevante para o aumento da competitividade da indústria brasileira. Ao penalizar em maior grau o comércio internacional

Nome de Utente

Eu entendo que participarei como sujeito do estudo intitulado ‘’Comportamento da percepção de esforço a cada repetição em séries de força em diferentes