목차
만약 같은 프로그램을 동시에 실행할 경우 어떤 일이 발생할까요?
같은 프로그램을 여러 개 실행할 경우, 운영 체제와 메모리 관리의 관점에서 프로세스의 메모리 구조와 관련된 다양한 상황이 발생합니다.
이를 코드 영역, 프로세스 스택, 힙 영역으로 나누어 설명하겠습니다.
1. 코드 영역
•
공유 가능성 : 코드 영역에는 프로그램의 실행 가능한 기계어 명령이 저장됩니다.
•
대부분의 운영 체제는 메모리 매핑 기술을 활용하여 동일한 프로그램의 코드 영역을 여러 프로세스 간에 공유하도록 설계되어 있습니다.
◦
예를 들어, Program.exe를 여러 번 실행하면, 동일한 코드 영역은 메모리에 한 번만 로드되고, 이를 모든 프로세스가 참조합니다.
◦
이렇게 하면 메모리 사용량을 줄이고 프로그램 실행을 더 효율적으로 관리할 수 있습니다.
2. 프로세스 스택
•
프로세스마다 독립적 : 스택 영역은 함수 호출, 지역 변수, 매개변수 저장 등 각 프로세스의 실행 상태를 관리하기 위해 사용됩니다. 스택은 각 프로세스에 대해 독립적으로 생성되며, 한 프로세스의 스택이 다른 프로세스와 공유되지 않습니다.
◦
같은 프로그램을 여러 번 실행해도 각 프로세스는 자신만의 스택을 가지고 독립적인 실행 흐름을 유지합니다.
◦
예를 들어, 함수 호출이 겹치거나 스택 오버플로우가 발생하는 상황은 각 프로세스 내에서만 영향을 미칩니다.
3. 힙 영역
•
동적 메모리 할당 : 힙 영역은 동적으로 할당된 메모리를 저장하는 공간으로, 프로세스마다 독립적으로 생성됩니다.
◦
한 프로세스에서 힙 영역에 데이터를 할당하거나 해제하더라도 다른 프로세스의 힙 영역에는 전혀 영향을 미치지 않습니다.
◦
같은 프로그램을 여러 번 실행하면 각 프로세스는 독립적인 힙 공간에서 동작하며, 메모리 충돌이나 데이터 경합이 발생하지 않습니다.
그럼 C#의 Reflection.Emit를 사용해서 코드를 수정하면 어떻게 될까요?
Reflection.Emit을 사용해 런타임에 코드를 생성하고 실행하는 경우, 해당 코드는 메모리와 실행 흐름에서 독립적인 방식으로 동작합니다.
즉, 이러한 코드는 코드 영역이 아닌 힙 영역에서 실행되는 것입니다.
Reflection.Emit을 통한 코드 생성 및 실행
1.
IL 코드의 생성
•
Reflection.Emit은 동적으로 IL 코드를 생성하여 실행할 수 있도록 합니다.
•
생성된 코드는 런타임에 DynamicMethod, TypeBuilder, MethodBuilder 등을 통해 정의됩니다.
2.
메모리 영역에 로드
•
생성된 IL 코드는 JIT 컴파일러에 의해 컴파일되고, 메모리에 로드됩니다.
•
이때, 해당 코드는 프로세스의 메모리 공간에 동적으로 추가되며, 고유한 실행 컨텍스트를 가집니다.
•
동적 코드 전용 영역으로 관리됩니다.
이는 C#의 특별한 영역으로, 코드 영역이면서 동시에 힙 영역의 특성을 가집니다. 즉, 힙 영역에 존재하는 코드 영역이라고 할 수 있습니다.
3.
독립적 실행
•
동적으로 생성된 코드는 기존의 프로그램 코드와 독립적으로 실행될 수 있습니다.
•
이 독립성은 다음과 같은 요소로 인해 보장됩니다
◦
동적 메서드는 자신만의 실행 스택을 사용합니다.
◦
필요한 경우 런타임 환경에서 생성된 코드를 동적으로 메모리에서 해제하여 더 이상 접근할 수 없게 만듭니다.
그럼 Reflection.Emit로 변경한 코드를 dll파일로 제작해서 실행하면 해당 코드는 공유할 수 있는 것일까?
DLL로 변환된 코드를 저장하고 이후 해당 DLL을 실행할 경우, 코드를 여러 프로세스 간에 공유할 수 있습니다.
DLL 파일로 생성된 코드의 특징
1.
Reflection.Emit으로 생성된 코드 저장
•
Reflection.Emit을 사용하여 런타임에 생성한 코드가 AssemblyBuilder.Save() 메서드 등을 통해 디스크에 DLL 파일로 저장될 수 있습니다.
•
이 DLL 파일은 일반적인 .NET 어셈블리처럼 동작하며, 다른 응용 프로그램에서 참조하거나 로드할 수 있습니다.
2.
DLL 파일로 저장된 코드의 실행
•
저장된 DLL 파일은 정적 컴파일된 코드처럼 취급되며, 프로그램 실행 시 로드되어 코드 영역에 포함됩니다.
•
DLL을 로드한 각 프로세스에서 해당 DLL의 코드를 필요에 따라 메모리에 매핑합니다.
유니티 개발자의 경우 Reflection.Emit 기능을 웬만하면 사용할 수 없으므로 이 내용은 간단히 넘어가도 됩니다.
또한 모바일 개발자의 경우, Android나 iOS 환경에서는 일반적으로 같은 앱의 중복 실행을 제한하고 있어 동시에 같은 프로그램을 실행할 수 없습니다.