OS를 만듭시다. 어때요~ 참 쉽죠? (7)

나빌레라의 이미지

OS. 영어로 풀어서 Operating System. 한자어로 번역해서 운영체제. 이것을 만든다고 하면 사람들은 굉장히 어렵게 생각한다. 리눅스나 윈도우같은 OS를 비교대상으로 본다면 OS를 만드는것은 정말 어렵고 힘들고, 개인이 만들기엔 어쩌면 불가능에 가까운 도전일지도 모른다. 하지만 OS의 기본 개념들은 대학교 학부과정에서 가르칠 정도로 이미 보편화 되어 있고 그 개념 자체들은 그다지 어렵지 않다. 개념 구현을 중심으로 동작하는 것 자체에 의미를 둔 OS를 만드는 것은 어쩌면 도전 해 볼만 한 가치가 있는 시도가 아닐까.

목차


1회
2회
3회
4회
5회
6회

7. 메모리맵의 구성

이지보드는 무려 64Mbyte의 SDRAM을 보드에 달고 있다. 아주 풍족한 메모리이다. 주소로는 0xA0000000 부터 0xA4000000 까지의 영역이 SDRAM의 영역이다. 맨처음 커널이미지를 일단 빌드해서 돌려보기 위해서 main-ld-script 라는 것을 수정할때 기억 나는가? 커널 이미지의 로딩 위치를 0xA0008000 으로 설정했었다. 바로 SDRAM 이 시작하고 32Kbyte 떨어진 위치에 커널 이미지를 올리는 것이다. 그 아래쪽 32Kbyte 에는 이지부트의 코드영역이 존재 한다.

Interrupt Handler 에서 Context 를 스택에 백업/복구 하는 부분을 설명할때 ARM은 각 동작모드 별로 별개의 sp와 lr을 가진다고 했었다. lr 에는 바뀌기 전 모드의 pc 가 저장되어 Exception 발생에 따라 진입하는 동작모드에서의 작업이 끝난 다음에 복귀 할 때 사용해야 하기 때문에 각 모드별로 서로 다른 lr을 유지 한다. 그리고 sp를 모드별로 별개로 가짐으로 해서 각 동작모드 별로 서로 다른 스택영역을 사용 하게 되어 서로 다른 동작모드가 같은 스택영역을 사용하지 않게 하고 있다.

같은 원리로 나빌눅스에서 동작하게 되는 각 테스크들은 각자의 TCB(Task Control Block)안에 개별적인 스택포인터를 저장할 공간을 가지고 있어서 Context Switching으로 실행권한을 얻을때마다 유저모드의 sp 에 자신의 TCB에 있는 스택포인터 주소를 세팅한다. 그러면 Context Switching 으로 동작하는 테스크가 바뀔때마다 sp값도 바뀌고 사용하는 스택위치도 바뀐다. 즉, 모든 테스크는 서로 다른 스택영역을 독자적으로 가지게 된다.

결론적으로 메모리 맵이라는 것은 각 모드별, 테스크별로 sp의 시작값을 뭘로 주느냐 하는 것을 결정하는 일이라 할 수 있겠다.

앞 강좌에서 설명 했듯이 ARM은 7가지의 프로세서 동작모드를 가진다. 각 동작모드는 Exception 발생과 같이 변경된다. Exception 이 발생되어 동작모드가 변경되고 Exception Vector Table을 타고 Exception Handler 로 진입하여 코드가 동작하는동안에 로컬변수등을 사용하려면 스택영역을 지정해 주어야 한다. 즉 OS가 부팅되는 시작 시점에 각 모드 별로 sp 레지스터의 값을 할당 해 주어야 한다.

필자는 나빌눅스에서 MMU를 사용한 vm(Virtural Memory)를 사용하지 않도록 설계 하였다. 그럼으로 인해 실상 나빌눅스의 메모리 관리 체계는 매우 허술하다. 하지만 덕분에 나빌눅스의 메모리 체계는 매우 명료하고 단순하다. 더불어 MMU 가 없는 ARM7 계열칩등에 포팅할때에 별다른 수정 작업 없이 바로 포팅이 가능 할 것이다.

