2014년 11월 13일 목요일

QNX Neutrino에서 램 디스크를 생성하는 여러 가지 방법 (v6.6기준)

1. devb-ram을 사용한 램 디스크 만들기.

1.1 기본값으로 생성하기 
devb-ram을 사용하면 QNX 4 타입의 파일 시스템을 가지는 램 디스크를 메모리에 생성할 수 있다. 아래 그림에서 기본 2MB 용량을 가지는 QNX 4 타입의 램 디스크가 생성 되었음을 확인할 수 있다. 


hd0는 type179 형식(QNX 6 파일 시스템 형식)으로 시스템 메인 하드 디스크이며, 생성된 램 디스크는 hd1로 type 77 형식(QNX 4 파일 시스템 형식)으로 생성되었다. devb-ram이 지원하는 램디스크의 파일 시스템은 QNX 4 파일 시스템이다. 하지만 뒤에서 다른 형식의 파일 시스템을 사용하여 램 디스크를 만드는 방법도 살펴볼 것이다.  

아래 표는 파일시스템 형식별 type번호 표이다.



1.2 장치 이름 바꾸기
기본 생성 시 /dev/아래 hd* 로 생성되는 장치 이름을 disk 옵션 중 name으로 바꿀 수 있다. disk 옵션은 devb-ram이 cam-disk.so 라이브러리로 전달하는 옵션이다. 



1.3  cache 크기 설정
일반적으로 입출력이 느린 블럭 장치는 램 메모리를 cache로 사용하는데, 램 디스크일 경우 cache는 램이 램을 cache하는 것이라 불필요한 성능 저하를 발생시키므로 최소 크기(512k)로 설정하는 것이 좋다. 아쉽게도 최소 크기는 0 바이트가 아니라 512KB 이다. 하지만 설정 시 0으로 설정해도 에러가 나지는 않는다.  cache 설정을 위해서는 blk옵션 중 cache를 사용한다. blk옵션은 devb-ram에서 사용하는 io-blk.so 라이브러로 전달되는 옵션이다. 


Q.) 실제 설정된 cache의 크기는 어떻게 확인할 수 있을까 ? 0으로 설정시 최소 크기인 512k로 설정되는지 확인하는 방법은 ? 

A) blk에 verbose옵션을 추가한다. 서브옵션이 여러가지 일 경우 ,(콤마)를 사용하여 나열하면 된다.
devb-ram disk name=rdsk blk cache=0,verbose
verbose옵션으로 추가된 로그는 sloginfo 명령으로 확인할 수 있다.


1.4 용량 변경
기본으로 생성되는 ramdisk는 512Byte의 blksize와 4096Byte의 capacity를 가진다. 실제 용량은 이 두 값의 곱으로 결정된다. (512 x 4096 = 2MB). 이 두 값은 ram 옵션을 사용하여 변경 가능하다.  아래와 같이 설정시 10MB(10240 x 1024)크기의 ramdisk가 생성된다. 



2. io-blk 내장 기능을 사용하여 램 디스크 만들기 

2.1 기본 사용 방법
devb-ram을 사용할 경우 기본적으로 cache를 사용하게 되므로 램 디스크에서는 불필요한 메모리 복사가 발생하게 된다. 이를 해결하기 위해서 io-blk.so에 내장된 ramdisk 기능을 사용할 수 있다. (이 ramdisk는 cache를 사용하지 않는다.)  io-blk.so는 어떤 블럭 장치 드라이버(devb-xxx)와도 함께 사용이 가능하므로 devb-ram이 아닌 다른 devb-xxxx를 사용하고 있다면, 굳이 devb-ram을 사용할 필요는 없다. 

아래 예에서는 devb-ram과 함께 사용하였는데, 이때 기본적으로 devb-ram이 생성하는 ramdisk를 사용하지 않을 것이므로 이름을 notuse로, 용량은 최소 용량(1 x 512 = 512 byte)으로 설정하였다. 또한 ram 옵션 중 nodinit을 설정하여 생성한 램 디스크를 파티션 및 포맷하지 않도록 한다.  이런한 설정들은 devb-ram이 아닌 다른 블럭 장치 드라이버를 사용할 경우에는 필요가 없다.  ( 여기서는 io-blk.so의 램 디스크 기능을 사용하는 경우이기 때문에 devb-ram의 램 디스크 기능을 최소화 하기 위해서 필요한 설정임) 이번 예에서도 io-blk가 생성하는 램 디스크 용량은 10MB로 하였다.  io-blk.so가 제공하는 램 디스크 기능을 사용 시에는 램 디스크 장치 이름은 변경할 수 없으며 항상 /dev/ram* 로 고정된다. 



