원래대로라면 이 시간에 애기랑 같이 자고 있겠지만, 어제 좀 일찍 잠이 든 관계로~!!! 아침에 웹서핑을 하고 있습니다. 그것도 아주 여유롭고 편안하게 말이죠. >ㅁ<)-b 아유~ 너무 신나는군요. 이게 얼마 만에 여유인지... ㅠㅠ 

즐겁게 웹을 한참 돌다보니 재미있는 PPT를 발견했는데요, 그것이 무엇이냐하면... 저에게 한 줄기 빛을 주신 monaca님께서 만드신 PPT입니다~!!! 제목도 엄청 도전적입니다. “프로그래머는 치킨집을 차릴 수 있는가?” PPT는 아래에서 보실 수 있습니다.

 


결론부터 말씀드리면... “치킨집을 차릴 노력이면 프로그래머로서도 충분히 성공할 수 있다~!!!” 가 되겠네요. ^^;;; PPT를 보시면 아시겠지만 상당히 현실적인 비용(?) 문제를 다루고 있으니, 혹시 창업의 꿈을 가지고 계신 분이 있다면 한 번 보시는 것도 나쁘지 않을 것 같습니다. 재미삼아 말이죠. ;)

간만에 주옥같은 PPT를 봐서 한자 남깁니다. monaca님, 앞으로 좋은 PPT 많이 부탁드립니다. ^^)/~ 

 ps) 보내주신 책들은 잘 보고 있습니다. ㅠㅠ 일이 갑자기 몰아치는 바람에 이제야 보기 시작했네요. 
       보는대로 후기를 올리겠습니다. >O<)/~~




프로그래밍을 하다보면 가끔 구문 강조(Syntax Highlight) 기능이 굉장히 고마울때가 있습니다(혹시 저만 그런가요? ^^;;;) 그래서 구문 강조(Syntax Highlight) 스타일들을 보면서 나름 신경을 쓰는 편인데요, 오늘 우연히 깔끔한 스타일을 발견했습니다.

Solarized라고 하는 스타일인데요, 일단 아래 스크린샷을 한번 보시죠. ^^

스타일이 워낙 사람들마다 호불호가 갈리는 부분이라... 보기에 불편하신 분들도 있겠지만, 제가 살짝 테스트해본 결과 나름대로 괜찮았습니다. 지금 vi 설정도 이 스타일로 바꿔놨어요. ^^;;;

vi 뿐만 아니라, 비주얼 스튜디오같은 다른 에디터들도 지원하니 흥미가 있으면 한번 들어가보는 것도 괜찮을 듯 합니다.

그럼 좋은 하루 되세요 ;)

무엇을 개발하느냐에 따라서 차이가 있겠지만, 윈도우 환경의 최강 개발툴을 꼽으라면 저는 당연히 비주얼 스튜디오(Visual Studio)라고 생각합니다. 강력한 자동완성(Auto-complete) 기능과 디버깅(Debuging) 기능이 정말 환상적이거든요. ^^;;;

그런데, 최강 비주얼 스튜디오에도 단점이 한 가지있으니... 바로 실행되는데 시간이 좀 걸린다는 겁니다. @0@)-b 뭐, 간단한 소스 파일 살짝 훑어 보려는데, 비주얼 스튜디오를 실행하기에는 부담스러운 경우가 종종 있더라구요.

그래서 제가 선택한 방법은... vi와 ctags, taglist 조합을 사용하는 겁니다. vi의 강력한 검색기능과 ctags의 함수 & 구조체 분석 능력, 그리고 taglist의 바로가기 기능은 간단한 파일을 분석하기에는 충분하거든요. ^^

설치 방법

먼저 ctags를 다운로드 해야 합니다. ctags는 [http://ctags.sourceforge.net/][]에서 다운로드할 수 있고, 압축을 푼다음 ctags.exe만 vi가 설치된 폴더에 복사해줍니다. 일반적으로 vi 폴더는 Program Files\Vim\vim73과 같은 형태입니다.

ctags를 설치했다면 이제, taglist를 설치할 차례입니다. taglist는 [http://www.vim.org/scripts/script.php?script_id=273][]에서 다운로드할 수 있고, 압축을 푼 다음 doc 폴더와 plugin 폴더를 vi가 설치된 폴더에 그대로 복사하면 됩니다. vi가 설치된 폴더를 보면 같은 이름의 폴더가 있으니 그리 어렵지 않을 겁니다. ^^;;;

실행~!!

설치가 끝났으니 남은 일은 소스 코드를 vi로 열어서 결과를 확인하는 것 뿐이네요. vi를 실행하신 다음 아무 코드나 연 다음 :Tlist를 입력하시면 아래처럼 왼쪽에 함수 목록이 표시됩니다. >ㅁ<)-b



그럼 즐거운 코딩하세요 ;)


ps) 이것 참... 이게 얼마만에 여유인가 모르겠네요. ㅠㅠ)-b 아빠들 화이팅~!!

      

그동안 아주 고맙게 잘 써왔던 스프링노트 서비스가 2012년 9월 27일을 기점으로 종료된답니다. ㅠㅠ 서비스 종료에 대한 자세한 내용은 여기에서 보실 수 있습니다.


<스프링노트 서비스>

다른 분들은 어떠실지 모르겠지만, 개인적으로는 상당히 유감입니다. 얼추 2004년부터 스프링노트에 데이터를 저장해왔고, 스프링노트(Springnote)와 블로그(티스토리, 이글루스) 동시에 활용하기라는 글을 쓸 정도로 열혈 유저였으니까요. ^^;;;; 사실 OS 만드는 내용도 스프링노트를 활용해서 정리했는데... 스프링노트가 없었더라면 64비트 멀티코어 OS 원리와 구조 세트도 아마 못나왔을 겁니다. ㅠㅠ

에디터가 간혹 오류가 나는 문제가 있긴 했지만... 정말 좋은 서비스였는데 좀 안타깝네요. 수익 모델을 찾지 못한 게 큰 원인이겠지만... 아쉬운 마음은 어쩔수가 없네요. ㅎㅎ 다른 서비스를 찾아봐야할 듯...

감사합니다. 오픈마루 스튜디오~!! 그동안 정말 잘 썼습니다. ㅠㅠ
안녕~ 스프링노트 ㅠㅠ

ps) 개인적으로 마크다운 기반의 글 쓰기 환경을 찾고 있는데...
      어디 좋은 거 없을까요? ㅎㅎ




<히가시노 게이고의 마구 - 출처 yes24>


"9회말 2사 만루 위기를 맞은 가이오 고교의 투수이자 천재 투수로 불리는 스다 다케시는 혼신의 힘을 담은 마지막 한구를 던진다. 그러나 마지막 한 구는 폭투가 되어 팀을 패배로 이끌고 그렇게 가이오 고교의 야구는 막을 내린다. 그리고 며칠 후, 스다와 배터리로 활약했던 포수 기타오카가 죽임을 당하고 경찰은 기타오카의 앨범에서 마구(魔球)라는 글자를 찾아낸다. 한편, 한 전기회사에서 폭발물이 발견되는데, 폭발물을 조사한 결과 실제로 폭발시키려는 의도는 없던 걸로 밝혀진다. 전혀 연관성이 없는 것 같은 두 사건을 조사하던 중, 경찰과 가족들은 잔혹한 진실을 마주하는데...."


제가 좋아하는 히가시노 게이고의 소설 마구(魔球)의 줄거리입니다. ^^ 주말이면 제가 밤시간을 맡아서 애기를 보는데요, 마침 애기가 잘 자줘서(고맙다 시율아 ㅠㅠ) 소설 한 편을 읽을 수 있었네요. 히가시노 게이고의 소설은 묘사가 간결하고 호흡이 빨라서 눈을 땔수가 없다는 건데요, 그래서 그런지 이번 소설 역시 단숨에 읽어버렸네요. 


이번 소설을 읽고나니 가족을 위해 이렇게 힘쓰는 사람도 있구나 하는 생각이 들었습니다. 그리고 저도 더 가족들한테 잘해야겠다는 생각이 들더라구요. ^^;;;


무료하고 반복되는 일상에 지쳐있다면 히가시노 게이고의 "마구"를 읽어보시는 건 어떨까요?

이제 주니어가 갓 50일을 지난지라 애보기에 여념이 없는데요, 요즘 드는 생각이... 애기를 보면서 뭘 좀 같이 할 수 있으면 좋겠더라구요. ^^;;; 사실 애기보면서 스마트폰도 사용해보고, 킨들 파이어도 써봤지만... 뭔가 읽는데 특화된 디바이스라서 그런지 글을 쓰거나 코딩(?)을 하는데는 영 불편합니다. ㅠ. ㅠ

 

애기보면서 코딩을 한다는 생각 자체가 일단 좀... 글러먹었을지도(?!) 모르겠는데... 그래도 아쉬운 건... ㅠㅠ 지금도 와이프랑 애기가 둘다 자고 있어서 겨우 컴퓨터 앞에 앉아있는데... 눈치를 안보고 애기를 보면서 생산적(?)인 일을 할 수 있는 디바이스가 있으면 좋겠네요. ㅠㅠ

 

혹시 그런 디바이스있으면 추천 좀 부탁드려요 >ㅁ<)-b

괜찮은 아이디어면 밥 한끼 쏘겠습니다. ㅋㅋ

리눅스 커널 부팅과정(Linux Kernel Booting Sequence) 분석

윈도우에 이어 리눅스도 부팅과정을 분석하고 있는데요, 소스코드가 있으니 윈도우보다는 그나마 조금 낫네요. ^^;;;; 여기저기서 자료를 좀 모았는데, 나름 정리해봅니다.

1.GRUB

윈도우에는 BOOTMGR이 있듯이 리눅스에는 GRUB이 있습니다. 이 GRUB의 역할은 Disk의 MBR 영역에 있다가 PC 부팅할 때 리눅스 커널(Linux Kernel)을 메모리에 올려주는 것입니다. GRUB은 Stage 1과 Stage 2로 구성되어 있는데요, Stage 1은 우리가 이야기하는 MBR 영역에 있는 1 Sector를 말하고 Stage 2는 Disk에 접근해서 커널을 로딩하는 파트를 말합니다.

  • Stage 1에서는 간단히 몇가지 BIOS 관련 Parameter를 설정하고 Stage 2로 넘어감
  • Stage 2에서는 리눅스 커널 파일(vmlinuz)을 분석해서 리눅스 커널의 부트로더(Bootloader)를 0x90000 ~ 에 로딩하고, 압축된 커널과 커널의 압축을 푸는 부분을 0x100000(1MB) ~ 에 로딩
  • Stage 2 후에는 커널 부트로더를 뛰어 넘어 0x90200 위치로 Jump(?)

사실 마지막 부분에 대한 내용은 제가 제대로 확인을 못해서 좀 애매하긴한데, 한빛미디어에서 나온 리눅스 커널의 이해를 보면 LILO 시절부터 0x90200 어드레스로 Jump를 하고 있습니다. 사실 GRUB 역시 크게 다르지 않을 것이라 생각합니다만... 좀 더 시간을 갖고 확인할 필요가 있을 것 같네요. ^^;;;;

GRUB에 대한 자세한 내용은 The GRUB MBR을 참고하세요 ;)

2. 리눅스 커널 부트로더

리눅스 커널 부트로더는 arch/x86/boot/header.S에 있는데요, 크기가 2 Sector입니다. ^^;;; 따라서 앞부분의 512Byte는 MBR 역할을 하고 그 뒤에 512Byte는 커널 부트로더와 커널을 이어주는 역할을 합니다.

