앞에서 작성한 VPOS 분석 문서들에 대해서 정리를 하는 내용으로 구성되어 있다.
부디 이 문서로 나마, 지금까지 알아봤던 VPOS 에 대해서 큰 그림을 그릴 수 있었으면 좋겠다.
내가 지금까지 이해한 내용을 생각나는 대로 편하게 기술했다.

VPOS 구조

VPOS 크게 부트로더와 커널로 구성이 되어 있다. 컴파일을 하면 각 소스파일을 컴파일한 결과의 오브젝트 파일(elf 포맷)이 생성되고,
ld 를 사용해서, .o 파일을 하나의 오브젝트 파일로 만든다. 이 때, 각각 부트로더와 커널의 오브젝트 파일이 따로 생성이 되고, objcopy 를 사용해서 binary 파일을 만든다. 마지막으로 dd 명령어를 이용해서 부트로더와 커널을 합친 binary 파일을 만든다. 이 파일을 타겟보드의 0x0 번지의 nor flash 영역에 write 된다.

타겟보드는 부팅시에 0x0 번지에서 부터 수행이되고, 이 주소에는 부트로더가 write 되어 있다. 부트로더는 cpu 를 초기화(watch dog, clock)하고, 메모리를 초기화 한다. 또한 인터럽트 벡터 테이블을 구성해서, 인터럽트가 발생하면 커널에 있는 핸들러로 호출하도록 징검다리 역할을 한다.
마지막으로 부트로더 뒤에 있는 커널을 메모리 상의 0x30000000 으로 복사한다. 복사를 끝내면, pc 를 0x30000000 으로 옮긴다.

크게 초기화, 메모리할당, 스케줄링, 그리고 애플래케이션 부분으로 나눠 볼수 있다.

초기화는 0x30000000 에서 부터 수행되는 루틴을 말하며, 여기서는 각각의 모드(user, fiq, irq, svc 등)의 모드에서 사용할 stack 의 영역을 할당하고, 캐시(cache)를 초기화(flush) 한다.
또한, 인터럽트가 발생시에 부트로더의 인터럽트 벡터 테이블에서 호출하는 핸들러 함수들도 정의하고 있다(컨텍스트 스위칭시에 쓰레드 저장).
마지막으로 C 언어 레벨인 VPOS_kernel_main 함수를 호출한다.

어떻게 보면, 가장 중요한 부분이면서 현재의 VPOS 에서 가장 문제가 되는 부분이라고 할 수 있겠다.
VPOS 에서 메모리를 할당 해줘야 하는 곳은 쓰레드를 생성할 때이다. 하나의 쓰레드를 생성시에 총 두번의 메모리 할당을 요청한다(vk_malloc).
첫번째는 쓰레드(vk_thread_t) 구조체를 할당하기 위함이고, 또 한가지는 스레드 stack 을 할당하기 위함이다.
char 형의 1byte 단위의 1000000 개의 heap 이라는 배열을 잡아서 메모리 할당을 해주고 있으며, 각각 쓰레드의 생성시 일정한 간격으로 메모리 할당이 되고 있는 상태다.

특정 시간 간격마다 타이머 인터럽트가 발생하면, 핸들러 호출에 의해서 스케줄러가 자동으로 호출된다.
스케줄러가 하는 역할은 쓰레드가 생성되면, 우선순위 별로ready_queue 에 저장이 되는 데, 우선순위가 높은 순으로 ready_queue 를 검색하여, 우선 순위가 높으면서도, head_list 에 가까운 쓰레드를 현재 쓰레드(vk_current_thread) 에 저장한다. 그리고 현재 쓰레드를 저장한다(vk_save_thread).
또한 현재 쓰레드가 저장된 쓰레드와 다르면, 컨텍스트 스위칭을 발생시킨다.
수행을 모두 마친 쓰레드는 ZOMBI 상태로 변경이 되는데, 이 쓰레드는 ready_queue 에서 빼서 vk_thread_join_table 로 옮긴다. 그 후에 메모리 해제를 시킨다.

메세지 큐나 세마포어 또는 POSIX 쓰레드 함수를 이용한 예제들이 명령어 형태로 구현이 되어 있다.

동작 정리

타겟보드를 부팅하면, 0x0 에 있는 부트로더에 실행되고, 커널을 메모리 주소인 0x30000000 으로 복사한다. 그리고 pc 를 커널이 올라간 주소로 넘긴다. 커널이 실행되면서, 하드웨어 초기화를 수행하고, VPOS_kernel_main() 를 호출한다.
vpos 에서 사용할 큐와 테이블을 초기화하고, 하드웨어 초기화 및 인터럽트 설정을 한다. 쓰레드를 만들기 위해, pthread_create() 를 호출하여, 쓰레드에 필요한 공간(쓰레드, 스택)을 할당받고, 큐에 등록한다.
특정시간이 되면 타이머 인터럽트에 의해 스케줄러를 호출하고, 스케줄러는 큐를 돌면서, 우선순위가 높은 쓰레드를 선택한다. 그리고는 기존의 저장되어 있는 쓰레드가 있는지 여부에 따라서 컨텍스트 스위칭을 한다.
수행을 모두 마친 쓰레드는 ZOMBI 상태로 바뀌고, thread_exit() 를 수행하고 나서, 기존에 있던 ready_queue 에서 수행을 마친 쓰레드의 목록을 지우고, 또한 counter 값을 1 감소시킨다. 그리고 vk_free() 을 이용해서 수행을 마칠 쓰레드의 스택주소를 인자로 넘겨, 해제한다.
그리고 컨택스트 스위칭을 해서 새로운 쓰레드를 수행시킨다.

문제점

앞에서도 잠시 언급했지만, 현재 메모리 할당 부분이 정상이 아니다. vk_malloc 을 호출해서 얻은 메모리 주소는

   p += p->s.size;   

연산을 수행하면서, 구조체가 깨지기 때문에 어떤 주소값이 반환되어 나올지 모른다. 지금은 다행히도, heap[] 배열 공간의 주소로 반환되어서 문제없이 수행이 되긴 하지만, 이로 인해서 어떤 문제가 차후에 발생할지는 아무도 모른다.
또 한가지는 스케줄링의 문제이다. 현재 스케줄링의 문제는 우선순위가 높은 쓰레드가 무한 루프를 돌면, 낮은 우선순위의 쓰레드는 전혀 수행이 될 수 없다는 것이다.

해결해야할 숙제

앞에서 문제점으로 제기한 것들을 모두 해결해야 한다. 그렇지 않고서는 vpos 위에 어떤 기능을 추가한다는 것은 아무런 의미가 없다.

  • computer/rtcclab/vpos_분석_-_6.정리.txt
  • Last modified: 3 years ago
  • by likewind