이번 Exploring Windows Azure 세미나를 시작으로 올 한해는 Windows Azure에 관련된 다양한 발표 및 소프트웨어 개발 활동을 병행해 나갈 계획에 있습니다. 계획의 일환으로, Windows Azure에서 사용할 수 있도록 이전부터 지속적으로 개발해 오던 xPlatform 프로젝트를 Windows Azure에서도 사용할 수 있도록 준비할 계획입니다.
xPlatform 프로젝트는 Windows를 시작으로, Linux, Mac OS X 등 다양한 운영 체제에서 기본적으로 닷넷의 기본 프레임워크에서는 노출하지 않는 Low Level API를 손쉽게 호출하고, 포인터나 비 관리 메모리 영역을 손쉽게 제어할 수 있도록 돕습니다. 현재는 32비트 버전의 Windows에서 사용할 수 있도록 코드를 완성하였고 첫 알파 릴리즈를 지난 가을에 런칭한 상태입니다.
Windows Azure는 단순히 웹 서비스를 클라우드 환경 위에 올리는것 뿐만이 아니라, Windows Azure의 바탕을 이루고 있는 64비트 버전의 운영 체제를 얼마나 잘 활용할 수 있는가에 대한 부분도 중요한 관건이 될 것입니다. Windows Azure의 기능을 극대화하고, 향후에 개발될 다양한 클라우드 기반 응용프로그램의 품질 향상에 기여할 수 있게 하기 위하여 xPlatform을 64비트 버전의 서버 운영 체제에서도 사용할 수 있도록 프로젝트 방향을 정하기로 하였습니다.
Windows Azure 개발 및 플랫폼 관련 프로그래밍에 관심있으신 개발자 여러분들의 많은 관심과 참여 부탁드립니다. 조만간, 64비트 버전으로 개량된 xPlatform 라이브러리를 런칭할 수 있도록 하겠습니다. 감사합니다. :-)
긴 여정 끝에, Project Blend: xPlatform의 2009년 11월 릴리즈를 공개하게 되었습니다. Project Blend: xPlatform (이하 xPlatform)은 .NET Framework 2.0 이상의 환경에서, C#이 아닌 다른 프로그래밍 언어들 (예: Visual Basic .NET, Visual J#, Iron Python 등)을 위한 비관리 프로그래밍 기술을 제공하고, 운영 체제에 대한 API를 제공하기 위하여 시작된 프로젝트입니다.
xPlatform 프로젝트는 Ms-PL (Microsoft Permissive License) 아래에서 배포되는 오픈 소스 기반 소프트웨어입니다. 그리고 아직까지는 일반 개발 환경에 직접 투입하기에 적절하지 않을 수 있음을 미리 알려드리며, 정식으로 릴리즈가 되기 이전까지는 많은 피드백과 테스트가 필요합니다.
다음의 코드는 실제로 xPlatform의 Core 라이브러리를 사용하여 작성한 샘플 코드로, 비 관리 영역 상의 메모리를 Unsafe 코드를 사용하지 않는 C#을 통하여 자유롭게 제어하는 예시를 담고 있습니다.
using System; using xPlatform; using xPlatform.Buffers;
class Program { static void Main() { int Length = 15;
using (GlobalHeapBuffer<double> myBuffer = new GlobalHeapBuffer<double>(Length)) { Pointer<double> ptr = myBuffer.TypedPointer;
for (int i = 0; i < Length; i++, ptr++) ptr.SetData(i * 0.2);
for (int i = 0; i < Length; i++) Console.WriteLine(">> ptr++[i]: {0}", ptr++[0]);
ptr = myBuffer.Address;
for (int i = 0; i < Length - 1; i++) Console.WriteLine(">> (++ptr)[i]: {0}", (++ptr)[0]); }
Console.ReadLine(); } }
릴리즈를 다운로드하여 설치를 마무리하면, Visual Studio 및 Visual Studio Express Edition의 프로젝트 참조 추가 대화 상자에서 다음과 같이 xPlatform 프로젝트에 대한 참조가 나타납니다. 현재 배포되고 있는 이들 항목들에 대한 설명을 드리면 다음과 같습니다.
xPlatform Core에는 형식화된 포인터, 메모리 버퍼, 문자열 버퍼, 비트 연산 관련 보조 루틴 등이 포함되어있으며, ModuleBase 클래스를 통하여 API 모음에 대한 서비스를 제공하게 될 것입니다. 별도의 예외 사항이 없는 한, API 묶음들은 xPlatform Core에서 제공하는 추상 클래스 ModuleBase 클래스를 상속받게 됩니다.
xPlatform kernel32 (x86)과 xPlatform msvcrt (x86)은 이번 릴리즈에서 공개하는 API 모음입니다. kernel32.dll과 msvcrt.dll에 대한 P/Invoke 호출들을 제공하므로 단지 이들 어셈블리를 프로젝트에 추가하고, 호출하기만 하면 됩니다.
아직 이 프로젝트가 가야 할 길이 멀고, 분명히 문제점이 있을 것입니다. 이러한 부분들을 피드백으로서 제 메일 (rkttu nospam rkttu dot com) 앞으로 보내주시거나, 가급적 http://blendxplatform.codeplex.com/ 에 접속하셔서 여기서 이용하실 수 있는 이슈 트래커에 올려주시면 최대한 반영할 수 있도록 하겠습니다.
더불어서, 프로젝트에 개발자, 코디네이터로 참여해주실 분이나, 후원해주실 분들도 언제든지 환영합니다. :-)
약 3개월만의 작업 기록 포스팅입니다. Core Runtime에 대한 테스트를 강화하고 코딩할 때 불편한 부분을 최소화하면서 실제 C 프로그래밍 언어와 비슷한 환경을 나타낼 수 있도록 만드는데에 많은 노력을 들였습니다.
Buffer, String 계열 클래스의 확장 및 개선
[Test] publicvoid memchrTest()
{ int ch = 'r';
GlobalHeapAnsiString str = new GlobalHeapAnsiString("lazy");
GlobalHeapAnsiString @string = new GlobalHeapAnsiString("The quick brown dog jumps over the lazy fox");
GlobalHeapAnsiString fmt1 = new GlobalHeapAnsiString(" 1 2 3 4 5");
GlobalHeapAnsiString fmt2 = new GlobalHeapAnsiString("12345678901234567890123456789012345678901234567890");
SBytePointer pdest; int result;
Console.Write("String to be searched:\n {0}\n", @string.ToString());
Console.Write(" {0}\n {1}\n\n", fmt1.ToString(), fmt2.ToString());
위의 코드에서 보시는것처럼 String Buffer 클래스에 직접 Add / Subtract 연산자 오버로드를 추가하여 실제 포인터 연산의 결과를 재현합니다. 형식화된 Pointer 클래스와 다른점이 있다면 자기 자신에 대한 주소 설정은 허용하지 않습니다. 이것은 비관리 메모리 영역의 주소가 변경됨으로 인하여 발생할 수 있는 할당 해지 실패로 인한 메모리 누수를 예방하기 위한 디자인입니다.
총 20여종의 주요 값 형식에 대한 형식화된 포인터 제공 (Typed Pointer)
형식화된 포인터에 세부적인 기능 조절을 더하고 제거하는 노력을 통하여 총 20여종의 주요 값 형식에 대한 형식화된 포인터를 제공하게 되었습니다. 이들 포인터 형식 모두는 외관상 CLS 표준 사양을 만족하도록 만들어졌으며 포인터 개념이 없는 프로그래밍 언어 (예: Visual Basic .NET, Visual J# 등)에서도 포인터 연산을 간접적으로 사용할 수 있게 디자인되어있습니다.
AutoCharPointer
BooleanPointer
BytePointer
DateTimePointer
DecimalPointer
DoublePointer
GuidPointer
Int16Pointer
Int32Pointer
Int64Pointer
IntPtrPointer
Pointer<T>
SBytePointer
SinglePointer
TimeSpanPointer
UInt16Pointer
UInt32Pointer
UInt64Pointer
UIntPtrPointer
WideCharPointer
사용 빈도가 높을 것으로 추정되거나 기본 형식들에 대한 형식화된 포인터는 모두 제공하고 있습니다. 하지만 특정한 목적으로 직접 추가한 구조체에 대한 지원도 필요했고 경우에 따라서는 제네릭에 대응되는 가변 포인터에 대한 구현을 필요로 하였기 때문에 Pointer<T> 형식을 새로 추가하였습니다. 전용 포인터를 이용하여 계산하는것보다는 연산 횟수가 많다는것이 단점입니다.
실제 메모리 구조의 크기를 조사해주는 unsafe sizeof() 연산자의 Managed 버전 제공
위의 테스트 코드에서 나열한 형식들에 대한 sizeof 연산자의 결과를 반환해주는것이 NativeSizeOf 메서드입니다. 그 외의 형식들에 대해서는 Marshal.SizeOf의 결과를 반환합니다.
NUnit 테스트 코드 프로젝트 시작
프로젝트에 대한 검증을 수행하기 위하여 NUnit 프로젝트를 생성하고 테스트하면서 조금씩 진척을 시켜나가고 있습니다. NUnit 테스트 코드의 내용은 실제 적용에 많은 도움이 되는 예제 코드로도 활용이 가능하니 한번 살펴보시길 권합니다.
향후 계획
조만간 공식 릴리즈를 0.1 버전으로 런칭할 계획을 세우고 있습니다. 그리고 준비가 되는대로 Windows CE, Windows Mobile에 관한 API도 수집하여 데이터베이스화를 수행할 예정이니 관심있으신 분들은 블로그나 데브피아 등을 통하여 연락주시면 언제든 채널이 열려있으니 함께 하실 수 있습니다.
Project Blend: xPlatform을 조금씩 차곡차곡 진행시켜 나가고 있는 중입니다. 그동안 다양한 방법으로 조사하고 코드를 테스트하고 디자인한 결과를 드디어 조금씩 실행에 옮기고 있습니다.
포인터에 관한 새로운 접근
using System;
using xPlatform;
using xPlatform.Strings;
using xPlatform.x86.msvcrt;
namespace xPlatform.Core.Test
{
class Program
{
[STAThread]
staticvoid Main(string[] args)
{
conststring targetString = "안녕하세요.";
using (GlobalHeapAnsiString str = new GlobalHeapAnsiString(targetString))
{
size_t len = msvcrt.strlen(str.Address);
Console.WriteLine("String: \"{0}\", ANSI Length: {1}", targetString, len);
BytePointer ptr = new BytePointer(str.Address);
for (size_t i = (size_t)0u; i < len; i++, ptr++)
Console.Write("{0} ", ptr.GetData().ToString());
}
Console.WriteLine();
Console.WriteLine();
using (GlobalHeapUnicodeString str = new GlobalHeapUnicodeString(targetString))
{
size_t len = msvcrt.wcslen(str.Address);
Console.WriteLine("String: \"{0}\", Unicode Length: {1}", targetString, len);
WideCharPointer ptr = new WideCharPointer(str.Address);
for (size_t i = (size_t)0u; i < len; i++)
Console.Write("{0} ", (ptr + (int)i).GetData().ToString());
}
Console.WriteLine();
Console.ReadLine();
}
}
}
기존에 Base Class Library만을 이용하여 포인터를 활용해서 프로그래밍하는 작업은 작성해야 하는 코드의 양도 많았고 실수하기도 쉬웠습니다. 이러한 단점을 보완하고 좀 더 실용적이고 직관적으로 포인터 프로그래밍을 수행할 수 있는 방법을 고심한 끝에 위와 같은 패턴의 코드를 구현하였습니다.
GlobalHeapAnsiString, GlobalHeapUnicodeString 등의 클래스
보통 닷넷의 문자열을 비관리 메모리 영역에 저장하기 위해서 Marshal 클래스의 StringToHGlobalAnsi 같은 메서드를 이용해서 처리하는 것이 보편적이었습니다. 그리고 FreeHGlobal 메서드로 해당 메모리 블럭을 소거해야 하는 일도 잊지 않아야 했습니다. 이러한 작업을 자동화하기 위하여 위와 같은 클래스를 디자인하였습니다. 권한에 관한 이슈를 해결하지 못하여 SafeHandle 기반의 상속 혜택을 구현하지는 못하고 있습니다만 곧 해결할 수 있을것으로 기대합니다. Dispose 메서드를 노출하지는 않고 있으나 IDisposable 인터페이스를 구현하도록 하였습니다.
BytePointer, WideCharPointer 등의 클래스
Base Class Library가 제공하는 IntPtr이나 UIntPtr은 C/C++의 void*나 LPVOID에 가까운 것입니다. 형식 정보가 없기 때문에 주소 탐색 기능을 이용할 수 없다는 한계가 존재하기 때문에 이를 보완하기 위하여 Marshal 클래스의 ReadByte, ReadInt32 같은 메서드나 WriteByte, WriteInt32 같은 메서드를 통해 오프셋을 지정하여 주소 탐색을 간접 처리하는 것이 전부였습니다. 그렇지만 xPlatform에서는 +, -, ++, --, ==, != 등의 포인터에 대해서 사용할 수 있는 주요 연산자를 정의하는 커스텀 포인터 구조체들을 대거 추가하였습니다. 향후 이 부분을 템플릿화하여 직접 만든 구조체나 나열 상수에 대한 포인터도 사용자 정의할 수 있게 만들 생각입니다.
size_t 등의 Native C 형식 지원
닷넷의 Enum 형식은 생각보다 씀씀이가 많습니다. 특히 Primitive Type 중 정수 계열 (int, uint, short, ushort, long, ulong 등)을 enum으로 재정의하여 C/C++이 원래 사용하던 size_t 같은 형식을 그대로 재현할 수 있었습니다. 문법적으로도 size_t와 uint 사이는 관련은 있지만 명시적 변환이 이루어져야만 하는 원래의 코드에서처럼 C#에서도 동일하게 동작합니다.
ptr++, (ptr + i) 등의 표현식 지원
주소 탐색을 위하여 더 이상 어렵고 복잡한 간접 우회 코드를 사용하지 마시고, 그냥 C/C++에서 하던 것처럼 원소 수를 더하거나 빼서 주소 탐색을 하시면 됩니다. :-)
향후 계획
코어 파트는 좀 더 개선할 부분이 많고 테스트가 많이 필요합니다. 많은 분들의 참여와 관심을 통하여 개선을 시켜 나가야 할 것으로 생각합니다. 그리고 더 나아가서는 C++ Name Mangling 등의 복잡한 이슈를 직접 풀고 C#에서 바로 C++ 코드를 액세스할 수 있게 만드는 작업도 욕심을 내보고 싶습니다.
CLIFX xPlatform Repository를 열었습니다. 소스 코드를 다운로드하여 필요하신 자료를 참조하실 수 있으며 라이센스는 LPGL 라이선스를 따릅니다. Subversion 기반의 리포지터리이며 TortoiseSVN 이나 svn 유틸리티등을 이용하여 소스 코드를 가져가실 수 있습니다.
xPlatform의 Win32 API 영역 중에 Microsoft Visual C++ Runtime에 관한 정보 수집이 어느 정도 완료되었고 이것을 바탕으로 Phase #1을 이번달 말에 예정대로 런칭할 계획입니다. Visual C++ Runtime (msvcrt.dll)의 상당 수의 시그니처는 libc와 일치하는 것으로 보이며 이 중 몇 가지를 시험삼아 Linux 버전으로 옮겨서 작업 중입니다.
아래의 동영상은 Asianux 2.0 SP1 기반의 PC에서 Mono 1.2.5.1_3을 이용하여 xPlatform DLL을 이용한 간단한 파일 입/출력 서비스를 보여주고 있습니다. 점진적인 테스트를 거쳐 Linux에서도 표준 파일 입/출력 서비스 및 기본 C 런타임을 사용할 수 있도록 준비할 계획입니다.
xPlatform은 Project Blend의 첫 프로젝트로서 Windows, Linux, Unix, Mac OS X 등의 운영 체제에서 사용하는 각종 C 언어 런타임 및 API 함수들에 대한 플랫폼 호출 문맥들을 수집한 라이브러리이며 Microsoft .NET Framework, Microsoft Rotor, Mono, DotGNU Portable .NET을 지원 대상으로하는 Lesser GPL 라이센스 기반의 오픈 소스 라이브러리입니다.
Phase #1에서는 xPlatform의 다양한 가능성을 처음 저울질하는 단계가 될 것이며 다음의 기능들을 포함하고 있습니다.
32비트 버전의 Microsoft Windows용 C Runtime Library에 대한 플랫폼 호출 서비스
특정 프로그래밍 언어를 대상으로 하여 프로토타입 문맥들을 번역하여 제공하는 프로토타입 공유 서비스 (C#, Visual Basic .NET, Managed Extensions for C++ v1.x/v2.0 렌더링 엔진 포함 예정)
C#의 __arglist는 MSIL로 번역되면서 Call Instruction에 varargs라는 추가 파라미터를 지정하는 방식으로 사용됩니다. gmcs 컴파일러에서는 Microsoft .NET Stack과의 호환성을 위하여 __arglist 키워드에 대한 정확한 처리를 수행하는 것 처럼 보입니다. 그러나 런타임에서 Microsoft .NET Framework의 경우 이것을 정확히 관리하고 있지만 Mono의 경우 가변 인수를 IL에서 사용할 수 없습니다. 이것은 __arglist가 표준 사양이 아니기 때문에 그렇습니다. 다음은 Mono에서 가변 인수 메서드를 호출할 때 발생하는 예외입니다.
[CODE] [root@localhost ~]# mono test.exe
Unhandled Exception: System.InvalidProgramException: Invalid IL code in Program: Main (): IL_0007: call 0x0a000001
[root@localhost ~]
[/CODE]
그러나 가변 인수를 사용하지 않고 필요한 만큼 인수를 직접 선언하는 방법은 유효합니다. 다음과 같이 직접 필요한 인수를 지정하여 플랫폼 호출 선언을 지정하여 사용하는 경우 정상적으로 실행됩니다.
[CODE] [DllImport("libc", CharSet = CharSet.Ansi, ExactSpelling=true)] public static extern int printf(string format, int testIntenger);
[/CODE]
xPlatform은 이 부분에 대한 문제 개선을 위하여 동적 플랫폼 호출 선언 관리 클래스를 별도로 구현할 예정에 있으며 이 클래스를 통하여 Mono의 __arglist 비호환성 문제를 비롯하여 타 언어에서도 가변 인수 메서드의 사용을 가능하게 할 것입니다.
printf 함수를 호출하면서 Visual Studio HOST 디버깅을 통해 결과를 확인하려고 하면 printf 결과가 누락되는 문제가 있습니다. 이것은 Visual Studio HOST 디버깅이 관리하는 표준 출력 스트림이 printf 함수로 다루는 영역과 다르기 때문에 발생하는 문제이므로 Visual Studio HOST 디버깅 환경 밖에서 실행하거나 Visual Studio HOST 디버깅 환경을 제거하고 디버깅해야 합니다.