그런데... 예전에는 바로 부팅할 수 있도록 했나 본데, 지금은 LILO나 GRUB의 등장으로 지원하지 않는 것 같습니다. 아래는 header.S에 있는 코드의 일부분인데요, 보시면 플로피를 통한 직접부팅을 지원 안하니까 다시 디스켓 빼라는 메시지와 함께 재부팅하는 코드만 달랑 있습니다. ^^;;;; 사실 이 부분의 코드가 GRUB에 의해 0x90000에 로딩되는 부분입니다.

 18 #include <asm/segment.h>
 19 #include <linux/utsrelease.h>
 20 #include <asm/boot.h>
 21 #include <asm/e820.h>
 22 #include <asm/page_types.h>
 23 #include <asm/setup.h>
 24 #include "boot.h"
 25 #include "offsets.h"
 26 
 27 BOOTSEG         = 0x07C0                /* original address of boot-sector */
 28 SYSSEG          = 0x1000                /* historical load address >> 4 */
 29 
 30 #ifndef SVGA_MODE
 31 #define SVGA_MODE ASK_VGA
 32 #endif
 33 
 34 #ifndef RAMDISK
 35 #define RAMDISK 0
 36 #endif
 37 
 38 #ifndef ROOT_RDONLY
 39 #define ROOT_RDONLY 1
 40 #endif
 41 
 42         .code16
 43         .section ".bstext", "ax"
 44 
 45         .global bootsect_start
 46 bootsect_start:
 47 
 48         # Normalize the start address
 49         ljmp    $BOOTSEG, $start2
 50 
 51 start2:
 52         movw    %cs, %ax
 53         movw    %ax, %ds
 54         movw    %ax, %es
 55         movw    %ax, %ss
 56         xorw    %sp, %sp
 57         sti
 58         cld
 59 
 60         movw    $bugger_off_msg, %si
 61 
 62 msg_loop:
 63         lodsb
 64         andb    %al, %al
 65         jz      bs_die             // <<= 무조건 Jump하면 메시지 출력후 bs_die로 이동~!!
 66         movb    $0xe, %ah
 67         movw    $7, %bx
 68         int     $0x10
 69         jmp     msg_loop
 70 
 71 bs_die:
 72         # Allow the user to press a key, then reboot
 73         xorw    %ax, %ax
 74         int     $0x16
 75         int     $0x19
 76 
 77         # int 0x19 should never return.  In case it does anyway,
 78         # invoke the BIOS reset code...
 79         ljmp    $0xf000,$0xfff0
 80 
 81         .section ".bsdata", "a"
 82 bugger_off_msg:
 83         .ascii  "Direct booting from floppy is no longer supported.\r\n"
 84         .ascii  "Please use a boot loader program instead.\r\n"
 85         .ascii  "\n"
 86         .ascii  "Remove disk and press any key to reboot . . .\r\n"
 87         .byte   0
 88 
 89 
 90         # Kernel attributes; used by setup.  This is part 1 of the
 91         # header, from the old boot sector.
 92 
 93         .section ".header", "a"
 94         .globl  hdr

이 바로 뒤는 당연히 0x90200에 위치하는데요, _start로 시작하며 커널의 bss 영역을 초기화하고 main 함수로 Jump하는 부분이 있습니다. 해당 부분의 코드는 arch/x86/boot/header.S를 참고하세요 ;)

104         # offset 512, entry point
105 
106         .globl  _start
107 _start:
108                 # Explicitly enter this as bytes, or the assembler
109                 # tries to generate a 3-byte jump here, which causes
110                 # everything else to push off to the wrong offset.
111                 .byte   0xeb            # short (2-byte) jump
112                 .byte   start_of_setup-1f
113 1:
114 
115         # Part 2 of the header, from the old setup.S

                             ... 생략 ...

224         .section ".inittext", "ax"
225 start_of_setup:
226 #ifdef SAFE_RESET_DISK_CONTROLLER
227 # Reset the disk controller.
228         movw    $0x0000, %ax            # Reset disk controller
229         movb    $0x80, %dl              # All disks
230         int     $0x13
231 #endif
232 
                             ... 생략 ...
271 
272 # Check signature at end of setup
273         cmpl    $0x5a5aaa55, setup_sig
274         jne     setup_bad
275 
276 # Zero the bss
277         movw    $__bss_start, %di
278         movw    $_end+3, %cx
279         xorl    %eax, %eax
280         subw    %di, %cx
281         shrw    $2, %cx
282         rep; stosl
283 
284 # Jump to C code (should not return)
285         calll   main       // <== 여기가 다음 스텝인 리눅스 커널 로더로 jump하는 부분

3. 리눅스 커널 로더

리눅스 커널 로더는 arch/x86/boot/main.c에 있습니다. 제가 이 코드에 리눅스 "리눅스 커널로더"라는 명칭을 사용해도 될지 모르겠지만, 일단 이 시점에서 32bit 모드로 변환하는 코드가 들어 있어서 커널로더라고 했습니다. ^^;;; 이 코드의 역할은 32bit 커널이 실행될 준비를 하는 건데요, BIOS를 통해 메모리 크기를 확인하고 키보드를 초기화한다음 32bit 커널로 Jump합니다. 이때 Jump하는 어드레스는 32bit 커널이 로딩되어 있는 0x100000(1MB)입니다. ^^;;; 해당 위치에 GRUB이 이미 올려놓았거든요 ;)

아래는 arch/x86/boot/main.c 파일의 일부분입니다. 좀더 자세한 코드는 arch/x86/boot/main.c를 통해서 확인할 수 있습니다. ^^;;;

125 void main(void)
126 {
127         /* First, copy the boot header into the "zeropage" */
128         copy_boot_params();
129 
130         /* End of heap check */
131         init_heap();
132 
133         /* Make sure we have all the proper CPU support */
134         if (validate_cpu()) {
135                 puts("Unable to boot - please use a kernel appropriate "
136                      "for your CPU.\n");
137                 die();
138         }
139 
140         /* Tell the BIOS what CPU mode we intend to run in. */
141         set_bios_mode();
142 
143         /* Detect memory layout */
144         detect_memory();
145 
146         /* Set keyboard repeat rate (why?) */
147         keyboard_set_repeat();
148 
149         /* Query MCA information */
150         query_mca();
151 
152         /* Query Intel SpeedStep (IST) information */
153         query_ist();
154 
155         /* Query APM information */
156 #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
157         query_apm_bios();
158 #endif
159 
160         /* Query EDD information */
161 #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
162         query_edd();
163 #endif
164 
165         /* Set the video mode */
166         set_video();
167 
168         /* Parse command line for 'quiet' and pass it to decompressor. */
169         if (cmdline_find_option_bool("quiet"))
170                 boot_params.hdr.loadflags |= QUIET_FLAG;
171 
172         /* Do the last things and invoke protected mode */
173         go_to_protected_mode();    // <== 여기가 32bit 커널로 Jump하는 부분이에요 :)
174 }

4. 리눅스 커널 - 압축된 커널

커널 로더에 의해서 32bit 커널로 jmp(0x100000)하면 이제 본격적으로 커널이 시작되는데요, 이때 수행되는 함수는 arch/x86/boot/compressed/head_32.S에 있는 가장 첫 번째 함수, startup_32입니다. 파일의 가장 앞부분에 있는 함수기 때문에 0x100000 어드레스로 Jump하는 것만으로 바로 저 함수에 도달하는 것이지요. ^^;;;

