Silverlight를 설치하려면 여기를 클릭합니다.*
Korea 대한민국변경|Microsoft 전체 사이트
MSDN
|개발자 센터|라이브러리|MSDN Online|다운로드|코드 센터|Subscriptions|MSDN 행사
MSDN Home   MSDN Home
MSDN 홈 > .NET Framework 홈 > 기술 문서(Articles) > 32 비트 관리형 코드의 64 비트 마이그레이션

32 비트 관리형 코드의 64 비트 마이그레이션

  


Microsoft Corporation

2005년 5월

적용 대상 :
   Microsoft .NET
   Microsoft .NET Framework 2.0

개요: 32 비트 관리형 응용 프로그램을 64 비트로 마이그레이션하기 위해 필요한 작업, 마이그레이션에 영향을 미치는 요소, 마이그레이션에 도움이 되는 툴 등에 대해 설명합니다.

목차

시작
32 비트 환경의 관리형 코드
64 비트 환경에의 CLR 의 입력
마이그레이션과 플랫폼의 호출
마이그레이션과 COM 의 상호 운용성
마이그레이션과 안전하지 않은 코드
마이그레이션과 마샬링
마이그레이션과 직렬화(Serialization)
마무리

시작

주요내용 :

  • 32 비트 관리형 코드의 64 비트 마이그레이션에 필요한 작업
  • 마이그레이션에 영향을 미치는 문제점
  • 마이그레이션에 유용한 툴

이 백서는 64 비트로의 마이그레이션 순서를 설명하는 것이 아니라, 64 비트로 마이그레이션 하는 과정에서 일어날 수 있는 여러 가지 문제를 이해하는 것이 목적입니다. 따라서64 비트로 마이그레이션하는 코드를 정확히 설명하는 수동적인 순서보다는 검토할 필요가 있는 여러 가지 문제사항을 확인할 수 있습니다.

관리형 어셈블리가 100% 형식이 안전한(Type-Safe) 코드가 아닌 경우는 응용 프로그램과 그 의존성을 재검토하고, 64 비트로 마이그레이션하기 위한 문제점을 확인해야 합니다. 앞으로 소개하는 문제들의 상당수는 프로그래밍 변경에 의해 발생 가능성이 있습니다. 만약 코드를 32 비트 환경과 64 비트 환경 모두에서 실행해야 하는 경우는 양쪽 환경에서 제대로 동작하기 위해서는 코드 업데이트 시간확보가 필요한 경우가 많습니다.

Microsoft .NET은 정보와 사람과 시스템과 디바이스를 연결하기 위한 소프트웨어 기술의 집대성입니다. 2002년 버전 1.0 이 나온 이후, 직접 개발 및 독립 소프트웨어 벤더(ISV)와의 협업으로 .NET 베이스의 솔루션 배치에 성공하였습니다. 32 비트 환경의 한계를 넘어선 여러 종류의 .NET 응용 프로그램이 있습니다. 해결과제는 보다 현실적인 연결 메모리의 필요성, 부동 소수점 성능 향상의 필요성 등이 있습니다. x64 및 Itanium은 x86에서 가능한 부동 소수점 처리 성능에 비해, 보다 좋은 성능을 제공합니다. 하지만 x64 또는 Itanium 에서 x86가 얻을 수 있는 결과와 달라질 가능성도 있습니다. 64 비트 플랫폼은 이러한 문제를 야기할 수 있습니다. 마이크로소프트는 .NET Framework 버전 2.0 출시와 함께 x64 및 Itanium 의 64 비트 플랫폼에서 실행하는 관리형 코드 지원을 포함했습니다.

관리형 코드는 .NET 의 표준 언어 런타임 (Common Language Runtime, CLR)은 다음과 같은 코어 서비스 군을 제공하는 것이 가능한 충분한 정보를 제공하는 단순 " 코드" 입니다.

  • 메타데이타에 의한 코드 및 데이터 설명
  • 스택 순회
  • 보안
  • 가비지 콜렉터
  • JIT 컴파일

마이그레이션 문제를 알아보기 위해 관리형 코드 외에도 몇 가지 중요한 용어를 알아둬야 합니다.

관리형 데이터 - 가비지 콜렉터에 의해서 수집된, 관리 힙에 할당할 수 있는 데이터

어셈블리 - CLR에 의한 응용 프로그램 컨텐츠의 완전 이해가 가능하고, 버전화 및 응용 프로그램이 정의한 의존성 규칙의 실행을 가능하게 하는 기능 단위

형식이 안전한(Type-Safe) 코드 - 관리형 데이터만을 사용하여, 데이터의 형태를 증명할 수 없는, 지원하지 않는 형태의 데이터 (즉, 식별되지 않는 공용체 또는 구조/ 인터페이스 포인터)의 변환, 강제 처리를 실시하지 않는 코드 /clr:safe 에서 컴파일 된 C#, Visual Basic.NET 및 Visual C++ 코드는 형식이 안전한(Type-Safe) 코드를 생성합니다.

안전하지 않은(unsafe) 코드 - 포인터의 선언과 처리, 포인터와 정수형의 변환, 변수의 주소 취득의 낮은 레벨의 처리 실행이 허가된 코드. 이러한 처리는 메모리에 연결된 된 디바이스에 액세스 하는지, Time-Crisis 알고리즘을 구현하는 방법으로, 기본 처리 시스템과의 교환을 허가합니다. 그러나 네이티브 코드는 안전하지 않습니다.

32 비트 환경의 관리형 코드

관리형 코드의 64 비트 환경에의 마이그레이션에 수반하는 complex system를 이해하기 위해, 관리형 코드가 32 비트 환경에서 어떻게 실행되는지를 검증해봅니다.

