Compartilhar via


Processo de execução gerenciada

O processo de execução gerenciada inclui as seguintes etapas, que são discutidas detalhadamente mais adiante neste tópico:

  1. Escolhendo um compilador. Para obter os benefícios fornecidos pelo common language runtime, você deve usar um ou mais compiladores de idioma direcionados ao runtime.
  2. Compilando seu código para linguagem intermediária. A compilação converte seu código-fonte em CIL (common intermediate Language) e gera os metadados necessários.
  3. Compilando CIL para código nativo. No momento da execução, um compilador JIT (just-in-time) converte o CIL em código nativo. Durante essa compilação, o código deve passar por um processo de verificação que examine o CIL e os metadados para descobrir se o código pode ser determinado como seguro de tipo.
  4. Executando código. O common language runtime fornece a infraestrutura que permite a execução e serviços que podem ser usados durante a execução.

Escolher um compilador

Para obter os benefícios fornecidos pelo CLR (Common Language Runtime), você deve usar um ou mais compiladores de linguagem direcionados ao runtime, como Visual Basic, C#, Visual C++, F#ou um dos muitos compiladores de terceiros, como um compilador Eiffel, Perl ou COBOL.

Como é um ambiente de execução multilíngue, o runtime dá suporte a uma ampla variedade de tipos de dados e recursos de linguagem. O compilador de linguagem que você usa determina quais recursos de runtime estão disponíveis e você projeta seu código usando esses recursos. O compilador, não o runtime, estabelece a sintaxe que seu código deve usar. Se o componente precisar ser completamente utilizável por componentes escritos em outras linguagens, os tipos exportados do componente deverão expor apenas os recursos de linguagem incluídos na CLS (Common Language Specification). Você pode usar o CLSCompliantAttribute atributo para garantir que seu código esteja em conformidade com CLS. Para obter mais informações, consulte Independência da linguagem e componentes independentes de idioma.

Compilar para CIL

Ao compilar para código gerenciado, o compilador converte seu código-fonte em CIL (common intermediate language), que é um conjunto de instruções independente de CPU que pode ser convertido com eficiência em código nativo. O CIL inclui instruções para carregar, armazenar, inicializar e chamar métodos em objetos, bem como instruções para operações aritméticas e lógicas, fluxo de controle, acesso direto à memória, tratamento de exceção e outras operações. Antes que o código possa ser executado, o CIL deve ser convertido em código específico da CPU, geralmente por um compilador JIT (just-in-time). Como o common language runtime fornece um ou mais compiladores JIT para cada arquitetura de computador compatível, o mesmo conjunto de CIL pode ser compilado por JIT e executado em qualquer arquitetura com suporte.

Quando um compilador produz CIL, ele também produz metadados. Os metadados descrevem os tipos em seu código, incluindo a definição de cada tipo, as assinaturas dos membros de cada tipo, os membros que seu código faz referência e outros dados que o runtime usa no momento da execução. A CIL e os metadados estão contidos em um arquivo executável portátil (PE) que se baseia e amplia o Microsoft PE publicado e o formato de arquivo de objeto comum (COFF) usado historicamente para conteúdo executável. Esse formato de arquivo, que acomoda CIL ou código nativo, bem como metadados, permite que o sistema operacional reconheça imagens de common language runtime. A presença de metadados no arquivo junto com o CIL permite que seu código se descreva, o que significa que não há necessidade de bibliotecas de tipos ou IDL (Interface Definition Language). O runtime localiza e extrai os metadados do arquivo conforme necessário durante a execução.

Compilar CIL para código nativo

Antes de poder executar a Common Intermediate Language (CIL), ela deve ser compilada em relação ao Common Language Runtime para o código nativo da arquitetura do computador de destino. O .NET fornece duas maneiras de executar essa conversão:

Compilação pelo compilador JIT

A compilação JIT converte CIL em código nativo sob demanda durante o tempo de execução do aplicativo, quando o conteúdo de um assembly é carregado e executado. Como o common language runtime fornece um compilador JIT para cada arquitetura de CPU com suporte, os desenvolvedores podem criar um conjunto de assemblies CIL que podem ser compilados por JIT e executados em computadores diferentes com arquiteturas de máquina diferentes. No entanto, se o código gerenciado chamar APIs nativas específicas da plataforma ou uma biblioteca de classes específica da plataforma, ela será executada somente nesse sistema operacional.

A compilação JIT leva em conta a possibilidade de que algum código nunca seja chamado durante a execução. Em vez de usar o tempo e a memória para converter todo o CIL em um arquivo PE em código nativo, ele converte o CIL conforme necessário durante a execução e armazena o código nativo resultante na memória para que ele seja acessível para chamadas subsequentes no contexto desse processo. O carregador cria e anexa um stub para cada método em um tipo quando o tipo é carregado e inicializado. Quando um método é chamado pela primeira vez, o stub passa o controle para o compilador JIT, que converte o CIL para esse método em código nativo e modifica o stub para apontar diretamente para o código nativo gerado. Portanto, as chamadas subsequentes para o método compilado por JIT vão diretamente para o código nativo.