- dinit는 생성 된 램 디스크를 파티션하고 QNX 4 타입의 파일 시스템으로 포맷하여 준다.
- cache=0으로 설정 되었지만 512KB가 최소 값이므로 512KB로 설정된다.  이는 램 디스크가 사용하는 것이 아닌, devb-ram이 사용하는 것이다. 위 예에서는 devb-ram이 제공하는 램 디스크를 사용하지 않으므로 이 cache는 그냥 낭비되는 공간이 된다. 하지만 다른 devb-xxx 드라이버를 사용하여 io-blk.so의 램 디스크 기능을 사용한다면, 이 캐시는 해당 블럭 장치가 사용하게 될 것이다. 

This approach has superior performance because it eliminates the memory-to-memory copies of devb-ram, it bypasses cache lookups, and the 4 KB sectors have smaller overheads.


2.2 다른 type의 파일 시스템 사용하기
devb-ram가 제공하는 램 디스크는 QNX 4 형식의 파일 시스템만 사용이 가능하지만, io-blk.so의 내장 램 디스크는 qnx4 파일 시스템 이외에 다른 파일 시스템을 사용할 수 있다. 그러기 위해서는 dinit대신 fdisk를 사용하여 파티션을 수행한 후 원하는 파일 시스템으로 포맷하면 된다. 아래는 dos 파일 시스템을 사용한 예이다. 


By default in QNX Neutrino 6.5 and later, io-blk.so allocates the filesystem buffer cache (blk cache=) on affected ARM platforms from a global memory region (SHMCTL_ANON | SHMCTL_GLOBAL) to avoid the per-process 32 MB limitation. To override this and make the allocation from the normal devb-* process heap, specify blk memory=sysram.
In QNX Neutrino 6.5 and later, io-blk.so by default allocates the filesystem buffer cache (blk cache=) on affected ARM platforms from a global memory region (SHMCTL_ANON | SHMCTL_GLOBAL) to avoid the per-process 32 MB limitation. To override this and make the allocation from the normaldevb-* process heap, specify blk memory=sysram.
 
3. devf-ram을 사용한 램 디스크
devf-ram은 ram상에 QNX의 NOR flash용 파일 시스템인 ffs3를 만들어 준다. 아래와 같이 /dev/fs* 가 생성되며, flashctl 유틸리티를 사용하여 파티션, 포맷 및 파일 시스템에 mount할 수 있다. devf 파일 시스템은 mount 명령은 지원하지 않는다. 
flashcltl에서 -p 옵션으로 마운트할 장치를 선택하고, -e -f 는 각각  erase및 format을 뜻한다. -n으로 mount할 위치를 정하며, -m이 mount 옵션이다. 




주의: 디스크의 크기는 실제 NOR flash의 크기와 마찬가지로 2의 n제곱 단위로 설정해야 한다. (1MB, 2MB,4MB,8MB ...)  예를들어 10MB로 2의 n제곱이 아닌 값으로 설정할 경우에는 10MB가 할당되지 않는다.

4. io-fs-media
io-fs-media를 사용하면 아래와 같이 사용이 가능하다. 

io-fs-media -d tmp,mount=/full_path_to/your_desired_mountpoint

5. /dev/shmem
기본적으로 QNX Netrion는  /dev/shmem에 RAM-based filesystem을 제공한다. 하지만 이는 완전한 파일 시스템이 아니며, 100% POSIX 호환이 되지는 않는다. 따라서 몇몇 utility사용에 제약이 있으며, sub-directory도 지원하지 않는다.  일반적으로 아래와 같이 /dev/shmem을 /tmp에 링크시켜 사용한다. 
ln -sP /dev/shmem /tmp

2013년 9월 30일 월요일

stack frame 해석하기

GDB에서 출력되는 frame 정보 해석