startup_32() 함수는 여러가지 일을 하는데요, 가장 중요한 역할은 자신에게 붙어있는 압축된 커널을 안전한 버퍼로 옮긴다음 압축을 푸는 겁니다. >ㅁ<)-b 그리고 압축을 풀고나면 이를 다시 0x400000 어드레스로 옮기고 jump하는 것이지요. 안전한 버퍼라는 말이 조금 애매하긴한데 대략 0x400000 어드레스 부근인 것 같습니다(정확하게는 _end 오프셋 뒷쪽인데요, 이 값은 빌드 타임에 정해지는 값이라 compressed 디렉터리의 코드량에 따라서 좀 달라질 수도 있을 것 같습니다. ^^;;; (정확하지는 않아요 ㅠㅠ)

다음은 압축된 커널을 안전한 곳으로 복사하고 압축을 푼 뒤에 압축이 해제된 곳으로 jump하는 코드입니다. 전체 내용은 arch/x86/boot/compressed/head_32.S를 참조하세요.


 26 #include <linux/linkage.h>
 27 #include <asm/segment.h>
 28 #include <asm/page_types.h>
 29 #include <asm/boot.h>
 30 #include <asm/asm-offsets.h>
 31 
 32 .section ".text.head","ax",@progbits
 33 ENTRY(startup_32)
 34         cld
 35         /* test KEEP_SEGMENTS flag to see if the bootloader is asking
 36          * us to not reload segments */
 37         testb $(1<<6), BP_loadflags(%esi)
 38         jnz 1f
 39 
 40         cli
 41         movl $(__BOOT_DS),%eax
 42         movl %eax,%ds
 43         movl %eax,%es
 44         movl %eax,%fs
 45         movl %eax,%gs
 46         movl %eax,%ss
 47 1:
                     ... 생략 ...

 87 /* Copy the compressed kernel to the end of our buffer
 88  * where decompression in place becomes safe.
 89  */
 90         pushl %esi
 91         leal _end(%ebp), %esi
 92         leal _end(%ebx), %edi
 93         movl $(_end - startup_32), %ecx
 94         std
 95         rep
 96         movsb
 97         cld
 98         popl %esi
 99 
100 /* Compute the kernel start address.
101  */
102 #ifdef CONFIG_RELOCATABLE
103         addl    $(CONFIG_PHYSICAL_ALIGN - 1), %ebp
104         andl    $(~(CONFIG_PHYSICAL_ALIGN - 1)), %ebp
105 #else
106         movl    $LOAD_PHYSICAL_ADDR, %ebp
107 #endif
108 
109 /*
110  * Jump to the relocated address.
111  */
112         leal relocated(%ebx), %eax
113         jmp *%eax
114 ENDPROC(startup_32)
115 
116 .section ".text"
117 relocated:
118 
119 /*
120  * Clear BSS
121  */
122         xorl %eax,%eax
123         leal _edata(%ebx),%edi
124         leal _end(%ebx), %ecx
125         subl %edi,%ecx
126         cld
127         rep
128         stosb
129 
130 /*
131  * Setup the stack for the decompressor
132  */
133         leal boot_stack_end(%ebx), %esp
134 
135 /*
136  * Do the decompression, and jump to the new kernel..
137  */
138         movl output_len(%ebx), %eax
139         pushl %eax
140                         # push arguments for decompress_kernel:
141         pushl %ebp      # output address
142         movl input_len(%ebx), %eax
143         pushl %eax      # input_len
144         leal input_data(%ebx), %eax
145         pushl %eax      # input_data
146         leal boot_heap(%ebx), %eax
147         pushl %eax      # heap area
148         pushl %esi      # real mode pointer
149         call decompress_kernel       <<= 요기가 실제로 압축 해제를 담당하는 부분
150         addl $20, %esp
151         popl %ecx
152 
153 #if CONFIG_RELOCATABLE
154 /* Find the address of the relocations.
155  */
156         movl %ebp, %edi
157         addl %ecx, %edi
158 
159 /* Calculate the delta between where vmlinux was compiled to run
160  * and where it was actually loaded.
161  */
162         movl %ebp, %ebx
163         subl $LOAD_PHYSICAL_ADDR, %ebx
164         jz   2f         /* Nothing to be done if loaded at compiled addr. */
165 /*
166  * Process relocations.
167  */
168 
169 1:      subl $4, %edi
170         movl 0(%edi), %ecx
171         testl %ecx, %ecx
172         jz 2f
173         addl %ebx, -__PAGE_OFFSET(%ebx, %ecx)
174         jmp 1b
175 2:
176 #endif
177 
178 /*
179  * Jump to the decompressed kernel.
180  */
181         xorl %ebx,%ebx
182         jmp *%ebp                   // <<= 압축을 푼 어드레스로 jump하는 부분

실제로 압축 해제는 decompress_kernel() 함수를 호출해서 처리하는데, 이 함수는 arch/x86/boot/compressed/misc.c 파일에 있습니다. decompress_kernel() 함수의 원형은 아래와 같습니다.

304 asmlinkage void decompress_kernel(void *rmode, memptr heap,
305                                   unsigned char *input_data,
306                                   unsigned long input_len,
307                                   unsigned char *output)

위의 함수 원형을 보면 결국 우리가 넘겨주는 output buffer에 차곡차곡 압축을 풀어줌을 알 수 있는데요, 저 output 버퍼의 위치는 decompress_kernel()을 호출하는 곳에서 LOAD_PHYSICAL_ADDR로 대략 정의하고 있는데요, 정확하게 말하기는 힘들지만 대략 0x400000(4MB) 어드레스 부근이라고 생각됩니다.

리눅스 커널의 이해를 보면 압축을 해제한 뒤에 다시 1MB 영역의 어드레스로 덮어쓴다는 이야기가 있던데, 실제 코드에서는 바로 jump하는 부분만 확인했을 뿐 데이터를 이동하는 부분은 찾지 못했습니다. ㅠㅠ 그렇다는 이야기는 이제는 더이상 안옮기거나 제가 못찾았다는 이야기겠지요. 혹시 찾으신 분 있으시면 덧글로 제보 부탁드릴께요 ㅠㅠ

5. 리눅스 커널 - 실제 커널

이제 압축까지 다 해제된 실제 커널로 왔습니다. 커널의 실제 엔트리 포인트는 startup_32() 함수이구요, arch/x86/kernel/head_32.S에 있습니다. 이 함수를 보면 페이지 테이블을 임시로 설정하고 start_kernel() 함수를 호출하는 것을 볼 수 있습니다. start_kernel()은 32bit 커널에서 C 코드가 시작되는 부분이지요. 여기까지만 오면 사실 부팅은 거의 끝난 겁니다. ^^;;;;

다음은 startup_32() 함수의 코드입니다. 전체 코드는 arch/x86/kernel/head_32.S 파일을 참조하세요.

269 #ifdef CONFIG_SMP
270 ENTRY(startup_32_smp)
271         cld
272         movl $(__BOOT_DS),%eax
273         movl %eax,%ds
274         movl %eax,%es
275         movl %eax,%fs
276         movl %eax,%gs
277 #endif /* CONFIG_SMP */

             ... 생략 ...

451         movl $(__KERNEL_STACK_CANARY),%eax
452         movl %eax,%gs
453 
454         xorl %eax,%eax                  # Clear LDT
455         lldt %ax
456 
457         cld                     # gcc2 wants the direction flag cleared at all times
458         pushl $0                # fake return address for unwinder
459 #ifdef CONFIG_SMP
460         movb ready, %cl
461         movb $1, ready
462         cmpb $0,%cl             # the first CPU calls start_kernel
463         je   1f
464         movl (stack_start), %esp
465 1:
466 #endif /* CONFIG_SMP */
467         jmp *(initial_code)     // <<= start_kernel() 함수의 어드레스가 들어 있음
468 

6. 리눅스 커널 - C 함수 시작

start_kernel() 함수는 init/main.c 파일에 있으며, 각종 커널 관련 자료구조(페이지 테이블 포함)를 다시 한번 초기화하고 init 프로세스를 생성하는 역할을 합니다. start_kernel() 함수의 가장 마지막은 rest_init() 함수인데요, 이 함수는 kernel_thread() 함수를 호출해서 별도의 스레드를 만든 다음 kernel_init 함수를 수행하게 만듭니다. 그리고 자기 자신은 계속 코드를 수행하면서 cpu_idle() 함수를 수행하여 idle process가 됩니다.

다음은 start_kernel() 함수의 일부분과 rest_init() 함수의 일부분을 나타낸 것입니다.

535 
536 asmlinkage void __init start_kernel(void)
537 {
538         char * command_line;
539         extern struct kernel_param __start___param[], __stop___param[];
540 
541         smp_setup_processor_id();
542 
543         /*
544          * Need to run as early as possible, to initialize the
545          * lockdep hash:
546          */
547         lockdep_init();
548         debug_objects_early_init();
549 
550         /*
551          * Set up the the initial canary ASAP:
552          */
553         boot_init_stack_canary();
554 
555         cgroup_init_early();
556 
557         local_irq_disable();
558         early_boot_irqs_off();
559         early_init_irq_lock_class();
560 
561 /*
562  * Interrupts are still disabled. Do necessary setups, then
563  * enable them
564  */
565         lock_kernel();
566         tick_init();
567         boot_cpu_init();
568         page_address_init();
569         printk(KERN_NOTICE "%s", linux_banner);
570         setup_arch(&command_line);
571         mm_init_owner(&init_mm, &init_task);
572         setup_command_line(command_line);
573         setup_per_cpu_areas();
574         setup_nr_cpu_ids();
575         smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
576 
577         /*
578          * Set up the scheduler prior starting any interrupts (such as the
579          * timer interrupt). Full topology setup happens at smp_init()
580          * time - but meanwhile we still have a functioning scheduler.
581          */
582         sched_init();
583         /*
584          * Disable preemption - early bootup scheduling is extremely
585          * fragile until we cpu_idle() for the first time.
586          */
587         preempt_disable();
588         build_all_zonelists();
589         page_alloc_init();
590         printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
591         parse_early_param();
592         parse_args("Booting kernel", static_command_line, __start___param,
593                    __stop___param - __start___param,
594                    &unknown_bootoption);
595         if (!irqs_disabled()) {
596                 printk(KERN_WARNING "start_kernel(): bug: interrupts were "
597                                 "enabled *very* early, fixing it\n");
598                 local_irq_disable();
599         }
600         sort_main_extable();
601         trap_init();
602         rcu_init();
603         /* init some links before init_ISA_irqs() */
604         early_irq_init();
605         init_IRQ();
606         pidhash_init();
607         init_timers();
608         hrtimers_init();
609         softirq_init();
610         timekeeping_init();
611         time_init();
612         sched_clock_init();
613         profile_init();
614         if (!irqs_disabled())
615                 printk(KERN_CRIT "start_kernel(): bug: interrupts were "
616                                  "enabled early\n");
617         early_boot_irqs_on();
618         local_irq_enable();
619 
620         /*
621          * HACK ALERT! This is early. We're enabling the console before
622          * we've done PCI setups etc, and console_init() must be aware of
623          * this. But we do want output early, in case something goes wrong.
624          */
625         console_init();
626         if (panic_later)
627                 panic(panic_later, panic_param);
628 
629         lockdep_info();
630 
631         /*
632          * Need to run this when irqs are enabled, because it wants
633          * to self-test [hard/soft]-irqs on/off lock inversion bugs
634          * too:
635          */
636         locking_selftest();
637 
638 #ifdef CONFIG_BLK_DEV_INITRD
639         if (initrd_start && !initrd_below_start_ok &&
640             page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
641                 printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
642                     "disabling it.\n",
643                     page_to_pfn(virt_to_page((void *)initrd_start)),
644                     min_low_pfn);
645                 initrd_start = 0;
646         }
647 #endif
648         vmalloc_init();
649         vfs_caches_init_early();
650         cpuset_init_early();
651         page_cgroup_init();
652         mem_init();
653         enable_debug_pagealloc();
654         cpu_hotplug_init();
655         kmem_cache_init();
656         kmemtrace_init();
657         debug_objects_mem_init();
658         idr_init_cache();
659         setup_per_cpu_pageset();
660         numa_policy_init();
661         if (late_time_init)
662                 late_time_init();
663         calibrate_delay();
664         pidmap_init();
665         pgtable_cache_init();
666         prio_tree_init();
667         anon_vma_init();
668 #ifdef CONFIG_X86
669         if (efi_enabled)
670                 efi_enter_virtual_mode();
671 #endif
672         thread_info_cache_init();
673         cred_init();
674         fork_init(num_physpages);
675         proc_caches_init();
676         buffer_init();
677         key_init();
678         security_init();
679         vfs_caches_init(num_physpages);
680         radix_tree_init();
681         signals_init();
682         /* rootfs populating might need page-writeback */
683         page_writeback_init();
684 #ifdef CONFIG_PROC_FS
685         proc_root_init();
686 #endif
687         cgroup_init();
688         cpuset_init();
689         taskstats_init_early();
690         delayacct_init();
691 
692         check_bugs();
693 
694         acpi_early_init(); /* before LAPIC and SMP init */
695 
696         ftrace_init();
697 
698         /* Do the rest non-__init'ed, we're now alive */
699         rest_init();               // <<= 요기가 실제 init 프로세스 생성으로 가는 부분
700 }

아래는 rest_init() 함수의 내용입니다. 

451 static noinline void __init_refok rest_init(void)
452         __releases(kernel_lock)
453 {
454         int pid;
455 
            // init 프로세스를 생성하는 kernel_init 스레드를 만드는 부분
456         kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
457         numa_default_policy();
458         pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
459         kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
460         unlock_kernel();
461 
462         /*
463          * The boot idle thread must execute schedule()
464          * at least once to get things moving:
465          */
466         init_idle_bootup_task(current);
467         rcu_scheduler_starting();
468         preempt_enable_no_resched();
469         schedule();
470         preempt_disable();
471 
472         /* Call into cpu_idle with preempt disabled */
473         cpu_idle();              // <<= Idle Process가 되는 부분
474 }

kernel_thread()는 아래 코드에서 볼 수 있듯이 마지막에 init_post() 함수를 호출하는데 이 함수가 최종적으로 init process를 생성합니다. 다음은 kernel_thread() 함수와 init_post() 함수의 코드입니다. 리눅스가 실행될 때 일반적으로 initrd와 같은 RAMDISK 이미지를 사용하기 때문에, init_post() 함수 안의 ramdisk_execute_command 변수에는 "/init" 이 들어가 있으며, 따라서 initrd의 루트 디렉터리의 init 프로세스를 실행합니다.

initrd에 있는 init은 사실 스크립트로 기본적인 드라이버를 로딩하고 루트 디스크를 마운트한 뒤, 실제 init process로 제어를 넘깁니다. 그러면 본격적으로 커널이 실행되는 것이죠. ^^;;;;

847 static int __init kernel_init(void * unused)
848 {
849         lock_kernel();
850         /*
851          * init can run on any cpu.
852          */
853         set_cpus_allowed_ptr(current, cpu_all_mask);
854         /*
855          * Tell the world that we're going to be the grim
856          * reaper of innocent orphaned children.
857          *
858          * We don't want people to have to make incorrect
859          * assumptions about where in the task array this
860          * can be found.
861          */
862         init_pid_ns.child_reaper = current;
863 
864         cad_pid = task_pid(current);
865 
866         smp_prepare_cpus(setup_max_cpus);
867 
868         do_pre_smp_initcalls();
869         start_boot_trace();
870 
871         smp_init();
872         sched_init_smp();
873 
874         do_basic_setup();
875 
876         /*
877          * check if there is an early userspace init.  If yes, let it do all
878          * the work
879          */
880 
881         if (!ramdisk_execute_command)
882                 ramdisk_execute_command = "/init";
883 
884         if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
885                 ramdisk_execute_command = NULL;
886                 prepare_namespace();
887         }
888 
889         /*
890          * Ok, we have completed the initial bootup, and
891          * we're essentially up and running. Get rid of the
892          * initmem segments and start the user-mode stuff..
893          */
894 
895         init_post();       // <<= init process 생성으로 가는 부분
896         return 0;
897 }

803 static noinline int init_post(void)
804         __releases(kernel_lock)
805 {
806         /* need to finish all async __init code before freeing the memory */
807         async_synchronize_full();
808         free_initmem();
809         unlock_kernel();
810         mark_rodata_ro();
811         system_state = SYSTEM_RUNNING;
812         numa_default_policy();
813 
814         if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
815                 printk(KERN_WARNING "Warning: unable to open an initial console.\n");
816 
817         (void) sys_dup(0);
818         (void) sys_dup(0);
819 
820         current->signal->flags |= SIGNAL_UNKILLABLE;
821 
822         if (ramdisk_execute_command) {   // <<= initrd와 같이 RAMDISK 이미지를 사용하는 경우 이 코드로 진입하고, 이때 ramdisk_execute_command는 /init이 들어 있음
823                 run_init_process(ramdisk_execute_command);
824                 printk(KERN_WARNING "Failed to execute %s\n",
825                                 ramdisk_execute_command);
826         }
827 
828         /*
829          * We try each of these until one succeeds.
830          *
831          * The Bourne shell can be used instead of init if we are
832          * trying to recover a really broken machine.
833          */
834         if (execute_command) {
835                 run_init_process(execute_command);
836                 printk(KERN_WARNING "Failed to execute %s.  Attempting "
837                                         "defaults...\n", execute_command);
838         }
839         run_init_process("/sbin/init");
840         run_init_process("/etc/init");
841         run_init_process("/bin/init");
842         run_init_process("/bin/sh");
843 
844         panic("No init found.  Try passing init= option to kernel.");
845 }

