Condividi tramite


Processo di esecuzione gestita

Il processo di esecuzione gestita include i passaggi seguenti, descritti in dettaglio più avanti in questo argomento:

  1. Scelta di un compilatore. Per ottenere i vantaggi offerti da Common Language Runtime, è necessario usare uno o più compilatori di linguaggio destinati al runtime.
  2. Compilazione del tuo codice in linguaggio intermedio. La compilazione converte il codice sorgente in common intermediate language (CIL) e genera i metadati necessari.
  3. Compilazione di CIL in codice nativo. In fase di esecuzione, un compilatore JIT converte l'CIL in codice nativo. Durante questa compilazione, il codice deve superare un processo di verifica che esamina il CIL e i metadati per determinare se il codice è sicuro rispetto ai tipi.
  4. Esecuzione del codice. Common Language Runtime fornisce l'infrastruttura che consente l'esecuzione e i servizi che possono essere usati durante l'esecuzione.

Scegliere un compilatore

Per ottenere i vantaggi offerti da Common Language Runtime (CLR), è necessario usare uno o più compilatori di linguaggio destinati al runtime, ad esempio Visual Basic, C#, Visual C++, F# o uno di molti compilatori di terze parti, ad esempio un compilatore Eiffel, Perl o COBOL.

Poiché si tratta di un ambiente di esecuzione multilanguage, il runtime supporta un'ampia gamma di tipi di dati e funzionalità del linguaggio. Il compilatore del linguaggio usato determina quali funzionalità di runtime sono disponibili e si progetta il codice usando tali funzionalità. Il compilatore, non il runtime, stabilisce la sintassi che il codice deve usare. Se il componente deve essere completamente utilizzabile dai componenti scritti in altri linguaggi, i tipi esportati del componente devono esporre solo le funzionalità del linguaggio incluse in Common Language Specification (CLS). È possibile usare l'attributo CLSCompliantAttribute per assicurarsi che il codice sia conforme a CLS. Per altre informazioni, vedere Indipendenza della lingua e componenti indipendenti dal linguaggio.

Compilare in CIL

Durante la compilazione in codice gestito, il compilatore converte il codice sorgente in common intermediate language (CIL), ovvero un set di istruzioni indipendente dalla CPU che può essere convertito in modo efficiente in codice nativo. CIL include istruzioni per il caricamento, l'archiviazione, l'inizializzazione e la chiamata di metodi su oggetti, nonché istruzioni per operazioni aritmetiche e logiche, flusso di controllo, accesso diretto alla memoria, gestione delle eccezioni e altre operazioni. Prima di poter eseguire il codice, CIL deve essere convertito in codice specifico della CPU, in genere da un compilatore JIT (Just-In-Time). Poiché Common Language Runtime fornisce uno o più compilatori JIT per ogni architettura di computer supportata, lo stesso set di CIL può essere compilato e eseguito in qualsiasi architettura supportata.

Quando un compilatore produce CIL, produce anche metadati. I metadati descrivono i tipi nel codice, inclusa la definizione di ogni tipo, le firme dei membri di ogni tipo, i membri a cui fa riferimento il codice e altri dati usati dal runtime in fase di esecuzione. I CIL e i metadati sono contenuti in un file eseguibile portabile (PE) basato sul formato Microsoft PE e che estende il formato comune di file oggetto (COFF), storicamente utilizzato per i contenuti eseguibili. Questo formato di file, che supporta CIL o codice nativo, oltre ai metadati, consente al sistema operativo di riconoscere le immagini common language runtime. La presenza di metadati nel file insieme a CIL consente al codice di descrivere se stesso, il che significa che non sono necessarie librerie di tipi o IDL (Interface Definition Language). Il runtime individua ed estrae i metadati dal file in base alle esigenze durante l'esecuzione.

Compilare CIL in codice nativo

Prima di poter eseguire Common Intermediate Language (CIL), è necessario compilarlo in Common Language Runtime in codice nativo per l'architettura del computer di destinazione. .NET offre due modi per eseguire questa conversione:

Compilazione da parte del compilatore JIT

La compilazione JIT converte CIL in codice nativo su richiesta in fase di esecuzione dell'applicazione, quando il contenuto di un assembly viene caricato ed eseguito. Poiché Common Language Runtime fornisce un compilatore JIT per ogni architettura cpu supportata, gli sviluppatori possono compilare un set di assembly CIL che possono essere compilati e eseguiti in computer diversi con architetture di computer diverse. Tuttavia, se il codice gestito chiama API native specifiche della piattaforma o una libreria di classi specifica della piattaforma, verrà eseguita solo in tale sistema operativo.

La compilazione JIT tiene conto della possibilità che un codice non venga mai chiamato durante l'esecuzione. Anziché usare tempo e memoria per convertire tutti i CIL in un file PE in codice nativo, converte l'CIL in base alle esigenze durante l'esecuzione e archivia il codice nativo risultante in memoria in modo che sia accessibile per le chiamate successive nel contesto di tale processo. Quando un tipo viene caricato e inizializzato, il caricatore crea e collega uno stub a ciascun metodo del tipo. Quando un metodo viene chiamato per la prima volta, lo stub passa il controllo al compilatore JIT, che converte l'CIL per tale metodo in codice nativo e modifica lo stub in modo che punti direttamente al codice nativo generato. Di conseguenza, le chiamate successive al metodo compilato da JIT passano direttamente al codice nativo.