관리형/비관리형 응용 프로그램의 실행 지시를 받으면, Windows 로더가 호출해 응용 프로그램 로드 방법의 결정으로부터 실행까지를 담당합니다. 이 프로세스에는 CLR이 필요한지 결정하기 위해, 실행 파일의 휴대용 실행 헤더 (PE 헤더) 안을 참조하는 작업이 포함됩니다. 예상할 수 있듯이 PE 헤더 안에, 관리형 코드인지를 나타내는 플래그가 있습니다. 관리형 코드의 경우, Windows 로더는 CLR 을 실행해, CLR관리형 응용 프로그램의 로드와 실행을 담당합니다. (여기서는 프로세스 전체를 단순화하여 설명하고 있습니다. 실제로는 CLR 버전의 결정이나, AppDomain의 'sandbox' 의 설정 등 아주 많은 스텝이 있습니다.)

상호 운용성

관리형 응용 프로그램은 작동 후, CLR 의 상호 운용 능력을 이용하여 (적절한 보안 퍼미션을 얻은 것으로 상정) 네이티브 API (Win32 API를 포함) 및 COM 객체와 교환을 합니다. 32 비트 환경내에서 완전하게 동작하는 경우, 네이티브 플랫폼 API 의 호출이나 COM 요구의 작성, 구조의 마샬링등이 있는 경우에 개발자가 데이터 타입의 크기나 데이터의 배열을 고려할 필요는 없습니다.

64 비트에의 마이그레이션을 검토할 때, 마이그레이션하는 응용 프로그램 의존성은 꼭 확인해야 합니다.

64 비트 환경 전용 CLR 입력

32 비트 환경에서 일반적으로 동작하는 관리형 코드를 64 비트 환경에서도 같이 동작하도록, .NET 팀은 Itanium 및 x64 의 64 비트 시스템 전용으로 Common Language Runtime (CLR)을 개발했습니다. CLR은32 비트 환경에서 실행했던 것처럼, .NET을 지원하는 모든 언어로 모든 코드와의 상호 운용을 가능하도록 Common Language Infrastructure (CLI) 규칙 및Common Language Type System 에 충실히 따라야 합니다. 또한 아래의 항목도 64 비트 환경 전용으로 마이그레이션 또는 개발할 필요가 있습니다.

  • 기반 클래스 라이브러리 (System.*)
  • JIT 컴파일러
  • 디버그의 지원
  • .NET Framework SDK

64 비트 관리형 코드의 지원

.NET Framework 2.0은 Itanium 및 x64 등64 비트 프로세서 실행을 지원합니다.

  • Windows Server 2003 SP1
  • 향후 출시 예정의 Windows 64 비트 클라이언트

(Windows 2000 에서는 .NET Framework 2.0을 인스톨 할 수 없습니다. .NET Framework 1.0 및 1.1 의 생성 파일은 64 비트 OS 에서는WOW64 기반으로 실행할 수 있습니다.)

64 비트 플랫폼에 .NET Framework 2.0을 인스톨 할 때는 관리형 코드를 64 비트 모드로 실행하기 위해 필요한 인프라를 인스톨 할 뿐만 아니라, Windows-on-Windows 하부조직, 혹은 WoW64 (32 비트 모드) 에서 관리형 코드를 실행하기 위해서 필요한 인프라도 인스톨 할 필요가 있습니다.

64 비트에의 간단한 마이그레이션

100% 형식이 안전한(Type-Safe) 코드의 .NET 응용 프로그램에 대해 살펴보겠습니다. 이 시나리오에서는 32 비트 머신으로 실행한 .NET 의 실행 파일을 64 비트 시스템으로 마이그레이션하여, 제대로 실행된다는 것을 보여줍니다. 어셈블리가 100 % 형식이 안전한(Type-Safe) 것은 네이티브 코드 및 COM 객체 의존이 전무하다는 것으로, 안전하지 않은 코드가 존재하지 않는다고 판단하여, 응용 프로그램은 CLR 관리하에서 완전하게 실행할 수 있습니다. CLR 은 JIT 컴파일의 결과로서 생성된 바이너리 코드는 32 비트와 64 비트에서는 다릅니다만, 이것을 실행하는 코드는 의미적으로는 같다는 것을 보증합니다. (Windows 2000 에는 .NET Framework 버전 2.0을 인스톨 할 수 없습니다. .NET Framework 버전 1.0 및 1.1을 사용하여 생성된 파일은 64 비트 OS, WOW64 의 기본으로 실행 가능합니다.)

실제로 위의 시나리오는 관리형 응용 프로그램을 로드 할 경우는 조금 복잡합니다. 앞에서 설명한 것처럼, Window의 로더는 응용 프로그램의 로드 방법과 실행 방법을 결정하는 임무를 담당합니다. 다만 32 비트 환경과는 달리 64 비트의 Windows 플랫폼에서 실행하는 경우, 응용 프로그램을 실행할 수 있는 환경에는 네이티브인 64 비트 모드 또는 WoW64 모드 두 개의 환경이 있습니다.