start_kernel() 함수 이하에 있는 함수들의 전체 코드는 init/main.c를 참고하기 바랍니다. ^^

약간 보기 불편한 점은 있지만 그래도 한번 정리해놓으니 좋군요.
그럼 다들 좋은 밤 되세요 ;)

참고 자료

애기가 세상에 나온지 얼마되지 않아서 정신없어 보내다가 이제야 메일함을 열어서 읽어보고 답멜도 보내고 있습니다. 그러다가 우연히 한빛미디어 사이트에 들어가게 되었는데요, 신간쪽에 보니까 흥미로운 책이 보이더군요. 바로 아래 책입니다.



<성능을 100% 끌어내는 멀티코어 애플리케이션 프로그래밍 - 출처 yes24>


제가 원래 멀티코어쪽에 흥미가 있어서 그런지 제목을 보고 저도 모르게 클릭을 했네요. ㅎㅎ 목차를 보니 나름대로 실용적인 주제를 다루고 있던데... 시간이 허락하면 한 권 구매해서 읽어보고 싶네요. ;) 혹시 나중에 읽게되면 서평을 올리겠습니다. ㅎㅎ


그럼 좋은 하루 되세요 ;)


ps) 애기가 쭈주 달라고 보챌 시간이 다되었는데... 어찌 일어날 기미가 보이지 않는군요. ㅠㅠ 

     망했어요 ㅠㅠ



사실 주니어가 탄생한지는 벌써 2주쯤 지났는데요, 애기본다고 정신이 없어서 이제야 알립니다. >ㅁ<)-b


39주 2일 걸려서 6월 15일에 태어났구요, 15시 47분에 3.48KG, 52cm로 튼튼하게 태어났습니다. ;)


앞으로 애기한테 부끄럽지 않게 열심히 살아야겠군요.





그럼 좋은 하루 되세요 ;)


ps) 해피피트의 주인공을 닮은 것 같다는... 쿨럭..;;;



<출처 - 네이버>

"64비트 멀티코어 OS 원리와 구조"가 출간된지 벌써 1년이 다되었습니다. ^^)-b 그동안 MINT64 OS 개발과 관련해서 많은 질답이 오고 갔는데요, 그중에서 빼 놓을 수 없는 것이 바로 "개발 환경 설정"과 관련된 내용입니다. ㅠㅠ 실제로 개발 환경 설정때문에 많은 분들이 고생을 하고 계신데요, 이 문제로 고민하시던 Ethobis님께서 해결책을 내놓으셨습니다(Ethobis님 감사합니다. ㅠㅠ).


Ethobis님께서 사용하신 방법은 Cygwin을 기본으로 설치하고 난 뒤 부록 CD에 있는 개발환경을 그대로 복사하는 방법인데요, Cross Compiler를 만드는 복잡한 단계를 거치지 않아도 되기 때문에 괜찮은 것 같습니다. ^^


부록 CD를 다운 받는 곳과 Ethobis님의 글은 아래에서 보실 수 있습니다. ;)


    부록 CD : http://jsandroidapp.cafe24.com/xe/220

    Ethobis님의 글 : http://ethobis.tistory.com/63


그럼 즐거운 OS 프로그래밍하세요 :)


ps) 좋은 팁을 알려주신 Ethobis님께 감사의 말씀을 전합니다. ^^

"64비트 멀티코어 OS 원리와 구조"가 2012년 대한민국 우수학술도서로 선정된지 얼마되지 않아서 또 기쁜소식이 들려왔습니다. Workspace님께서 MINT64 OS를 Mac OS X 10.7.4에서 빌드하는 방법을 올려주셨거든요 ;)


아래 글 링크 나갑니다.


    OS X 10.7.4에서 개발 환경 구축하기: http://osdevlog.tistory.com/1

    Chapter 7 C Kernel 실행까지~: http://osdevlog.tistory.com/2


Workspace님 감사합니다. :)

즐거운 OS 개발 하시길 바래요 ^^)-b

근 한달동안 모든 코딩을 어셈블리어로만 하고 있다보니 호출 규약(Calling Convention)을 다시 살펴보고 있습니다. C로 프로그래밍하던 시절에는 호출 규약은 별로 신경쓰지 않았는데, 어셈블리어로 함수를 호출하려니 안 볼수가 없더군요. ㅠㅠ


MINT64 OS를 만들 때 어셈블리어로 코딩했으면서 왠 엄살이냐고 생각하실텐데... 리눅스에서 사용하는 호출 규약(Calling Convention)과 윈도우에서 사용하는 호출 규약은 상당한 차이가 있더라구요. ㅠㅠ 그래서 적응하는데 살짝 힘들었습니다. 사실 적응한다기 보다는 차이를 아는데 시간이 좀 걸렸지요. ㅠㅠ

64비트 리눅스의 호출 규약

리눅스는 64비트 모드에서 파라미터를 전달할 때 윈도우보다 레지스터를 더 많이 사용합니다. 정수 타입의 파라미터를 전달할 때는 순서대로 RDI, RSI, RDX, RCX, R8, R9까지 6개의 레지스터를 사용하고 7개 이상이면 스택을 통해 전달합니다. 실수 타입의 파라미터의 경우는 XMM0 ~ XMM7까지 8개를 순서대로 사용하고 그 이상이면 스택으로 전달하지요. ;)


반환값은 정수일 때 RAX(하위 64비트), RDX(상위 64비트)를 사용하고, 실수일때는 XMM0(하위 128비트), XMM1(상위 128비트)를 사용합니다. 아래는 64비트 멀티코어 OS 원리와 구조의 11.2.2 장에서 추출한 호출 규약 그림입니다. ;)




64비트 윈도우의 호출 규약

반면, 윈도우는 64비트 모드에서 파라미터를 전달할 때 레지스터 4개만 사용합니다. 정수 타입의 경우는 순서대로 RCX, RDX, R8, R9를 사용하고 나머지는 스택으로 전달합니다. 실수의 경우는 XMM0 ~ XMM3까지 4개를 순서대로 사용하며 나머지는 스택으로 전달합니다. 여기까지 보면 파라미터로 사용하는 레지스터의 종류와 개수만 차이가 나는 것 같습니다만.... 실제로 보면 스택을 사용하는 방법도 차이가 있습니다.


리눅스의 경우 파라미터를 전달할 때 스택을 꽉꽉 채워서 사용하는 반면, 윈도우의 경우는 4개의 레지스터가 들어갈 공간만큼을 띄워서 사용합니다. 즉, 파라미터 4개가 RCX, RDX, R8, R9를 통해 전달되지만, 스택에 이 4개를 위한 공간을 할당해놓는 것이죠. 아래 그림을 보시면 좀 더 이해하시기 편할 겁니다. ^^;;;





이 차이 때문에 한참을 헤맸네요. ㅠㅠ 스택에 저 공간을 안 할당해놓으면 파라미터가 잘못 전될되서 보기좋게 함수 호출이 실패를... 쿨럭..;;; 그리고 주의할 점은 비록 파라미터가 4개 미만이더라도 함수 호출 시 저 영역은 무조건 할당해야 한다는 겁니다. 에궁... 진짜 알고나면 별 것 아닌데... 코드를 몇 번이나 갈아 엎었는지 모르겠네요. ㅠㅠ


C로 코딩을 했으면 아예 신경을 안써도 되는 부분인데... 어셈블리어로 코딩하다보니 삽질을 하고 말았군요. ㅎㅎ 윈도우 호출 규약에 대한 보다 자세한 내용이 궁금하시다면 MSDN 사이트를 참고하시기 바랍니다. ^^


그나저나 PE32 파일 포멧을 읽어 메모리에 로딩하여 실행하는 가볍게 어셈블리어로 작성하는 괴수(?)를 어떻게해야 따라잡을 수 있을까요? 아우... 따라가려니 죽을 것만 같네요. ㅠㅠ


그럼 좋은 밤 되세요 ;)

<64비트 멀티코어 OS 원리와 구조>


제가 오랜시간을 걸쳐 정리한 책, 64비트 멀티코어 OS 원리와 구조가 "2012년 대한민국학술원 우수학술도서"에 선정되었습니다. 이런 영광이... 제게도 이런 날이 오는군요. ㅠㅠ)-b 우수 학술 도서 목록은 http://www.nas.go.kr/info/notice/view.jsp?NP_Code=10000043&NP_DataCode=20000014&NGB_Code=10001430에서 살펴보실 수 있습니다. ^^


그동안 64비트 멀티코어 OS 원리와 구조를 구매해주신 많은 분들께 감사드리며, 우수학술도서로 선정될 수 있도록 힘써주신 한빛미디어의 한동훈님께 감사드립니다. 그리고 책이 나올 수 있도록 도와주시고 응원해주신 많은 분들께도 감사의 말씀을 드립니다. 앞으로 책이 많이 나가서 얼른 2쇄 찍고 부끄러운 오탈자를 정리할 수 있으면 좋겠네요. ㅠㅠ)-b


앞으로 더욱 열심히 하는 kkamagui가 되겠습니다. ^^)-b

그럼 좋은 밤(?) 되세요 ;)


지난번에 VMware와 IDA를 이용해서 MBR부터 윈도우 부팅과정을 따라가는 방법에 대한 글을 올렸는데요, Ilho님께서 WinDBG를 이용해서 부팅과정을 따라가는 방법도 있다고 알려주셔서 실험해봤습니다. ^^

일단 디버거가 붙은 시점은 32비트 부트매니져가 시작되는 시점이더라구요. MBR부터 따라가지 못하는 점은 조금 아쉽지만... 강력한 장점은 윈도우 심볼을 로드할 수 있어서 따라가기 편하다는 겁니다. @0@)-b (llho님 감사드려요 ㅠㅠ)

VMware 설정

일단, 부팅과정을 따라가려면 VMware에 시리얼을 추가해야 하는데요, VMware의 왼쪽 편에 있는 "Edit vritual machine settings"를 클릭하여 뜬 "Virtual Machine Settings" 창에서 왼쪽 아래에 있는 "Add"을 누르면 됩니다. 그러면 "Add Hardware Wizard"창이 표시되는데, "Serial Port" 항목을 선택한 뒤 "Next"를 누릅니다.

이제 시리얼 포트 타입과 옵션을 설정할 차례인데요, "Serial Port Type""Output to named pipe"로 하고 "Named pipe"의 이름은 "\.\pipe\com_1", "This end is the server", "The other end is a virtual machine", "Connect at power on"로 각각 선택해주시면 됩니다(좀 항목이 많은데 아래 그림을 참고하세요 ^^;;). 그리고 시리얼 포트가 생성되면 마지막으로 "Virtual Machine Settings" 창의 오른쪽 아래에 있는 "Yield CPU on poll"을 클릭해주시면 VMware의 설정은 끝납니다.


<VMware 시리얼 포트 등록 화면 1>


<VMware 시리얼 포트 등록 화면 2>


윈도우 7 설정

다음으로... 윈도우 7에 부트 디버그 옵션을 설정해야 하는데요, cmd.exe를 관리자 권한으로 실행해서 bcdedit.exe로 옵션을 설정해줘야 합니다. cmd.exe를 실행한 다음 아래처럼 입력해주면 됩니다.

