본문 바로가기
언어(C, C++, C#)

[C#] .NET, 컴파일 과정, CLS, CTS, CIL, CLI, CLR, JIT 컴파일

by 적용1 2024. 11. 6.
728x90

C# 스크립트는 .NET 실행 환경에서 동작하게 되는데, 이 때 .NET이란 무엇이고 C# 스크립트가 실행되기까지 어떤 과정을 거치는지 정리하려 한다.

.NET Framework

.NET Framework(이후 닷넷 또는 .NET)는 마이크로소프트에서 만든 소프트웨어 개발 및 실행 환경이다. 네트워크 작업과 인터페이스 등 많은 작업을 캡슐화하고, Windows 응용 프로그램을 개발하고 실행하는 데 필요한 라이브러리와 런타임 환경을 제공한다. 닷은 크게 두 가지 주요 구성 요소로 나눌 수 있다 :

  • CLR(Common Languange Runtime) : 프로그램 실행 시 메모리 관리, 예외 처리, 가비지 걸렉션 등을 담당하는 런타임 환경이다.(일종의 가상 머신이라고 생각해도 된다.)
  • BCL(Base Class Library) : 데이터 구조, 파일 입출력, 네트워크, 그래픽 등 기본 기능을 제공하는 클래스 라이브러리이다.

C#의 컴파일 과정

1. C# 코드 작성 및 컴파일

  • C# 소스 코드가 컴파일러에 의해 IL(Intermediate Language) 또는 MSL(Microsoft Intermediate Language)이라는 중간 언어로 변환된다. 이때 컴파일된 IL 파일은 .exe 또는 .dll 확장자(실행 파일 형태)를 가지게 된다.
  • IL 코드는 닷넷의 중간 언어로, 플랫폼 독립적이고 운영 체제와 하드웨어에 구애받지 않도록 설계된 코드이다.

2. 어셈블리 생성

  • IL 코드로 변환되면서 코드 실행에 필요한 정보를 담는 어셈블리(Assembly)가 생성된다.
  • 이 때 어셈블리에 담기는 정보로는 :
    타입 정보 : 클래스, 인터페이스, 구조체 등 코드에 사용된 모든 타입에 대한 정의와 해당 타입의 메서드, 필드, 속성 등
    참조 정보 : 코드에서 참조하는 다른 어셈블리와의 종속성 정보(ex : System.Collections와 같은 외부 라이브러리에 대한 참조)
    보안 정보 : 해당 어셈블리에 대한 접근 권한이나 보안 관련 정보
    버전 정보 : 어셈블리의 버전, 제작사 정보 등

3. CLR에 의해 JIT 컴파일

  • 프로그램이 실행되면 CLR이 IL 코드를 운영 체제의 네이티브 코드(기계어)로 변환하는 JIT(Just-In-Time) 컴파일을 수행한다.
  • JIT 컴파일러는 처음 호출되는 메서드를 실행할 때 IL 코드를 기계어로 변환하여 운영 체제가 이해할 수 있는 코드로 만든다.
  • 한 번 JIT 컴파일된 코드는 캐시되어, 이후 같은 코드가 호출될 때에는 JIT 컴파일 없이 빠르게 실행될 수 있다.

4. 프로그램 실행 및 메모리 관리

  • 네이티브 코드로 변환된 프로그램은 CLR의 관리하에 실행된다. CLR은 가비지 컬렉션을 통해 메모리를 관리하고, 예외 처리를 수행하고, 프로그램이 예기치 않은 종료 없이 안정적으로 실행되도록 한다.

요약

1. C# 코드 작성 → 컴파일러가 IL 코드로 컴파일

2. IL코드로 컴파일되는 과정에 어셈블리 생성

3. CLR에서 IL 코드를 네이티브 코드로 JIT 컴파일

4. 네이티브 코드로 실행

CLS(공통 언어 사양, Common Language Specification)

  • CLS는 닷넷 환경에서 여러 프로그래밍 언어들이 상호 운용성을 유지하며 함께 사용할 수 있도록 하는 규약이다. 쉽게 말해, 닷넷 호환 언어가 만족해야 하는 최소 사양이다.
  • 닷넷은 여러 언어를 지원하는 플랫폼이기 때문에 서로 다른 언어들이 원활하게 협력하기 위해 공통 규칙이 필요한데, CLS가 이 규칙을 제공하는 역할을 한다.

CTS(공통 형식 시스템, Common Type System)

  • CTS는 닷넷에서 여러 프로그래밍 언어가 함께 사용할 수 있도록 정의된 데이터 형식 시스템이다. 쉽게 말하면 닷넷 호환 언어가 지켜야 할 타입의 표준 규격을 정의한 것이다.
  • 예시 : C#의 int와 VB.NET의 Integer가 같은 정수형이지만 언어마다 이름이 다르다.

    CTS는 언어 간 데이터 형식을 일관되게 관리할 수 있게 해주며, 모든 언어가 공통적으로 사용할 수 있는 데이터 타입을 정의한다.
  • CTS가 정의하는 형식 또는 속성
    클래스
    구조체
    열거형
    인터페이스
    대리자
    
    액세스 한정자
    상속 및 오버로드 작동 방식
  • CLS는 CTS에 속한다. 즉, CTS가 더 넓은 범위의 형식이므로 CTS의 모든 형식을 만족할 필요는 없지만, 최소한의 언어 사양을 정의한 좁은 범위의 CLS는 반드시 만족해야 한다.

CIL(공통 중간 언어, Common Intermediate Language)

  • CIL은 닷넷의 모든 언어가 공통적으로 사용할 수 있도록 설계된 중간 언어이다. 줄여서 IL이라고도 한다.
  • CIL은 각 언어의 차이를 극복하고 플랫폼 독립적인 코드를 제공하는 데 중요한 역할을 한다.
  • 닷넷 호환 언어를 컴파일하면 IL로 번역되어 .exe, .dll 확장자의 실행 파일을 생성한다. 이렇게 IL로 작성된 실행 파일을 닷넷 어셈블리(.NET Assembly)라고 한다.
    IL은 바이트코드(Bytecode)라 하는 가상 머신이 이해할 수 있는 중간 코드이며, 가상 머신이 이 바이트코드를 각 하드웨어 아키텍처에 맞는 기계어로 번역하게 된다.

CLI(공통 언어 인프라, Common Language Infrastructure)

  • CLI는 닷넷에서 프로그램이 언어와 관계없이 실행될 수 있도록 표준을 정의한 프레임워크이다. 쉽게 말해 닷넷 프레임워크 구조의 표준 사양이라고 볼 수 있다.
  • CTS, CLS, IL 코드 등 다양한 요소를 포함하여 언어 간 상호 운용성을 지원하는 플랫폼을 제공한다. 닷넷 프레임워크를 CLI의 구현체라고 볼 수 있다.

CLR(공통 언어 런타임, Common Language Runtime)

  • 닷넷의 핵심 구성 요소이자 실행 환경이다. 닷넷 호환 언어를 컴파일하여 생성된 싱행 파일인 닷넷 어셈블리는 CLR 위에서 동작하고, CLR에 포함된 JIT(Just-In-Time) 컴파일러 등으로 IL을 기계어로 번역해 실제 OS에서 동작할 수 있게 된다.
  • CLR이 제공하는 기능
    1. 메모리 관리 : 가비지 컬렉션을 통해 메모리를 자동으로 관리하여 개발자가 메모리 관리를 수동으로 할 필요를 줄여준다.
    2. 예외 처리 : 예외 처리를 일관되게 수행하여 안정적인 프로그램 실행을 지원한다.
    3. 보안 관리 : 코드 액세스 보안과 같은 기능을 통해 프로그램의 보안 수준을 조절한다.
    4. JIT 컴파일 : IL 코드를 운영 체제에서 실행 가능한 네이티브 코드(기계어)로 변환한다.
  • CLR이 모든 런타임 작업을 담당하기 때문에 개발자를 코드 작성과 논리적 구조에 집중할 수 있다.

JIT(Just-In-Time) 컴파일

  • JIT 컴파일은 프로그램이 실행될 때 필요한 코드만 실시간으로 컴파일하는 방식이다. C# 코드가 처음 컴파일됐을 때 생성되는 IL 코드는 플랫폼 독립적이기 때문에 직접적으로 실행할 수 없다. 따라서 JIT 컴파일을 통해 운영체제나 하드웨어에 맞게 기계어로 번역되어 실행되어야 한다.

    플랫폼 독립적 : 특정 플랫폼에 종속되지 않고, 어떤 운영체제나 하드웨어에서든 동일하게 실행될 수 있는 형태
  • JIT 컴파일러의 동작 방식
    1. 실행 시점 컴파일 : 프로그램을 처음 실행할 때 메서드 단위로 IL 코드를 네이티브 코드로 변환한다. 모든 코드를 한 번에 컴파일하는 것이 아니라 필요한 코드만 실행 시점에 컴파일한다.
    2. 최적화 : 프로그램이 실행되는 동안 컴파일을 수행하므로, CPU 아키텍처에 맞게 최적화할 수 있다.
    3. 캐시 : 한 번 JIT 컴파일된 코드는 캐시되어, 동일한 메서드가 호출될 때 다시 컴파일하지 않고 캐시된 네이티브 코드를 재사용한다.
  • JIT 컴파일은 전체 코드를 미리 컴파일하는 AOT(Ahead-Of-Time) 컴파일과 다르게 프로그램 실행 중 필요한 코드만 선택적으로 컴파일함으로써 성능과 메모리 효율을 높인다.(필자는 여기서 paging 기법에서의 lazy load와 비슷한 느낌을 받았다.)
728x90