Windows 로더는 PE 헤더로부터 검출한 내용에 근거해 결정해야 합니다. 관리형 코드 안에는 이 프로세스로 도움이 되는 설정 가능한 플래그가 있습니다. (PE 의 설정에 대해서는corflags.exe를 참조해 주세요.) PE 를 확인할 수 있는 의사결정의 프로세스로 도움이 되는 정보를 정리하였습니다.

  • 64-bit 경우: 개발자가 64 비트 처리를 목적으로 해 작성한 어셈블리인 것을 나타냅니다.
  • 32-bit : 개발자가 32 비트 처리를 목적으로 해 작성한 어셈블리인 것을 나타냅니다. 이 경우, 이 어셈블리는 WoW64 환경에서 동작합니다.
  • 어그노스틱 : 개발자가 어셈블리를 Visual Studio 2005 에서 작성하여, 어셈블리는 64 비트 모드 및 32 비트 모드로 실행 가능한 것을 나타냅니다. 이 경우 64 비트 Windows 로더는 어셈블리를 64 비트로 실행합니다.
  • 레거시 : 어셈블리를 " Whidbey " 이전의 환경에서 작성한 것을 나타내 보입니다.이 경우, 어셈블리는 WoW64 환경에서 실행됩니다.
주의   어셈블리가 특정의 아키텍처 전용으로 작성된 경우, PE는 Windows 로더에 통지하는 정보가 포함되어 있습니다. 부가적인 정보에 의해서, 어셈블리가 특정의 아키텍처 전용으로 작성되고 있는 것을 확인할 수 있기 위해, 로드 방법을 틀릴 것은 없습니다.

C#, Visual Basic .NET, 및 C++ Whidbey 의 컴파일러는 PE 헤더에 적절한 플래그를 설정합니다. 예를 들어, C# 및 THIRD 의 경우 /platform:{anycpu, x86, Itanium, x64}의 컴파일러의 옵션이 준비되어 있습니다.

주의   컴파일 후의 어셈블리의 PE 헤더중의 플래그를 변경하는 것은 기술적으로 가능하지만, 이 방법은 추천 하지 않습니다.

어떠한 방법으로 이러한 플래그가 관리형 어셈블리로 설정되는지 알고 싶다면, .NET Framework SDK에서 제공하는 ILDASM을 실행합니다. 이하는"레거시" 응용 프로그램의 예입니다.

주의할 점은 개발자가 Win64로 표기했을 경우, 응용 프로그램의 모든 의존성은 64 비트 모드로 실행됩니다. 64 비트로의 처리에는 32 비트 컴포넌트를 사용할 수 없습니다. (32 비트 처리에는 64 비트 컴포넌트를 로드 할 수 없습니다.) 또, 시스템이 64 비트 처리에 어셈블리를 로드하는 기능은 자동적으로 확실한 실행을 보증하는 것이 아닙니다.

그런데, 100% 형식이 안전한(Type-Safe) 코드인 관리형 코드로 구성된 응용 프로그램은 64 비트 플랫폼에 복사할 수 있는 것 (혹은 xcopy 를 실행합니다), JIT 처리 후 64 비트 모드의 .NET 을 확실히 실행할 수 있는 것을 알았습니다.

그러나 이 글의 주제인 마이그레이션에 관한 문제를 깊이 살펴보기 위해, 다른 상황을 생각해봅시다.

100% 형식이 안전한(Type-Safe) 코드는 아니지만, 64 비트 환경에서도 .NET 에 의해 확실히 실행할 수 있는 응용 프로그램에 대해서 생각해 봅시다. 여기서 중요한 것은 다음 섹션에서 설명하는 잠재적인 문제를 염두에 두고, 마이그레이션하는 응용 프로그램을 살펴보고 64 비트 환경에서 확실히 동작할지를 결정하는 것입니다.

마이그레이션과 플랫폼의 호출

.NET 의 플랫폼 호출 능력 (또는 p/invoke)은 비관리형 코드 및 네이티브 코드의 호출을 실시합니다. 전형적인 시나리오에서는 네이티브 코드는 동적 링크 라이브러리(DLL), 시스템의 일부 (Windows API 등), 응용 프로그램의 일부, 서드 파티의 라이브러리의 일부입니다.

비관리형 코드의 사용은 64 비트에의 마이그레이션에 문제가 없다는 것을 명확하게 나타내는 것은 아닙니다. 오히려, 좀 더 조사가 필요한지 확인하기 위한 인디케이터(indicator)로서 인식해야 합니다.

Windows 의 데이터 종류

모든 응용 프로그램 및 오퍼레이팅 시스템에는 추상 데이터 모델이 있습니다. 많은 응용 프로그램은 이 데이터 모델을 명시적으로는 나타내지는 않지만, 응용 프로그램의 코드가 기술된 방법을 명시합니다.(ILP32 모델로서 알려짐) 32 비트의 프로그램 모델에서는 정수, 길이 및 포인터의 데이터 타입은 32 bit 길이입니다. 대부분의 개발자는 이 사실을 모른 체, 이 모델을 사용했습니다.

64 비트 Microsoft Windows는 이 데이터 타입의 크기에서는 패리티 인수는 무효입니다. 모든 데이터 타입을 64 bit 길이로 하는 것은 공간 낭비입니다. 왜냐하면, 대부분의 응용 프로그램은 증대 된 크기가 필요 없습니다. 그러나, 응용 프로그램은 64 비트 데이터에의 포인터를 필요로 하고, 특정의 경우에 대해서는 64 비트 데이터 타입을 가질 수 있는 능력이 필요합니다. 이러한 조건이Windows 팀을LLP64 (또는 P64)이라는 추상 데이터 모델의 선택하였습니다. LLP64 데이터 모델에서는 포인터만 64비트로 확장되었습니다. 다른 기본 데이터 타입 (int 형태, long 형) 은32 bit 길이인 채입니다.