Sample Program
------------------------------------------------------------------------------------------------------
1  #include <stdio.h>
2
3  int func1(int arg1, int arg2);
4  int func2(int a1,int a2);
5
6  int func1(int arg1, int arg2)   //시작주소는 0x804841C, gdb에서 x func1명령으로 확인 가능
7  {
8      int temp=3;
9      temp = func2(arg1, arg2); // 이 명령 위치는 0x0804843b, 본문 참조
10    return temp;
11 }
12
13
14 int func2(int a1, int a2)   //시작주소는 0x8048443, gdb에서 x func2명령으로 확인 가능
15 {
16     int tmp=9;
17     return a1+a2+tmp;
18 }
19
20 int main(void)   //시작주소는 0x804845f, gdb에서 x main명령으로 확인 가능
21 {
22     int a=10, b=40,c=0;
23  
24     c = func1 (a,b); //이 명령의 위치는  0x08048494, 본문 참조
25
26     printf(" c = %d\n");
27
28     return 0;
29 }
30
------------------------------------------------------------------------------------------------------
(gdb) bt
#0 func2 (a1=10,a2=40) at a.c:17
#1 0x0804843b in func1 (arg1=10,arg2=40) at a.c:9
#2 0x08048494 in main ( ) at a.c:24
------------------------------------------------------------------------------------------------------

#0 func2 (a1=10,a2=40) at a.c:17 
   - 현재 sample program의 파일 이름이 a.c
   - program이 a.c파일의 17번째 라인이 실행 될 차례임.
   - func2가 호출될 때 a1=10, a2=40으로 호출되었음  위 내용은 frame 명령으로 재확인 가능
----------------------------------------------
(gdb) frame
 #0 func2(a1=10, a2=40) at a.c:17
17                       return a1+a2+tmp;
----------------------------------------------

#1 0x0804843b in func1 (arg1=10,arg2=40) at a.c:9
   - func2는 func1에서 호출되었고 이는 a.c파일 Line 9에서 호출됨

#2 0x08048494 in main ( ) at a.c:24
   - func1은 a.c파일에 있는 main 함수의 Line 24 에서 호출됨

------------------------------------------------------------------------------------------------------
(gdb) info frame 1
Stack frame at 0xbffff160
  eip = 0x804843b in func1 (a.c:9); saved eip 0x8048494
  called by frame at 0xbffff190, caller of frame at 0xbffff130
  source language c.
  Arglist at 0xbffff158, args: arg1=10, arg2=40
  Locals at 0xbffff158, Previous frame's sp is 0xbffff160
  Saved registers:
    ebp at 0xbffff158, eip at 0xbffff15c
------------------------------------------------------------------------------------------------------

(gdb) info frame 1
   - func1번의 stack frame 정보 요청

Stack frame at 0xbffff160
   - func1의 stack frame은 0xbffff160부터 아래쪽(낮은 주소)임.
   - 0xbffff160 - 4 부터 낮은 주소쪽으로 func1의 stack frame임.  (stack은 높은 주소에서 낮은 주소 방향으로 자라남)

eip = 0x804843b in func1 (a.c:9); saved eip 0x8048494
   - func1에서 func2를 호출한 func1의 코드 위치는 a.c파일의 9라인이며 이 주소는 0x0804843b(eip)임.  stack frame상의 최하위 프레임(주소가 낮은)의 경우 이 값이 현재의 cpu의 instruction pointer값임.
   - saved eip는 func1이 호출된 후 실행되어야 할 명령이 있는 main함수상에서 func1호출 다음에 있는 명령의 주소임.

called by frame at 0xbffff190, caller of frame at 0xbffff130
   - func1를 호출한 함수(main)의 stack frame은 0xbffff190이며, func1이 호출한 함수(func2)의 stack fame은 0xbffff130임.  호출할수록 stack frame의 주소가 작아짐.

source language c.
    
Arglist at 0xbffff158, args: arg1=10, arg2=40
    - func2의 argument는 2개 이며 각각 10, 40의 값을 가지고 호출 되었음.
    - 0xbffff158은 func2의 bp이며 arg1은 bp +8, arg2는 bp+12
    - bp(base pointer)과 arg1사이(bf+4)에는 return address가 있음.

Locals at 0xbffff158, Previous frame's sp is 0xbffff160
     