주의: bcdedit로 부트 디버그 설정을 하고 나면 재부팅 후 디버그가 붙기 전까지는 부팅 시작이 아주 늦게 시작되더군요(한 1분쯤 기다려야 부팅이 시작되었던듯...). ㅠㅠ 실제로 사용하시는 윈도우에 실험삼아 하셨다가는 큰일이 날수도 있으니 주의하시기 바랍니다. ㅠㅠ

bcdedit /bootdebug {bootmgr} on
bcdedit /bootdebug on
bcdedit /debug {bootmgr} on 
bcdedit /debug on 
bcdedit /set {bootmgr} debugtype serial
bcdedit /set {bootmgr} baudrate 115200
bcdedit /set {bootmgr} debugport 1

그리고 재부팅을 하면 부팅이 시작되지 않고 디버거가 붙기를 기다립니다. 이제 WinDBG만 설정해주면 디버깅 준비가 모두 끝납니다.

WinDBG 설정

WinDBG는 마이크로소프트에서 제공하는 디버거로 유저 모드 디버깅과 커널 모드 디버깅을 모두 지원합니다. 그리고 윈도우 자체를 디버깅할 때 심볼을 자동으로 로드해주는 강력한 기능이 있기 때문에 윈도우를 분석하는데 정말 좋은 툴이라고 생각합니다. ^^;;;

WinDBG는 마이크로소프트 사의 페이지에서 다운받을 수 있으며, 다운 받은 설치 파일을 실행하면 끝납니다. 설치가 끝나면 이제 옵션을 주어 VMware의 시리얼 포트와 연결되도록 해야 하는데요, 먼저 windbg.exe 파일을 끌어서 바로가기를 만듭니다. 그리고 속성을 클릭해서 아래 화면처럼 대상의 내용을 수정해 줍니다. -k부터 주욱 입력해주시면 됩니다. ^^;;;

C:\WinDDK\7600.16385.1\Debuggers\windbg.exe -k com:port=\\.\pipe\com_1,baud=115200,pipe,reconnect


<WinDBG 설정 화면>


자, 입력까지 끝났으면 이제 바로가기를 클릭하여 windbg를 실행합니다. 그러면 창이 뜨고 아래처럼 "Waiting to reconnect..."라는 메시지가 표시될 텐데요, VMware를 시작하면 디버거가 연결되고 윈도우가 실행됩니다. 이 상태로는 원하는 부트 디버깅을 할 수 없으니 WinDBG 창에서 Control+break를 눌러서 중단 시킵니다. 그리고 아래로 넘어가서 심볼 경로 설정과 브레이크 포인트 설정을 해줍니다.

Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
Copyright (c) Microsoft Corporation. All rights reserved.

Waiting for pipe \\.\pipe\com_1
Waiting to reconnect...

WinDBG 심볼 설정 및 브레이크 포인트 설정하기

그럼 먼저 심볼을 로드할 수 있게 설정해보겠습니다. 심볼 경로는 "File" -> "Symbol File Path"를 클릭해서 지정해 줄 수 있으며, 아래처럼 입력해주면 웹에서 찾아 알아서 심볼을 다운로드 해줍니다. 굳이 윈도우 버전에 따라서 심볼을 찾느라 고생하지 않아도 된다는 거지요. ^^;;;

srv*c:\symbols.pub*http://msdl.microsoft.com/download/symbols

심볼 경로를 지정해줬으니 이번에는 bootmgr부터 디버깅을 하기 위해 WinDBG의 커맨드 창에서 아래처럼 입력해줍니다.

sxe ibp
sxe ld:bootmgr

다 입력했으면 VMware를 재시작 한 뒤에 WinDBG의 커맨드 창에서 .restart를 입력해서 재시작합니다. 그리고 잠시 기다리면 VMware와 연결되서 bootmgr부터 디버깅이 가능합니다. ^^)-b


<Bootmgr 디버깅 화면>



그럼 즐거운 디버깅(?)하세요 ;)

MBR부터 윈도우 부팅 과정 디버깅!?!

요 몆주간 윈도우 부팅 과정(MBR부터 시작하는...)을 확인할 필요가 있어서 여러가지로 찾던 중, VMware와 IDA Pro를 이용하는 방법을 발견했습니다. 물론, 이 방법 외에도 여러 방법들이 있겠지만, VMware의 빠른 속도와 IDA Pro의 강력함이 더해지니 꽤 괜찮아서 이렇게 쓰고 있습니다. ^^;;;
(혹시 다른 방법을 알고 계시면 살짝 알려주세요 >ㅁ<)/~)

VMware에 디버그 옵션 설정하기

VMware로 생성한 가상 머신이 있는 폴더에 가보면 .vmx로 끝나는 파일이 있을 겁니다. 이 파일을 노트패드와 같은 에디터로 열면 여러 설정값들이 보일텐데, 가장 아래 줄로 이동해서 다음 3줄을 추가합니다.

debugStub.listen.guest32 = "TRUE"
debugStub.hideBreakpoints = "TRUE"
monitor.debugOnStartGuest32 = "TRUE"

위 3줄은 VMware가 디버그 포트를 열어서 GDB로 OS를 디버깅할 수 있게 하는 옵션인데요, 이 옵션을 추가한 뒤 VMware를 실행시키면 바로 시작이 안되고 그냥 멈춰있습니다. ^^;;; 까만 화면이 나오거나 아래처럼 화면 가운데에 녹색 Play 버튼 같은게 나올 수 있는데, 둘다 정상이니 걱정하지 않으셔도 됩니다.


<VMware 디버깅 대기 화면>


IDA Pro로 VMware에 연결하기

VMware가 멈춰있다면 이제 IDA Pro를 실행합니다. 그리고 메뉴의 debug -> Attach -> Remote GDB debugger를 클릭하여 VMware에 붙일 준비를 합니다. "Remote GDB debugger"를 선택하면 옵션창이 표시되는데, "Hostname" 항목에는 localhost를 입력하고 "Port" 항목에는 8832를 입력하면 접속할 준비가 모두 끝납니다. ^^;;;


<Remote GDB debugger 선택 화면>



<Debug Option 설정 화면>


"Choose process to attach to"라는 창이 뜨면 그냥 ID 0번, 즉 <attach to the process started on target>으로 맞춰주세요. ^^;;;

Happy Debugging

처음에 디버거가 붙으면 BIOS의 첫부분부터 시작이 될텐데요, 사실 BIOS의 동작은 크게 안 궁금하니 브레이크 포인트를 0x7c00에 하나 걸어주고 브레이크 포인트가 걸린 뒤부터 진행하면 MBR부터 확인할 수 있습니다. ^^


 

<디버깅 화면>


이제 따라가는 일(?)만 남았군요. ^^)-b
그럼 즐거운 디버깅(?) 하세요 ^^)/~

아시는 분들도 있겠지만 새로운 회사로 이직해서 적응 중입니다. ^^;;; 기존에 하던 것과는 다른 전혀 새로운 분야라 초반에 멘탈 데미지(?)가 상당했는데... 이제 조금씩 익숙해지고 있습니다. ㅠㅠ


처음에는 정말... 일을 받고 나니 암담하더라구요. ㅠㅠ 업무 레벨이 너무 달라서 이게 가능한 일인가 싶기도 했습니다. 그런데 주위를 둘러보니 선배들은 더 어려운 걸 하고 있더군요. ㅡ_ㅡa... 그래서 일단 기를 쓰고 맡은 일부터 하고 있습니다. ^^;;;;


어우... 이럴 줄 알았으면 평소에 Windows OS쪽도 깊게 봐둘 걸 그랬네요. 그동안 너무 안일했나봐요. ㅠㅠ 어느정도 적응이 되고 나면 틈틈이 재미있는 글(?) 올리겠습니다. ㅎㅎ


그럼 즐거운 주말 되세요 ;)

혹시 성공을 위해 가족을 멀리하고 앞만 보고 달리고 계시진 않나요? 아니면, 지금 이 고비만 넘기면 좋은 날이 올거라 생각하며 몸이 힘든 걸 억지로 참고 계시지는 않나요? 이런 분들이 있다면 꼭 추천해드리고 싶은 책이 있습니다. 바로 오늘 내가 살아갈 이유 입니다.

오늘 내가 살아갈 이유 - yes24

<이미지 출처 - yes24>

이 책의 주인공인 위지안씨는 30살에 남부럽지 않은 성공을 거두었지만, 큰 병에 걸려 모든 걸 잃고 말았습니다. 그런데... 인생의 끝자락에 와서야 자신이 얼마나 많은 것을 버리며 살아왔는지, 또한 얼마나 많은 것을 받으며 살아왔는지를 깨닫습니다. 그 순간, 그녀는 깨달음을 다른 사람들에게 전달하기 위해 블로그에 글을 쓰기로 결심합니다. 죽는 그 순간까지 말이죠. ㅠㅠ

오늘 내가 살아갈 이유는 위지안씨가 자신의 블로그에 남긴 글을 책으로 엮어 낸 겁니다. 읽어보시면 알겠지만 담담하고 진실된 그녀의 글은 여러분에게 다시 한번 가족과 인생의 의미를 떠올리게 할 겁니다. 안타까운 점은 위지안씨가 세상을 떠났기 때문에 후속 편을 볼 수 없다는 겁니다. ㅠㅠ

눈코 뜰 새 없이 바쁜 일상에 묻혀 무언가를 잃고 산다는 느낌이 드신다면, 오늘 내가 살아갈 이유를 추천합니다. ^^

ps) 위지안님... 부디 편히 잠드시길...

얼마전 맥미니(Mac mini)를 중고로 구매한지라 맥에서 이것 저것 많이 해보고 있습니다. ^^;;; 윈도우를 쓰다가 맥을 쓰니 불편한 점이 한두 가지가 아닌데요, 그중에 오늘 겪은 건... RAR 파일 압축 해제입니다. OTL.... 그냥은 압축이 안풀리더군요.

그래서 찾은 것이 바로~!! UnRarX입니다.

 http://www.unrarx.com/


위의 링크에서 다운 받은 뒤 압축 풀 파일을 드래그 & 드랍 해주시면 알아서 풀어줍니다. ^^;;;
다들 아시겠지만 무언가를 쓴다는 자체가 사실은 상당히 집중력을 요하는 작업입니다. ㅠㅠ 그래서 주위에서 말을 걸거나 아니면 걱정 거리가 있는 상황에서는 진도를 못나가고 계속 앉아있는 상황이 되버리지요. ㅠㅠ

그래서 편하게 작업할 수 있는 환경을 만들고 싶어 마크다운을 배우고 이를 이용해서작업할 수 있는 환경을 구축했습니다. 사실 별 것 아닌 것 같지만 상당히 심적 부담도 덜어지고 작업하기도 편하더군요(뭐랄까요... 심플함 + 내가 대충써도 잘 나오겠지 하는 안도감의 조합 때문이랄까요? ㅎㅎ)

그러다보니 이제는 글쓰는 툴까지 내려가게 됬는데요, 웹 환경에서 상당히 편하게 작업할 수 있는 툴을 발견했습니다. 발견 한 곳은 여기인데, 웹 환경에서 쓰는 툴도 있고 데스크톱에 설치해서 쓰는 툴도 있습니다. 이중에서 제가 선택한 툴은 웹 환경의 Writer와 데스크톱 환경의 JDarkRoom 입니다.

Writer의 경우, 웹상에서 동작하니 OS를 타지 않는 것이 장점이라고 할 수 있겠네요. ^^;;; 그 외에 다른 것은 사실 간단한 텍스트 에디팅 기능을 크게 넘지 않아서 뭐라 표현하기가 힘들군요. 마크다운(Markdown) 문법이라도 지원해주면 좋으련만....

Writer

JDarkRoom의 경우, 전체화면을 지원하고 마크다운 또한 지원하기 때문에 작성된 텍스트 파일의 결과물을 html 형식으로 만들 수 있습니다. 그것 외에는 역시 단순한 에디팅 정도군요. ^^;;;

JDarkRoom

