Friday, February 11, 2011
[Under-writing] 마이크로커널 구조 - On Microkernel Architecture
1. Whey Micro-kernel architecture? -> The problem of old monolithic kernel -> Millions of lines of code. Hard to manage. -> Hard to debug, Hard to obtain reliability. 1. Micro-kernel Architecture의 등장 배경 2. The principle of Micro-kernel architecture. -> Divide the Monolithic Kernel into two parts. The core is called micro-kernel which is light and operates in kernel mode. The other part runs in User-mode as (mostly) threads. -> The micro-kernel provides only a few set of core services such as Scheduling, Memory Management, etc. (The user mode OS part can utilize thise services to implement their own Scheduling and Memory Managment policies.) 2. Micro-kernel Architecture의 원리 Brief Layout of Micro-kernel Architecture. App1 | App2 | App3| 3. List of services provided by Micro-kernel. -> The most critical functions of OS are gathered here. 1) Address Space (Memory Management). 3. Micro-kernel이 제공하는 기능들. 4. Strengths of Mircokernel architecture. 1. Small kernel -> less prone to errors. 2. Even if the user mode OS services goes down, the system does not crash. (It can safely re-configure the system). 3. You can run different kinds of OS on the same Micro-kernel. So, you can still use the legacy OS system. (e.g. Mach OS (base for MacOSX) run BSD, DOS, and so on..) 4. 장점. 5. Weaknesses. 1. Every kernel service involves "messaging" to the micro-kernel. -> It can degrade the performance. 2. You can use various Operating Systems but you should re-write some part of 5. 단점 1. Microkernel 방법론이 왜 나왔는가? (배경) 기존의 OS는 Memory Management, Scheduler, File system, IPC, Device Drivers 등의 수많은 기능을 포함하는 하나의 큰 프로그램으로 작성되었다. 이를 Monolithic Kernel 이라고 부른다. Unix(BSD, Linux), DOS, 초기 버전의 Windows 등이 Monolithic Kernel의 대표적인 예이다. (쉬어가 기 역사: Steve Jobs가 NextStep을 설립할 때 카네기 멜론 대학에서 Mach OS를 개발하던 Avie Tavarian을 데려왔죠. 이 사람은 Mach OS에 기반한 NextStep OS를 개발했습니다. 그리고 NextStep이 Apple에 흡수되면 자연스럽게 Apple Mac OS로 발전하게 되었죠. Avie Tavarian은 Apple OS Division을 책임지면서 Apple내 No.2의 위치에까지 올랐으나, 2005년도인가 Apple을 떠나 Tellme Networks라는 회사에 합류하게 됩니다.) (INTEL CPU의 구조는 관련 자료를 참조) 3. 현재 Microkernel 구조를 채용한 OS는 어떠한 것들이 있는가? Linux : Monolithic 4. Microkernel을 어떻게 이해해야 하는가? |
Using jpeglib on Windows - decoding (디코딩)
jpeg 파일을 jpeglib를 이용하여 디코딩하는 루틴을 설명합니다.
코드에 코멘트를 잘 달아놓았으므로 이해하는데 큰 문제는 없을 겁니다.
Step 1. 파일 첫머리에 다음과 같이 include를 해줍니다.
// jpeglib를 사용하기 위한 include 파일.
//
#include "jinclude.h"
#include "jpeglib.h"
Step 2. 디코딩 함수는 다음과 같습니다.
a_pFilename -> jpeg 파일 이름입니다.
a_pBuffer -> 이미지를 담을 버퍼에 대한 포인터입니다.
버퍼는 함수 내부에서 자동으로 할당합니다.
bool read(char *a_pFilename, JSAMPLE **a_pBuffer)
{
// JPEG 파일을 해석합니다.
// 해석 과정에서 width, height, color space, bytes per pixel 같은 값들이
// 추출됩니다.
//
// Data strucure required for decoding JPEG file.
//
struct jpeg_decompress_struct cInfo;
struct jpeg_error_mgr cErrMgr;
FILE *pFile;
int iRowStride, i;
JSAMPARRAY pRows;
// 파일 이름부터 체크하고,
//
if( !a_pFilename )
return false;
// 파일을 엽니다.
// 반드시 binary mode로 열어야 합니다.
//
pFile = fopen(a_pFilename, "rb");
if( !pFile )
return false;
// 1. Install error manager.
//
cInfo.err = jpeg_std_error(&cErrMgr);
// 2. Create JPEG decompressor object.
//
jpeg_create_decompress(&cInfo);
// 3. Specify the data source.
//
jpeg_stdio_src(&cInfo, pFile);
// 4. Read JPEG Header to extract image information.
//
(void) jpeg_read_header(&cInfo, TRUE);
// 이 시점부터 cInfo 구조체를 통해 jpeg image의 원래 정보들에 접근할 수 있습니다.
// 예를 들면, width, height, color space, number of components per pixel 등등
//
// 5. JPEG 이미지를 어떤 출력 포맷으로 디코딩할 것인지 지정합니다.
// 여기서는 JCS_RGB 포맷으로 디코딩합니다.
// 이전 포스팅에서 추가한 JCS_BGRA 포맷을 지정하면 32bit BGRA 포맷으로
// 디코딩됩니다. 출력포맷은 반드시 아래 Step 6에서 jpeg_start_decompress() 함수 호출
// 이전에 지정해야 합니다.
//
cInfo.out_color_space = JCS_RGB;
// 6. Initiate decompress procedure.
// 이 함수는 decoding을 위한 전처리를 하는 함수입니다.
// cInfo.output_width, cInfo.output_height와 같이 최종 출력 이미지에 대한
// 정보들이 여기에서 계산됩니다.
//
(void) jpeg_start_decompress(&cInfo);
// 7. 이미지 디코딩을 위한 메모리를 할당합니다.
// 디코더는 2단계의 버퍼를 사용합니다. 하나는 Row buffer로서 각 line별로 출력 버퍼를
// 가리킵니다. 다른 하나는 이미지 버퍼로서 실제 이미지를 저장합니다.
// 일반적인 경우, 각 Row[i]는 이미지 버퍼에서 i번째 line의 시작 주소를 저장합니다.
//
// 이미지의 한 scanline의 크기 계산.
//
iRowStride = cInfo.output_width * cInfo.output_components;
// 이미지 버퍼를 할당한다.
//
(*a_pBuffer) = new JSAMPLE[ iRowStride * cInfo.output_height ];
if( !(*a_pBuffer) )
{
// Allocation failed.
jpeg_destroy_decompress(&cInfo);
fclose(pFile);
return false;
}
// Row 버퍼를 할당한다.
// 여기서는 한번에 한 줄씩만 디코딩한다.
//
pRows = new JSAMPROW[ 1 ];
if( !pRows )
{
// Allocation failed.
delete[] (*a_pBuffer);
(*a_pBuffer) = NULL;
jpeg_destroy_decompress(&cInfo);
fclose(pFile);
return false;
}
// 디코딩 루프: 모든 line이 디코딩될 때까지 반복한다.
//
i = 0;
while (cInfo.output_scanline < cInfo.output_height)
{
// Decode it !
pRows[0] = (JSAMPROW)( (*a_pBuffer) + iRowStride * i); // Row 버퍼 설정
(void) jpeg_read_scanlines(&cInfo, pRows, 1); // 디코딩 함수 호출
i++;
}
// 8. Finish decompression.
//
(void) jpeg_finish_decompress(&cInfo);
// 9. Reclaim resources and return.
//
delete[] pRows;
jpeg_destroy_decompress(&cInfo);
fclose(pFile);
return true;
}
Using jpeglib on Windows (윈도우에서 jpeglib 사용하기)
대부분의 경우, 소스 다운로드 받고, 프로젝트를 생성한 후, 프로젝트에 파일을 추가하고
컴파일하면 잘 돌아갑니다.
하지만, 경우에 따라서 몇가지 문제가 발생할 수 있는데, 제가 경험했던 문제들과
그 해결책을 여기에 기록으로 남깁니다.
참고로 jpeglib는 ImageMagick-windows Package에 포함되어 있는 것을
사용했습니다. 해당 Package에 VC++에서 컴파일되는 jpeglib가 만들어져 있습니다.
1. undefined refernce to "_imp_jpeg_xxxx" error
일반적으로 이 문제는 Project간 Dependency가 제대로 잡혀 있지 않을 때 나옵니다.
Linking 과정에서 함수가 포함된 Lib를 못찾는 경우죠.
하지만 제가 만난 경우는 C와 C++간 호환성 문제였습니다.
jpeglib는 C 코드로 컴파일되었고, 이 Lib를 사용하는 Windows Application은 C++ 코드로
작성되었기 때문이죠. 그런데, jpeglib 내부를 살펴보면 이 문제가 고려되어 있습니다.
C++로 컴파일 할 때에는 jpeglib.h 헤더 전체를 extern "C" { ... } 구문으로 감싸게 하는거죠.
...
매크로, 데이터 타입, 함수 선언 등등.
...
#ifdef __cplusplus
#ifndef DONT_USE_EXTERN_C
}
#endif
#endif
답답해서 Windows Application C++ 파일에 extern "C" jpeg_xxx(..) 형태로 함수를
선언해봤습니다. 이러면 제대로 컴파일이 되더군요. 라이브러리가 제대로 생성되지 않은 것은
아니었고, 옵션에 문제가 있는 거였죠.
더 추적을 해보니, Windows에서는 컴파일하기 위해서 몇가지 옵션이 필요했습니다.
#define _VISUALC_
저는 이 옵션들을 Visual C++ Project Properties의 Preprocessr Field에 정의해 사용했습니다.
그래서 혹시 이 부분이 문제인가 싶어서, Preprocessor Field에서 해당 옵션을 제거하고,
jpeglib.h 첫부분에 매크로 정의를 추가하였습니다.
...
Visual Studio에서 Preprocessor Macro를 처리하는 방식의 문제인지, 아니면
소스코드 구성의 문제인지는 모르겠으나 재미있는 경헙이었습니다.
2. INT32 compile error
jpeglib를 Win32 환경에서 C++ Application과 같이 컴파일하면 INT32라는 이름이
재정의되었다고 불평합니다.
윈도우에서 제공하는
jpeglib에서 사용하는 jmorecfg.h 파일에는 typedef long INT32로 정의되어 있기 때문입니다.
같은 이름을 서로 다른 타입으로 정의했으니 에러가 발생할 수 밖에요.
이 문제는 jmorecfg.h에서 typedef long INT32를 typedef signed int INT32로 변경하면 없어집니다.
윈도우 환경에서는 이렇게 해도 큰 문제가 발생하지 않습니다.
3. 출력 포맷 변경하기.
jpeglib 사용법을 읽어보면, jpeglib 자체에서 이미지 출력 포맷을 지정할 수 있게 되어 있습니다.
정확히는
cInfo.out_color_space = (J_COLOR_SPACE)target_format;
으로 지정한 후, decoding 작업을 수행하면 됩니다.
(void) jpeg_start_decompress(&cInfo)
....
그런데 J_COLOR_SPACE로 지정할 수 있는 기본 출력 포맷은 몇가지 안됩니다.
그 외의 포맷은 사용자가 추가해서 넣어야 하죠.
기본 포맷들은 다음과 같습니다.
} J_COLOR_SPACE;
일반적인 애플리케이션의 경우, JCS_RGB를 가장 많이 사용합니다.
하지만, 실제 Application에서는 RGBA 혹은 BGRA 포맷이 필요한 경우도 많습니다.
알파를 사용하는 다른 이미지(PNG 같은)와 Blending을 하는 경우에 특히 그렇죠.
출력포맷을 추가하는 작업은 간단합니다.
위 J_COLOR_SPACE enum에 원하는 포맷의 이름을 추가하고,
jinit_color_deconverter()@jdcolor.c 함수에 변환 루틴을 추가하면 됩니다.
(그냥 jpeglib에서 JCS_RGB로 디코딩한 후, 그 이미지를 다시 RGBA 혹은 BGRA로
변환해도 됩니다. 하지만 이렇게 하면 두 번 변환 작업을 하는 것이기 때문에 성능도
저하되고, 메모리도 2배 많이 사용해야 합니다. 모바일 환경에서는 이런 조건이
불가능한 경우가 많죠. 다음 설명과 같이 디코딩 과정에서 바로 변환을 하는 것이
제일 좋습니다. )
간단하게 32 bit BGRA 포맷으로 변환하는 경우를 살펴보면 다음과 같습니다.
(Windows에서는 BGRA 포맷을 사용합니다.)
Step 1. JCS_BGRA라는 이름으로 포맷을 J_COLOR_SPACE enum에 추가합니다.
JCS_BGRA, /* 새로 추가한 포맷입니다. */
} J_COLOR_SPACE;
Step 2. jinit_color_deconverter()@jdcolor.c 함수에 변환 구문을 추가합니다.
GLOBAL(void)
jinit_color_deconverter( j_decompress_ptr cinfo)
{
{
...
// 아래와 같이 JCS_BGRA인 경우의 변환 규칙을 추가합니다.
// 원래 코드에는 아래 case 문이 없습니다.
//
case JCS_BGRA:
// JCS_BGRA 출력은 4바이트로 이루어져 있으므로, BGRA_PIXELSIZE = 4
// 입니다. 이 매크로는 jmorecfg.h에 정의하면 됩니다.
//
cinfo->out_color_components = BGRA_PIXELSIZE;
if (cinfo->jpeg_color_space == JCS_YCbCr)
{
// BGRA포맷으로 변환할 것을 설정합니다.
//
build_ycc_rgb_table(cinfo);
}
else if (cinfo->jpeg_color_space == JCS_GRAYSCALE)
{
// 이용하여 BGRA포맷으로 변환할 것을 설정합니다.
//
}
else if (cinfo->jpeg_color_space == JCS_RGB && RGB_PIXELSIZE == 3)
{
// 이용하여 BGRA포맷으로 변환할 것을 설정합니다.
//
}
else
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
break;
}
물론 ycc_bgra_convert(), gray_bgra_convert(), rgb2bgra_convert() 함수는
jpeglib에 포함되어 있지 않습니다.
기존 함수들을 참조하여 사용자가 직접 제공해야 합니다.
제가 사용한 함수들의 구현은 다음과 같습니다.
읽어보시면 직관적으로 이해가 될 겁니다.
// YCbCr to BGRA 32 bit format, cyberyanne@gmail.com, 2010/01/28.
//
METHODDEF(void)
ycc_bgra_convert (j_decompress_ptr cinfo,
JSAMPIMAGE input_buf, JDIMENSION input_row,
JSAMPARRAY output_buf, int num_rows)
{
my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
register int y, cb, cr;
register JSAMPROW outptr;
register JSAMPROW inptr0, inptr1, inptr2;
register JDIMENSION col;
JDIMENSION num_cols = cinfo->output_width;
/* copy these pointers into registers if possible */
register JSAMPLE * range_limit = cinfo->sample_range_limit;
register int * Crrtab = cconvert->Cr_r_tab;
register int * Cbbtab = cconvert->Cb_b_tab;
register INT32 * Crgtab = cconvert->Cr_g_tab;
register INT32 * Cbgtab = cconvert->Cb_g_tab;
SHIFT_TEMPS
while (--num_rows >= 0) {
inptr0 = input_buf[0][input_row];
inptr1 = input_buf[1][input_row];
inptr2 = input_buf[2][input_row];
input_row++;
outptr = *output_buf++;
for (col = 0; col < num_cols; col++) {
y = GETJSAMPLE(inptr0[col]);
cb = GETJSAMPLE(inptr1[col]);
cr = GETJSAMPLE(inptr2[col]);
/* Range-limiting is essential due to noise introduced by DCT losses. */
outptr[2] = range_limit[y + Crrtab[cr]];
outptr[1] = range_limit[y +
((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr],
SCALEBITS))];
outptr[0] = range_limit[y + Cbbtab[cb]];
outptr[3] = 0xff; // 출력 이미지의 ALPHA는 255(완전불투명)으로 지정한다.
outptr += BGRA_PIXELSIZE;
}
}
}
// grayscale to RGBA format. by cyberyanne@gmail.com, 2010/01/28.
//
METHODDEF(void)
gray_bgra_convert (j_decompress_ptr cinfo,
JSAMPIMAGE input_buf, JDIMENSION input_row,
JSAMPARRAY output_buf, int num_rows)
{
register JSAMPROW inptr, outptr;
register JDIMENSION col;
JDIMENSION num_cols = cinfo->output_width;
while (--num_rows >= 0) {
inptr = input_buf[0][input_row++];
outptr = *output_buf++;
for (col = 0; col < num_cols; col++)
{
/* We can dispense with GETJSAMPLE() here */
outptr[RGB_RED] = outptr[RGB_GREEN] = outptr[RGB_BLUE] = inptr[col];
outptr[3] = 0xff; // 출력 이미지의 알파값은 완전불투명(255)로 지정한다.
outptr += RGBA_PIXELSIZE;
}
}
}
// RGB to BGRA format. by cyberyanne@gmail.com, 2010/01/28.
//
METHODDEF(void)
rgb2bgra_convert (j_decompress_ptr cinfo,
JSAMPIMAGE input_buf, JDIMENSION input_row,
JSAMPARRAY output_buf, int num_rows)
{
register JSAMPROW inptr, outptr;
register JDIMENSION col;
JDIMENSION num_cols = cinfo->output_width;
while (--num_rows >= 0)
{
inptr = input_buf[0][input_row++];
outptr = *output_buf++;
for (col = 0; col < num_cols; col++)
{
/* We can dispense with GETJSAMPLE() here */
outptr[2] = inptr[RGB_RED];
outptr[1] = inptr[RGB_GREEN];
outptr[0] = inptr[RGB_BLUE];
outptr[3] = 0xff; // 출력 이미지의 알파값은 완전불투명(255)로 지정한다.
inptr += RGB_PIXELSIZE;
outptr += BGRA_PIXELSIZE;
}
}
}
jpeglib를 사용하는데 조금이나마 도움이 되었으면 좋겠습니다.
끝.