64 비트 플랫폼 전용의 .NET CLR같은 LLP64 추상 데이터 모델을 사용합니다. 잘 알려지지는 않았지만 .NET 에는 정수 데이터 타입이 있습니다." 포인터" 정보를 보관 유지한다고 하는 특명을 받고 있는 IntPtr 입니다. 크기는 실행하는 플랫폼에 의존합니다 (예를 들어32 비트 또는 64 비트). 코드를 살펴 봅시다.

[C#]
public void SizeOfIntPtr() {
Console.WriteLine( "SizeOf IntPtr is: {0}", IntPtr.Size );
}

32 비트 플랫폼에서 실행하고 있는 경우는 콘솔에 다음과 같이 출력됩니다.

SizeOf IntPtr is: 4

64 비트 플랫폼의 경우는 콘솔에 다음과 같이 출력됩니다.

SizeOf IntPtr is: 8
주의   실행시에 64 비트 환경에서 실행하고 있는지 확인할 수 있습니다. IntPtr.Size 를 사용하는 것이 유일한 확인 방법입니다.

마이그레이션을 위한 검토 사항

p/invoke를 사용하는 관리형 응용 프로그램을 마이그레이션 할 때는 다음과 같은 점을 고려해야 합니다.

  • 64 비트 DLL 의 유효성
  • 데이터 타입의 사용 방법

유효성

가장 먼저 확인해야 할 것은 응용 프로그램이 의존하고 있는 비관리형 코드가 64 비트로 이용할 수 있는지 여부입니다.

이 코드가 사내에서 개발된 것이면, 마이그레이션 성공률이 높습니다. 물론 비관리형 코드를 64 비트에 이식하기 위해서, 자원을 할당해야 하고, 테스트, 품질 보증, 그 외를 위한 적절한 자원도 필요하게 됩니다. (이 백서는 개발 프로세스에 관해서 추천 할 수 있는 지시 등은 제공하지 않습니다. 코드를 이식하기 위해서 할당이 필요하다고 생각되는 자원을 보여줍니다.)

이 코드가 써드 파티제의 경우는 이 써드파티가 이미 64 비트로 이용할 수 있는 코드를 준비하고 있는지 조사하여, 이 코드를 판매할 의사가 있는지 확인할 필요가 있습니다.

써드파티가 이 코드의 지원을 중지하거나, 이 작업을 원하지 않은 경우는 리스트가 큰 문제입니다. 이러한 경우는 다양한 라이브러리에서 같은 기능을 완수하는 것이 있는지 클라이언트에 이식을 시키는지 조사할 필요가 있습니다.

다행인 것은 64 비트 의존 코드의 인터페이스의 서명이 변경되어 있을 가능성이 있습니다. 이것은 추가 개발 작업이나, 32 비트판과 64 비트의 응용 프로그램의 차이를 해결하기 위한 작업을 한 가능성을 보여줍니다.

데이터 타입

p/invoke 을 사용하여 .NET 에서 개발된 코드는 관리형 코드가 타겟으로 하고 있는 메소드의 프로토 타입을 선언합니다.

[C++]
typedef void * HANDLE
HANDLE GetData();

다음은 프로토 타입 선언한 메소드의 예를 나타냅니다.

[C#]

[DllImport( "sampleDLL", CallingConvention=CallingConvention.Cdecl )]
      public static extern int DoWork( int x, int y );

[DllImport( "sampleDLL", CallingConvention=CallingConvention.Cdecl )]
      public unsafe static extern int GetData();

이러한 예를 64 비트에의 마이그레이션이라고 하는 관점으로부터 검증합시다.

첫번째 예는 두개의 32 비트 정수로 메소드 DoWork를 호출합니다. 32 비트 정수가 반환되는 것을 기대합니다. 64 비트 플랫폼에서 실행하고 있습니다만, 정수는 32 비트인 채입니다. 이 예에서는 우리의 마이그레이션 작업의 방해요소가 아무것도 없습니다.

두번째 예는 64 비트로의 실행을 확실히 하기 위해 조금 변경이 필요합니다. 여기에서는 GetData 메소드를 호출하여, 정수가 돌려주어지는 것을 기대하고 선언했지만, 실제로는 함수는 int 포인터를 돌려주었습니다. 문제는 여기입니다. 정수는 32 비트이지만, 64 비트 포인터는 8 바이트입니다. 32비트의 세계에서는 상당한 양의 코드가 포인터를 상정해 기술합니다. 정수는 같은 길이로 4 바이트였지만, 64 비트에서는 성립되지 않습니다.

마지막 케이스에서 메소드 선언의 IntPtr의 장소를 int를 사용하도록 고치면 문제를 해결할 수 있습니다.

public unsafe static extern IntPtr GetData();

이 변경을 실시는 32 비트와 64 비트의 어느 환경에서도 기능합니다. IntPtr은 플랫폼 특유의 것임을 잊지 않도록 합시다.

p/invoke를 관리형 응용 프로그램이 사용하기 때문에, 64 비트 플랫폼에의 마이그레이션이 불가능하다는 것은 아닙니다. 또, 반드시 문제가 있는 것도 없습니다. 중요한 것은 관리형 응용 프로그램이 가지는 비관리형 코드의 의존성을 확인하여 문제가 있는지 판별하는 것입니다.

마이그레이션과 COM의 상호 운용성

COM 의 상호 운용성은 .NET 플랫폼에 상정되고 있는 능력의 하나입니다. 앞의 플랫폼 호출에 관한 설명과 같이 COM 의 상호 운용성을 이용하는 경우, 관리형 코드가 비관리형 코드의 호출을 실시합니다. 그러나 플랫폼의 호출과는 달리COM 의 상호 운용성을 이용하는 경우에는 비관리형 코드가 COM 컴포넌트인 것 같이 관리형 코드를 호출할 수 있습니다.

한번 더 설명하지만, 관리형 코드가 아닌 COM 코드를 사용한다고, 64 비트에의 마이그레이션에 반드시 문제가 있는 것이 아닙니다. 더 조사가 필요한 것을 나타내는 지시자(indicator)로서 파악하면 좋을 것입니다.

마이그레이션을 위한 검토 사항

.NET Framework 버전 2.0 의 출시와 함께, 현재 중간 아키텍처의 상호 운용성 지원은 전혀 없다는 것을 알아 둡시다. 간단히 설명하면, 32 비트/64 비트간에는 같은 프로세스에 대해서, COM 의 상호 운용성은 이용할 수 없습니다. 다만, 아웃 오브 프로세스 COM 서버가 있는 경우는 32 비트/64 비트간에 COM 의 상호 운용성을 이용할 수 있습니다. 아웃 오브 프로세스 COM 서버를 사용할 수 없는 경우는 32 비트 COM 객체와 공동 이용할 수 있도록 프로그램을 WoW64 에서 실행시키기 위해서, 관리형 어셈블리를 Win64이 아닌Win32 또는 어그노스틱 으로 하는 것을 생각하겠지요.

다음 64 비트 환경에서, 관리형 코드가 COM를 호출하는 중에, COM 의 상호 운용성을 이용할 수 있듯이 하기 위해서 검토해야 할 다른 일을 검증합니다. 내용은 다음과 같습니다.

  • 64 비트 DLL 의 유효성
  • 데이터 타입의 사용 방법
  • 타입 라이브러리

유효성

p/invoke 에 관한 설명으로 64 비트 의존 코드의 유효성에 대해 다루었지만 이 섹션과도 관련합니다.

데이터 타입

p/invoke 에 관한 설명으로 64 비트 의존 코드의 데이터 타입에 대해 다루었지만, 이 섹션과도 관련합니다.

타입 라이브러리

어셈블리와는 달리 라이브러리는 "중립"으로 할 수 없습니다. Win32 또는 Win64 둘 중 어딘가에 두어야 합니다. 또, 타입 라이브러리는 COM을 실행하는 각 환경에 등록해야 합니다. 타입 라이브러리로부터 32 비트/64 비트 어셈블리를 생성하려면 tlbimp.exe를 사용합니다.

관리형 응용 프로그램이 COM 의 상호 운용성을 사용한다고, 64 비트 플랫폼에의 마이그레이션이 불가능한 이유는 아닙니다. 또, 반드시 문제가 있다는 것도 아닙니다. 관리형 응용 프로그램의 의존성을 재검토하고, 문제가 있는지 판별하는 것이 중요합니다.

마이그레이션과 안전하지 않은 코드

C# 언어는 데이터 타입에 포인터가 없다고 하는 점으로, C 및 C++과는 크게 다릅니다. 대신 C#은 레퍼런스와 가비지 콜렉터에 의해서 관리되는 객체를 작성하는 능력을 제공하고 있습니다. C# 언어는 초기화되지 않은 변수나 포인터, 자신의 경계를 넘는 배열을 인덱스 추가는 하지 않습니다. 따라서 C 및 C++ 프로그램으로 일상적으로 골치 아픈 버그 부류가 완전히 배제되어 있습니다.

실제로는 C 또는 C++로 구성되는 모든 포인터 타입은 C# 에 두고 대응 부분이 되는 레퍼런스 타입을 가집니다. 포인터 타입에의 액세스가 필요한 상황이 있습니다. 예를 들어 기본OS 와의 상호작용, 메모리에 기억된 기기에의 액세스 Time Crisis알고리즘의 구현은 포인터에 액세스 하지 않으면 불가능한 것으로 실용적이지는 않습니다. 이 수요에 대처하기 위해 C# 은 안전하지 않은 코드를 쓰는 능력을 갖추고 있습니다.

안전하지 않은 코드 중에 포인터 선언이나 포인터 처리를 하고, 포인터와 정수형의 변환, 변수의 주소 취득, 그 외를 실행하는 것이 가능합니다. 어떤 의미에서 안전하지 않은 코드를 기술하는 것은 C# 프로그램 중에 C 의 코드를 쓰는 것으로 아주 비슷합니다.

안전하지 않은 코드는 수식자 unsafe라고 명확하게 할 필요가 있습니다. 이렇게 하면 개발자가 안전하지 않은 코드의 기능을 잘못 사용하지는 않을 것입니다.

마이그레이션을 위한 검토 사항

안전하지 않은 코드의 잠재적인 문제에 대해 설명하기 위해, 다음의 예를 검증합니다. 샘플의 관리형 코드는 비관리형 DLL 를 호출합니다. 한정하여100 개의 아이템을 돌려주는 GetDataBuffer 라는 메소드가 있습니다. (이 예에서는 아이템의 고유 번호를 돌려주고 있습니다) 각 아이템은1 정수와 1 포인터로 구성되어 있습니다. 아래의 샘플 코드는 이 돌려주어진 데이터의 처리를 담당하는 안전하지 않은 함수를 보이는 부분을 관리형 코드로부터 발췌한 것입니다.

[C#]

public unsafe int UnsafeFn() {
   IntPtr * inputBuffer = sampleDLL.GetDataBuffer();
   IntPtr * ptr = inputBuffer;
   int   result = 0;

   for ( int idx = 0; idx < 100; idx ++ ) {
      // Add 'int' from DLL to our result
      result = result + ((int) *ptr);

// Increment pointer over int (
      ptr = (IntPtr*)( ( (byte *) ptr ) + sizeof( int ) );

      // Increment pointer over pointer (
      ptr = (IntPtr*)( ( (byte *) ptr ) + sizeof( int ) );
   }
   return result;
}
주의   이 예는 안전하지 않은 코드를 사용하지 않고 완성하는 것이 가능했습니다. 구체적으로는 다른 테크닉을 적용하는 것도 가능했습니다. 예를 들면 마샬링입니다. 그러나 본래의 목적을 위해 안전하지 않은 코드를 사용하고 있습니다.

UnsafeFn은 100 개의 아이템 모두를 루프하여, 정수 데이터의 총합을 요구합니다. 데이터의 버퍼를 대충 보고하여 코드는 정수와 포인터를 왕래할 필요가 있습니다. 32 비트 환경에서는 이 코드는 확실히 동작합니다. 그러나, 먼저 설명한 것처럼 64 비트 환경에서는 포인터는 8 바이트입니다. 보통 프로그래밍 테크닉을 사용하기 때문에, 예를 들어, 포인터를 정수와 같게 취급하는 등 코드 세그먼트(segment)는 바르게 동작하지 않습니다.

// Increment pointer over pointer (
ptr = (IntPtr*)( ( (byte *) ptr ) + sizeof( int ) );

이 코드가 32 비트 환경과64 비트 환경의 양쪽 모두로 동작하기 위해서는 이하와 같이 코드를 변경할 필요가 있습니다.

// Increment pointer over pointer (
ptr = (IntPtr*)( ( (byte *) ptr ) + sizeof( IntPtr ) );

지금까지 살펴본 것처럼 안전하지 않은 코드를 사용할 필요가 있는 인스턴스가 있습니다. 대부분 관리형 코드가 다른 인터페이스에 의존하는 것에 기인하여 필요성이 생깁니다. 안전하지 않은 코드가 존재하는 이유와 관계없는 것으로, 마이그레이션 프로세스의 일환으로서 재검토해야 합니다.

위에서 사용한 샘플은 꽤 단순하고, 프로그램을 64 비트로 동작하기 위한 간단한 수정입니다. 안전하지 않은 코드를 사용하는 더 복잡한 샘플이 많이 있습니다. 신중히 재검토가 필요한 프로그램도 있습니다. 한 걸음 나아가 관리형 코드를 사용하는 접근을 재검토해야 합니다.

다시 말하지만, 안전하지 않은 코드를 관리형 응용 프로그램으로 사용하기 때문에, 64 비트 플랫폼에의 마이그레이션이 불가능하다고 하는 것이 아닙니다. 또, 반드시 문제가 있다는 것은 아닙니다. 중요한 것은 관리형 응용 프로그램에 포함된 안전하지 않은 코드를 재검토하여 문제가 없는가 판별하는 것입니다.

마이그레이션과 마샬링

마샬링은 비관리형 메모리의 할당, 비관리형 메모리 블록의 복사, 관리형 타입으로부터 비관리형 타입에의 변환을 행하기 위한 메소드군을 제공하는 것 외에 비관리형 코드와의 교환으로 사용하는 여러가지 메소드를 제공합니다.

마샬링은 .NET Marshal클래스에서 나타납니다. Marshal 클래스에 정의된 Visual Bacic 의 static 또는 shared 메소드는 비관리형 데이터를 사용하는 작업에는 불가결합니다. 관리형/비관리형 프로그램 모델간의 브릿지를 제공하는 필요성으로부터, 독자적으로 Marshaler를 작성하는 상급 개발자는 일반적으로 정의되고 있는 메소드의 대부분을 사용합니다.

마이그레이션을 위한 검토 사항

마샬링은 응용 프로그램의 64 비트에의 마이그레이션에 관련되는 매우 복잡한 도전을 제시하고 있습니다. 마샬링에 의해서 개발자가 수행하려고 시도하고 있는 내용은 관리형/비관리형 코드간의 정보의 구조를 전송 하는 것입니다. 시스템을 지원하기 위해 가끔 낮은 레벨의 정보를 제공하기도 합니다.

레이아웃에 관해서는 개발자에 의해서 작성할 수 있는 두가지 선언이 있습니다. 이 선언은 보도 속성의 코드화를 적용해 작성합니다.

LayoutKind.Sequential

.NET Framework SDK 의 도움말로 제공되고 있는 정의에 대해 검토합니다.

"객체 멤버는 비관리형 메모리에 export 되었을 때에 나타나는 순서로 순차적인 배치되어 있습니다. 멤버는 StructLayoutAttribute.Pack (영문)에 지정된 패킹에 근거해서 배치되어 있습니다. 비 연속에서도 상관하지 않습니다."

레이아웃은 정의된 순서에 근거합니다. 여기서 주의하는 것은 관리형/비관리형 선언을 확실히 같이 하는 것뿐입니다. 그러나 패킹은 구성요소가 중요하다고 말합니다. 개발자에 의한 명시적인 조정이 없어도 디폴트 팩트 값이 있다는 사실에 그다지 놀라지 않을 것입니다. 예측하고 있었다고 생각합니다만, 디폴트의 팩트 값은 32 비트 시스템과 64 비트 시스템과는 다릅니다.

비연속 멤버에 관한 정의중의 상태는 디폴트의 팩 크기가 있기 때문에, 메모리에 배치되어있는 데이터는 byte 0, byte 1 ,byte2 . . . .에는 존재하지 않는 경우가 있는 것을 언급하고 있습니다. 제 1 멤버는 byte 0 에, 제 2 멤버는 byte 4 에 배치됩니다. 시스템은 이 디폴트의 패킹을 하는 것에 의해서 머신이 배치 미스의 문제에 관련되지 않고 멤버에 액세스 할 수 있도록 합니다.

패킹에 대해서 신중하게 확인해 둘 필요가 있는 영역이 있습니다. 동시에 시스템을 바람직한 모드로 활동시키는 것을 시도합니다.

이하는 관리형 코드로 정의한 구조체와 이것에 상당하는 구조체를 비관리형 코드로 정의한 샘플입니다. 이 샘플이 두개의 환경에서 팩트 가치를 설정하는 방법을 신중하게 확인해 주세요.

[C#]
[StructLayout(LayoutKind.Sequential, Pack=1)]
public class XYZ {
      public byte arraysize = unchecked((byte)-1);
      [MarshalAs(UnmanagedType.ByValArray, SizeConst=52)]
      public int[] padding = new int[13];
};
[unmanaged c++]
#pragma pack(1)
typedef struct{
      BYTE arraysize;      // = (byte)-1;
      int      padding[13];
} XYZ;

LayoutKind.Explicit

.NET Framework SDK 의 도움말로 제공되고 있는 정의에 대해 검토합니다.

" 비관리형 메모리중의 각 객체 멤버의 정확한 위치는 명시적으로 제어됩니다. 각 멤버는 FieldOffsetAttribute를 사용하고, 타입중에 목적 필드의 위치를 나타내지 않으면 안됩니다."

정보의 마샬링을 보조하기 위해서, 개발자가 정확한 오프셋을 제공합니다. 따라서 개발자는 FieldOffset속성의 정보를 정확하게 지정하는 것이 불가결합니다.

그런데, 잠재적인 문제란 무엇일까요? 필드의 오프셋은 처리중의 데이터의 멤버의 크기를 알고 있어 정의되는 것을 기억해 둬야 합니다. 32 비트와 64 비트에서는 모든 데이터 타입의 크기가 같지 않은 것을 기억해 두는 것이 중요합니다. 구체적으로는 포인터는 4 바이트 또는 8 바이트입니다.

다음은 특정 환경 전용으로, 관리형 소스 코드를 업데이트 할 필요가 있는 케이스입니다. 아래의 샘플은 포인터를 포함한 구조체입니다. 포인터 IntPtr 를 작성했습니다만, 이것만으로는 64 비트에 마이그레이션시 차이가 있습니다.

[C#]
[StructLayout(LayoutKind.Explicit)]
    internal struct FooValue {
        [FieldOffset(0)] public int dwType;
        [FieldOffset(4)] public IntPtr pType;
        [FieldOffset(8)] public int typeValue;
    }

구조체내의 마지막 데이터 멤버의 필드의 오프셋을 조정할 필요가 있습니다. 실제로는 64 비트에서는 오프셋은 8 이 아니고 12 부터 시작됩니다.

[C#]
[StructLayout(LayoutKind.Explicit)]
    internal struct FooValue {
        [FieldOffset(0)] public int dwType;
        [FieldOffset(4)] public IntPtr pType;
        [FieldOffset(12)] public int typeValue;
    }

관리형 코드와 비관리형 코드의 복잡한 상호 운용이 필요한 경우는 마샬링을 적용하는 것이 현실적입니다. 이 강력한 능력을 사용할 수 있다고 해서, 32 비트 응용 프로그램을 64 비트 환경으로 마이그레이션 할 수 있는 것은 아닙니다. 마샬링 사용은 복잡하기 때문에 이 부분은 주의하셔야 합니다.

코드 분석에 의해서, 각각의 플랫폼용으로 별개의 바이너리를 준비할 필요가 있는지 아닌지 확인할 수 있습니다. 또, 패킹등의 문제에 대한 대처법으로서 비관리형 코드를 변경할 필요가 있는지 아닌지도 확인할 수 있습니다.

마이그레이션과 직렬화(Serialization)

직렬화란 어느 객체의 상태를 보관 유지할 수 있는지 전송 할 수 있는 폼으로 변환하는 프로세스입니다. 직렬화는 스트림을 객체로 변환하는 직렬화 해제로 보완합니다. 이 두개의 프로세스에 의해서, 데이터를 간단하게 보존하여 전송 할 수 있습니다.

.NET Framework 은 두 가지의 직렬화 기능을 갖추고 있습니다.

  • 바이너리 직렬화는 타입의 충실성을 유지합니다. 한 개의 응용 프로그램에 의한 다른 호출을 실행할 때에 객체의 상태를 유지하는데 도움이 됩니다. 예를 들어, 객체를 클립보드를 사용하여 직렬화하면, 다른 응용 프로그램간에 객체를 공유할 수 있습니다. 객체를 스트림, 디스크, 메모리, 네트워크 경유, 등등에 직렬화할 수 있습니다. .NET 리모팅은 어떤 컴퓨터, 또는 응용 프로그램의 도메인으로부터, 다른 도메인에 객체를 건네줄 때, 직렬화된 값에 따라 실시합니다.
  • XML 직렬화는 public 속성과 필드만을 직렬화합니다. 타입의 충실성은 유지하지 않습니다. 이 방법은 대상 데이터를 사용하는 응용 프로그램을 제한하지 않고, 데이터를 제공 또는 소비할 경우에 도움이 됩니다. XML은 오픈 표준적이기 때문에, Web에서 데이터를 공유하는데 매력적인 방법입니다. 게다가, SOAP도 오픈 표준적이기 때문에 더욱 더 매력적입니다.

마이그레이션을 위한 검토 사항

직렬화를 검토할 때 무엇을 달성하려는 지가 중요합니다. 64 비트에의 마이그레이션은 다른 플랫폼간에 직렬화한 정보를 공유하는 것이 목적인지 물음을 염두에 둡니다. 바꾸어 말하면, 64 비트 관리형 응용 프로그램은 32 비트 관리형 응용 프로그램으로 보존된 정보를 읽는 것인가 (직렬화 해제를 하는 것인가) 라는 것입니다.

답에 의해서 솔루션의 complex system가 바뀝니다.

  • 플랫폼에 책임을 가지기 위해서, 독자적인 직렬화 루틴을 작성하고 싶은지.
  • 각 플랫폼에 자신의 데이터의 읽고 쓰기를 허가한 데다가, 정보의 공유를 제한하고 싶은지.
  • 직렬화된 데이터를 재차 방문하고, 어떠한 문제의 예방책으로서 사용하고 싶은지.

결국, 무엇이 직렬화에 관한 검토 사항?

  • IntPtr는 플랫폼에 의해서4 또는 8 바이트가 됩니다. 정보를 직렬화하려면, 결과물로서 플랫폼 특유의 데이터를 씁니다. 즉, 이 정보를 공유하려고 시도하면 문제가 발생합니다.

앞에서 마샬링과 오프셋에 대해 설명했습니다만, 이것을 검토할 경우 직렬화에 의해서, 패킹 정보는 어떻게 처리되는지에 대해서 의문이 몇 가지 있습니다. 바이너리 직렬화에서는 .NET은 내부적으로 타당한 비동맹 액세스를 사용하고, 직렬화된 스트림에 액세스 해, 바이트 베이스의 읽기를 실시하여, 정확하게 데이터를 처리합니다.

지금까지 살펴본 것처럼, 직렬화를 사용하여, 64 비트에의 마이그레이션을 방해할 것은 없습니다. XML직렬화를 사용하는 경우는 플랫폼간의 차이로부터 멀어지고 직렬화 프로세스 중에, 네이티브 관리형 타입으로부터 네이티브 관리형 타입에 변환해야 됩니다. 바이너리 직렬화를 하는 경우는 풍부한 솔루션이 준비되어 있지만, 다른 플랫폼간으로의 직렬화한 정보를 어떻게 공유 하는지에 대해서는 결정이 필요한 상황이 있습니다.

마무리

차세대에는 64 비트에의 마이그레이션이 필요합니다. 마이크로소프트는 32비트 관리형 응용 프로그램에서 64 비트로의 마이그레이션을 최소화하기 위해서 노력하고 있습니다.

그러나 32 비트 코드를 64 비트 환경에서 간단하게 실행할 수 있다고 가정하거나, 무엇을 마이그레이션 하는지 확인하지 않고 실행하는 것은 바람직하지 않습니다.

앞에서 말한 것처럼, 100% 형식이 안전한(Type-Safe) 관리형 코드의 경우는 단지 64 비트 플랫폼에 복사하는 것만으로 64 비트 CLR 의 원으로 확실히 실행할 수 있습니다.

그러나 관리형 응용 프로그램이 다음과 같은 문제들이 있습니다.

  • p/invoke 에 의한 플랫폼 API 호출
  • COM 객체 호출
  • 안전하지 않은 코드 사용
  • 정보 공유 기능으로서의 마샬링 사용
  • 상태 보관 유지 방법으로서 직렬화 사용

응용 프로그램이 어떤 문제를 포함하는 지와는 별개로 코드가 어떠한 의존성을 가지고 있는지 확인하는 것이 중요합니다. 이 문제가 확인한 후 실시여부를 결정합니다.

  • 코드를 일절 변경하지 않고 마이그레이션한다.
  • 64 비트 포인터를 정확하게 취급할 수 있도록 코드를 변경한다.
  • 벤더, 그 외와 공동으로 기존 제품의 64 비트버전을 준비한다.
  • 마샬링/ 직렬화에 대처할 수 있도록 논리를 변경한다.

관리형 코드의64 비트에의 마이그레이션을 포기하는 경우도 있을 수 있습니다. 이 경우 Widows 로더가 실행시에 올바르게 동작하도록 어셈블리의 플래그를 설정합니다. 다운 스트림의 의존성은 응용 프로그램 전체에 직접 영향을 주는 것을 잊지 말아 주세요.

FxCop

한가지 더 마이그레이션에 도움이 되는 툴에 대해 언급합니다.

현재 마이크로소프트에서는 FxCop이라는 툴을 준비하고 있습니다. 이것은 코드 분석 툴로 .NET 의 관리형 코드 어셈블리나 Microsoft .NET Framework의 설계 가이드 라인에 모순되는지 체크합니다. 리플렉션, MSIL 파싱을 사용하여 콜 그래프 분석을 사용하고, 200 이상의 결함 항목에 대해 어셈블리를 조사합니다. 범위는 명명 규칙, 라이브러리 설계, 로컬라이즈, 보안, 성능입니다. FxCop 에는 GUI 판과 커멘드 라인판이 있는 것 외에 독자적인 규칙을 생성하기 위한 SDK가 있습니다. 마이크로소프트는 마이그레이션 작업에 도움이 되는 정보를 제공하기 위해 추가 가능한 FxCop (영문) 규칙을 개발 중입니다.

실행시에 실행중의 환경을 확인하는데 도움이 되는 관리형 라이브러리 함수도 있습니다.

  • System.IntPtr.Size : 32 비트 모드인가 64 비트 모드인지를 확인할 수 있습니다.
  • System.Reflection.Module.GetPEKind : 특정의 플랫폼 또는WOW64 에서만 실행해야 하는지 확인하기 위해 .exe 또는 .dll 를 프로그램적으로 문의합니다.

  

Top of Page Top of Page


Microsoft