위에서 이지보드는 64Mbyte의 SDRAM을 가지고 있다고 하였다. 이중 4Mbyte에 커널과 테스크 코드이미지와 커널 스택영역이 존재하고 나며지 60Mbyte 를 테스크가 스택과 힙 영역으로 사용하게 할 것이다. 각 테스크에게 할당할 스택영역의 크기를 얼마로 하고, 전체 테스크가 사용할 스택 영역의 크기를 얼마로 할 것인지에 따라 나빌눅스에서 할당 가능한 테스크의 갯수가 정해 지게 된다.

예를 들어 60Mbyte 전체를 테스크 스택영역으로 사용하게 하고, 각 테스크에게 각각 1Mbyte 씩의 스택을 할당한다면 나빌눅스에서 사용가능한 전체 테스크의 갯수는 60개가 된다. 만약 각 테스크에게 할당하는 스택크기를 반으로 줄여 512Kbyte 씩으로 한다면 사용가능한 테스크의 갯수는 120개가 된다. 다르게 생각해서 전체 테스크들이 사용할 수 있는 스택영역을 30Mbyte 로 한다면, 30Mbyte 를 0.5Mbyte 씩 테스크들이 사용하게 되므로 60개의 테스크를 사용 할 수 있게 되는 것이다.

나머지 4Mbyte 의 영역은 커널이 사용한다. 나빌눅스는 커널코드외에 사용자테스크 코드까지 모두 커널이미지에 포함되어 하나의 이미지파일로 만들어져 보드에 올라간다. 이 커널이미지 파일을 포함해서 커널이 사용하는 스택영역이 모두 4Mbyte 안에 들어가게 하였다. 실상 나빌눅스의 이미지 파일은 다 합쳐봐야 20Kbyte 도 넘지 않을 것이다. 아무리 커져도 수십Kbyte 수준으로 100Kbyte는 절대 넘지 않을 것이라 예상된다. 그러므로 커널에게 할당한 4Mbyte 의 영역은 꽤나 방대한 영역일 수 있다.

이전 강좌에서 잠시 나왔던 entry.S를 다시 살펴 보자.

.globl _ram_entry
_ram_entry:
    b   kernel_init 
	b	_ram_entry
    b   Core_swiHandler
    b   Core_irqHandler
 
 
#define svc_stack   0xa0300000
#define irq_stack   0xa0380000
#define sys_stack   0xa0400000
 
.global kernel_init
kernel_init:
    msr     cpsr_c,#0xc0|0x13    //SVC mode
    ldr     r0,=svc_stack
    sub     sp,r0,#4
 
    msr     cpsr_c,#0xc0|0x12    //IRQ mode
    ldr     r0,=irq_stack
    sub     sp,r0,#4
 
    msr     cpsr_c,#0xc0|0x1f    //SYSTEM mode
    ldr     r0,=sys_stack
    sub     sp,r0,#4
 
    msr     cpsr_c,#0xc0|0x13
 
    bl      main
    b       _ram_entry

아래쪽에 Core_swiHandler 와 Core_irqHandler 는 제외하고 kernel_init 부분만 보자. 이 레이블은 말 그대로 커널을 초기화 하기위한 어셈블리 코드가 들어 있다. 나빌눅스에서 FIQ는 처리 하지 않는다. Abort 모드와 Undefined Exception 이 발생하면 따로 처리 하지 않고 그냥 재부팅 해 버린다. 그러므로 SVC모드와 IRQ모드, SYSTEM모드에 대해서 sp레지스터를 설정한다.

앞서 강좌에서 설명 했듯이 arm-linux-gcc컴파일러는 스택을 주소가 감소하는 방향으로 포인터를 옮겨가면서 사용한다. 그러므로 최초에 sp에 할당 하는 값은 해당모드에 할당된 스택영역의 최대값 주소 이다. 각각 0xA0300000, 0xA0380000, 0xA0400000 으로 define 된 svc_stack, irq_stack, sys_stack 을 보면 이름만 보고서도 이들 define된 값들이 각 모드의 스택 시작 값임을 알 수 있다.