개발 환경이 Windows + Linux 였는데, 여기에 Mac까지 더해지니 환경 구성하는 것 또한 일이군요. 리눅스에서는 일사천리로 셋팅되던 루비가 윈도우나 맥에서는 쉽지 않다는 것부터 시작해서 키 맵핑 문제와 파일 공유 등등... 아우 신경쓸게 한두가지가 아니네요. ^^;;; 이제 왠만한 프로그램은 크로스 플랫폼이 되는 걸로 선택해야 겠습니다. ㅎㅎ

새로운 글 쓰기 환경이 필요하시다면, WriterJDarkRoom은 어떠세요?

얼마 전에 회사에 갓 입사하신 분을 만났습니다. 그분의 고민도 들어줄 겸해서 말이지요. ^^;;; 사실 제가 누구를 만나서 좋은 말을 해줄 수 있는 사람이 아니기 때문에 조금 망설였는데요, 예전에 제가 했던 고민과 같은 고민을 하고 계시더군요. ㅠㅠ 그래서 혹시 도움이 될까해서 저녁에 잠시 만나 고기 한 점하며 이런 저런 이야기를 나누었습니다.

그분의 고민은 개발하는 게 좋은데 입사한 선배들이 이야기를 들어보면 다들 개발보다는 영어 공부를 하라고 하니 어떻게 하면 좋겠냐는 거였습니다. 사실 저도 입사하기 전에 선배들한테 이런 저런 이야기를 많이 들었는데요, 그중에 가장 많이 들었던게 "영어 공부"였습니다. 물론, 그분들이 개발을 그리 즐기지 않기 때문에 그런 것도 있겠지만, 사실은 회사에 입사해서 필요한 스킬 중에 한 가지가 영어였기 때문입니다. ^^;;;

사람의 능력은 한계가 있기 때문에 두마리 토끼를 다 잡기는 어렵지요. 그래서 "선택과 집중"을 해야하는데 제가 택한 방법은 "개발"에 집중하면서 주로 영어로된 자료나 사이트에서 정보를 얻는 겁니다. 요즘은 실력있는 분들이 블로그나 다른 웹사이트 형태로 지식을 많이 정리해두어 한글로 된 자료를 찾기가 쉬워졌는데요, 제가 OS를 만들던 2002년만 해도 그렇지 않았거든요. ^^;;;

뭐 하나 만들려면 외국 웹사이트를 죄다 뒤져서 하드웨어 관련 스펙을 찾아 진행해야 했으니 말이죠. 처음에는 영어로 된 자료를 읽기가 쉽지 않았는데 자꾸 읽다보니 어느새 익숙해지더라구요(물론 그렇다고 술술 읽을 수 있는 건 아닙니다. ㅠㅠ 사전이 필수라는... OTL).

"개발"도 하고 "영어 공부"도 하고... 나름대로 괜찮은 방법 아닌가요? ㅎㅎ 이 때문인지 저는 지금까지 근무하면서 영어로 된 스펙을 읽어 업무를 처리한다거나 같이 협업을 하는데 큰 어려움이 없었습니다. 그리고 하고 싶은 개발에 시간을 계속 투자할 수 있었기 때문에 64비트 멀티코어 OS 원리와 구조도 출간할 수 있었구요. ^^

<64비트 멀티코어 OS 원리와 구조>


그분께 제가 드렸던, 그리고 드리고 싶었던 메시지는 이거였습니다. 또한, 같은 고민을 하시는 분께 드리고 싶은 메시지이기도 합니다.

개발을 포기하지는 마세요. 하나만 선택해야하고 다른 것은 포기해야 한다는 법은 없습니다. 조금만 생각하면 원하는 일을 하시면서 다른 일도 같이할 수 있는 방법이 있습니다. 인생은 하고 싶은 일만 하고 살기에도 짧습니다. 꾸준이 개발하시고 즐거움을 찾으시다보면 곧 다른 길이 열릴 겁니다.

이 글과 일맥상통하는 제 글이 있기에 링크를 걸어봅니다. 저는 지금도 여전히 하고 싶은 일을 하면서 살고 있습니다 - 1편, 저는 지금도 여전히 하고 싶은 일을 하면서 살고 있습니다 - 2편

그럼 여러분 모두 건승하시기 바랍니다. ^^

XCode 설치 시 iTunes 를 종료해야 다음으로 넘어간다는 메시지가 뜰 때가 있습니다. 이럴 때는 다음과 같이 터미널을 열어서 iTunes 관련 프로세스를 모두 종료하면 다음으로 진행됩니다.

$ ps x | grep iTunes     <== iTunes와 관련된 모든 프로세스를 표시
  127   ??  S      0:00.10 /Applications/iTunes.app/Contents/MacOS/iTunesHelper.app/Contents/MacOS/iTunesHelper -psn_0_36873
 2878 s000  S+     0:00.00 grep iTunes
$ kill 127                       <== 프로세스 죽이기 $ kill 2878

아아... 맥은... 그냥 이쁜 리눅스 같군요... OTL... 환상이 마구 깨지고 있는 중입니다. ㅠㅠ

스타트 루비(Start Ruby) #1

이 시간에는 루비 언어의 탄생 배경과 루비 언어의 특징에 대해서 간단히 알아보겠습니다. 루비 언어는 여러 언어의 장점을 취해서 만든만큼 다양한 기능들이 있지만, 사실 세컨드 랭귀지(Second Language)로 사용하는 정도라면 이번 장과 다음 장에서 설명하는 내용만으로도 충분할 것입니다. 자, 그럼 루비의 세계로 한 번 들어가 보겠습니다.

루비(Ruby)?

루비는 마츠모토 유키히로라는 일본 사람이 만들었습니다. 유키히로씨는 쉽게 쓸 수 있으면서 객체지향적인 언어를 만드는 걸 목표로 했고, 그래서 탄생한 것이 바로 루비(Ruby)입니다.

사실, 루비는 루비 온 레일즈(Ruby on Rails) 덕분에 더 널리 알려졌는데요, 레일즈 프레임워크는 스프링 프레임워크처럼 웹 개발을 편하게 해주는 프레임워크의 한 종류랍니다. 지금 보시는 이 사이트도 루비 온 레일즈로 만들어졌습니다.

루비는 다른 스크립트 언어가 갖고 있는 대부분의 기능이 포함되어 있습니다. 물론 거기에는 다양한 확장 라이브러리도 포함해서 말이지요. ;) 여러분이 필요로 하는 대부분의 기능은 이미 다른 라이브러리에 포함되어 있을 겁니다. 잘만 활용한다면 쉽고, 빠르게 개발을 진행할 수 있습니다.

이러한 특징때문에 반복되는 작업을 자동화하는 간단한 프로그램이나, 본격적인 개발 전에 프로토타입을 만들어보는 용으로 사용하면 딱입니다(물론 제 개인적인 소견이에요. ㅠㅠ).

콘솔 출력

루비에서 데이터를 출력하는 방법은 아주 간단합니다. 앞에서 본 것처럼 puts를 쓰면 대부분의 데이터를 출력할 수 있습니다. 단, 원하는 형태로 데이터를 출력하고 싶다면 조금 고민을 해야하지만, C 언어를 조금 안다면 그리 어렵지 않습니다. C 언어의 printf와 비슷한 기능을 하는 sprintf()가 있기 때문이죠. C처럼 생긴 sprintf()가 싫다면 "문자열 포맷" %[] 형식의 루비 스타일도 있으니 입맛대로 사용하면 됩니다. ^^;;;;

a = sprintf("제 이름은 %s이고 나이는 %d 입니다.", "루비", 1)
b = "제 이름은 %s이고 나이는 %d 입니다." %["루비", 1]
c = sprintf("PI는 .2f 입니다.", 3.1425);

puts a
puts b
puts c

콘솔 입력

루비에서 콘솔 입력은 한 줄 또는 여러 줄을 입력받을 수 있습니다. 한 줄 입력은 gets() or readline() 함수를 사용하면 되고, 여러 줄 입력은 readlines() 함수를 사용하면 됩니다.

a = gets()              # 엔터가 입력될 때까지 저장
b = readline()          # 위와 같은 결과
c = readlines()         # 여러 줄을 읽을 때 사용

루비 데이터 타입

루비에는 크게 7가지 기본 타입이 있는데요, 숫자와 문자열, 배열(Array)과 해시(Hash), 범위(Range)와 심볼(Symbol), 그리고 정규표현식(Regular Expression)이 그겁니다.

숫자와 문자열, 배열은 기존에 다른 언어와 큰 차이가 없기 때문에 간단히 살펴보고 넘어가겠습니다. 그렇다고 나머지가 엄청 복잡한 건 아니니 너무 걱정안하셔도 됩니다(사실 정규표현식은 좀 낯설긴 해요. ^^;;;;).

숫자, 문자열

아래는 루비에서 숫자와 문자열을 나타낸 예입니다.

a = 5                         # 정수 5
b = 10.1                      # 실수 10.1
c = "이것은 문자열입니다."    # 문자열

puts a, b, c                  # a,b,c 변수의 값을 차례로 출력

결과:
5
10.1
이것은 문자열입니다.

위의 a, b, c를 변수라고 하며, 변수는 값을 저장하는 일종의 공간입니다. 루비의 변수는 타입이 정해져있지 않고 우변에 대입하는 값에 따라서 변합니다. C나 C++ 언어처럼 선언할 때 변수의 타입을 지정해주는 것과는 다른 방식이지요.

puts는 파라미터로 넘어온 값을 출력하는 함수로, 숫자 타입부터 배열이나 해시까지 아주 편하게 출력할 수 있습니다. 앞으로 자주 사용할 것이니 puts() 함수를 눈여겨봐두기 바랍니다.

배열, 해시, 범위

배열과 해시 역시 아주 직관적입니다. 아래는 배열과 해시의 예를 나타낸 겁니다.

a = [0, 1, 2, 3, 4, 5]        # 0 에서 5까지 정수가 들어있는 1차원 배열
# 사과와 바나나를 키로 사용하고, 그 값으로 빨강, 노랑을 연결한 해시
b = {"사과" => "빨강", "바나나" => "노랑"}

puts a, a.length, b["사과"], c["바나나"]

결과:
0
1
2
3
4
5
6
빨강
노랑

배열은 다른 언어와 마찬가지로 []로 감싸고 그 안에 배열의 요소를 ,로 구분하여 넣으면 됩니다. 잠시 후에 설명하겠지만 루비의 모든 타입은 클래스이기 때문에, 기본적으로 몇가지 함수나 속성을 가지고 있습니다. 위에서 나온 length도 그중에 한 가지며, 배열의 길이를 나타내는 속성입니다.

해시는 키(Key)와 값(Value)로 이루어진 테이블이며, 다른 말로는 맵(Map)이라고도 합니다. 해시는 배열과 달리 키를 사용하여 데이터를 읽고 쓸 수 있으므로, 연속적이지 않은 데이터를 다루는데 유용합니다. 해시에서 키와 값은 => 로 연결하며, 항목이 여러 개일 경우 ,로 구분합니다.

다음 범위와 정규표현식으로 넘어가기 전에 간단히 2차원 배열을 생성하는 방법을 살펴보겠습니다. 2차원 배열은 1차원 배열을 내부에 가지고 있는 배열입니다. 따라서 아래처럼 2차원 배열에 담을 배열을 먼저 생성한 뒤에, 다른 1차원 배열을 생성해서 그 안에 두면 됩니다.

a = [0, 1]          # 1차원 배열
b = [2, 3]          # 1차원 배열
c = [a, b]          # 2차원 배열

puts c
puts c[0][0]
puts c[0][1]
puts c[1][0]
puts c[1][1]

결과:
[[0, 1], [2, 3]]
0
1
2
3

위의 코드는 2차원 배열을 만들고 각 요소에 접근하는 방법을 나타낸 겁니다. 2차원 배열이 "1차원 배열을 요소로 갖고 있는 1차원 배열"이란 것을 생각해보면, 쉽게 예측할 수 있는 코드입니다. ^^)-b

