O código fonte para este artigo pode ser encontrado aqui.
Máquinas virtuais e frameworks de software constituem uma parte inicial de nossa vida digital. Há VM complexa e frameworks de software simples. Estes dois artigos (virtual machine simples e tempo de execução de framework simples através de exemplo) mostram o quão fácil pode ser implementar você mesmo. Eu fiz o possível para descrever a forma como o código VM pode interagir com código nativo e o sistema operacional, no entanto, a interação ainda é deixada para trás sem explicação. Este artigo irá corrigir essa omissão.
Como de costume – nota para nerds:
O código fonte dado neste artigo é apenas para exemplo. Eu sei que este framework está longe de ser perfeito, portanto, este artigo não é um HOW TO ou tutorial – apenas uma explicação do princípio. Verificações de erro são omitidas de propósito. Você deseja implementar um quadro real – faça você mesmo, incluindo verificações de erro. Ao dizer código VM, não me refiro à implementação da máquina virtual, mas ao pseudo-código que corre dentro dela.
Visão geral da arquitetura
Desnecessário mencionar que a capacidade de transmitir eventos/sinais a um código executado pela máquina virtual implica em uma mais complexa arquitetura VM. Enquanto todos os exemplos anteriores foram baseados em uma única função responsável pela execução, a adição de eventos não significa apenas a adição de uma outra função, mas teremos que introduzir tópicos para nossa implementação.
Pelo menos dois segmentos são necessários:
- Actual VM – esta thread é responsável pela execução do código executável da VM e pelo envio de eventos da fila (processador);
- Event Listener – esta thread é responsável pela coleta de eventos relevantes para os sistemas operacionais e pela adição deles na fila de processos da da VM.
Você pode ver que a função Core(), no código fonte anexo, cria thread adicional.

Fig.1: Arquitetura VM com Event Listener
Event Listener: Essa thread coleta eventos do sistema operacional (movimento do mouse, key up/down etc.) e adiciona uma nova entrada para a lista de estruturas de eventos.
typedef struct _EVENT
{
struct _EVENT* next_event; // Pointer to the next event in the queue
int code; // Code of the event
unsigned int data; // Either unsigned int data or the address of the buffer
// containing information to be passed to the handler
}EVENT;
O código para o listener é bastante simples:
while(WAIT_TIMEOUT == WaitForSingleObject(processor_thread, 1))
{
// Check for events from the OS
if(event_present)
{
EnterCriticalSection(&cs);
event = (EVENT*)malloc(sizeof(EVENT));
event->code = whatever_code_is_needed;
event->data = whatever_data_is_relevant;
add_event(event_list, event);
event->next_event = NULL;
LeaveCriticalSection(&cs);
}
}
O código é suficientemente auto-explicativo. Antes de tudo, ele verifica eventos disponíveis (essa parte é omitida e substituída por um comentário). Se houver um novo evento para transmitir à VM, ele o adiciona à fila. Enquanto neste exemplo a coleta de eventos é implementada como um loop, na vida real, você pode fazê-la na forma de callbacks e usar o loop acima apenas para esperar a thread do processador para sair.
Processador
Obviamente, o “processador” thread vai ser um pouco mais complicado, então, no artigo anterior (tempo de execução de framework simples através de exemplo), como além de executar a função run_opcode (CPU **), ele tem que verificar se há eventos pendentes e passar o controle de fluxo para o handler associado no código VM.
typedef struct _EVENT_HANDLER
{
struct _EVENT_HANDLER* next_handler; // Pointer to the next handler
int event_code; // Code of the event
unsigned int handler_base; // Address of the handler in the VM's code
}EVENT_HANDLER;
DWORD WINAPI RunningThread(void* param)
{
CPU* cpu = (CPU*)param;
EVENT* event;
EVENT_HANDLER* handler;
do{
EnterCriticalSection(&cs);
if(NULL != events)
{
event = events;
events = events->next_event;
// Save current context by pushing VM registers to VM's stack
cpu->regs[REG_A] = (unsigned int)event->code;
cpu->regs[REG_B] = event->data;
handler = handlers;
while(NULL != handler && event->code != handler->event_code)
handler = handler->next_handler;
cpu->regs[REG_IP] = handler->handler_base;
free(event);
}
LeaveCriticalSection(&cs);
}while(0 != run_opcode(&cpu));
return cpu->regs[REG_A];
}
Estamos quase terminando. Nosso framework já sabe como passar eventos para um handler correto no código da VM. Duas coisas ainda precisam ser descobertas – registrar um handler e retornar de um handler.
Retornando do handler
Devido ao fato de que o Event Handler não é uma rotina regular, não podemos retornar dele usando a instrução regular RET, em vez disso, vamos introduzir uma outra instrução – IRET. Como o evento realmente interrompe o fluxo de execução do programa, IRET – retorno de interrupção é exatamente o que precisamos. O código fonte que manipula essa instrução é tão simples, que não há necessidade de fornecê-lo aqui no texto do artigo. Tudo o que ele faz é simplesmente restaurar o contexto do código da VM disparando os registos previamente inseridos na pilha.
Registrando um Event Handler
A última coisa que resta é “ensinar” o programa escrito em pseudo assembly a registrar um handler para um tipo determinado de evento. Para fazer isso, precisamos adicionar uma chamada de sistema simples – SYS_ADD_LISTENER. Essa chamada de sistema aceita dois parâmetros:
- Código do evento para handler.
- Endereço da função handler.
loadi A, 0 ;Code of the event
loadi B, handler ;Address of the handler subroutine
_int sys_add_listener ;Register the handler
Código de exemplo
O código de exemplo anexado a este artigo é a implementação de todos os itens acima. Ele faz o seguinte:
- Registra em event handler;
- Entra em um loop infinito imprimindo ‘.’ a cada vários milissegundos;
- A primeira thread espera um pouco e gera um evento;
- Event Handler encerra o loop infinito e retorna;
- O programa imprime uma mensagem e sai.
Espero que este artigo tenha sido útil ou, pelo menos, interessante.
Vejo vocês na próxima.
?
Texto original disponível em http://syprog.blogspot.com.br/2012/05/passing-events-to-virtual-machine.html