커널이미지가 올라가는 위치는 SDRAM시작위치에서 32Kbyte 떨어진 위치라 하였다. 즉 나빌눅스는 SDRAM 가장 아래쪽의 32Kbyte는 그냥 버린다. 사용하지 않는다. 그 32Kbyte 아깝다고 그거 사용하려고 이것저것 삽질하느니 그냥 32Kbyte 안써버리는 편을 택했다. 커널이미지 크기는 위에서 말했듯 수십Kbyte 수준이다. 아래쪽에 버리는 32Kbyte를 생각 하더라도, 코드가 차지하는 크기는 100Kbyte 가 되지 않는다. 위 코드를 다시 보면 SVC 스택의 시작위치는 0xA0300000 이다. 즉 SDRAM의 시작위치로 부터 3Mbyte 떨어진 부분 부터 SVC 모드의 스택이 시작된다. 스택은 아래쪽으로 자라므로 사실상 SVC 모드에 3Mbyte 정도의 영역을 할당 한 것이다.

다음으로 IRQ모드의 스택 시작위치는 0xA0380000 이다. IRQ모드의 할당영역은 0xA0300000 부터 0xA0380000까지의 512Kbyte 이다. 물론 위에서도 설명 했듯이 OS 내분에서 메모리 영역의 침범은 따로 체크하지 않는다. OS의 안정성을 생각한다면 당연히 메모리 영역을 할당하고 난 뒤에는 각 영역별로 스택 침범을 OS가 감시하고 제어 해야 하겠지만, 필자는 그냥 IRQ핸들러에서 사용하는 스택영역이 512Kbyte 보다 커지지 않을 것이라 가정했다. 수십Kbyte 짜리 정적배열을 백여개 이상 선언하지 않는 이상 512Kbyte 의 영역을 넘겨서 사용할 리는 없을 것이다. 그리고 그정도 크기의 배열을 IRQ핸들러에서 선언한다는 것이 좀 이상하지 않은가. 게다가 나빌눅스는 임베디드 운영체제 이다. 개별 코드나 자료구조는 절대 크지 않다.

그 이쪽으로 커널에 할당한 스택영역이 끝나는 4Mbyte 지점. 0xA0400000 까지 512Kbyte 영역은 SYSTEM모드에 할당했다. SYSTEM 모드에 대해서 Arm Architecture Reference Manual 에는 아래와 같이 나와 있다.

Quote:

The remaining mode is System mode, which is not entered by any exception and has exactly the same registers available as User mode. However, it is a privileged mode and is therefore not subject to the User mode restrictions. It is intended for use by operating system tasks that need access to system resources, but wish to avoid using the additional registers associated with the exception modes. Avoiding such use ensures
that the task state is not corrupted by the occurrence of any exception.

영어를 잘 하시는 분은 직접 읽어보시기 바란다. 필자가 영어를 못해서 어설프게 번역했는데, 대충 의미는 같은것 같으니 너무 다그치지진 말고 번역 오류는 댓글로 지적 바란다.

남아있는 모드는 SYSTEM 모드이다. SYSTEM 모드는 Exception 이 발생해서 진입하는 모드가 아니고 USER 모드와 모든레지스터를 공유한다. 하지만 SYSTEM 모드는 특권모드이므로 USER 모드일때의 제한을 받지 않는다. OS 에서 테스크가 시스템 자원에 접근하고자 할때 사용되지만, Exception 모드에 따른 추가적인 레지스터의 사용을 피할 수 있다. Exception 발생으로 인해 여러 오류 상황들로 부터 안전하다.

이상이다. 다시 원래의 설명으로 넘어가서, 메모리맵에 대한 설명을 계속 하겠다.

IRQ모드와 SYSTEM모드에는 0.5Mbyte 할당하고서 SVC모드에는 왜 3Mbyte 씩이나 할당했는가. 그것은 SVC모드가 실제 커널이 동작하는 대부분의 시간을 차지하는 모드일 뿐더러, swi 로 진입하는 Software Interrupt 역시 SVC모드에서 동작한다. swi 는 Software Interrupt를 사용해서 System Call을 구현한다. System Call 은 OS에서 제공하는 API이다. 중첩해서 실행되기도 하고, 내부에서 많은 계층을 들어가기도 한다. 스택의 소요량이 많다. 그리고 커널코드 자체가 사용하는 스택도 모두 SVC모드의 스택영역이다. 아무리 그렇다 해도 3Mbyte는 과도하게 많은 양을 할당한것이긴 하지만 메모리 관리를 하지 않는 대신 아주 많은 양의 메모리를 할당해 주어서 "왠만하면" 문제가 생기지 않게 의도 하였다.