Geração de código em tempo de instalação usando o NGen.exe.

Como o compilador JIT converte o CIL de um assembly em código nativo quando métodos individuais definidos nesse assembly são chamados, ele afeta o desempenho negativamente em runtime. Na maioria dos casos, esse desempenho reduzido é aceitável. Mais importante, o código gerado pelo compilador JIT está associado ao processo que disparou a compilação. Ele não pode ser compartilhado em vários processos. Para permitir que o código gerado seja compartilhado entre várias invocações de um aplicativo ou em vários processos que compartilham um conjunto de assemblies, o common language runtime dá suporte a um modo de compilação antecipada. Esse modo de compilação antecipada usa o Ngen.exe (Gerador de Imagem Nativa) para converter assemblies CIL em código nativo, assim como o compilador JIT. No entanto, a operação de Ngen.exe difere da do compilador JIT de três maneiras:

  • Ele executa a conversão de CIL para código nativo antes de executar o aplicativo em vez de enquanto o aplicativo está em execução.
  • Ele cria um assembly inteiro por vez, em vez de um método de cada vez.
  • Ele persiste o código gerado no Cache de Imagem Nativa como um arquivo em disco.

Verificação de código

Como parte de sua compilação para código nativo, o código CIL deve passar por um processo de verificação, a menos que um administrador tenha estabelecido uma política de segurança que permita que o código ignore a verificação. A verificação examina CIL e metadados para descobrir se o código é tipo seguro, o que significa que ele acessa apenas os locais de memória que está autorizado a acessar. A segurança do tipo ajuda a isolar objetos uns dos outros e ajuda a protegê-los contra corrupção inadvertida ou mal-intencionada. Ele também fornece garantia de que as restrições de segurança no código podem ser impostas de forma confiável.

O runtime depende das seguintes declarações serem verdadeiras para o código que é comprovadamente fortemente tipado:

  • Uma referência a um tipo é estritamente compatível com o tipo que está sendo referenciado.
  • Somente operações definidas adequadamente são invocadas em um objeto.
  • Identidades são o que dizem ser.

Durante o processo de verificação, o código CIL é examinado na tentativa de confirmar que o código pode acessar locais de memória e chamar métodos apenas por meio de tipos definidos corretamente. Por exemplo, o código não pode permitir que os campos de um objeto sejam acessados de uma maneira que permita que os locais de memória sejam invadidos. Além disso, a verificação inspeciona o código para determinar se o CIL foi gerado corretamente, pois o CIL incorreto pode levar a uma violação das regras de segurança de tipo. O processo de verificação passa um conjunto bem definido de código fortemente tipado, e ele passa apenas código fortemente tipado. No entanto, algum código de tipo seguro pode não passar na verificação devido a algumas limitações do processo de verificação e alguns idiomas, por design, não produzem um código seguro de tipo verificável. Se o código de tipo seguro for exigido pela política de segurança, mas o código não passar na verificação, uma exceção será gerada quando o código for executado.

Executar código

O common language runtime fornece a infraestrutura que permite que a execução gerenciada ocorra e serviços que podem ser usados durante a execução. Antes que um método possa ser executado, ele deve ser compilado para código específico do processador. Cada método para o qual o CIL foi gerado é compilado por JIT quando é chamado pela primeira vez e, em seguida, executado. Na próxima vez que o método for executado, o código nativo compilado por JIT existente será executado. O processo de compilação JIT e execução do código é repetido até que a execução seja concluída.

Durante a execução, o código gerenciado recebe serviços como coleta de lixo, segurança, interoperabilidade com código não gerenciado, suporte à depuração entre idiomas e suporte aprimorado para implantação e controle de versão.

No Microsoft Windows Vista, o carregador do sistema operacional verifica módulos gerenciados examinando um bit no cabeçalho COFF. O bit que está sendo definido indica um módulo gerenciado. Se o carregador detectar módulos gerenciados, ele carregará mscoree.dll, e _CorValidateImage e _CorImageUnloading notificarão o carregador quando as imagens dos módulos gerenciados forem carregadas e descarregadas. _CorValidateImage executa as seguintes ações:

  1. Garante que o código seja um código gerenciado válido.
  2. Altera o ponto de entrada na imagem para um ponto de entrada no runtime.

No Windows de 64 bits, _CorValidateImage modifica a imagem que está na memória transformando-a do formato PE32 para PE32+.

Consulte também