logo
Contato | Sobre...        
rebarba rebarba

Rodrigo Strauss :: Blog

Add to Google Subscribe with Bloglines

Talvez você ache interessante ver a lista com todos os posts ou minha lista com os melhores posts.

Win32: sincronização parte 2

Preparando esse post eu percebi que o post de sincronização parte 1 não explica nada sobre sincronização, só apresenta o problema que a sincronização resolve. Estou parecendo aqueles vendedores de empresas de software que gastam mais tempo tentando provar que você tem um problema do que realmente mostrando a solução. Agora chegou a hora de mostrar a solução. :-)

Como vimos na primeira parte, o maior problema em programação multithread é o compartilhamento de dados entre as threads. Existem duas solução para isso. A primeira e mais usada é sincronizar o acesso aos dados, de forma que somente uma thread por vez manipule os dados em questão. Ou seja, a solução envolve "eliminar" (notou as aspas?) o multithread da parte que ele atrapalha. A segunda e pouco usada opção é eliminar o compartilhamento dos dados, usando filas, controle restrito de ownership e trabalhando com cópias do dados. Esse é o enfoque usado pelo Erlang, uma nova linguagem de programação.

A forma mais básica para sincronização em vários SOs é o critical section. Esse recurso permite definir um trecho de código que é executado por várias threads, mas que precisa ser sincronizado entre elas. Sincronizado == somente uma thread pode executar esse trecho de código por vez.

Revisando a nossa classe de lista ligada, vamos ver quais são os dados da classe:

template<typename T>

class LinkedList
{
  struct NODE
  {
    NODE* previous;
    NODE* next;
    T data;
  };
 
  NODE rootNode_;
  unsigned int count_;

 ...

A princípio parece que precisamos sincronizar o rootNode_ e o count_. Apesar disso estar correto, não é simples assim, não esqueça que todos os nós apontados pelo rootNode_ são dados controlados pela classe, que também precisam ser sincronizados.

Olhando o fonte dessa classe, todos os métodos manipulam ou lêem essas duas variáveis. A forma mais fácil e óbvia seria cercar com um critical section o corpo de todas as funções da classe.

Em Win32, um critical section é definido pelo objeto (surpresa!) CRITICAL_SECTION. Esse objeto deve ser inicializado e terminado, dessa forma:

CRITICAL_SECTION cs;

//
// adivinha?
//
InitializeCriticalSection(&cs);

//
// aqui entramos no critical section. Quando uma thread está "dentro" de
// um critical section, se alguma outra thread tentar entrar ficará
// travada dentro da funcão EnterCriticalSection até que a primeira
// thread chame LeaveCriticalSection. Isso garante que somente uma thread
// executará esse trecho de código em um dado momento.
//
EnterCriticalSection(&cs);

//
// Saímos do critical section. Isso faz com que a próxima thread que estiver
// esperando no ponto do EnterCriticalSection seja liberada. Note que somente
// uma thread é liberada por vez. Caso mais de uma thread esteja esperando, elas
// são liberadas na ordem que chegaram "na portinha" do critical section
//
LeaveCriticalSection(&cs);

//
// análogo ao CloseHandle
//
DeleteCriticalSection(&cs);

Resumindo: o Critical Section cria um "gargalo", onde só passa uma thread de cada vez. Ah, existe uma coisa interessante a ser acrescentada à explicação do post sobre threads: as threads permitem que um processo execute várias linhas de execução ao mesmo tempo, mas essas linhas de execução podem rodar o mesmo código, que está no mesmo lugar na memória. Ou seja, elas compartilham dados e código executável.

No próximo post iremos para o exemplo prático.

Em 30/01/2009 10:17 - Comentários (8)

Ferramentas de apoio Win32: pskill

O pskill é mais um ferramenta Sysinternals sem a qual a vida fica muito difícil. A única coisa que ele faz - e bem feito - é matar um processo. Parece simples, mas quando você precisa matar 7 processos toda vez que seu servidor dá pau, uma bat cheia de chamadas ao pskill pode salvar a sua vida.

Alguns usos:

  • Ela aceita tanto o nome do processo quanto o PID: você pode chamar pskill notepad.exe ou pskill 4576.
  • Quando você usa o nome, ele mata TODOS os processos com o mesmo nome: Ou seja, um pskill notepad.exe vai matar TODAS as instância do notepad. Se você quer matar uma instância específica, use o PID. Se precisar saber o PID do processo em command line, use o pslist, ferramenta que veremos em mais detalhes depois.
  • Matar um processo e todos criados por ele: pskill -t meu-servico.exe. Mata o processo pai e toda sua família.
  • Funciona remotamente: você pode chamar pskill \\outra-maquina notepad.exe ou pskill \\outra-maquina 4576. Como sempre, você precisa ter permissão para isso na máquina remota

Informação muito importante: pskill \\maquina-remota winlogon.exe causa um BugCheck (tela azul) instantâneamente na máquina remota. Use com moderação.

Em 31/10/2008 13:55 - Comentários (4)

Ferramentas de apoio Win32: psexec

Achei melhor "desamarrar" esses posts de ferramentas da palestra do quinto encontro, pois assim eu posso escrever sobre mais ferramentas do que eu falei ou planejei falar.

O psexec é mais uma ferramenta Sysinternals, e permite que você execute um processo remotamente sem fazer login interativo (no popular, "sentar na máquina"). A sintaxe é simples, algo como "psexec \\maquina-remota executavel.exe". O que é mais interessante é que o psexec redireciona a saída (stdout) do programa para sua máquina, assim como redireciona seu stdin para a máquina remota. Isso permite que você faça de qualquer máquina um servidor de telnet chamando "psexec \\servidor cmd.exe". Use sua criatividade para usar isso para diagnóstico, resolução de problemas em servidores, execução de um servidor para debug remoto, rodar scripts, etc. Ah, antes que você ache o mundo um lugar maravilhoso demais, devo lembrá-lo que você precisa ser administrador da máquina remota. Só espero não ter afetado o estado emocional de ninguém subitamente.

Ele instala um serviço na máquina remota, e esse serviço cria os processos e redireciona o input/output:

(você também consegue instalar serviços em uma máquina remota usando a API CreateService ou usando o comando sc do Windows).

Uma coisa que deve ser notada é que o psexec executa o processo remoto com impersonate - simplificadamente, muda o token processo para um token do seu usuário - e o processo criado não tem acesso à rede. Para conseguir acesso à recursos remotos, use "psexec -u dominio\usuario \\nome-da-maquina-remota comando". Ele vai pedir para você digitar a senha e a enviará sem criptografia via um Named Pipe, que acaba indo via socket (para ver os Named Pipes do psexecsvc, use o Process Explorer. No post anterior eu expliquei como ver os handles de um processo).

(Sim, é possível rodar "psexec \\maquina-remota wmplayer.exe fart.mp3" em uma máquina remota se você for admin. Mas eu não disse nada)

Em 17/10/2008 01:19 - Comentários (2)

Palestra do Quinto Encontro: Process Explorer

Aos que não acompanham a lista de discussão e o site do nosso glorioso http://www.ccppbrasil.org, foi realizado no sábado passado o Quinto Encontro de Programadores C e C++, no espaço cedido pela Microsoft Brasil. Para fazer as honras ao anfitrião e saciar minha vontade de falar sobre os softwares que me salvam a pele todos os dias, falei sobre as ferramentas de apoio à programação, debug e diagnóstico na plataforma Windows.

Como uma palestra de 1:15hs (um pouco menos se consideramos o problema no som...) não é nem de longe suficiente para explicar todas as ferramentas, vou explicá-las melhor aqui no meu recentemente abandonado blog. Aos que esperam o prosseguimento da série de Win32, vamos esperar a Apple devolver meu carregador que foi para garantia e nunca mais voltou, assim eu consigo recuperar o fonte dos próximos posts. Bom chega de choramingar, daqui a pouco vou começar os posts com "meu querido diário"...

A primeira ferramenta que eu mostrei, e de longe a mais útil é o Process Explorer, o Task Manager vitaminado da Sysinternals:

Principais usos:

  • Visualizar processos em árvore: Isso possibilita saber facilmente qual processo iniciou outro processo. CTRL+T
  • Finalizar uma árvore de processos: Muitos programas (setups geralmentes) criam vários processos filhos que fazem o mesmo, ficando difícil finalizar todos os processos criados. Um SHIFT+DEL no processo pai resolve esse problema, matando sem remorso o processo pai e toda sua família.
  • Mostrar indicadores detalhados de memória: A pergunta "quanto de memória esse processo está usando?" é vaga e subjetiva. Clicando com o botão direito no header da lista com os processos aparece a opção "Select Columns...". Na aba "Process Memory" encontramos os indicadores detalhados de memória, como "Private Bytes" e "Working Set". Mais detalhes sobre isso no material da minha palestra "Por Dentro do Windows: Gerenciamento de Memória" (que eu já ministrei 3 vezes e se me convidarem eu faço de novo, gosto bastante desse assunto)
  • Saber qual processo está mantendo um determinado arquivo aberto: CTRL+F, digite o nome do arquivo. Você verá uma lista dos processos que mantém handles abertos para esse arquivo.
  • Visualizar quais conexões TCP/IP o processo mantém: Duplo clique no processo, depois aba TCP/IP.
  • Resumo do status do sistema (consumo de processador, memória, I/O, etc): CTRL+I ou menu View >> System Information. Apesar de parecer com a janela do Task Manager, nessa janela temos informações também sobre o I/O total do sistema, além de mais um monte de coisas.
  • Descobrindo qual processo criou uma determinada janela: Arraste o último botão da barra de ferramentas (um alvo) para cima da janela. O processo dono será selecionado. Muito útil para descobrir qual dos programas inúteis do fabricante do seu notebook estão ativando aqueles insuportáveis popups. Ou qual das 15 instâncias do seu programa deve ser debugada depois de mostrar um MessageBox(NULL, "Passou por aqui!!!", "DEBUG", MB_OK);.
  • Descobrir quais serviços rodam em uma instância do svchost.exe: Muitos serviços nativos do Windows são implementados como DLLs que rodam dentro de um dos inúmeros processos do svchost.exe. Abra as propriedades de um processo (duplo clique ou botão direito e "Properties"), você verá uma aba "Services" com a lista.
  • Descobrir quais processo são do usuário XYZ: Na tela de seleção de colunas, existe uma coluna "User name" na aba "Process Information".
  • Lista de todos os handles (arquivos, sockets, chaves de registro mutexes, events, etc) de um processo: CTRL+H.
  • Listar DLLs carregadas por um processo: CTRL+D. Botão direiro + opção "Properties" na DLL e você vê o caminho completo da DLL, sua versão e o nome do fabricante/fornecedor.
  • Ver os processos que mais consomem CPU: Na lista de colunas, aba "Process Performance", escolha a coluna "CPU Time", que mostra o total de tempo de processador que o processo já consumiu desde seu início. Útil para quem quer economizar bateria do notebook. Ou descobrir qual dos 19 antispywares que seu usuário instalou estão monopolizando a CPU e não deixando muito espaço para sua aplicação (que o usuário gentilmente chama de "carroça").
  • Iniciar o Process Explorer minimizado no "tray" na inicialização do Windows: Crie um atalho do Explorer para o comando "procexp.exe /t" e coloque-o em Start Menu >> Programs >> Startup.

Mais sugestões nos comentários serão bem vindas e muito úteis para quem ler esse post no futuro. E torçam para o deus/santo/padroeiro/entidade/orixá/sei-lá-o-que da organização (deve existir um) me ajude a separar mais tempo para escrever no blog, pois eu realmente gosto de fazer isso...

Em 09/10/2008 00:55 - Comentários (4)

E o bug é....

O bug está no uso incorreto da função WaitForMultipleObjects:

DWORD WINAPI WaitForMultipleObjects(
  __in  DWORD nCount,
  __in  const HANDLE *lpHandles,
  __in  BOOL bWaitAll,
  __in  DWORD dwMilliseconds
);

Parameters:

nCount [in]:The number of object handles in the array pointed to by lpHandles. The maximum number of object handles is MAXIMUM_WAIT_OBJECTS.

Vendo o valor de MAXIMUM_WAIT_OBJECTS em winnt.h:

#define MAXIMUM_WAIT_OBJECTS 64     // Maximum number of wait objects

E no meu post:


 static const DWORD threadCount = 100;

...


//
// esperando TODAS as threads retornarem
//             |___________________________________
//                                                 |
//                                                 v
//
WaitForMultipleObjects(threadCount, &threads[0], TRUE, INFINITE);


Não podemos esperar 100 threads de uma vez só. Agora sabemos que o programa não espera todas as threads terminarem antes de mostrar o resultado.

Em 23/09/2008 20:23 - Comentários (1)


Posts anteriores >>
rebarba rebarba
  ::::