관리되는 실행 프로세스에는 이 항목의 뒷부분에서 자세히 설명하는 다음 단계가 포함됩니다.
- 컴파일러 선택 공용 언어 런타임에서 제공하는 이점을 얻으려면 런타임을 대상으로 하는 하나 이상의 언어 컴파일러를 사용해야 합니다.
- 코드를 중간 언어로 컴파일합니다. 컴파일은 소스 코드를 CIL(공용 중간 언어)로 변환하고 필요한 메타데이터를 생성합니다.
- CIL을 네이티브 코드로 컴파일합니다. 실행 시 JIT(Just-In-Time) 컴파일러는 CIL을 네이티브 코드로 변환합니다. 이 컴파일 중에 코드는 CIL 및 메타데이터를 검사하는 확인 프로세스를 통과하여 코드가 형식이 안전한지 확인할 수 있는지 확인해야 합니다.
- 코드를 실행합니다. 공용 언어 런타임은 실행이 수행될 수 있는 인프라와 실행 중에 사용할 수 있는 서비스를 제공합니다.
컴파일러 선택
CLR(공용 언어 런타임)에서 제공하는 이점을 얻으려면 런타임을 대상으로 하는 하나 이상의 언어 컴파일러(예: Visual Basic, C#, Visual C++, F#) 또는 Eiffel, Perl 또는 COBOL 컴파일러와 같은 여러 타사 컴파일러 중 하나를 사용해야 합니다.
다국어 실행 환경이므로 런타임은 다양한 데이터 형식 및 언어 기능을 지원합니다. 사용하는 언어 컴파일러에 따라 사용 가능한 런타임 기능이 결정되며 이러한 기능을 사용하여 코드를 디자인합니다. 런타임이 아닌 컴파일러는 코드에서 사용해야 하는 구문을 설정합니다. 다른 언어로 작성된 구성 요소에서 구성 요소를 완전히 사용할 수 있어야 하는 경우 구성 요소의 내보낸 형식은 CLS(공용 언어 사양)에 포함된 언어 기능만 노출해야 합니다. 특성을 사용하여 CLSCompliantAttribute 코드가 CLS 규격인지 확인할 수 있습니다. 자세한 내용은 언어 독립성 및 언어 독립적 구성 요소를 참조하세요.
CIL로 컴파일
관리 코드로 컴파일할 때 컴파일러는 소스 코드를 네이티브 코드로 효율적으로 변환할 수 있는 CPU 독립적 명령 집합인 CIL(공용 중간 언어)로 변환합니다. CIL에는 개체에 대한 메서드 로드, 저장, 초기화 및 호출에 대한 지침과 산술 및 논리 작업, 제어 흐름, 직접 메모리 액세스, 예외 처리 및 기타 작업에 대한 지침이 포함되어 있습니다. 코드를 실행하려면 먼저 일반적으로 JIT(Just-In-Time) 컴파일러에서 CIL을 CPU별 코드로 변환해야 합니다. 공용 언어 런타임은 지원하는 각 컴퓨터 아키텍처에 대해 하나 이상의 JIT 컴파일러를 제공하므로 지원되는 모든 아키텍처에서 동일한 CIL 집합을 JIT 컴파일하고 실행할 수 있습니다.
컴파일러가 CIL을 생성하면 메타데이터도 생성됩니다. 메타데이터는 각 형식의 정의, 각 형식 멤버의 서명, 코드가 참조하는 멤버 및 런타임이 실행 시 사용하는 기타 데이터를 포함하여 코드의 형식을 설명합니다. CIL 및 메타데이터는 이전에 실행 콘텐츠에 사용되었던 Microsoft의 PE(이식 가능한 실행 파일) 및 COFF(공용 개체 파일 형식) 형식을 기반으로 하고 이를 확장한 PE 파일에 포함됩니다. CIL 또는 네이티브 코드와 메타데이터를 수용하는 이 파일 형식을 사용하면 운영 체제에서 공용 언어 런타임 이미지를 인식할 수 있습니다. 파일에 메타데이터가 CIL과 함께 있으면 코드가 자신을 설명할 수 있으므로 형식 라이브러리 또는 IDL(인터페이스 정의 언어)이 필요하지 않습니다. 런타임은 실행 중에 필요에 따라 파일에서 메타데이터를 찾아 추출합니다.
CIL을 네이티브 코드로 컴파일
CIL(공용 중간 언어)을 실행하려면 먼저 공용 언어 런타임에 대해 대상 컴퓨터 아키텍처의 네이티브 코드로 컴파일해야 합니다. .NET은 이 변환을 수행하는 두 가지 방법을 제공합니다.
- .NET JIT(Just-In-Time) 컴파일러입니다.
- Ngen.exe(네이티브 이미지 생성기).
JIT 컴파일러에 의한 컴파일
JIT 컴파일은 어셈블리의 콘텐츠가 로드되고 실행될 때 애플리케이션 런타임에 요청 시 CIL을 네이티브 코드로 변환합니다. 공용 언어 런타임은 지원되는 각 CPU 아키텍처에 대해 JIT 컴파일러를 제공하므로 개발자는 JIT 컴파일 및 다른 컴퓨터 아키텍처를 사용하는 다른 컴퓨터에서 실행할 수 있는 CIL 어셈블리 집합을 빌드할 수 있습니다. 그러나 관리 코드가 플랫폼별 네이티브 API 또는 플랫폼별 클래스 라이브러리를 호출하는 경우 해당 운영 체제에서만 실행됩니다.
JIT 컴파일은 실행 중에 일부 코드가 호출되지 않을 가능성을 고려합니다. PE 파일의 모든 CIL을 네이티브 코드로 변환하는 데 시간과 메모리를 사용하는 대신 실행 중에 필요에 따라 CIL을 변환하고 결과 네이티브 코드를 메모리에 저장하여 해당 프로세스의 컨텍스트에서 후속 호출에 액세스할 수 있도록 합니다. 로더는 형식이 로드되고 초기화될 때 형식의 각 메서드에 스텁을 만들고 연결합니다. 메서드가 처음으로 호출되면 스텁은 JIT 컴파일러에 제어를 전달합니다. 이 컴파일러는 해당 메서드의 CIL을 네이티브 코드로 변환하고 생성된 네이티브 코드를 직접 가리키도록 스텁을 수정합니다. 따라서 JIT 컴파일 메서드에 대한 후속 호출은 네이티브 코드로 직접 이동합니다.
NGen.exe 사용하여 설치 시간 코드 생성
JIT 컴파일러는 해당 어셈블리에 정의된 개별 메서드가 호출되면 어셈블리의 CIL을 네이티브 코드로 변환하므로 런타임에 성능에 부정적인 영향을 줍니다. 대부분의 경우 성능 저하가 허용됩니다. 더 중요한 것은 JIT 컴파일러에서 생성된 코드가 컴파일을 트리거한 프로세스에 바인딩된다는 것입니다. 여러 프로세스에서 공유할 수 없습니다. 생성된 코드를 애플리케이션의 여러 호출 또는 어셈블리 집합을 공유하는 여러 프로세스에서 공유할 수 있도록 공용 언어 런타임은 미리 컴파일 모드를 지원합니다. 이 미리 컴파일 모드는 Ngen.exe(네이티브 이미지 생성기) 를 사용하여 JIT 컴파일러와 마찬가지로 CIL 어셈블리를 네이티브 코드로 변환합니다. 그러나 Ngen.exe 작업은 다음과 같은 세 가지 방법으로 JIT 컴파일러의 작업과 다릅니다.
- 애플리케이션이 실행되는 동안 대신 애플리케이션을 실행하기 전에 CIL에서 네이티브 코드로 변환을 수행합니다.
- 한 번에 하나의 메서드 대신 전체 어셈블리를 한 번에 컴파일합니다.
- 생성된 코드를 네이티브 이미지 캐시에 디스크의 파일로 유지합니다.
코드 확인
네이티브 코드에 대한 컴파일의 일환으로 CIL 코드는 관리자가 코드를 통해 확인을 우회할 수 있는 보안 정책을 설정하지 않은 한 확인 프로세스를 통과해야 합니다. 확인은 CIL 및 메타데이터를 검사하여 코드가 형식이 안전한지 여부를 확인합니다. 즉, 액세스 권한이 부여된 메모리 위치에만 액세스합니다. 형식 안전은 개체를 서로 격리하는 데 도움이 되며 실수로 인한 손상이나 악의적인 손상으로부터 개체를 보호하는 데 도움이 됩니다. 또한 코드에 대한 보안 제한을 안정적으로 적용할 수 있음을 보장합니다.
런타임은 다음 명령문이 안전한 형식의 코드에 대해 true라는 사실에 의존합니다.
- 형식에 대한 참조는 참조되는 형식과 엄격하게 호환됩니다.
- 개체에서 적절하게 정의된 작업만 호출됩니다.
- 정체성은 그들이 주장하는 대로입니다.
확인 프로세스 중에 코드가 메모리 위치에 액세스하고 올바르게 정의된 형식을 통해서만 메서드를 호출할 수 있는지 확인하기 위해 CIL 코드가 검사됩니다. 예를 들어 코드는 메모리 위치를 오버런할 수 있는 방식으로 개체의 필드에 액세스하도록 허용할 수 없습니다. 또한 잘못된 CIL이 형식 안전 규칙을 위반할 수 있으므로 확인은 코드를 검사하여 CIL이 올바르게 생성되었는지 여부를 확인합니다. 확인 프로세스는 잘 정의된 형식 안전 코드 집합을 전달하고 형식이 안전한 코드만 전달합니다. 그러나 일부 형식 안전 코드는 확인 프로세스의 일부 제한 사항으로 인해 확인을 통과하지 못할 수 있으며 일부 언어는 의도적으로 검증 가능한 형식 안전 코드를 생성하지 않습니다. 보안 정책에서 타입-안전한 코드를 요구하지만 코드가 확인을 통과하지 못하면 코드가 실행될 때 예외가 발생합니다.
코드 실행
공용 언어 런타임은 관리형 실행을 수행할 수 있는 인프라와 실행 중에 사용할 수 있는 서비스를 제공합니다. 메서드를 실행하려면 먼저 프로세서별 코드로 컴파일해야 합니다. CIL이 생성된 각 메서드는 처음 호출된 후 실행될 때 JIT 컴파일됩니다. 다음에 메서드를 실행할 때 기존 JIT 컴파일 네이티브 코드가 실행됩니다. 실행이 완료될 때까지 JIT 컴파일 및 코드 실행 프로세스가 반복됩니다.
실행하는 동안 관리 코드는 가비지 수집, 보안, 비관리 코드와의 상호 운용성, 언어 간 디버깅 지원, 향상된 배포 및 버전 관리 지원과 같은 서비스를 받습니다.
Microsoft Windows Vista에서 운영 체제 로더는 COFF 헤더에서 비트를 검사하여 관리되는 모듈을 확인합니다. 설정되는 비트는 관리되는 모듈을 표시합니다. 로더가 관리되는 모듈을 감지하면 mscoree.dll가 로드되고, 관리되는 모듈 이미지가 로드되거나 언로드될 때 _CorValidateImage
와 _CorImageUnloading
가 로더에 알립니다.
_CorValidateImage
다음 작업을 수행합니다.
- 코드가 유효한 관리 코드인지 확인합니다.
- 이미지의 진입점을 런타임의 진입점으로 변경합니다.
64비트 Windows _CorValidateImage
에서는 PE32에서 PE32+ 형식으로 변환하여 메모리에 있는 이미지를 수정합니다.
참고하십시오
.NET