Saved registers:
    ebp at 0xbffff158, eip at 0xbffff15c
   - func2의 ebp는 0xbffff158임.  ebp의 역할은 해당k 함수에서 기준 위치로 첫번째 변수는 ebp-4, 두번째 변수는 ebp-8등으로 stack상의 지역변수를 ebp로 부터의 offset으로 표시함.  (꼭 첫번째 변수가 ebp - 4가 되는 것은 아님)
   - func2의 eip는 0xbffff15C에  저장되어있으며 이는 func1에서 func2를 호출한 다음 명령의 주소임. 이 값은 위에 saved eip값으로 확인 할 수 있음.
    
0xBFFFF190 : 이 아래부터 main의 stack frame
------------------------------------------------------------
0xBFFFF18C : main의 return address
0xBFFFF188 : main의 bp
...
0xBFFFF17C : main의 변수 c  =>  -12(%ebp)와 같이 표시됨 bp로 부터 12바이트 아래 있음.
0xBFFFF178  : main의 변수 b =>  -16(%ebp)
0xBFFFF174  : main의 변수 a
...
0xBFFFF164  : arg2 = 40
0xBFFFF160  : arg1 = 10  : 이 아래부터 func1의 stack frame
------------------------------------------------------------
0xBFFFF15C : func1의 return address : main함수에서 func1이 return되면 다음에 실행할 명령의 주소(ip:instruction pointer)
0xBFFFF158 : func1의 bp
...
0xBFFFF14c : func1의 변수 temp  => -12(%ebp)와 같이 표시됨 bp로 부터 12바이트 아래 있음.
..
0xBFFFF134 : a2 = 40
0xBFFFF130 : a1 = 10 : 이 아래부터 func2의 stack frame
------------------------------------------------------------
0xBFFFF12C: func2의 return address
0xBFFFF128: func2의 bp
0xBFFFF124: func2의 첫번째 변수 tmp  -4(%ebp)k


각 함수의 시작 주소는 아래와 같이 확인 가능
------------------------------------------------------------------------------------------------------
(gdb) x func1
0x804841C
(gdb) x func2
0x8048443
(gdb) x main
0x804845f

------------------------------------------------------------------------------------------------------
(gdb) x/20i func2
  ==>  func2시작부터 20줄의 역어셈블 코드가 출력됨  i는 역 어셈블하라는 뜻

2013년 7월 1일 월요일

printf와 시스템 성능 그리고 UART baud rate

CPU 사용율이 높지 않음에도 불구하고 이유없이 프로그램 수행 속도가 극도로 느려지는 문제가 발생. 예상되는 원인은 과다한 로그 출력. 임베디드 환경에서는 로그 출력이 UART를 통하여 이루어 지는 경우가 많은데, 이 때 UART의 baud rate이 프로그램 동작의 병목으로 작용하는 것이다. 비록 버퍼가 있다고 하지만, 로그양이 버퍼양을 넘어서면 printf등의 함수도 바로 리턴하지 못하고, 대기할 수 밖에 없을 것이다.  하지만 로그가 많다고 무턱대도 이를 줄이는 것도 쉽지 않은 일이다. 각각의 로그는 각 개발자들이 문제 발생을 확인하고 해결하기 위하여 최적화하여 넣어놓은 것으로 이를 제거하면 나중에 디버깅이 어려워지거나 불가능해 지기 때문이다.

가설 검증을 위해 로그 출력에 사용되는 stderr/stdout용 serial(UART)장치 즉 /dev/ser1의 baud rate을 올려보았다. 문제 해결!!!  수정된 이미지를 배포하지만 얼마 지나지 않아서 생산기술에서 연락이 온다. Baud rate 변경으로 인해 생산 테스트 장비를 모두 바꿔야하고, Baud rate 상승으로 인한 통신 에러 증가가 예상되어 절대로 Baud rate를 변경하면 안된단다.   다시 원점.

Baud rate이 115200 bps라면 대충 계산해도 115,200/8=14,400 character 이상의 로그는 (1초에)출력이 불가능하다. 실제로는 UART 전송 시 설정에 따라  Start/Stop bit 및 1 char당 bit수 그리고 char간 delay 등을 고려해야하고 이 경우 실제 전송 가능한 char수는 14,400보다 훨씬 적어지게 된다.

따라서 Baud rate을 올리는 방법으로 문제 개선이 가능하지만, 예상치 못한 문제로 인하여 Baud rate는 올리지 못하는 것으로 결론.

