Rodrigo Strauss :: Blog
|
|
|
Talvez você ache interessante ver a lista com todos os posts ou minha lista com os melhores posts.
A volta dos que foram mas voltaram logo
Algumas notícias importantes para movimentar meu quase abandonado site enquanto eu fico pensando em uma boa desculpa para essa situação:
- Voltei para São Paulo, e conseqüentemente, estou quase toda terça degustando um chopp no Bar Barão. Sim, isso é um convite. Para todos.
- O seminário da Tempo Real sobre Portabilidade e Performance foi muito bom, muito bom mesmo. Parabéns a todos nós do "C/C++ Brasil" por fazermos eventos com qualidade técnica tão alta, coisa que eu nunca vi nessas bandas. Fico feliz por poder compartilhar o que eu aprendi em incontáveis horas programando, dezenas de livros e outras dezenas de sites e artigos lidos. E fico muito mais feliz em poder aprender com pessoas que percorreram esse mesmo caminho, muitos que estudaram e leram muito mais do que eu. Eu sei que sou bom nesse negócio de programação (modéstia é um defeito que eu não tenho), mas minha vontade de aprender ainda é bem maior que meu ego. É **muito** bom conhecer e compartilhar conhecimento com gente que é melhor do que eu. Parabéns e obrigado a todos.
- Depois de mais de um mês offline (sim, essa é a desculpa), segue aqui a apresentação usada na minha palestra "Portabilidade via STL e Boost", onde eu fiz pelo STL e Boost o que os palestrantes da Microsoft/Sun/IBM/etc costumam fazer pelas empresas que pagam seus salários:
Como um trecho de código vale mais do que uma apresentação cheia de piadinhas não-tão-engraçadas, segue o dito cujo que usei durante a demo da palestra:
#define _CRT_SECURE_NO_WARNINGS #define _SCL_SECURE_NO_WARNINGS #include <vector> #include <string> #include <sstream> #include <iostream> #include <boost/thread.hpp> #include <boost/bind.hpp> <boost/asio.hpp> #include <boost/function.hpp> #include <boost/array.hpp> #include <boost/lexical_cast.hpp> #include <boost/shared_array.hpp> #include <boost/enable_shared_from_this.hpp> #include <boost/ptr_container/ptr_map.hpp> #include <boost/ptr_container/ptr_set.hpp> #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/join.hpp> #include <boost/format.hpp> #include <boost/date_time/gregorian/gregorian.hpp> #include <boost/program_options.hpp> #include <boost/tuple/tuple.hpp> #include <boost/foreach.hpp> #include "boost/filesystem.hpp" #include <iostream> using namespace std; using boost::lexical_cast; using boost::format; using boost::shared_ptr; boost::mutex log_mutex; void Log(int level, const string& str) { boost::mutex::scoped_lock l(log_mutex); cout << boost::format("%d - %s") % level % str << endl; } void f(int level, string s1, string s2) { Log(level, s1 + string(" ") + s2); } typedef vector<shared_ptr<boost::thread> > UmVectorDeThreads; UmVectorDeThreads Test_Threads() { #if 0 boost::array<boost::thread, 32> threads; threads.at(2) = boost::thread(boost::bind(&f, "Eu programa aos sábados.", "E daí")); #endif boost::array< shared_ptr<boost::thread>, 32> threads; for(unsigned int i = 0 ; i < threads.size() ; i++) { threads[i] = shared_ptr<boost::thread>( new boost::thread( boost::bind(&f, 2, "Eu programo aos sabados.", lexical_cast<string>(i)))); } // // ai... // for(boost::array< shared_ptr<boost::thread>, 32>::iterator i = threads.begin() ; i != threads.end() ; ++i) { } BOOST_FOREACH(shared_ptr<boost::thread> t, threads) { } boost::thread t; UmVectorDeThreads threads2(threads.size()); std::copy(threads.begin(), threads.end(), threads2.begin()); return threads2; } void VamosEsperarAsThreads(UmVectorDeThreads& threads) { BOOST_FOREACH(shared_ptr<boost::thread> t, threads) { t->join(); } } void Test_FileSystem() { using namespace boost::filesystem; path p("c:\\temp\\abobrinha"); for(path::iterator i = p.begin() ; i != p.end() ; ++i) cout << *i << endl; } void Test_STL() { vector<string> v; list<string> l; map<int, string> m; for(int a = 0 ; a < 20 ; a++) { string nome = "Nome " + lexical_cast<string>(a); v.push_back(nome); m[a] = nome; } copy(v.begin(), v.end(), back_inserter(l)); BOOST_FOREACH(const string& nome, v) cout << nome << endl; for_each(l.begin(), l.end(), boost::bind(&Log, 1, _1)); } int main() { UmVectorDeThreads threads = Test_Threads(); Test_STL(); Test_FileSystem(); for_each(threads.begin(), threads.end(), boost::bind(&boost::thread::join, _1)); // // olha mamãe, sem desalocar memória // return 0; }
Em 21/06/2008 16:42 - Comentários (1)
Win32: Handles
Como visto nos artigos anteriores, tudo no Windows é controlado por handles. Arquivos, sockets, objetos de sincronização, etc. O tipo HANDLE é na verdade um void* opaco, e somente o Windows sabe o que tem lá dentro (diga "veja mamãe, sem os fontes do sistema operacional!"). A única coisa a saber é: quem abre o HANDLE é o responsáve por fechá-lo usando a função CloseHandle. Fechar um handle mais de uma vez é um bug não-muito-grave, já que você não vai ganhar um GPF ou algo assim. Mas um GetLastError() retornará um ERROR_INVALID_HANDLE.
Acho que eu já disse isso algumas vezes, mas vou repetir. Todos os handles são fechados automaticamente quando o processo termina. É *impossível* que um programa termine e deixe um arquivo aberto, por exemplo. Impossível.
Dentro do kernel do Windows, todo processo tem uma tabela de handles referenciada pela estrutura de controle do processo, EPROCESS. Apesar do tipo do handle ser void*, ele é na verdade um offset para a tabela de handles, onde fica um ponteiro para o OBJECT_HEADER dentro do kernel. É tentador acessar a tabela para ver as informações sobre o objeto, mas isso não é possível... Como os objetos são gerenciados pelo Object Manager em kernel mode, os ponteiros são todos para a memória do kernel (endereço maior que 0x80000000). Se você quiser enumerar e ver os handles, terá que fazer um driver ou usar o Process Explorer:

Em 24/04/2008 17:56 - Comentários (4)
Win32: threads
Uma thread é basicamente uma linha de execução independente, contida dentro de um processo. Ou seja, uma thread permite que um processo "faça varias coisas de forma simultânea", já que um processo pode conter várias threads. Todas as threads que fazem parte de um mesmo processo compartilham vários recursos, como o espaçamento de memória e os handles. Ou seja, elas acessam as mesmas variávies e podem usar os mesmos handles ao mesmo tempo.
Quando um processo é criado no Windows, é criado junto com ele a thread principal, que é thread que roda a função main ou WinMain. A partir daí é possível criar novas threads, como no exemplo abaixo:
#include "stdafx.h" #include <windows.h> #include <iostream> using namespace std; // // WINAPI é um #define para __stdcall // DWORD WINAPI ThreadProc(void* lpv) { // // vamos esperar até 500 ms com o nosso random() de pobre // Sleep(GetTickCount() % 500); return 0; } int main() { DWORD dwThreadID; static const DWORD THREAD_COUNT = 20; HANDLE hThreads[THREAD_COUNT]; // // vamos criar nossas threads // for(DWORD a = 0 ; a < THREAD_COUNT ; a++) { hThreads[a] = CreateThread(NULL, // segurança, vamos deixar o default NULL, // tamanho da pilha. O default é 1MB &ThreadProc, // função da thread NULL, // parâmetro da thread NULL, // flag de criação. Podemos usá-lo para criar um thread suspensa &dwThreadID); // // O Sleep(0) diz ao scheduler do Windows que ele // pode agendar a próxima thread em espera, e nós // vamos para o fim da fila. // Sleep(0); } // // coloque um breakpoint aqui, vá no menu Debug >> Windows >> Threads // e veja as threads que criamos na lista // __asm nop; // instrução x86 que não faz absolutamente nada // // Para esperar uma thread parar (o que os linux boys conhecem como 'join') // usamos WaitForSingleObject ou WaitForMultipleObjects. Essas funções // funcionam com handles de processos, threads, mutexes e outras coisas mais // que veremos depois // DWORD dw = WaitForSingleObject( hThreads[0], // handle 100); // vamos esperar 100 ms if(dw == WAIT_TIMEOUT) { // // não retornou em 100ms... Vamos esperar para sempre // dw = WaitForSingleObject(hThreads[0], INFINITE); } else if(dw == WAIT_OBJECT_0) { // // esperamos menos de 100 ms // } // // agora vamos ver qual thread para primeiro // dw = WaitForMultipleObjects( THREAD_COUNT - 1, // quantos handles na array hThreads + 1, // array de threads + 1, porque a thread 0 já acabou FALSE, // não quero esperar por todas, se algum thread acabar está bom INFINITE); // senta e espera DWORD qualThread = dw - WAIT_OBJECT_0; cout << "a thread mais rápida foi a " << qualThread << endl; // // agora vamos esperar todas de uma vez --------------| // v dw = WaitForMultipleObjects(THREAD_COUNT, hThreads, TRUE, INFINITE); // // Como a API é C, vamos fechar os handles. Lembrando que // o Windows faz isso automaticamente quando o processo acaba // for(DWORD a = 0 ; a < THREAD_COUNT ; a++) CloseHandle(hThreads[a]); return 0; }
A única diferença entre as threads criadas posteriormente e a thread principal de um processo é que quando a thread principal acaba, todas as outras threads são forçosamente terminadas e o processo acaba - caso contrário uma thread esquecida poderia deixar o processo em um estado de limbo. Em um computador rodando Windows, em uma determinada hora, temos várias threads rodando, e a thread é única entidade que faz uso do processador. No Windows, um processo não "roda" nem é executado. O que "roda" é uma thread (que por sua vez pertence à um processo). Dessa forma, o scheduler (agendador) do Windows trabalha compartilhando e dividindo o uso do processador entre as threads do sistema, independente do processo que contém a thread.
Em uma de suas atuações, o scheduler do Windows (e de qualquer SO) funciona basicamente respondendo à uma interrupção de timer que acontece em intervalos de milissegundos. Quando essa interrupção ocorre, a thread atual é interrompida, e o scheduler toma seu lugar no processador para verificar se é necessário trocar de thread. Caso o quantum (tempo de cpu determinado para que a thread rode antes de outra thread assumir o processador) da thread atual tenha terminado, o scheduler salva os registradores que definem o estado atual da thread no thread context (veja a estrutura _CONTEXT no winnt.h) e carrega os registradores da próxima thread a ser executada. Com a nova thread pronta para utilizar o processador, o scheduler coloca a thread atual no fim da fila. Falando em código, o ponteiro da estrutura ETHREAD que representa a thread é colocada no fim de uma lista duplamente ligada que controla a fila de threads em estado ready.
Quando só existe um processador ou core no computador, o scheduler dá a impressão de que as threads estão sendo executadas simultaneamente, alternando entre elas dessa forma. Quando mais de um processador ou core estão disponíves, várias threads podem ser executadas simultaneamente, o que faz com que o scheduler precise distribuir as threads entre os N processadores disponíveis.
O tempo de interrupção citado é de 20 à 120ms, dependendo do processador e da versão do Windows. Nas versões Home, Professional e Business, os intervalos são de 20ms, para que o sistema seja mais responsivo ao usuário interativo. O intervalo de 120ms é usado nas versões Server, pois quanto maior o quantum, maior a probabilidade da thread conseguir atender a requisição em um quantum só, aumentando a vazão do servidor.
Outra situação onde o scheduler aparece é quando uma thread deve esperar uma operação de I/O. Nesse caso a thread perde o controle do processador antes que seu quantum acabe, e ela só será colocada novamente na lista de threads ready (prontas para serem executadas) quando essa requisição de I/O for respondida.
Como sempre, mais informações podem ser encontradas na MSDN (sobre as funções CreateThread e WaitForXXX), Wikipedia (sobre conceitos do sistemas operacionais, como quantum e thread) e no livro "Windows Internals" (sobre a implementação dos conceitos no kernel do Windows).
Em 15/04/2008 15:46 - Comentários (1)
Resultado do Quarto Encontro de Programador C & C++
Mais um encontro de programadores C++ foi realizado, outra ótima oportunidade para trocar idéias e conhecer outros profissionais e outras visões. Como meu amigo Wanderley Caloni fez um reporte detalhado, vou me abster.
Aqui estão a apresentação e o vídeo da minha palestra sobre Boost.Asio:
Gostaria também de deixar registrado meu sincero agradecimento ao nosso amigo Alberto Fabiano, que foi o membro da comunidade responsável por fazer com que esse encontro acontecesse. Foi ele quem fez todos acordarem para o fato de que um evento de verdade, com palestra, comida de graça e sorteio de brindes é possível mesmo para um banco de escovadores de bits como nós.
Em 08/04/2008 00:50 - Comentários (7)
Quarto Encontro de Programadores C++
No próximo sábado, dia 29 de março, os escovadores de bits estarão reunidos para o "4º Encontro de Programadores C e C++", que acontecerá em São Paulo. Eu farei uma palestra sobre programação TCP/IP usando Boost.Asio, e ainda teremos palestras sobre Qt, programação de drivers para Windows e programação para microcontroladores.
O evento durará o dia todo, e custa a bagatela de R$ 15,00 se você pagar até quarta-feira. Faça sua inscrição no site da Tempo Real Eventos, e veja mais informações no wiki da nossa comunidade C++.
Em 24/03/2008 17:45 - Comentários (2)
Posts anteriores >>





