A Aldeia Numaboa ancestral ainda está disponível para visitação. É a versão mais antiga da Aldeia que eu não quis simplesmente descartar depois de mais de 10 milhões de pageviews. Como diz a Sirley, nossa cozinheira e filósofa de plantão: "Misericórdia, ai que dó!"

Se você tiver curiosidade, o endereço é numaboa.net.br.

Informática Numaboa - Tutoriais e Programação

O sistema operacional Windows

Sab

25

Abr

2009


11:25

(194 votos, média 4.18 de 5) 


Iniciantes

Quando se pretende programar em Assembly é essecial conhecer o processador e o sistema operacional para os quais o executável se destina. Neste texto abordaremos apenas o sistema operacional Windows.

O Windows assume o controle do computador praticamente desde o instante em que é ligado até o momento em que é desligado. Uma aplicação pode rodar apenas com a permissão do Windows, com a assitência do Windows e sob o controle do Windows. É deste modo que o Windows oferece a previsibilidade e a consistência da interface com o usuário e é por este motivo que possui a capacidade de (aparentemente) rodar diversos programas simultaneamente (a chamada multi-tarefa). Este sistema opercaional deveria oferecer maior robustez em casos de falhas, mas isto é um capítulo à parte tongue2

Para resumir a história, o sistema operacional Windows domina a sua máquina, controlando tudo o que for possível: Hardware, Aplicativos, Microprocessador, Memória e Dados, Interface do usuário e Arquivos.

Controle do hardware

A maioria dos microchips que trabalham com a unidade central de processamento (CPU) são programáveis. Por exemplo, a placa de vídeo precisa ser informada do scan rate, resolução e cores corretos. Os chips de entrada/saída da impressora precisam conhecer a porta normalmente utilizada e a que velocidade os dados devem ser transferidos. Os chips do teclado devem conhecer a taxa de repetição que devem usar. A comunicação com estes dispositivos precisa ser controlada – cada um terá sua própria área de memória e precisa ser informado do que se espera e quando. O Windows realiza todas estas tarefas básicas, como qualquer outro sistema operacional.

O Windows também assume o controle total da escrita e da leitura destes dispositivos, o que pode ser uma grande vantagem para o programador. Por exemplo, para imprimir um documento, o aplicativo precisa apenas informar em que parte da memória o documento se encontra e qual o seu tamanho. O Windows se encarrega da impressão usando o driver de impressão adequado para a impressora em uso e coloca a tarefa na posição correta da fila de impressão, que pode conter outras tarefas de impressão de outros aplicativos. A tarefa de impressão é sempre efetuada no modo gráfico. O Windows informa a impressora onde cada ponto de impressão deve ser colocado no papel para formar a imagem impressa. As vantagens do programador do aplicativo são significantes - não há a necessidade de escrever drivers de impressão nem algoritmos gráficos.

O Windows faz um trabalho ainda melhor na tela do monitor, que também sempre está no modo gráfico. Ele atende vários aplicativos simultaneamente e é capaz de colocar diversas janelas na tela, algumas delas sobrepostas. Sua tarefa é criar a imagem final e de gerenciar cada uma das janelas respeitando a prioridade, a ordem na tela, tipo e estilo de cada uma delas. Uma boa parte do trabalho de programação consiste em aplicar estes fatores ao programa e obter uma saída correta para a tela.

Em resumo: o Windows controla todo o hardware dos periféricos e impede (ou deveria impedir) o acesso direto a eles através de aplicativos.

Controle dos aplicativos

O que acontece quando se clica o ícone de um programa? O Windows sabe exatamente a posição do clique e qual o ícone que se encontra sob o cursor do mouse. Também sabe, através da sua lista de "atalhos" e "propriedades", qual programa deve ser iniciado quando este ícone em particular for clicado.

Para iniciar o programa, o Windows carrega o programa lendo o arquivo correspondente e colocando-o na memória. Depois, o Windows simplesmente chama este programa, ou seja, informa o processador para que ele execute todas as instruções a partir do endereço inicial do programa até encontrar uma instrução RET. Após o RET, termina o programa e volta ao sistema operacional.

Controle do microprocessador

Ao iniciar um programa, o registrador EIP do processador recebe do Windows o endereço inicial do programa. O Windows também controla todos os valores dos registradores do processador, mantendo-os numa área de memória chamada de contexto de registradores. O Windows pode (e o faz com frequência) parar o processador, armazenar os valores contidos nos registradores e solicitar que o processador rode um programa diferente por algum tempo, ou seja, fornece a outro programa uma fatia de tempo. Terminando o segundo programa, o Windows reconstitui os valores armazenados e continua executando o primeiro programa a partir do ponto de interrupção.