범위는 숫자와 .. 또는 ...으로 이루어진 조합으로, 시작과 끝에 포함되는 모든 정수를 뜻합니다. 즉 1..10은 1부터 10까지 모든 정수(1,2,3...10)을 뜻한다는 것이죠. .....의 차이는 ..는 끝 숫자를 범위에 포함하지만, ...는 끝 숫자를 범위에 포함하지 않는 것입니다. 아래는 그 사용법과 결과를 나타낸 겁니다.

a = 0..10          # 0부터 10까지 범위의 정수
b = 0...10         # 0부터 9까지 범위의 정수

puts a
puts b

결과:
0..10
0...10

위의 결과를 보면 살짝 당황스러울 겁니다. 0부터 10까지의 숫자가 출력되기를 바랬는데, 우리가 만든 범위를 그대로 출력해주니 말입니다. 여기에 살짝 for 문을 추가하여 실제로 범위에 들어가는 값을 출력해보겠습니다.

a = 0..10
b = 0...10

puts "0..10"
for i in a
    puts i
end

puts "0...10"
for i in b
    puts i
end

결과:
0..10
0
1
2
3
4
5
6
7
8
9
10
0...10
0
1
2
3
4
5
6
7
8
9

정규표현식(Regular Expression)과 문자열 다루기

루비의 정규표현식은 /로 구분합니다. 즉 /Ruby/처럼 사용하면 Ruby와 일치하는 문자열을 나타내는 정규표현식이 됩니다. 정규 표현식 뒤에는 페턴을 어떻게 일치시킬지를 나타내는 옵션이 들어가고 옵션의 의미는 다음과 같습니다.

* /Ruby/i : 대소문자 구분 안함
* /Ruby/m : 줄바꿈 문자도 일반 문자처럼 다룰 때 사용
* /Ruby/x : 공백문자와 주석을 허용(RE 확장 문법)

정규표현식에서 (), [], {}, ., ?, +, *, |, ^, $는 각자 의미가 있습니다. 각 기호의 의미는 말로 설명하는 것보다 직접 사용법을 보는 것이 더 이해가 쉽습니다. 다음은 각 기호의 사용법입니다.

/(R|r)uby/          # Ruby 또는 ruby와 일치
/[Rr]uby/           # Ruby 또는 ruby와 일치
/[0-9]/             # 모든 숫자 한자리와 일치
/[0-9]+/            # 모든 숫자 한자리 이상과 일치
/Ruby!*/            # Ruby 또는 Ruby 뒤에 !가 1개 이상 붙은 문자열과 일치
/Ruby!?/            # Ruby 또는 Ruby!와 일치
/Ruby{3}/           # Ruby가 3번 반복되는 문자열과 일치
/Ruby{1,3}/         # Ruby가 최소 1번부터 최대 3번까지 반복되는 문자열과 일치
/[^0-9]/            # 숫자를 제외한 문자열과 일치
/ruby$/             # ruby로 끝나는 문자열과 일치

기호로 인해 더 쉽게 정규표현식을 만들 수 있게 된 건 좋은데, 이런 기호를 실제로 쓰고 싶으면 어떻게 할까요? 방법은 간단합니다. 앞에서 나온 기호들을 실제 패턴 일치에 사용하고 싶으면 \를 앞에 붙여주면됩니다. 아래처럼 말이죠 ;)

/Ruby\?/            # Ruby?와 일치
/Ruby\+/            # Ruby+와 일치

+, *와 같은 반복 기호 뒤에는 ? 기호를 사용할 수 있는데, 이는 패턴을 일치시킬 때 범위를 어디까지로 한정하는가에 사용됩니다. 아래와 같이 Ruby> 가 있을 때 ?가 있을 때와 없을 때의 차이는 다음과 같습니다.

a = "<ruby>Ruby>"

결과:
a =~ /<.*>/
$~
=> #<MatchData "<ruby>Ruby>">

a =~ /<.*?>/
$~
=> #<MatchData "<ruby>">

위에서 $~로 나타낸 것은 정규표현식에서 일치한 부분을 나타내는 전역 변수입니다. 객체지향으로 보면 Regexp.last_match와 같습니다. 사실 Regexp.last_match는 $~의 값을 그대로 반환합니다.

정규표현식에는 문자와 숫자를 의미하거나 공백 또는 모든 문자를 의미하는 표현식이 있습니다. 위의 .이 바로 그것인데요, 다음은 해당 표현식을 나타낸 것입니다.

* /./ : 줄바꿈을 제외한 모든 문자와 일치
* /./m : 줄바꿈을 포함한 모든 문자와 일치
* /\d/ : 숫자와 일치
* /\D/ : 숫제를 제외한 모든 문자와 일치
* /\s/ : 공백 문자와 일치 \t, \r, \n, \f
* /\S/ : 공백을 제외한 문자와 일치
* /\w/ : 단어 하나와 일치
* /\W/ : 단어가 아닌 문자에 일치

사실, 문자열의 경우 배열의 인덱스에 정규표현식을 넣어서 일치하는 부분만 잘라낼 수 있습니다. 아래처럼 말이죠. slice() 함수를 사용해도 같은 결과를 얻을 수 있고, slice!()를 사용하면 원하는 결과만 문자열에서 꺼내고 기존 문자열은 나머지만 남길수도 있습니다.

a = "<ruby>Ruby>"

a[/<.*>/]
a[/<.*?>/]
a.slice(/<.*>/)
a.slice!(/<.*?>/)
puts a

결과:

<ruby>Ruby>         #a[/<.*>/]
<ruby>              #a[/<.*?>/]
<ruby>Ruby>         # a.slice(/<.*>/)의 결과

<ruby>              # a.slice!(/<.*?>/)의 결과
Ruby>               # puts a의 결과

그리고, 패턴을 비교하는데 =~만 쓰란 법도 없습니다. 문자열이라면 .match()를 사용해서 MatchData 객체를 반환하게 만들수도 있습니다. =~는 일치하는 인덱스를 반환하거나 nil을 반환하는 반면 .match()MatchData를 반환하거나 nil을 반환하는 것이 다릅니다.

명령어 실행