Generazione di codice in fase di installazione con NGen.exe

Poiché il compilatore JIT converte l'CIL di un assembly in codice nativo quando vengono chiamati singoli metodi definiti in tale assembly, influisce negativamente sulle prestazioni in fase di esecuzione. Nella maggior parte dei casi, le prestazioni ridotte sono accettabili. Più importante, il codice generato dal compilatore JIT è associato al processo che ha attivato la compilazione. Non può essere condiviso tra più processi. Per consentire la condivisione del codice generato tra più chiamate di un'applicazione o tra più processi che condividono un set di assembly, Common Language Runtime supporta una modalità di compilazione anticipata. Questa modalità di compilazione anticipata usa il Ngen.exe (Generatore di immagini native) per convertire gli assembly CIL in codice nativo in modo analogo al compilatore JIT. Tuttavia, l'operazione di Ngen.exe differisce da quella del compilatore JIT in tre modi:

  • Esegue la conversione da CIL a codice nativo prima di eseguire l'applicazione anziché durante l'esecuzione dell'applicazione.
  • Compila un intero assembly alla volta, anziché un metodo alla volta.
  • Rende persistente il codice generato nella cache di immagini native come file su disco.

Verifica del codice

Nell'ambito della compilazione nel codice nativo, il codice CIL deve superare un processo di verifica a meno che un amministratore non abbia stabilito un criterio di sicurezza che consenta al codice di ignorare la verifica. La verifica esamina il CIL e i metadati per scoprire se il codice è sicuro per i tipi, il che significa che accede solo alle posizioni di memoria a cui è autorizzato ad accedere. La sicurezza dei tipi consente di isolare gli oggetti l'uno dall'altro e di proteggerli da danneggiamenti accidentali o dannosi. Garantisce inoltre che le restrizioni di sicurezza sul codice possano essere applicate in modo affidabile.

Il runtime si basa sul fatto che le seguenti affermazioni sono vere per il codice verificabile e conforme al tipo:

  • Un riferimento a un tipo è strettamente compatibile con il tipo a cui si fa riferimento.
  • Solo le operazioni definite in modo appropriato vengono richiamate su un oggetto .
  • Le identità sono ciò che sostengono di essere.

Durante il processo di verifica, il codice CIL viene esaminato nel tentativo di verificare che il codice possa accedere ai percorsi di memoria e chiamare i metodi solo tramite tipi definiti correttamente. Ad esempio, il codice non può consentire l'accesso ai campi di un oggetto in modo da consentire l'overrun delle posizioni di memoria. Inoltre, la verifica controlla il codice per determinare se il CIL è stato generato correttamente, perché un CIL non corretto può causare una violazione delle regole di sicurezza del tipo. Il processo di verifica passa un set ben definito di codice sicuro per i tipi e passa solo codice sicuro per i tipi. Tuttavia, un codice sicuro per tipi potrebbe non superare la verifica a causa di alcune limitazioni del processo di verifica e alcuni linguaggi, per loro natura, non producono codice verificabile sicuro per tipi. Se il codice indipendente dai tipi è richiesto dai criteri di sicurezza, ma il codice non supera la verifica, viene generata un'eccezione quando viene eseguito il codice.

Eseguire il codice

Common Language Runtime fornisce l'infrastruttura che consente l'esecuzione gestita e i servizi che possono essere usati durante l'esecuzione. Prima di poter eseguire un metodo, è necessario compilarlo in codice specifico del processore. Ogni metodo per il quale è stato generato CIL viene compilato JIT quando viene chiamato per la prima volta e quindi eseguito. Alla successiva esecuzione del metodo, viene eseguito il codice nativo esistente compilato con JIT. Il processo di compilazione JIT e quindi l'esecuzione del codice viene ripetuto fino al completamento dell'esecuzione.

Durante l'esecuzione, il codice gestito riceve servizi come Garbage Collection, sicurezza, interoperabilità con codice non gestito, supporto del debug tra linguaggi e supporto avanzato per la distribuzione e il controllo delle versioni.

In Microsoft Windows Vista il caricatore del sistema operativo verifica la presenza di moduli gestiti esaminando un bit nell'intestazione COFF. Il bit impostato indica un modulo gestito. Se il caricatore rileva i moduli gestiti, carica mscoree.dlle _CorValidateImage_CorImageUnloading invia una notifica al caricatore quando le immagini del modulo gestito vengono caricate e scaricate. _CorValidateImage esegue le azioni seguenti:

  1. Assicura che il codice sia un codice gestito valido.
  2. Modifica il punto di ingresso nell'immagine in un punto di ingresso nel runtime.

In Windows a 64 bit modifica _CorValidateImage l'immagine in memoria trasformandola dal formato PE32 a PE32+.

Vedere anche