É desta forma que funciona a multi-tarefa do Windows. Cada um dos programas que estiver sendo executado recebe uma fatia de tempo - o processador é rateado entre todos eles e, mesmo em máquinas com apenas uma CPU, o usuário tem a impressão de que os programas são executados simultaneamente. O Windows faz o rateio de tempo de acordo com várias prioridades. Por exemplo, operações de leitura e escrita em disco possuem prioridades muito altas e podem bloquear a execução de outros programas até que sejam finalizadas.

Um programa pode pedir ao Windows para que inicie uma nova linha de execução (thread). Neste caso, o Windows atribuirá a este thread fatias de tempo próprias, valores de registradores próprios e um pilha própria. A nova linha de execução parece estar sendo executada ao mesmo tempo que o thread principal do programa. Isto é muito útil quando um programa precisar dar continuidade a uma determinada tarefa, por exemplo um cálculo muito longo, e, ao mesmo tempo, manter a interface do usuário ativa. Isto é chamado de multi-threading.

Controle de memória e dados

Num dado momento, um programa pode ter todos os seus dados na memória. Estes são mantidos na memória que foi estabelecida por endereçamento direto ou então na pilha. Como o Windows preserva estes dados quando ratear o tempo entre vários programas?

O Windows mantém um mapa de memória de todos os dados de programas, ou seja, ele conhece o local exato dos dados dos programas na memória física do computador. Este mapa de memória é mantido numa área de memória chamada de contexto de memória. Se a memória física começar a se esgotar, o Windows passa a usar o disco rígido para armazenar os dados dos programas. Isto explica porque, em sistemas com pouca memória, a atividade do HD é muito maior do que em sistemas com mais memória física. Esta memória em disco é chamada de memória virtual.

Quando um programa precisa acessar seus dados, ele o faz usando um endereço virtual. Isto significa que o endereço da área de memória na verdade não é o mesmo que o endereço dos dados na memória física. O Windows informa o processador onde as áreas de memória requeridas se encontram realmente fornecendo o endereço da sua tabela de mapeamento de páginas ao programa através do registrador CR3.

Controle da interface do usuário

O Windows possui uma interface de usuário consistente, largamente difundida e utilizada. As vantagens de uma interface padronizada são óbvias - uma delas é que, independentemente do programa que esteja sendo executado, o usuário se sente "em casa".

O Windows adquiriu esta uniformidade por que fornece componentes padrão que podem ser incluídos nos programas. Exemplos disto são os menus que aparecem sob a barra de título das janelas, diálogos, botões, barras de rolagem e arquivos de ajuda padronizados. O conjunto destes componentes padrão é denominado de GUI ou Graphical User Interface.

Os aplicativos podem fazer uso destes componentes padrão fazendo uma chamada a uma API (Applications Programming Interface). As APIs contêm procedimentos (ou funções) que podem ser chamados por um nome e que fornecem o componente desejado. Todas as APIs são armazenadas em arquivos chamados DLL ou Dynamic Linked Library. Na verdade, as DLLs são executáveis com a extensão .dll que contêm funções nominadas que podem ser exportadas (ou seja, um aplicativo importa funções).

Controle de arquivos

O Windows mantém registros de todos os arquivos vitais ao sistema e de drivers de dispositivos periféricos. Para isto usa o Registro (registry), uma base de dados com informações sobre a configuração do sistema e dos aplicativos que devem rodar neste sistema. Além disto, mantém o registro de todas as pastas (diretórios) com seus respectivos conteúdos para que possam ser localizados quando solicitados.

A comunicação sistema-aplicativo

Como vimos, o sistema Windows controla todos os aspectos importantes do computador e dos aplicativos que estejam rodando. Para que este controle cerrado possa ser mantido, é necessário haver um sistema de comunicação entre o sistema e os aplicativos.

Um aplicativo tem necessidade de se comunicar com o sistema quando quiser obter alguma informação da GUI, por exemplo, o tamanho de uma janela em particular ou o tamanho de uma string numa determinada fonte. O mesmo acontece quando o aplicativo necessitar de algum recurso da API, pois precisa informar com exatidão como este recurso deve ser aplicado.

Os métodos mais comuns usados por aplicativos para se comunicarem com o sistema são:

  • Dados na pilha: antes de fazer uma chamada a uma API, é necessário colocar na pilha, utilizando PUSH, os dados exigidos pela função da API. Os dados, na maioria das vezes, são valores dword. Em alguns casos podem ser ponteiros de estruturas que contenham mais dados ou ponteiros de strings de texto.
  • Mensagens: pode-se enviar mensagens ao sistema chamando a função SendMessage da API. Na realidade, a mensagem é um dword colocado na pilha e que pode ser acompanhado por até 3 dwrods de dados adicionais.

O sistema também precisa se comunicar com o aplicativo para fornecer o resultado de uma chamada à API ou para informar o aplicativo de que algo está acontecendo na GUI ou que algo importante está ocorrendo com o próprio sistema.