루비는 언어 내에서 `로 명령어를 실행하는 것을 지원합니다. 예를들어 `ls`를 하면 ls가 실행되어 그 결과가 문자열로 반환이됩니다. 다음은 ls 명령어를 실행하여 현재 디렉터리의 목록을 받아와 출력하는 예제입니다.

a = `ls`
puts a

결과:
posts.html
posts_files
start-ruby-1.md
test.css
test.html
test_files
vim-markdown-preview-설정.md

마무리...

이상으로 루비 언어의 특징에 대해서 간단하게 알아봤습니다. 다음 시간에는 루비 언어를 이용해서 귀찮은 작업을 한방에 처리할 수 있는 방법을 알아보겠습니다.

Android-x86 Project

오늘 블로그를 순회하다가 재미있는 프로젝트를 발견했습니다. 스마트폰에서 동작하는 안드로이드 OS(Android OS)를 PC에서 실행시키는 프로젝트인데요, Android-x86가 바로 그것입니다.

<Virtual Box에서 실행 중인 Android ICS 버전>


Virtual Box에서 실행하기

Android-x86에 가보면 ICS(Icecream Sandwitch) 버전까지 포팅이 되어있는데, 가상머신의 한 종류인 Virtual Box를 사용하면 번거롭게 설치하지 않고도 실행해볼 수 있습니다. Virtual Box에서 실행하는 방법은 아주 간단합니다. Virtual Box에서 새로 만들기를 선택한 다음, 운영체제버전을 각각 OtherOther/Unknown으로 설정해주고 프로젝트를 생성하면 됩니다. 단, 원활한 실행을 위해 RAM512MB 이상으로 설정해주는 것이 좋습니다.

새로운 가상머신을 생성했으면, 이제 Android-x86에서 Live CD를 다운받을 차례인데요, 다운로드 리스트로 이동하셔서 android-x86-4.0-RC1-eepc.iso를 다운받으면 됩니다.

다운받은 뒤에는 아까 생성한 VirtualBox 가상머신에 다운받은 iso 이미지를 CD-ROM으로 추가해줘야 합니다. CD-ROM을 추가하려면 설정 메뉴를 눌러 뜨는 설정창에서 저장소를 클릭합니다. 그리고 오른쪽에 있는 IDE 컨트롤러에 보이는 + 표시된 CD를 클릭하여 CD-ROM과 이미지를 선택해줍니다. 글로 쓰려니 좀 복잡한데, 아래 그림을 보면 쉽게 진행할 수 있을 겁니다. ^^;;;;

CD-ROM까지 추가했으면 이제 실행합니다. 그리고 잠시 기다리면 ICS 설정화면이 표시되고, 안드로이드 OS가 동작할 겁니다.

아쉬운점....

안타깝게도... 아직 네트워크가 정상적으로 동작하지 않네요. ㅠㅠ 그래서 사실 기본으로 설치된 기능을 확인하는 것 외에는 아무것도 할수가 없습니다. ㅠㅠ 그래도 빠르게 발전하고 있는 만큼, 곧 모든 기능을 사용할 수 있는 날이 올 것 같네요. ;)

Sun2ace님의 제보로 이미 네트워크 사용이 가능한 ICS 버전이 있다는 사실을 알았습니다. http://sun2ace.blog.me/130134391597 로 가시면 마켓 이용까지 가능한 버전을 받을 수 있으니 참고하세요 ;)

그럼 좋은 하루 되세요 ;)

아시는 분은 아시겠지만, 정든 집(?)을 떠나 새로운 곳으로 옮기게 됐습니다. ^^)/~ 한 2년 전부터 과연 10년 뒤에 어떤 모습일까를 생각해왔는데... 그리 밝지 않더라구요. ㅠㅠ 물론 오래 다니신 분들이 들으시면 웃으시겠지만, 제가 그렇게 느끼는 걸 어떻하겠습니까? ㅠㅠ

그래서 이직을 생각하고 여러군데 연락을 드렸는데... 미천한 저를 여러군데서 불러주셔서 깜짝 놀랐습니다(저를 좋게 봐주신 많은 분들께 감사드립니다). 모두 이름있고 쟁쟁한 곳이라 고민이 많았는데요... 결국 한 곳을 선택했습니다~!!! @0@)/~ 이 곳을 선택한 이유는 다른 곳들과 분위기가 조금 달라서였는데요, 일반 회사와는 다른 곳이라 궁금하기도 했고 고용이 비교적 안정적이더라구요.

물론 지금있는 곳도 고용은 안정적이지만... 의미가 조금 다릅니다. ^^;;;; 지금 있는 곳은 경쟁에서 도태되면 짜르지는 않지만 한직으로 배치받고 스스로 퇴직을 하도록 만드는 분위기라.... ㅠㅠ 회사를 다녀도 다니는 것이 아닌 것이죠. ㅠㅠ 이런 면은 이직하는 곳이 더 나은 것 같습니다. ^^;;;; 물론 아직 잘 몰라서 그렇게 생각하는 걸수도 있지만.... 쿨럭..;;;;

4월에 출근이 결정된지라... 정든 집(?)은 3월 말까지만 출근하겠군요. 유종의 미를 거둘 수 있도록 잘 마무리 해야겠지요. ^^;;; 아우... 1월부터 이거 고민하느라 머리를 너무 썼더니 죽을 것 같네요. 코딩이나(?) 하면서 머리를 식혀야 겠습니다. ㅎㅎ

그럼 좋은 하루 되세요 ;)

 

마크다운(Markdown)?

얼마 전에 마크다운(Markdown)이란 것을 새로 접했습니다. 위키처럼 텍스트 기반으로 편하게 문서를 작성할 수 있고, 문법 자체도 아주 직관적이어서 배우기 쉽더군요. 물론, 여기서 직관적이라는 건... 사람에 따라 조금 다르게 느낄 수도 있는 부분이지만, 위키를 한번 접해보신 분이라면 "아하~!!"라고 하실 겁니다.

그래서 루비 온 레일즈(Ruby on Rails)의 튜토리얼을 보고 간단하게 만든 사이트에 마크다운 파서인 레드카펫(Redcarpet)을 설치해서 마크다운 블로그를 만들었습니다.

사실 마크다운 형식으로 쓴 글을 파싱해서 보여주는 것이 전부라 블로그라고 하기도 좀 그렇지만... 일단 대략 완성되었으니 한번 공개해봅니다. ^^;;;; kkamagui의 마크다운 블로그로 바로가기~!! 간단 마크다운 문법 정리로 가면 마크다운 문법이 어떤 것이고 어떻게 보이는지를 간단히 확인할 수 있으니 궁금하시면 한번 들러보시기 바랍니다. ^^;;;

새로 만든 블로그에 글을 적다보니 약간 불편한 점이 있더군요. 글 쓰는 것 자체는 편해졌는데, 단지 웹에서 작업을 해야 한다는 것이 좀 불편했습니다. 만일 로컬에서도 작업할 수 있다면, VI 에디터에서 글을 쓰고 바로바로 확인하는 방법으로 더 편하게 작업할 수 있을 테니까요. ^^;;;

그래서 찾아보던중... 아웃사이더님의 블로그에서 VIM Plugin을 발견했습니다. VIM에서 작업하다가 Command Line에 :Mm만치면 웹 브라우저에서 바로 확인할 수 있는 멋진 녀석이더군요. ;)

VIM Markdown Preview 설치 방법

설치방법 또한 아주 간단했습니다. vim-markdown-preview사이트로 이동해서 파일을 다운로드한 뒤, plugin 폴더와 doc 폴더의 모든 파일을 홈 디렉터리 아래에 있는 .vim 디렉터리 아래에 넣어두면 됩니다. ^^;;; 직접 복사하는 것이 귀찮다면 아래처럼 install.sh를 직접 실행하셔도 됩니다. 단, 홈 디렉터리 아래에 .vim/plugin 폴더가 이미 생성되어 있어야 합니다.

$> install.sh

실행 방법

설치가 모두 끝났다면 vim으로 파일을 하나 열어서 아래처럼 텍스트를 그대로 입력한 다음 Command Line에 :Mm을 입력해보세요. 기본 CSS가 Github Style로 되어 있어서, Github처럼 예쁘게 포매팅된 문서를 보실 수 있을 겁니다. ^^

#1.안녕하세요 H1 입니다.
## 1.1 H2에요

이것은 일반 텍스트입니다. 마크다운으로 아주 편하게 문서를 작성할 수 있어요. ;)

* 리스트1
* 리스트2
* 리스트3

이제 코드를 한번 보겠습니다.

    이 부분은 코드로 표시될 부분입니다. 탭을 1번 입력하거나 스페이스바를 4번 입력하면 코드 블럭을 사용할 수 있습니다. 

> 이것은 인용 블럭입니다. 정말 쉽죠? ^^;;;

[kkamagui blog](http://kkamagui.tistory.com)는 링크를 거는 간단한 방법입니다. 이것 외에도 많은 방법이 있어요 ;) 더 많은 내용은 [마크다운 문법](http://blog-kkamagui.cloudfoundry.com/posts/1)을 참고하세요.

결과:


1.안녕하세요 H1 입니다.

1.1 H2에요

이것은 일반 텍스트입니다. 마크다운으로 아주 편하게 문서를 작성할 수 있어요. ;)

  • 리스트1
  • 리스트2
  • 리스트3

이제 코드를 한번 보겠습니다.

이 부분은 코드로 표시될 부분입니다. 탭을 1번 입력하거나 스페이스바를 4번 입력하면 코드 블럭을 사용할 수 있습니다. 

이것은 인용 블럭입니다. 정말 쉽죠? ^^;;;

kkamagui blog는 링크를 거는 간단한 방법입니다. 이것 외에도 많은 방법이 있어요 ;) 더 많은 내용은 마크다운 문법을 참고하세요.


CSS 바꾸는 방법

Github Style도 충분히 만족스럽지만 각자의 취향 차이가 있으니 자신이 원하는 CSS로 바꾸는 방법도 살짝 알아보겠습니다. 홈 디렉터리에 있는 .vim/plugin 폴더 아래의 preview 관련 폴더로 이동해보면 아래와 같은 파일이 보일 겁니다.

kkamagui@ubuntu:~/.vim/plugin/vim-markdown-preview/stylesheets$ ls
github.css  safari-reader.css  simple-print.css

여기서 github.css를 자신의 입맛대로 바꾸거나, 아니면 여기에 css 파일을 복사하고, plugin 폴더 아래에 있는 vmp.vim 파일을 수정해도 됩니다. 파일을 열어서 23번 라인 근처를 보시면 stylesheet를 지정하는 부분이 보이는데요, 요 부분을 수정해줍니다. 저는 application.css 파일을 사용하고 있어서 요걸 지정해줬습니다.

 22 if !exists('g:VMPstylesheet')
 23     let g:VMPstylesheet = 'application.css'
 24 endif

자, 다 변경했으면 이제 다시 Command Line에서 :Mm을 입력해보겠습니다. 그러면, 새로운 페이지가 열리면서 지정한 CSS가 적용된 화면을 볼 수 있을 겁니다. ^^;;;

마크다운 덕에 재미있는 일을 많이 해보는군요. ;) 텍스트로 만들어진 문서를 작성하는 데는 마크다운도 괜찮은 선택인 것 같습니다. 그림이 들어가야 한다면... 그림을 어디 올려서 링크를 걸어야 하는 점이 약간 불편하긴 한데요, 그래도 링크만 있다면 그림을 넣는 건 편리하기 때문에 어떻게든 될 것 같네요.

HTML 태그 중에 pre 태그는 그 내부에 있는 글을 있는 그대로 보여주는 특징이 있습니다. 그래서 Code를 보여줄 때 pre 태그를 사용해서 쓰고 있는데요, 이게 한 라인이 길어지면 스크롤바를 만들어서 보여주더라구요. ^^;;;;

사실 스크롤바를 보여줘도 상관은 없는데, 매번 스크롤해서 보려니 귀찮기도 하고... 그냥 다른 태그들처럼 다음 라인으로 개행을 해서 보여줘도 되는 것 같기도해서 방법을 찾아봤습니다.

역시나 구글신이 친절히 알려주셨는데, 아래처럼 css의 pre 태그 항목에 white-space: pre-wrap; 속성을 사용하면 됩니다. 좀더 자세한 내용은 http://stackoverflow.com/questions/248011/how-do-i-wrap-text-in-a-pre-tag 을 참고하시면 됩니다.

pre{
    background-color: #E6E6FA;
    padding:10px;
    overflow: auto;
    white-space: pre-wrap; /* pre tag내에 word wrap */
}  


아우... 이거 원 모르는게 많아서 진도가 나가질 않네요. ㅠㅠ 웹세상은 제가 아는 세상과 또 다른 세계인 것 같아요. ㅠㅠ

난데없이 요상한 곳(?)에 불이 붙어서 요즘 루비 온 레일즈(Ruby On Rails)로 간단한 웹페이지를 만들어 보고 있습니다. ^^;;; 그래봤자 루비 온 레일즈 사이트에 있는 예제를 보고 끄적거리는 수준입니다만, 제 손으로 만든 게 돌아가는 걸 보니 정말 신기하네요. 웹은 돌아가는 것이 눈에 바로 바로 보여서 정말 짱인 것 같습니다. ㅎㅎ

그런데 만든 웹페이지를 스마트폰으로 들어가보니... 글자가 점처럼 보일 정도로 광활하게 나오더군요. 이런... ㅠㅠ 그래서 어떻게 하면 스마트폰에서도 잘 보일까 싶어서 구글 검색을 하던 도중 메타 태그(Meta Tag)를 사용하면 된다는 것을 알았습니다. :) 아래처럼 말이죠.

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=3.0, minimum-scale=0.5, user-scalable=yes" />


위의 각 항목은 아래와 같은 의미를 가지고 있습니다.

- width=device-width : 플랫폼 가로 크기에 맞춤, 수치를 넣으면 그수치에 맞게 맞춰짐(ex, 300px)
- initial-scale=1.0 : 페이지 로딩시 확대비율
- maximum-scale=3.0 : 최대확대비율
- minimum-scale=0.5 : 최소축소비율
- user-scalable=yes : 사용자의 확대보기 허용 여부(no/yes)


위의 태그를 넣어주고 모바일 보기 모드일 때 가로 폭을 좀 줄여줬더니 스마트폰에서도 나름대로 볼만한 크기로 잘 나오는군요. ㅎㅎ
그럼 좋은 하루 되세요 ;)

ps)  참고 사이트
루비 온 레일즈에서 html 파싱에 사용하는 Nokogiri를 설치할 때 아래처럼 libxslt 관련 오류가 발생하면 libxslt를 설치해주면 됩니다.

Installing nokogiri (1.5.0) with native extensions
Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension.

        /usr/bin/ruby1.8 extconf.rb
checking for libxml/parser.h... yes
checking for libxslt/xslt.h... no
-----
libxslt is missing.  please visit http://nokogiri.org/tutorials/installing_nokogiri.html for help with installing dependencies.
-----
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.  Check the mkmf.log file for more
details.  You may need configuration options.

Provided configuration options:
    --with-opt-dir
    --without-opt-dir
    --with-opt-include
    --without-opt-include=${opt-dir}/include
    --with-opt-lib
    --without-opt-lib=${opt-dir}/lib
    --with-make-prog
    --without-make-prog
    --srcdir=.


설치 방법은 다음과 같습니다.
sudo apt-get install libxslt-dev libxml2-dev

아아... 고난과 역경의 연속이군요. OTL....
우분투에서 루비 온 레일즈를 이용해서 이것 저것 삽질하고 있습니다. ^^;;;; 아무래도 처음하다보니 이것저것 문제가 많은데요. 이번에는 만든 예제를 릴리즈하려고 bundle package를 하다보니 mysql2 관련 오류를 만났습니다. ㅠㅠ

kkamagui@kkamagui-VirtualBox:~/project/blog$ sudo gem install mysql2 -v '0.3.11'Building native extensions.  This could take a while...
ERROR:  Error installing mysql2:
        ERROR: Failed to build gem native extension.

        /usr/bin/ruby1.8 extconf.rb
checking for rb_thread_blocking_region()... no
checking for rb_wait_for_single_fd()... no
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lm... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lz... no
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lsocket... no
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lnsl... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lmygcc... no
checking for mysql_query() in -lmysqlclient... no
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.  Check the mkmf.log file for more
details.  You may need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/usr/bin/ruby1.8
        --with-mysql-config
        --without-mysql-config
        --with-mysql-dir

역시나 해결책은 구글신이 알려주셨습니다. ㅠㅠ 구글신 쵝오 ㅠㅠ)-b (http://railsforum.com/viewtopic.php?id=40776)
아래처럼 libmysqlclient-dev 패키지를 설치하면 깔끔하게 해결됩니다.

sudo apt-get install libmysqlclient-dev

아흑... 뭐라도 하나 제대로 되야할텐데... 쉽지 않네요 ㅠㅠ

우분투(Ubuntu)에서 루비 온 레일즈(Ruby on Rails)버전을 3.2.1로 업그레이드한 뒤에, rails server를 실행했더니 아래와 같은 오류가 발생했습니다. OTL... 정말 이거 원 쉬운 게 하나도 없군요. ㅠㅠ

kkamagui@ubuntu:~/rails/blog/script$ rails server
/usr/lib/ruby/gems/1.8/gems/execjs-1.3.0/lib/execjs/runtimes.rb:50:in `autodetect': Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
    from /usr/lib/ruby/gems/1.8/gems/execjs-1.3.0/lib/execjs.rb:5

역시 구글을 검색했더니 아래와 같은 해결책을 하사해주셨습니다.(http://stackoverflow.com/questions/6282307/rails-3-1-execjs-and-could-not-find-a-javascript-runtime)

In your Gemfile

write this

gem 'execjs'
gem
'therubyracer'

and then run

bundle install

everything works fine for me :)


역시 구글은 짱인 것 같아요. ㅠㅠ)-b

+ Recent posts