어림 계산으로 1초에 10,000 글자가 전송 가능하다고 하고, 라인당 100글자를 넘지 않는다고 하면, 1초에 100줄 정도의 로그가 출력 가능. 로그가  1초에 100줄이 넘지 않는다는 가정하에 로그 출력을 담당하는 전담 thread를 만든다면 다른 thread는 baud rate과 상관 없이 동작 가능할 것 이다.

로그를 출력하는 매크로를 사용하여 printf 대신 매크로를 사용하여 메모리에 로그를 기록하고, 메모리는 mutex로 보호한다.  로그 출력 thread에서는 계속해서 메모리에서 로그를 읽어와 puts로 출력하도록 하여 문제 해결. (단 1초에 100줄 이상의 로그가 지속적으로 발생한다면, 로그용 메모리 버퍼가 full나면서 mutex block이 걸리는 시간이 늘어나 다시 시스템 성능이 느려질 수 있다. )


Linux Vs QNX Neutrino 파일 디스크립터 관련

리눅스에서는 한 프로세스에서 파일 디스크립터가 할당 될 때마다 그 값이 증가하게 된다. 예를 들어 어떤 프로세스가 처음으로 파일을 open할 경우 3이 할당되며, 이후 4,5,6 순으로 파일 디스크립터가 할당되어 나간다. (0,1,2는 각각 stdin, stdout, stderr에 할당되어 있다) 프로그램이 실행하여 나가면서 파일이 close되는 경우 해당 파일 디스크립터가 반환되게 되는데, 파일 디스크립터가 할당 될 때에는 값이 항상 증가만 하므로, 중간에 반환된 이러한 파일 디스크립터들은 바로 재사용되지 않게 된다. 계속된 할당으로 파일 디스크립터가 사용 가능한 최대 값에 이르면 비로서 다시 0 부터 시작하여 반환된 파일 디스크립터를 찾아서 할당에 사용하게 된다.

최근에 경험한 바에 의하면, QNX Neutrino OS에서는 파일 디스크립터가 반환되면, 바로 다음 open 요청에 재사용되게 된다. 아마도 파일 디스크립터 할당 시, 항상 가장 작은 사용 가능한 파일 디스크립터를 반환하는 식으로 구현이 되어 있는 것으로 추정된다.  이 경우 user application의 상당한 주의가 요구된다. 만일 어떤 파일 디스크립터를 close 한 후에 실수로 한번 더 close를 할 경우, 두번째 close하는 파일 디스크립터가 이미 다른 thread에서의 open에 재사용되는 경우가 발생할 수 있기 때문이다. 이 경우 영문을 모른채 file이 open되자마자 close되어 버리는 문제가 발생하고, 이는 꽤 원인을 파악하기 어려운 문제가 된다.

리눅스에서는 파일 디스크립터를 계속 증가시키는 방법으로 이러한 문제를 영리하게 피해간 것으로 보이는데, QNX Neutrino에서는 관련해서 수정 계획이 없다고 하니, 계속해서 주의가 필요할 것으로 보인다.


2013년 5월 9일 목요일

ISO26262관련 정리

ISO26262에는 5개의 등급이 있으며, 등급별 FIT(Failure In Time)는 아래와 같다.
즉 1 FIT는 10^9 시간(11만 4155년)동안 에러가 발생하는 횟수를 의미한다.

                 QM         ASIL A       ASIL B       ASIL C       ASIL D
--------------------------------------------------------------------------------------
FIT      1000이상   1000이하   100이하    100이하     10이하
  • FIT = Failure in Time = 1 failure / 10^9 hours
  • 1 year MTTF = 109 / (24 * 365) FIT = 114,155 FIT
  • SER FIT = SDC FIT + DUE FIT               

가령 1000년동안 어떤 HW 장치가 동작중 1번만 failure가 발생하여도 114 FIT이므로, ASIL level B를 만족시키지 못하게 된다.

표에서 알 수 있듯이 ASIL D를 만족시킨다는 것은 10 FIT이하아며 이는 10^8 시간 동안 에러가 한번 이하로 발생하는 것이다. (1만 1415년동안 1회 이하의 에러발생)

추가 정보:
  • SDC = Silent Data Corruption
  • DUE = Detected + Unrecoverable Error
  • SER = Soft Error Rate =  SDC + DUE 
  • MTTF = Mean Time to Failure
  • MTTR = Mean Time to Repair
  • MTBF = Mean Time Between Failures = MTTF + MTTR
  • Availability = MTTF / MTBF