Os métodos mais comuns de comunicação entre o sistema e um aplicativo são:

  • Retornando de uma API, o sistema geralmente põe um valor que representa o resultado da chamada no registrador EAX.
  • Em alguns casos, retornando de uma API, o sistema deixa dados na memória, num local especificado pelo programa quando fez a chamada à API. Este local deve ter sido especificado pelo aplicativo PUSHando um ponteiro para a pilha antes da chamada à API.
  • Mensagens do sistema para o aplicativo. Quando isto ocorre, o sistema também envia dados para a pilha. O sistema precisa ter sido avisado pelo aplicativo do endereço no seu código que corresponde ao procedimento que gerencia este tipo de chamada. Este procedimento é chamado de procedimento "callback" (chamada de retorno), "windows procedure" (procedimento windows) ou simplesmente "WndProc".

Manipuladores e Contextos de dispositivo

Todos os "objetos" com os quais o Windows trabalha possuem manipuladores (handles). Estes objetos podem ser janelas, controles, menus, diálogos, processos, threads, áreas de memória, displays, impressoras, arquivos, drives de disco e até fontes, brushes e pens usados para desenhar e escrever. Um manipulador é um valor dword que pode ser requisitado pelo aplicativo. Uma vez obtido, este manipulador é utilizado pelo aplicativo para se comunicar com o Windows e solicitar seu uso ou modificações.

Todos os dispositivos que mostram ou produzem uma saída possuem contextos de dispositivo. O contexto de dispositivo é uma área de memória, mantida pelo Windows, que contém informação sobre como o dispositivo deve mostrar sua saída. Portanto, uma janela em particular terá um contexto de dispositivo que conterá informações sobre a fonte e a cor que devem ser usadas para qualquer coisa que for desenhada ou escrita nesta janela. Uma impressora terá um contexto de dispositivo contendo as características da impressora, tamanho do papel, cores disponíveis e assim por diante.

Tipos de executáveis

Um "executável" é um arquivo que contém código que pode ser executado pelo processador. Como iniciante, só é preciso conhecer dois tipos: os arquivos com extensão .exe (aplicativos) e os arquivos com extensão .dll (dynamic link library ou blibliotecas dinâmicas).

Para poder ser executado pelo Windows, um arquivo executável precisa estar no formato PE (Portable Executable). Como o nome sugere, este tipo de arquivo possui portabilidade, o que permite pode seja executado em computadores tanto com processadores Intel, MIPS, Alpha, Power PC, Motorola 68000, assim como RISC. É claro que, independentemente do tipo de processador, o sistema operacional precisa ser Windows e o versão do formato PE precisa corresponder ao processador utilizado.

O Windows sabe que o executável é um arquivo PE devido à presença da assinatura "PE" logo no início do arquivo. Um arquivo não-PE, por exemplo um executável DOS, não possui esta assinatura e o Windows precisa tomar outras providências para rodá-lo.

Uma Dll é usada quando seu código ou seus dados precisam ser compartilhados entre diversos aplicativos. O Windows usa Dlls para armazenar o código da sua API. Uma Dll possui exportações. Isto reduz sensivelmente o tamanho de cada exe porque, quando precisar do código, recorre a uma Dll. Um campo de grande importância num arquivo PE é a lista de importação. Esta é uma lista das funções das quais o Exe depende e que poderá chamar quando estiver sendo executado. Esta lista também possui o nome da Dll que contém a função. Ao carregar o Exe, o Windows checa se todas as funções e todas as Dlls estão disponíveis. Se não estiverem disponíveis, o sistema não roda o programa.

Diferentes versões do Windows possuem Dlls diferentes. Fica claro que, com toda a probabilidade, um programa escrito para rodar em Win98 acabe não rodando no WinNT. Para evitar este problema pode-se utilizar a função da API GetVersionEx, a qual determina a versão atual do Windows, e, de posse desta informação, chamar a API correta. Só que há um detalhe: se a API for chamada do modo usual, a Dll entra na lista de importação e, se não estiver presente, o arquivo não é executado. Contorna-se este problema utilizando a função da API LoadLibrary (que carrega a Dll desejada se já não tiver sido carregada) e GetProcAddress (que acha o endereço na Dll da função desejada).

Assim como o Windows usa Dlls, também é possível escrever Dlls que acompanhem um aplicativo composto de mais de um programa. Desta forma os programas podem compartilhar o código e os dados existentes na Dll. Existe mais um caso no qual o uso de Dlls pode ser vantajoso: coloca-se código e dados que precisam de manutenção frequente numa Dll. Ao invés de ter que atualizar todo o arquivo exe, trabalha-se com um arquivo menor e "segregado", o que facilita a manutenção.

Informações adicionais