cpsr을 직접 수정하여 순서대로 SVC, IRQ, SYSTEM모드로 바꿔 가면서 미리 define 해 놓은 값을 해당 모드의 sp에 4바이트씩 빼서 할당 하였다. 굳이 뺄 필요는 없지만, 그냥 각 모드의 스택영역 경계에 4바이트의 빈공간을 두어서 경계로 삼으려 했다. 임베디드 환경에서 개발하는 사람이 메모리를 이렇게 생각없이 낭비해도 될런지 모르겠지만 그냥 별 의도 없이 저렇게 해 놨다. :)

그러면 나빌눅스의 메모리 맵은 아래 와 같을 것이다. 본 강좌에서는 최대한 그림을 그려 넣지 않을 것이라고 썻었다. 그래도 이해를 돕기 이위해 텍스트로 나마 그려 보겠다. 어여쁘게 봐 주기 바란다.

             +---------+ 0xA4000000
             |         |
             |         |
             |         |
             | USER    |
     60Mbyte | Task    |
             | Work    |
             | Stack   |
             |         |
             |         |
             |         |
             +---------+ 0xA0400000
    512Kbyte |  SYSTEM |
             +---------+ 0xA0380000
    512Kbyte |  IRQ    |
             +---------+ 0xA0300000
             |  SVC    |
             |         |
      3Mbyte |         |
             | Kernel  |
             | Image   |
             +---------+ 0xA0008000
     32Kbyte |don't use|
             +---------+

사실 메모리관리 부분은 나빌눅스에서 가장 신경쓰지 못한 부분이고 필자도 자신없어 하는 부분이다. MMU를 더 공부하고 이해 해서 나빌눅스에 MMU를 적용해 vm을 구현해 보고 싶다. 지금은 단지 mmuless 운영체제 입니다. 라고 당당하게 말 할 따름이다. :)

다음 강좌에서는 메모리맵을 단순하게 만든 덕에 단순 해 질 수 밖에 없는 테스크 스택 할당자를 작성해 보겠다.

이 글은 http://raonlife.com/navilera/blog/view/81/ 에 동시 연재 됩니다.

댓글

모지리의 이미지

이분 참 맘에 듭니다.

용기에 박수를 짝짝짝~~~

저는 업종이 이쪽인데 글을 쓸라면 이런 글을 써야 되는데

맨날 스노보드 글만 쓰네요.ㅎㅎㅎ

powerc20의 이미지

강좌 감사드립니다.

시스템 모드에 대한 설명입니다.
( Arm Architecture Reference Manual에서 인용했습니다. 혹시 문제가 되면 삭제하겠습니다. )

The remaining mode is System mode, which is not entered by any exception and has exactly the same registers available as User mode. However, it is a privileged mode and is therefore not subject to the User mode restrictions. It is intended for use by operating system tasks that need access to system resources, but wish to avoid using the additional registers associated with the exception modes. Avoiding such use ensures
that the task state is not corrupted by the occurrence of any exception.

powerc20의 이미지

강좌 감사드립니다.

시스템 모드에 대한 설명입니다.
( Arm Architecture Reference Manual에서 인용했습니다. 혹시 문제가 되면 삭제하겠습니다. )

The remaining mode is System mode, which is not entered by any exception and has exactly the same registers available as User mode. However, it is a privileged mode and is therefore not subject to the User mode restrictions. It is intended for use by operating system tasks that need access to system resources, but wish to avoid using the additional registers associated with the exception modes. Avoiding such use ensures
that the task state is not corrupted by the occurrence of any exception.

나빌레라의 이미지

감사합니다.

본문에 내용을 포함하였는데.. 제가 영어 실력이 부족해서
제대로 해석을 했는지 모르겠네요..^^;

---
얇은 사 하이얀 고깔은 고이 접어서 나빌레라

----------------------
얇은 사 하이얀 고깔은 고이 접어서 나빌레라

verimuch의 이미지

...
---------------------------------
Life is not fair, Get used to it.

---------------------------------
Life is not fair, Get used to it.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.