2012년 3월 28일 수요일

최근 6년간 구매한 개발 관련 서적 리스트

교보문고에서 구매한 것만...

구매일자 상품명 주문금액
 
2012-03-27 GoF의 디자인 패턴(개정판)(양장본 HardCover) ₩25,000
2012-03-21 소프트웨어 아키텍처 이론과 실제 ₩36,000
2012-03-21 소프트웨어 아키텍처 문서화 (에이콘 소프트웨어 아키텍처 시리즈 3) ₩36,000
2012-03-04 프로그래머 열정을 말하다 ₩14,000
2012-03-04 프로그래머 그 다음 이야기 (사람과 프로그래머 1) ₩14,800
2012-03-04 애자일 마스터 ₩20,000
2012-03-04 시스코 네트워킹(3RD EDITION)(후니의 쉽게 쓴)(3판)(CD1장포함) ₩32,000
2012-02-18 무선 네트워크 공격과 방어(해킹 초보를 위한) (에이콘 해킹 보안 시리즈 31) ₩20,000
2012-02-18 데이터통신과 컴퓨터 네트워킹(IT Cookbook 한빛교재 시리즈 303) ₩24,000
2012-02-18 인사이드 VMware vSphere 4(비제이퍼블릭 인사이드 시리즈 2) ₩26,000
2012-02-18 커맨드라인 활용 가이드(윈도우 시스템 관리자를 위한) ₩35,000
2012-01-07 개발자를 부탁해 ₩13,000
2012-01-07 차세대통신망의 IMS와 VOIP ₩25,000
2012-01-07 프로세서를 지탱하는 기술 ₩27,000
2011-11-20 통신생활 속이 보인다(유비쿼터스를 겨냥한) (Whats 9) ₩17,000
2011-11-20 차세대 광대역통합망의 이해 ₩28,000
2011-11-20 이동통신 입문(IT Cookbook 한빛교재 시리즈 302) ₩30,000
2011-10-29 손에 잡히는 정규 표현식 ₩14,800
2011-10-29 펄(거침없이 배우는) ₩27,000
2011-10-29 CERT C 프로그래밍(버그 없는 안전한 소프트웨어를 위한) (에이콘 해킹 보안 시리즈 25) ₩40,000
2011-10-29 안드로이드 아나토미 (개발자가 행복한 세상 플랫폼 시리즈 1) ₩40,000
2011-10-01 LTE를 통한 3G 진화(SECOND EDITION) ₩29,000
2011-10-01 3G 4G 이동통신 시스템(쉽게 설명한)(개정판) ₩35,000
2011-09-18 NHN은 이렇게 한다 소프트웨어 품질관리 ₩18,000
2011-09-18 소프트웨어 아키텍트가 알아야 할 97가지 ₩20,000
2011-09-18 데미안의 WI FI ON ₩30,000
2011-09-18 CAN LIN FlexRay를 활용한 차량용 네트워크(에이콘 임베디드 시스템 프로그 ₩40,000
2011-07-17 팀장의 역할 ₩13,000
2011-07-17 그린카 콘서트 (양장본 HardCover) ₩15,000
2011-07-17 시리얼 포트 완전정복 ₩30,000
2011-07-17 안드로이드의 모든 것 분석과 포팅 (한빛미디어 모바일 시리즈 11) ₩40,000
2010-11-27 스타트업 바이블 ₩13,000
2010-11-27 DEBUG HACKS ₩27,000
2010-11-13 허수아비춤 (양장본 HardCover) ₩12,000
2010-11-13 스티브 잡스 이야기(청소년 롤모델 시리즈 5) ₩12,000
2010-11-13 애플 VS 구글 ₩12,000
2010-11-13 고약한 문제 합당한 해결 ₩14,000
2010-11-13 안드로이드 2.2 프로그래밍(프로요)(위키북스 임베디드 모바일 시리즈 7) ₩36,000
2010-09-19 인텔스레딩 빌딩블록 ₩24,000
2010-09-04 유닉스 리눅스 프로그래밍 필수 유틸리티(개정판) ₩34,000
2010-05-30 JAVA가 보이는 그림책(개정증보판) ₩14,000
2010-05-30 성공과 실패를 결정하는 1%의 JAVA 프로그래밍 원리 ₩14,800
2010-05-30 C#이 보이는 그림책 ₩15,000
2010-05-30 멀티코어 CPU 이야기(프로그래머가 몰랐던) (BLOG2BOOK 09) ₩22,000
2010-04-25 GIT 분산 버전 관리 시스템 ₩20,000
2010-04-25 FEDORA LINUX TOOLBOX ₩22,000
2010-04-25 QT 실전 프로그래밍 ₩28,000
2010-04-25 안드로이드: 플랫폼 포팅과 활용 ₩28,000
2010-04-25 리눅스 쉘 스크립트 프로그래밍 입문(김태용의) ₩32,000
2010-04-25 TCP IP 완벽 가이드 ₩50,000
2010-04-10 통쾌한 인간관리 이야기(IT 개발자가 쓴) ₩15,000
2010-04-10 TI CORTEX-M3 펌웨어 개발 ₩25,000
2010-02-16 임베디드 리눅스 입문 ₩23,000
2010-02-16 MORE JOEL ON SOFTWARE(조엘 온 소프트웨어를 넘어서) ₩23,000
2010-02-16 EMBEDED USB INSIDE ₩25,000
2010-02-16 UML로 EMBEDDED SYSTEM 프로그래밍하기 ₩25,000
2010-02-16 ARM CORTEX-M3 완벽가이드 ₩25,000
2010-02-04 임베디드 USB 완벽 마스터 세트 (전2권) ₩59,000
2009-10-16 ADVANCED UNIX PROGRAMMING (제2판) ₩32,000
2009-10-16 MICRO C/OS-2 실시간 커널(보급판)(CD1) ₩35,000
2009-09-05 HARD CODE ₩25,000
2009-09-05 윈도우 프로젝트 필수 유틸리티 (CD1장) ₩26,000
2009-08-08 초난감 기업의 조건 ₩18,000
2009-08-08 소프트웨어 컨플릭트 2.0 (시대를 뛰어넘는 즐거운 논쟁) ₩22,000
2009-08-08 소프트웨어 크리에이티비티 2.0 (위기북스 IT LEADERS 8) ₩25,000
2009-05-31 겸손한 개발자가 만든 거만한 소프트웨어 ₩16,800
2009-05-09 WINDOWS CE 실전 가이드 ₩40,000
2009-03-29 루비(입문자를 위한) ₩18,000
2009-03-29 Programming Perl, 3/E ₩52,000
2009-03-27 언어 자료 처리를 위한 PERL ₩10,000
2009-03-06 C++ 핵심공략(꼭 알아야 할) ₩13,000
2009-03-06 MORE EFFECTIVE C++ ₩20,000
2009-03-06 맨먼스 미신 ₩24,000
2009-03-06 드리밍 인 코드 ₩25,000
2009-03-06 RAPID DEVELOPMENT 프로젝트 쾌속 개발전략 ₩28,000
2008-12-26 임베디드 리눅스 시스템 설계와 개발 ₩25,000
2008-05-24 리팩토링 ₩25,000
2008-05-24 HEAD FIRST DESIGN PATTERNS ₩28,000
2008-05-17 다이어그램으로 쉽게 배우는 UML ₩18,000
2008-05-17 HEAD FIRST OBJECT ORIENTED ANALYSIS DESIGN ₩28,000
2008-05-05 아키텍트 이야기 ₩12,000
2008-05-05 CODE CRAFT: 뛰어난 코드 작성을 위한 실천 지침 ₩28,000
2008-04-26 애자일 프랙티스 (애자일 시리즈 004) ₩18,000
2008-01-09 무선 LAN 보안 프로토콜 ₩28,000
2007-11-10 Fundamentals of Wireless Networking ₩29,000
2007-11-10 Bulletproof Wireless Security(Paperback) ₩35,000
2007-10-20 리눅스 커널 2.6 구조와 원리 ₩28,000
2006-10-14 조엘 온 소프트웨어 ₩22,000
2006-10-14 소프트웨어 블로그 베스트 29선(조엘이 엄선한) ₩22,000
2006-10-14 실용주의 프로그래머 (프로그램 프로그래밍 프로그래머 2)(양장본 HardCover) ₩25,000
₩2,265,200


내가 미쳤지...