에구에구... 오늘 교육 끝나고 집으로 쏜살같이 달려와서는 그 동안 마음에 걸렸던 부분들을 수정했습니다. 사실 더이상 손대기에는 시간도 부족하고, 목표로 정한 것이 있어서 더이상 구현만 할 수는 없었다는... ㅠㅠ 그래서 완성도는 약간 떨어지지만, 프로토타입을 완료했습니다. ㅎㅎ

 지금까지 진행된 것은 멀티 태스킹 + 파일 시스템 + 동적 메모리 관리 + GUI 시스템 + 기타 시리얼, 마우스, 하드 디스크 드라이버 등등 정도 입니다. 뭐 많이 하긴했는데, 적어놓고보니 별거 없군요. ㅠㅠ 아래는 1.0 버전의 스크린샷입니다.

 파일 시스템이 추가되어서 시리얼로 실행파일을 매번 전송해서 실행하는 번거로움이 없어졌습니다. "Application Pannel"에서 "GUI Shell"을 실행한 다음 루트 디렉토리에 있는 테트리스 실행파일을 load 명령으로 실행하면 테트리스가 실행됩니다. ^^)-b GUI 쉘은 화면을 지우는 cls 명령과 디렉토리를 표시하는 dir 또는 ls 명령만 가지고 있습니다. 

 기념삼아 프로토타입을 테스트할 수 있는 파일을 올립니다. 아래 파일을 다운 받으셔서 압축을 푸신 다음 "qemu64MyOs.bat" 파일을 실행하시면 됩니다. 


 아아~ 오늘도 늦게까지 작업했더니 피곤합니다. ㅠㅠ. 일찍 끝낼 수 있었는데 QEMU가 Single Core 모드로 동작할때 IOAPIC 쪽을 사용하지 않는 걸 몰라서 한참 삽질했습니다. ㅡ_ㅡa... 커널이 잘못된 줄 알고 2시간동안 갈아 엎었습니다. 디버그 코드 덕에 겨우 알았다는... 싱글 코어일때는 PIC를 그대로 쓰도록 바꿔놔야겠군요. ㅎㅎ

 이크~ 저는 이만 자야겠습니다. 다들 좋은 밤 되세요~ ㅎㅎ

 

 
 이번주는 결혼식이다 뭐다해서 그렇게 많은 시간을 투자하지 못했습니다만, 막판 스퍼트해서  파일시스템을 추가하는데 성공했습니다. >ㅁ<)-b 사실 대부분의 코드는 윈도우 환경에서 시뮬레이션하면서 검증했기 때문에, 그냥 컨트롤 C+V 신공으로 끝냈습니다. ㅎㅎ

 그리고 내친김에 GUI 쉘에 dir 기능도 추가했습니다. GUI 쉘을 만든지는 좀 됬지만 귀찮아서 그냥 뒀는데, 점점 불편함이 커지더군요. 쉘 기능이 필요할때는 콘솔 모드 커널로 빌드해서 쉘을 쓰다가 다시 GUI 테스트할때는 윈도우 모드로 빌드해서 부팅하고... 이걸 수백번 정도 반복하다보니 그냥 만드는게 편하겠다는 생각이 들었습니다. 그래서 뚝딱~!!! 만들었습니다(사실 이 일이 더 많이 걸렸다는... ㅠㅠ).

 아래는 GUI 쉘로 루트 폴더와 이미지 폴더를 본 화면입니다. 깔끔하게 잘 나오는 걸 볼 수 있습니다. ㅎㅎ (이미지 윈도우는 /image/image0.img 파일을 읽어서 화면에 표시하도록 수정되었습니다)

 이거 뭐 점점 기능이 추가될수록 갈아 엎는 코드의 양도 많아지는군요. 이번에도 몇군데나 갈아 엎었습니다. ㅠㅠ 처음에는 간단한 OS를 만들자는 생각이었는데, 욕심이 자꾸 생겨서 이것도 추가하고 저것도 추가하게 되네요. 이렇다 언제 거사(?)를 시작하게 될지... ㅡ_ㅡa...

 진짜 내일 동적 메모리 할당쪽 알고리즘만 좀 더 수정하고, 거사를 시작해야겠습니다. 고칠게 있으면 거사를 진행하면서 고치던가 해야겠군요. ㅎㅎ

 내일 교육은... 또 물건너 간 것 같습니다. 푹 자다 올듯... ㅠㅠ
 다들 좋은 밤 되시길 ;)

 오늘도 집에 오자마자 컴퓨터를 키고 열심히 파일 시스템에 캐시를 추가하는 작업을 했습니다. 일단 어제 고민했던 문제 중에 FAT 영역 즉 클러스터 맵이 있는 영역에서 이상하게 캐시 히트가 낮게 나오는 문제를 뒤져봤는데... 역시나 문제가 있더군요. ㅎㅎ

 클러스터를 할당하는 코드가 클러스터 맵의 첫번째부터 마지막까지 검색하며 빈 클러스터를 찾게 되어있는데, 이런 경우 하드에 용량이 어느정도 차게되면 앞부분은 이미 대부분의 클러스터가 할당되어있음에도 불구하고 우직하게(?) 검색합니다. 따라서 클러스터 맵 영역의 캐시 또한 순차적으로 플러시되면서 거의 재사용되지 못하고 버려지게 되더군요.

 위와 같은 문제가 있어서 마지막으로 할당한 클러스터가 있는 클러스터 맵 인덱스를 저장하고, 클러스터 할당 요청이 올 때마다 가장 마지막으로 클러스터를 할당해준 클러스터 맵부터 검색을 시작하도록 수정했습니다. 이렇게 하니 거의 대부분 마지막으로 할당한 클러스터 맵에서 할당되서 거의 90%에 해당하는 캐시 히트가 나오더군요. 클러스터 맵을 쓰는 경우는 클러스터 할당/해제의 특성상 클러스터를 할당하려면 해당 맵을 읽어서 검색한 후, 할당했다고 마크를 해야하기 때문에 쓰는 경우는 100% 가 나왔습니다. 실로 엄청난 결과~!!! 이걸 실제 HDD에 적용할 경우 10번당 1번 하드를 읽는 것과 마찬가지기 때문에 시간을 엄청나게 절약할 수 있을 것 같습니다. >ㅁ<)-b

 하지만 이 기쁨도 잠시... 클러스터 영역을 캐시하는 기능도 추가했는데, 캐시 히트가... 20% 정도... ㅠㅠ 캐시 블럭의 개수를 128개 정도로 설정한 상태인데, 이를 더 늘리면 히트는 올라가겠지만 메모리 소모가 늘어나는 문제가 있기에 적당히 타협을 해야할 것 같습니다. 테스트 환경에서 캐시 블럭의 수를 2048개까지 늘리니까 40%까지는 히트가 올라가던데, 메모리 소모에 비해서 히트의 상승폭이 좀 낮아서 일단 다시 낮춰 놨습니다. 테스트는 가상으로 파일 3개를 만들고 랜덤한 파일 오프셋에서 랜덤한 사이즈를 읽고 쓰도록 하는 방법으로 하고 있는데, 아무래도 랜덤하게 읽고 쓰다보니 같은 클러스터가 다시 쓰이는 경우가 적은 것도 하나의 이유인 듯 합니다. 좀 더 고민해보고 캐시 블럭의 개수를 결정해야겠습니다. >ㅁ<)-b

 뭐든 구현하고 테스트할 때가 제일 재미있는 것 같습니다. 특히나 오늘처럼 예상하지 못한 결과가 나왔을 때는 더 즐겁습니다. 하핫~!!!
 헛.. 글고보니 내일은 고향에 가야되는데... 또 이렇게 시간이... ㅠㅠ 그만 자야겠습니다
 다들 좋은 밤 되세요. >ㅁ<)/~~


 어이쿠 또 시간이 이렇게나 흘렀군요. ㅠㅠ 방금 파일 시스템에 FAT(File Allocation Table) 영역에 해당하는 부분에 캐시 버퍼를 추가했는데, 대충 캐시 히트률이 60% 정도 나왔습니다. 생각보다 좀 저조한듯 하기도 하고... 일단 Sequential Write를 랜덤하게 돌려서 쓰고 읽고 검증하는 테스트를 돌리고 있는데, 에러가 안나오는거 보면 큰 문제는 없는 것 같습니다. 약간 문제라면... 캐시 히트률이 좀 낮은 게 문제라면 문제랄까요? ^^;;;;

 일단 캐시 알고리즘은 LRU(Least Recently Used)을 선택했고, Dirty 비트를 추가해서 Dirty 또는 Clean 중에 왠만하면 Clean인 놈을 선택하도록 했습니다. 물론 단순히 Age 만을 비교하는 것도 괜찮지만, 만약 쓰기가 수행된 캐시 버퍼 즉 Dirty 비트가 켜진 캐시 버퍼를 선택하게 되면 하드에 한번 더 써야하는 문제가 발생할 수 있어서 Clean을 우선적으로 선택하게 만들었습니다. Clean 인 놈은 쓰기가 수행되지 않고 읽기만 수행된 버퍼기 때문에 플러쉬(캐쉬에서 내보낼때)할 때 HDD에 안써도 되기 때문이지요. ㅎㅎ

 아흑... 원래는 더 일찍 끝났어야 하는데, lseek를 추가한다고 시간이 이렇게 흘러버렸군요. 처음에 예상했던 크기보다 점점 커지고 있어서 살짝 걱정이 됩니다. 어휴... 이걸 어떻게 해야될지... ㅠㅠ 그래도 일단 하던 작업은 끝내는게 맞는 듯 하니 이것까지만 해야겠습니다. ^^;;;

 내일은 알고리즘을 좀 손보고 데이터가 있는 클러스터 영역을 캐싱하는 기능도 추가해야겠습니다. 그리고 OS에 바로 옮겨봐야겠군요. ㅎㅎ 살짝 기대가 된다는... 과연 캐시를 사용하는 것과 사용하지 않는 것이 가상 머신에서 어떻게 차이가날지... 완전 기대됩니다. ㅎㅎㅎ

 그럼 내일 출근을 위해 전 이만 취침하러... ㅎㅎ
 다들 좋은 밤 되시길~ ;)


 주말 동안 쉬엄 쉬엄 파일 시스템을 설계했습니다. FAT 파일 시스템처럼 클러스터의 링크 형태로 말이지요. ^^ 최대한 간단하게 구현하려고 디렉토리 구조는 더욱 더 단순화시켰습니다. 그리고 코딩에 들어갔는데, 구현 초기에는 쉽게 쉽게 끝나는가 싶더니... 결국 또 복잡해지더군요.

 일단 멀티 태스크나 멀티 코어 환경에서 동기화 문제는 고려하지 않고, 단순히 파일 및 디렉토리 관련 API 만 만드는데, 꼬박 이틀이 걸렸습니다. 사실 API는 간단하게 만들고, 응용 프로그램이 잘 알아서 쓰도록하면 정말 간단한 구조를 유지할 수 있는데... 욕심이 생기다보니 이것 저것 많이 만들었습니다. 끄응.... ㅡ_ㅡa...

 덕분에 쓰기는 편해졌습니다만... 설명할게 많아져서 고민입니다. 현재 opendir, readdir, rewinddir, closedir, openfile, readfile, writefile, closefile, rewindfile, createfile, deletefile, createdirectory, deletedirectory까지 만들어져 있는데, 소스가 장난이 아니네요. 클러스터를 걸치는 녀석들 읽는거 처리하랴, Path 관련 처리하랴... 어휴... ㅠㅠ 하는 김에 rewindfile은 좀 더 고민해서 lseek로 만들어야겠습니다. 원래 lseek는 고려하지 않았지만... 만들다 보니 lseek 빼고는 다 만들었군요. ㅡ_ㅡa...

 만들긴 했는데... 과연 이 API가 얼마나 사용될지.... 삽질한건 아닌가 걱정이됩니다. ㅠㅠ
 억... 또 벌써 시간이 이렇게 됬군요.

 다들 좋은 밤 되시길.... ^^)-b

 ps) 아흑... 파일 동기화 문제는 또 어떻게 해결하지... ㅠㅠ


 아이쿠... 원래 계획은 좀 더 진도를 나가는 거였는데... 그 동안 무리를 좀 했더니 저녁에 잠깐 졸았습니다. 한 40분 잤으니 존거 치고는 시간이 좀 길군요. ^^;;;; 아무리 피곤해도 존적은 별로 없는데... 몸이 좀 피곤하긴 한가 봅니다. 그러거나 말거나 할꺼는 해야하니 세수 한판하고 또 진도 나갔습니다.

 일단 어제 대충 만든 테트리스를 좀 수정해서 블럭 색깔하고, 내부 알고리즘을 좀 정리했습니다. 원래 테트리스가 그리 복잡한 알고리즘이 필요없는 게임이라... 알고리즘이라는 말을 쓰기도 부끄럽습니다만... ㅡ_ㅡa... 발로 짜다보니 도저히 안되겠더군요. ㅎㅎ 내일쯤 정리되면 한번 올리겠습니다. ^^;;;;

 테트리스를 수정한 뒤에 한참을 고민했습니다. 이제 더 뭘 구현해야 하는가.... 가만히 보니 3개 정도가 더 남았더군요. 디지털 시계, 텍스트 뷰어, 이미지 뷰어 정도 남았던데... 디지털 시계는 텍스트로 날짜와 시간만 표시해줄 생각이니 크게 어렵지는 않을 것 같고... 텍스트 뷰어는 말 그대로 뷰어니까 그냥 텍스트만 출력해주면되니 이것도 별로 어렵지 않을 것 같았습니다.

 그렇다면 남은 것은 이미지 뷰어인데, 어떤 파일 포맷을 사용할 것인가가 문제더군요. 가장 쉽게 처리할 수 있는게 BMP 파일 형식인데... BMP도 다양한 버전이 있기 때문에 어떤걸 해야할지 고민이 됬습니다. 그러다가 문득 NDS 홈브루를 개발할때 만들었던 KNG(kkamagui NDS Graphic) 파일 포맷이 생각나서 그걸 사용하기로 했습니다. NDS 홈브루도 16bit Color로 설정되어있고, 마침 지금 만드는 OS도 16bit Color로 설정되어있으니 큰 수정없이 사용할 수 있을 것 같았습니다. KNG를 사용하는 NDS 홈브루는 http://kkamagui.tistory.com/49http://kkamagui.tistory.com/468 에서 보실 수 있습니다.


 위의 화면은 수정된 테트리스와 그 뒤로 이미지 뷰어가 실행된 화면입니다. 이미지는 KNG로 변환해서 OS에 시리얼로 전송하는 방식으로 했습니다. KNG를 사용한 덕분에 작업이 상당히 빨리 끝났습니다. ^^;;; 이것 저것 많이 만들다보니 이런 날도 오는군요~ 좋구로 ㅎㅎ

 작업을 진행하면서 느끼는 QEMU의 장점은 TCPIP로 가상 시리얼을 연결할 수 있어서 굉장히 편리하게 시리얼 통신을 할 수 있는 점입니다. 속도도 굉장히 빠르고 QEMU 내부적으로 버퍼 관리도 해주니 OS에서 구현된 허접한 시리얼 드라이버로도 잘 됩니다. 덕분에 귀찮은 작업이 얼마나 줄었는지... ㅠㅠ)-b QEMU 만세~!!!

 아아... 벌써 또 시간이 이렇게 됬군요. ㅠㅠ 언제쯤 일찍 자려나... 그러려면 집에 굉징히 일찍오던지... 아니면 컴퓨터를 안켜야하는데, 둘다 실현 가능성이 낮군요. ㅠㅠ 거의 다 완료될때까지 잠을 줄이는 수 밖에는 없을 것 같습니다. 아흑....

 그럼 다들 좋은 밤 되세요. ^^)/~

ps) 헛... 이런 잠결에 만들다보니 화면에 있는 이미지 뷰어에 스펠링이 틀렸군요. 이런 ㅠㅠ

 헉헉... 아이구 머리야... 예전에 만든 소스를 긁어다 붙이는 것도 힘들군요. ㅎㅎ 금방 끝날줄 알았는데... 벌써 시간이 이렇게... 일단 아래는 실행한 화면입니다. 아직 프로토타입의 형태라서 버그도 좀 있고, 상태도 별로 안좋습니다. ㅎㅎ


 자세한 내용은 내일 올리겠습니다. 좀 늦어서... ㅠㅠ)-b
 그럼 다들 좋은 밤 되세요 >ㅁ<)-b



 이제까지 GUI 어플리케이션은 모두 커널레벨에서 동작하는 커널 스레드와 같은 개념이었습니다. 즉 커널 코드에 포함되어있으며, 스택만 별도로 사용하는 존재였지요. ^^;;; 그래서 "어플리케이션"이라고 확실히 부를 수 있는 유저 레벨 태스크를 이용해서 GUI 어플리케이션을 만들어봤습니다.

 위의 화면에서 좌측 상단에 보이는 User Level Application이라고 표시된 윈도우가 유저 레벨에서 동작하는 GUI 어플리케이션입니다. OS 코드가 아주 간단하지만, 현대 OS가 갖추고 있는건 쪼금 쪼금씩 구현해 놓은지라, 당연히 커널 영역에는 접근하지 못하고 시스템콜(System Call)을 통해서만 커널 함수를 쓸 수 있습니다. 다만 아직 GUI 관련 시스템콜이 그리 많지 않아서 덩그러니 윈도우 기본 화면만 표시하고 있다는... ㅠㅠ 원래는 Hello, World를 찍어야하는데 시스템 콜을 만드는게 귀찮아서 그냥 뒀습니다. ㅠㅠ 너무 늦게 퇴근해서... ㅠㅠ

 그리고 유저 레벨 프로그램은 역시 커널과 별도로 빌드해서 실행하는게 제 맛이기 때문에, 별도로 elf 파일을 생성했고 지난번에 만든 File Downloader를 통해 OS에 전송했습니다. OS에는 당연히 윈도우 버전의 File Downloader를 만들었구요. ㅎㅎ 위의 화면에서 가운데 있는 것이 윈도우 버전의 File Downloader 입니다. Memory Monitor의 그라데이션 태스크 바를 그대로 썼습니다. ㅎㅎ 그러고보니 스프링노트에 ELF 재배치에 대한 글을 적어놨었는데, 깜빡했군요. 언능 마무리해서 포스팅해야겠습니다. ㅎㅎ

 시간이 지날수록 점점 커지는 걸 보니 흐믓합니다. ^^ 이제 조금만 더하면 되겠군요. ㅎㅎ 내일은 좀 일찍오면 테트리스를 만들어 올려봐야겠습니다. 그리고 가능하면 텍스트 뷰어나 BMP 뷰어같은 것도 만들어봐야겠네요. ㅎㅎ

 어이쿠 벌써 이렇게 시간이.... 전 이만 자러가야겠습니다.
 다들 좋은 밤 되세요 ^^)/~



 간단히 시작한 일인데... 버그가 나오는 바람에 연짱 7시간 정도 디버깅한 것 같습니다. ^^;;; 멀티 코어라는걸 자꾸 까먹고 작업을 하다보니 이상한 고정관념에 사로잡혀서 제대로 코딩을 안했더군요. ㅠㅠ)-b 크윽... 오늘은 꼭 기억할껍니다. ㅠㅠ 이렇게 허접할수가...

 멀티 코어임에도 불구하고 CPU가 초기화되는 순서가 0번부터 순차적일꺼라는 말도 안되는 생각을 계속 하고 있었습니다. 그래서 코딩도 순차적으로 CPU가 초기화 되니 스택도 순차적으로 할당해 주도록 했더군요. 허나 실상은... 초기화 되는 순서를 아무도 모른다는... ㅠㅠ 그래서 CPU의 ID를 직접보도록 수정했습니다. 끄응... ㅡ_ㅡa...

 그리고 막판에 짬을 좀 내서 CPU Monitoring Application을 추가했습니다. 원래 이게 목적이었는데, 장난삼아 Dual Core에서 Quad Core로 QEMU 옵션을 변경했다가 버그를 발견하고 디버깅만 주구장창했다는... 내일 출근은 또 어찌할지... 머리도 띵하고... 흑흑....

 아래는 급히 찍은 스크린샷입니다. Quad Core로 만들어 놓고 실행시킨 화면인데, 원체 허접하게 CPU Load를 계산하는지라... 별로 믿음직스럽지 못하군요. ㅎㅎ 그래도 된다는데 의의를... ^^;;; 시험삼에 Hexa Core(16개)까지 QEMU에서 테스트해봤는데, PC도 버벅거리고 QEMU에서 속도가 제대로 안나오더군요. 그나마 Quad Core까지는 쓸만한 것 같습니다.
사용자 삽입 이미지

 어휴... 테스트를 제대로 안해보고 거사를 시작했다면 어찌됬을까 아찔합니다. ㅠㅠ 역시 여유를 갖고 검토하는 시간을 갖길 잘한 듯 합니다. 에혀~

 이제 자야겠군요. 내일 하루종일 좀비모드로 지낼 것 같다는... ㅠㅠ
 다들 좋은 밤 되세요 ;)


 다들 즐거운 추석 연휴를 보내시고 계신가요? ㅎㅎ 저는 할머니댁에 당일치기로 갔다오는 바람에 시간이 많이 남아서 오늘도 어김없이 OS를 만들고 있습니다. 이런 걸 보면 역시 프로그래머가 천직인 것 같네요. ㅎㅎ ^^;;; 다만 좀 아쉬운 점이 있다면 마제스터치 키보드와 함께하지 못하고 허접한 삼성 멤브레인 키보드를 치고있다는 것... 기계식 키보드를 치다가 멤브레인 키보드를 치니 끝까지 눌렸는지를 확실히 알 수 없군요. ㅡ_ㅡa... 오타 작렬입니다. ㅠㅠ

 잡담은 이쯤하고... OS 개발도 거의 막바지로 가고 있어서 그 동안 고민했던 것들을 하나, 둘 마무리하고 있습니다. 옛날부터 OS를 몇번이나 뒤엎고 다시 들고 있지만, FPU 즉 실수 연산에 대해서는 그리 깊게 고민하지 않았습니다. 왜냐하면 finit, fxrstor, fxsave와 같은 멋진 어셈블리어 명령어들이 x387 Coprocessor의 상태를 관리해줬기 때문이지요. 그때는 단순히 이 명령들만 호출하면 된다고 생각했습니다. ^^;;

 그런데 막상 해보니 실상은 좀 다르더군요. ㅡ_ㅡa... fxsave 및 fxrstore를 이용해서 FPU 상태를 저장/복원했을 때 사용되는 메모리의 크기는 총 512Byte 입니다. 이건 상당히 큰 사이즈입니다. @0@)/~!!! 이것을 만약 Task Switching 시마다 저장/복원을 한다고 가정하면 Core에 있는 Register를 저장/복원하는 크기보다 더 크기때문에 거의 2배 정도의 Context Switching 시간이 걸립니다. ㅡ_ㅡa... 무시무시한 시간이지요.

 요 며칠동안 고민을 많이 했었는데, 가만히 생각해보니 뭔가 답이 나오더군요. Task 중에 FPU를 사용하는 Task가 여러개 있다고 가정하면, 인코딩 프로그램과 같은 프로그램을 제외하고는 실수 연산이 그리 빈번하지 않습니다. 즉 Task의 전체 코드에 비해 FPU 연산이 차지하는 비중이 작다는 것이지요. 그리고 또 한가지 포인트는 모든 Task가 FPU를 사용하는 것은 아니라는 겁니다. 경우에 따라 정수 연산으로도 충분한 Task가 있으니까요. ^^

 그래서 OS에서 마지막으로 FPU 연산(mmx, sse, sse2, sse3관련 어셈블리어 명령 사용)을 사용한 Task를 저장하고 있다가, 어떤 Task가 FPU 연산을 수행하면 마지막으로 사용한 Task와 비교해서 처리하는 방식을 생각했습니다. 사실 이 방법은 INTEL 문서(Volume 3)에도 나와있는 방법입니다. ^^;;; CR0 레지스터의 TS bit를 잘 사용하면 FPU 연산이 발생할때마다 7번 Exception이 발생하게 할 수 있고, 이것을 이용해서 처리하도록 가이드가 나와있습니다.

 제가  사용한 간략한 알고리즘은 아래와 같습니다.
  • FPU 연산이 발생했을 때, 마지막으로 FPU를 사용한 Task가 있는지 확인한다.
  • 마지막으로 FPU를 사용한 Task가 현재 Task이면 아무것도 해줄 필요 없다.
  • 마지막으로 FPU를 사용한 Task가 현재 Task가 아니면 마지막으로 사용한 Task에 FPU의 상태를 저장한다(fxsave).
  • 현재 Task가 FPU를 사용한 적이 없으면 FPU 상태를 초기 상태로 설정한다(finit).
  • 현재 Task가 FPU를 사용한 적이 있으면 Task에서 FPU 상태를 복원한다(fxrstor).
  • FPU를 마지막으로 사용한 Task 정보를 현재 Task로 설정한다.

 위와 같은 방식으로 총 300개의 FPU Task를 생성해서 돌려봤습니다. 처음에 FPU 상태를 저장하지 않았을때는 FPU가 여기저기 Task에서 건드려져서 정상적으로 계산 결과가 나오지 않더군요. 하지만 위와 같은 알고리즘으로 구현하고나니 계산 결과가 정확하게 나왔습니다. >ㅁ<)-b

 이렇게 또 한고비가 넘어가니 기분이 좋습니다. ㅎㅎ 잠깐 나갔다 와서 다른 장난(?)도 좀 쳐봐야겠군요. ㅎㅎ
 다들 즐거운 연휴 보내세요. ^^)-b



 
 어제 급히 Application Level, 즉 User Level을 추가했다가 낭패를 크게 봤습니다. ㅡ_ㅡa...  첫번째는 아무 생각없이 Supervisor Mode로 맵핑되어있는 Paging 때문에... 두번째는 요상하게 설정된 User Level Descriptor 때문에... 세번째는 Segment Selector에서 잘못된 RPL Field 때문에... 네번째는 RFLAGS에 있는 IOPL 때문에... 다섯번째는 Interrupt 발생 시, User Level에서 Kernel Level로 변경되어 자동으로 발생하는 Stack Switching 때문이었습니다. 헥헥... 나열하니 이거 끝이 없군요. ;)

 원래의 목적은 Application Level에서 커널 함수를 사용할 때 Interrupt를 사용해서 Kernel 레벨로 바꾸고, Interrupt를 활성화 한 상태에서 커널 코드를 실행하는 것이었습니다. 이렇게 하면 인터럽트 불가로 동작하는 시간이 줄어들기 때문에 인터럽트에 좀 더 기민하게 반응할 수 있습니다. 만약 윈도우 전체를 다시 그리는 커널 코드를 실행한다고 가정하면 그 시간이 얼마나 걸릴지... 그리고 그동안 인터럽트 처리가 안되면 어떻게 될지는 금방 상상이 되실겁니다. ^^;;;;;

 그런데 이게 Stack Switching이 발생하다보니, Kernel Level Stack을 Application들 끼리 공유하게 되더군요. 32bit 같은 경우는 Task 별로 TSS 영역이 존재해서 별도의 Kernel Stack을 할당해 줄 수 있었지만, 64Bit에서는 TSS가 Task에 종속적으로 존재하는 것이 아닙니다. 거의 Global하게 공통적으로 사용한다고 해도 될만큼 말입니다. ㅠㅠ

 하지만 커널 코드 수행 시, 인터럽트를 활성화하려면 태스크 별로 커널 스택을 별도로 가져야 하는데... 어떻게 할까 고민하다가 결국 Interrupt Service Routine에서 System Call용은 Kernel Stack에서 Application Stack으로 돌아가도록 했습니다. Stack Switching을 한번 더 한다는 이야기지요. 이렇게 하면 System Call 호출로 인해 Kernel Mode에 진입해 있지만 Stack은 Application의 Stack을 사용하기 때문에 커널 함수를 수행하는 도중 Task Switching이 발생해서 다른 Application이 Kernel로 진입해도 문제 없습니다.

 일단 대충 코드를 구현했는데... 오늘은 일찍 자는 날이라서 내일 테스트 해야겠군요. 별것 아닌 것 같은데... 해보니 별거(?)였다는... ㅠㅠ

 아직 내공이 많이 부족한가 봅니다. 크윽... 저의 애마(?) 마제스터치 키보드와 함께 계속 달려야겠군요.
 다들 그럼 좋은 밤 되세요. ;)


 드디어 응용프로그램을 외부에서 전송하여 실행하는데 성공했습니다. 외부 즉 윈도우와 가상 머신은 Serial Port로 연결했습니다. QEMU의 옵션 중에 가상 Serial Port를 열어주는 옵션이 있는데, 그것을 이용해서 가상머신에 Serial을 만들고 제가 만든 OS에서 그 Serial을 사용함으로써 통신에 성공했습니다. @0@)/~

 QEMU에서 TCP/IP로 Serial Port를 생성할 수 있는데, 아주 편하더군요. 덕분에 간단한 윈도우 소켓 프로그램으로 파일을 OS에 넘겨줄 수 있게 됬습니다. 아래는 제가 사용하는 QEMU 옵션인데, 맨 뒷줄 -serial 부터가 TCP/IP로 Serial Port를 생성해주는 옵션입니다. ^^

qemu-system-x86_64.exe -L . -fda d:/64bitos/disk.img -hda 64BitOS.img -m 32 -localtime -M pc -smp 2 -boot a -serial tcp::4444,server,nowait


 아래는 QEMU에서 돌고 있는 제 OS에 파일 매니져가 응용프로그램 바이너리(elf64 파일 포맷)을 넘겨주는 화면입니다. OS에서 Serial로 열심히 데이터를 다운받아서 "."을  찍고있는 걸 볼 수 있습니다. ^^;;;;
사용자 삽입 이미지

 다운로드가 끝나면 OS에서 ELF64 파일 포맷인가 확인한 뒤, Relocation을 수행합니다. 그후 메모리를 할당받아 Relocation된 실행 파일을 복사하고 프로세스를 생성합니다. 원래는 ELF64 파일 포맷을 가공하려고 했습니다만, Relocation을 분석하는 과정에서 크게 복잡한 부분이 없어서 그냥 쓰기로 했습니다. ^^;;;

 아래는 여러 개의 Application을 OS에 업로드하여 실행한 화면입니다. 각자 자신의 Task ID를 표시하게 되어있습니다.
사용자 삽입 이미지

 어휴... 진짜 이것 저것 많이 했군요. 이제 내일은 GUI 쪽 System Call을 추가하고 좀 복잡한 Application을 로딩해서 정상적으로 동작하나 확인해 봐야겠습니다. >ㅁ<)-b

 이크~ 벌써 시간이 이렇게 됬군요. 그만 자야겠습니다. ㅎㅎ
 그럼 다들 좋은 밤 되세요 ;)


 와우~ 거의 일주일 동안 작업해서 GUI 프로토타입을 완성했습니다. ㅠㅠ 삽질한걸 생각하면 눈물이 앞을... ㅠㅠ 흑흑... 오늘 윈도우 매니져가 개별 윈도우 메시지 큐에 메시지를 전달하는 기능을 추가하면서, 프로토타입을 겨우 완성했습니다.
 
 항상 추가된 기능은 어떻게라도 테스트를 해야하는 법~!! 테스트는 마우스가 윈도우 위를 지날때 윈도우 매니져가 마우스 이벤트를 윈도우 큐에 넣고,  윈도우는 큐에 데이터가 있는지 체크하는 방식으로 했습니다. 만약 마우스 데이터가 큐에 있다면 윈도우 가운데 있는 사각형 색깔을 바꾸도록 말이지요. ^^

 궁금하신 분들은 아래 파일을 다운 받으셔서 압축을 푸신 다음 "qemu64MyOs.bat" 파일을 실행하시면 GUI 프로토타입을 테스트 할 수 있습니다. ^^ 마우스가 클릭되거나 윈도우 위를 지나면 사각형이 반짝이는 걸 보실 수 있습니다.
아래는 스크린샷입니다. ㅎㅎ
사용자 삽입 이미지


 마지막으로... QEMU에서 마우스를 빠져나오게 하려면 Ctrl 과 Alt를 몇번 동시에 누르면 됩니다. ^^;;; 한번에 빠져나오는 경우도 있지만 몇번 눌러야 빠져나오는 경우도 있으니 당황하지 마시고 몇번 더 시도해보세요 ;)

 아아~ 이제 그만 자야겠습니다. 이번 주는 대충 여기까지~!!!
 그럼 좋은 밤 되세요 ^^)/~

 어제 오늘 열심히 삽질해서 GUI라고 부를 수 있는 기능을 넣었습니다. 아래는 데모 화면입니다. ㅎㅎ 고전인 Hello World를 한번 넣어봤습니다. @0@)-b
사용자 삽입 이미지
 
 일단 만들긴 만들었는데... 이게 알고리즘이 후지다보니 굉장히 느리군요. ㅠㅠ 특히 큰 윈도우가 겹쳐져서 이동하는 경우, Draw를 상당히 많이 해야되서 거의 죽음입니다. ㅠㅠ 이거 무슨 수를 내던지 해야겠군요. 나름대로 적은 부분만 그릴려고 노력했는데... 아직은 역.부.족. ㅎㅎ

 어휴... 벌써 4시... ㅠㅠ 비빔면이나 하나 먹고 자야겠습니다. 내일은 꼭 어떻게든 속도를... ㅠㅠ)-b


 요 며칠 계속 알고리즘 고민한다고 거의 코딩 안하고 그림만 계속 그렸습니다. 자꾸 마음 속에 메모리를 적게 사용하고픈 하는 욕망이 타올라서 어떻게 하면 메모리 사용량을 줄일까만 고민했습니다.ㅠㅠ 메모리를 적게 사용하려면 결국 개별 윈도우가 임시 버퍼를 가지지 않고 프레임 버퍼에 바로 그려줘야 하는데, 이때 발생하는 깜빡임과 화면 업데이트 속도때문에 결정을 못하겠더군요. ㅠㅠ

 메모리를 적게 쓰려다 속도를 포기하는 상황까지 오게되자, 결국 개별 윈도우가 임시 버퍼를 가지는 방식으로 확!! 기울었습니다. ㅡ_ㅡa... 어플리케이션 몇개 실행하지 않을 건데 굳이 메모리를 아껴야하는 생각도 들고, 전체 화면으로 생성하는 어플리케이션이 몇 안되면 크게 문제가 안될 것 같은... ^^;;;; 어차피 간단한 GUI를 구현하고 잘 동작함을 보여주는 것이 목적이라서, 간단히 구현할 수 있는 방향으로 가는 것이 옳은 것 같습니다. ㅎㅎ

 아래는 간단히 구현해 본 윈도우 겹침 테스트 화면입니다. 총 3가지 색의 윈도우가 있고 마우스 포인터(파란색 작은 사각형)로 클릭하면 윈도우를 움직일 수 있도록 했습니다. memcpy, memset 함수가 대충 구현되어있을 때는 무시무시하게 느리던데(그리는게 다보여요 ㅠㅠ), 약간 튜닝해주니 광속으로 움직이는군요. @0@)-b 역시 64bit 짱~!!! 1024 * 768 화면 전체를 다시 그리는 최악의 경우에도 볼만하네요. ;)
사용자 삽입 이미지

 이제 윈도우 메니져와 응용 프로그램 사이에 Lock 처리만 해주고 API만 구현하면 대충 될 것 같습니다. 아휴~ 이거 원 정신이 하나도 없네요. ㅠㅠ 왜 이렇게 할 게 많은지... 어서 빨리 마무리해야 할텐데 말이죠. ;) 이렇게 나가다간 끝이 없겠습니다. 그런데 초보자용으로 시작한건데... OS 코드가 점점 복잡해지는군요. ㅠㅠ 어휴~ 이일을 어째... ㅠㅠ

 끄응~ 밤이 깊었으니 나머지 구현은 내일해야겠습니다.
 그럼 다들 좋은 밤 되시길 ;)


 
 요즘 야근이 너무 잦아서 생각보다 진도를 빨리 못나가고 있습니다. ㅎㅎ 그래서 밤잠을 줄여가며 코딩하고 테스트하고 있는데, 아흙 죽을 것 같군요. 내일도 회사에 출근해야해서 더 죽겠다는... ㅠㅠ

  현재 OS 진행 상황은 그래픽 모드로 전환까지는 완료한 상태입니다. 이제 남은 것은 Mouse를 사용하는 간단한 GUI를 만드는 것 정도인데... ^^;;; GUI를 만드는 것은 둘째치고 지금 Mouse 인터럽트 처리 때문에 고전하고 있습니다. ㅠㅠ

 아시는 분도 계시겠지만, PS/2의 Mouse 같은 경우 Keyboard의 데이터 포트와 같이 사용합니다. 즉 데이터 포트를 읽었을때 이것이 마우스 데이터가 될수도 있고 키보드 데이터가 될수도 있는 것이지요. ;) 자~ 그럼 어떻게 하면 가장 빨리 구분할 수 있을까요? 권장하는 방법은 인터럽트를 사용해서 인터럽트 서비스 루틴에서 바로 읽는 겁니다. 인터럽트는 데이터를 포트에 밀어넣은 즉시 발생하므로 정확하게 구분해서 읽을 수 있습니다.

 하지만 멀티 프로세서 환경에서 인터럽트를 사용하니, 기존에 없던 문제가 생기더군요. ㅡ_ㅡa... 인터럽트를 각 프로세서에 브로드캐스팅(Broad Casting)하므로, 프로세서 #0 이 마우스 인터럽트를 처리하는 동안 프로세서 #1은 키보드 인터럽트를 처리할 경우가 생기는 겁니다. @0@)-b

 위와 같은 상황에서 싱글 프로세서는 인터럽트 서비스 루틴에서 인터럽트가 불가로 설정하고, 순차적으로 데이터를 읽는 방법으로 처리할 수 있습니다. 하지만 멀티 프로세서의 경우는 동일한 포트에 순차적으로 접근하는 것을 보장해주는 기능이 추가적으로 필요합니다. 즉 프로세서 #0이 마우스 데이터를 읽는 동안, 프로세서 #1이 먼저 접근해서 키보드 데이터인줄 착각하고 읽는 일을 막아야한다는 것이지요. ㅡ_ㅡa...

 어떻게 처리할 지 상당히 고민됩니다. 같은 포트를 사용하는 서로 다른 인터럽트 소스의 경우에 싱글 프로세서처럼 하나의 프로세서에 할당해주면 간단히 해결할 수 있는데... 더 좋은 방법이 있을 것 같아서 ^^;;;; 아우... 생각보다 빡시군요. ㅎㅎ

 일단 고민을 좀 더 해봐야겠습니다. ;)
 그럼 다들 좋은 밤 되시길~

ps) Qemu 같은 경우 키보드 컨트롤러와 CPU의 키보드 인터럽트 간에 타이밍 문제(?) 비슷한게 있는 것 같더군요. 각 인터럽트에서 데이터를 읽을 때, 데이터 포트에 데이터가 있는지 확인하도록 했더니 마우스와 키보드를 동시에 움직이는 경우 간혹 문제가 발생했습니다. ㅡ_ㅡa... 키보드 데이터가 마우스 인터럽트에서 나오더군요. 싱글 프로세서인데 말이지요. ㅡ_ㅡa... 결국 데이터가 있는지 확인 안하도록 해서 해결했습니다. 이건 뭐... ㅎㅎㅎ


 주말에 살짝 작업해서 시리얼 통신과 그래픽 모드로 전환하는 기능을 추가했습니다. @0@)/~ 예전에 썼던 코드들을 그대로 긁어 붙였더니 진도는 잘나가는데... 왠지 좀... 찔리는 것이... ㅎㅎㅎ

 아래는 1024 * 768에 16bit Color로 설정해서 파란 사각형을 그린 화면입니다. ;) 아우 신납니다. ㅎㅎ 이제 NDS에서 사용했던 그래픽 라이브러리를 올려볼까 생각 중입니다. 그래픽 라이브러리를 다시 만들려니 머리가 아파서 ^^;;;;
사용자 삽입 이미지

 밤이 늦었으니 일단 자고 나머지는 내일해야겠군요. ㅎㅎ
 아래는 그래픽 모드 전환에 대한 문서인 VBE 3.0 스펙입니다. ;)

 그럼 다들 좋은 밤 되세요 ;)


 ELF64의 Relocation 쪽을 실컷 파다가 문득 정신을 차려보니 잘 시간이 넘었더군요. 그래서 블로그의 덧글이나 확인하려고 들어갔는데, 요 며칠 포스팅한 글을 보니 잡담에 가까운 글이 많았습니다. ^^;;;; 아무래도 최근에 하고 있는 삽질 때문인 것 같습니다. ㅎㅎ

 언능 삽질을 끝내고 빨리 정리해야 할텐데... 욕심이 점점 많아져서 이것 저것 많이 하게 되는군요. 일단 지금은 간단한 GUI까지 해서 결과를 정리할까 생각 중입니다. GUI라고 해봤자 Frame Buffer 모드 비슷하게만 만들면 간단한 알고리즘으로 윈도우 비슷하게 흉내낼 수 있으니, 모양만 갖추는 거지요. ;) 아니면 닌텐도 DS에 올린 GUI 관련 소스를 사용해도 될 것 같습니다. ㅎㅎ

어휴, 할 것은 많고... 고민할 것도 많아서 머리가 지끈거립니다. 연이은 야근으로 인해 머리도 복잡하고 ㅎㅎ... 그래도 머리가 복잡한 덕분에 얻은 주옥같은 아이디어가 있어서 그나마 위로가 되는 것 같습니다.@0@)-b  주옥같은 아이디어가 뭔고 하니, 바로 한글 입력 처리에 대한 알고리즘입니다. ^^)/~

 일반적으로 한글 입력 처리는 오토마타를 사용해서 해당 상태에 따라 한글을 출력하는 방식을 사용합니다. 오토마타를 이해하면 쉽게 구현할 수 있는 부분이긴 하지만, 한글 입력 오토마타라는게 간단히 몇줄로 설명할 수 있는 내용이 아닙니다. 그래서 오토마타를 사용하지 않고 처리할 수 있는 부분에 대해서 고민하고 있었습니다. ㅠㅠ 그런데 갑자기 테이블을 사용해서 매치하는 방법이 떠오르더군요. @0@)-b

 아이디어를 간단히 설명하면 키 순서와 일치하는 한글을 테이블로 만들어서 입력된 키값과 테이블에 있는 값을 비교해서 처리하도록 하는 것입니다. 완성형 한글의 경우 숫자도 그리 많지 않으므로 충분히 테이블로 커버할 수 있을 거라는 생각이 듭니다. 구현 또한 아주 간단하고 오토마타보다는 더 직관적이지요. ;)

 아아~ 또 즐거운 주말이 다가오는군요. 이번 주말은 좀더 빡시게 굴러서 대충 마무리를 해야겠습니다. ;)
 그럼 다들 좋은 밤 되세요. ^^)/~



 고민 끝에 Ultra DMA쪽은 다음에 하는 걸로 미루고, ELF64의 Relocation 처리 부분을 하고 있습니다. PCI 부분을 다루자니 배보다 배꼽이 더 커지는 것 같고, 또한 굳이 DMA를 사용하지 않아도 HDD를 읽는데 문제가 없기때문에 작업량을 늘릴 필요가 있겠나 하는 생각이 들었습니다. ㅠㅠ 그래서 결국 다음에 시간나면 천천히 하는 걸로 결정했습니다.

 ELF64 File Format Spec을 보면서 느낀건데, PE File Format과 굉장히 비슷하군요. 차이점이라면 Section 별로 Relocation 정보를 가진 Section이 딸려오는 것이랄까요... r_link 및 r_info Field를 이용해서 Relocation 및 Symbol 정보가 포함된 Section으로 찾아갈 수 있습니다.

 아래는 간단히 VC로 만들어본 ELF64 Analyzer의 화면입니다. 각 Section 정보를 추출해서 Symbal 및 Relocation 정보를 나열하고 있습니다.
사용자 삽입 이미지

 이제 실제로 Relocation해서 메모리에 올리는 일만 남았군요. ;) 점점 삽질하는 속도가 빨라지고 있습니다. ㅠㅠ 좋은 건지 나쁜 건지... 일단 ELF64에 대한 내용은 천천히 정리해서 포스팅하겠습니다. ;)

 요거 끝나고 나면 GUI를 살짝 만들어보고 바로 작업(?)에 들어가야겠습니다. ㅎㅎ 아웅~ 잘되야할텐데... 걱정입니다. ㅎㅎ
 그럼 다들 좋은 밤 되세요 ^^)/~



 요 며칠간 일이 좀 있어서 소스코드를 깊게 볼 시간이 없었습니다. ㅎㅎ 그러다보니 오늘에서야 겨우 소스를 뒤져볼 수 있게 됬습니다만, 간만에 소스를 보니 너무 테스트 코드가 많아서 눈이 빙빙 돌더군요. @0@ 그래서 코드 정리에 돌입했습니다.

 사실 그동안 PIT(Programmable Interval Timer)때문에 삽질을 좀 많이 했습니다. MP Spec에 따르면 INIT -> SIPI -> SIPI를 보내는 동안 각각 10ms, 200us, 200us 씩 Delay를 하게 되어있습니다. 따라서 이 Delay를 맞추기 위해 생각한게 Timer를 사용하는 것인데... 생각보다 에뮬레이터들이 완벽하지 않더군요.

 Timer의 Interval을 변경할때 종종 문제가 발생했습니다. ㅠㅠ 그래서 Timer이 Interval을 변경하지 않고 Tick을 계속 읽어들여서 Counting하는 방법으로 해결했습니다. 약간 꼼수긴하지만 어쨋든 되긴하니 ^^;;;; 패스~

 정리하는 김에 Dual Processor만 지원하도록 되어있던 부분을 실행 시에 Processor의 수를 읽어서 동적으로 셋팅하는 코드로 변경했습니다. 커널 스택을 1M 정도로 잡아놔서 Processor가 많아지면 많아질수록 할당될 스택의 크기가 줄어드는 문제가 있긴하지만... 커널에서 큰 지역변수를 쓰지 않는다면 괜찮을 것 같습니다.

 현재 QEMU를 이용해서 16개까지 해봤는데, 큰 문제가 없는 것으로 보아 잘되는 것 같습니다. ^^)/~ 물론 더 많이도 지원할 수 있습니다만, 아직까지 그럴 필요성을 못느껴서 16개로 한정했습니다. 아래는 실제 실행한 화면입니다.
사용자 삽입 이미지

 Bootstrap Processor는 Task 1과 Task 2의 Multi-Tasking을 수행중이고, 나머지 15개의 Application Processor는 화면에 자신이 실행된 Count를 출력하도록 되어있습니다. 아래쪽에 각 Processor가 Keyboard Interrupt를 얼마나 처리했는가 하는 값이 출력되어있는데, 여러 Processor가 Interrupt를 처리하고 있음을 볼 수 있습니다. ^^

 아아~ 이제 ELF64에 Relocation만 보면 어느 정도 끝날 것 같습니다. ;) 오늘 저녁에 뚝딱 해치워야겠군요.
 그럼 다들 좋은 주말 보내시길~ ^^)/~


 삽질을 끊임없이 한 결과(??) 64bit + Dual Core + Symmetric I/O System 까지 진행했습니다. 일단 다른 Core도 깨워서 64bit Mode로 전환시켜놨고, Interrupt 처리는 IO APIC와 APIC를 손봐서 두 Core에 Broadcasting 하도록 설정했습니다.

 Interrupt가 두 Core에 모두 전달되니, 서로 Interrupt를 처리하겠다고 경쟁하게 되더군요.  키보드 버퍼에 이상한 값이 들어가고 버퍼를 잘못 건드려 엉망이 됬습니다. 그래서 Spin Lock으로 먼저 Interrupt Handler에 접근한 Core가 처리하고 다른 Core는 그냥 End Of Interrupt만 송신하도록 했더니 나름대로 잘 동작합니다. >ㅁ<)/~ 이렇게 하는게 정석인지는 모르겠습니다만... 별 문제 없으니 그냥 패스.. ^^;;; 나중에 시간나면 Linux쪽에서 처리하는 방식을 한번 봐야겠군요(혹시 Linux의 Symmetric I/O 처리에 대해서 아시는 분 덧글 부탁드립니다 ㅠㅠ)-b ).

 아래는 Qemu에서 동작시킨 화면입니다. "showinterruptcount" 명령으로 각 Core가 Keyboard Interrupt를 얼마나 처리했는가를 표시했는데, 비슷하게 나오는군요. 의도하지 않은 Load Balancing이... ^^;;; 아직은 Cache 관련 부분이 Disable 되어 동작합니다.
사용자 삽입 이미지

 여기까지 해놓고 Cache를 Enable 하려니 IO APIC 및 APIC가 Memory Mapped I/O 방식으로 되어있어서 이 영역을 Cache가 불가능하게 설정해야 하더군요. 그래서 INTEL 문서를 뒤져보니 MTRR(Memory Type Range Register)를 사용해야한다고 나와있었습니다. ㅡ_ㅡa... 이것만 할게 아니라 CR0와 Page Table쪽도 다 같이 변경해줘야하고, 다른 쪽 Core 또한 같은 MTRR 설정값을 공유해야하는 문제가... 그래서 잠시 보류했습니다. ^^;;;;

 이거 생각 좀 해보고 넣어야겠군요. Cache를 활성화한다고 가상머신에서 돌아가는 OS가 크게 빨라질까 하는 생각도 들고... 차라리 Cache를 다 날려버리고 간단하게 구현하는게 더 나을지도 모른다는 생각이 계속 머리에서 맴돌아서... ㅠㅠ...

 자료가 별로 없어서 무한 테스트로 자료를 만드는데... 완전 죽음입니다. ㅠㅠ 아흑... 진도도 빨리 안나가고... 그래도 많이 왔네요. ㅠㅠ)-b 조금만 더 파보면 본격적인 작업(??)을 진행할 수 있을 것 같습니다. ;)

 그럼 다들 좋은 밤 되세요 ;)



 
 지금까지 진행된 소스 코드를 정리하고 있습니다. 테스트 코드를 워낙 많이 넣었더니 테스트 코드를 지우다가 실수로 원래 코드를 지우는 경우도 생겨서... ㅠㅠ 이거 표시를 제대로 해놔야겠더군요.

 제가 "변수명이나 매크로는 그 이름 자체가 자신을 설명할 수 있어야한다"는 주의라서 변수명/함수명 등등을 굉장히 길게쓰는 편입니다. ^^;;;; 특히나 커널 코드처럼 장래에 계속 볼일이 있는 코드 같은 경우는 더더욱 심한 편이지요.

 시간이 지나면 잊혀지기 마련인데, 주석이나 잡 문서로 아무리 잘 설명해 놓은들 변수나 함수 이름을 보고 무슨 뜻인지 한번에 알 수 없다면 문제가 있다고 생각합니다. 물론 각자의 스타일이 있으니 아니라고 생각하시는 분들도 있을거라 생각합니다(짧고 간결하게 쓰는게 더 낫다고 생각하시는 분들은 돌 던지지 말아주세요 ㅠㅠ 그냥 제 생각입니다. ㅠㅠ).

 그런데 너무 길게쓰니 함수 호출하거나 할 때 한 컬럼이 굉장히 길어지더군요. 저는 왠만하면 80 컬럼을 맞춰서 쓰려고 노력합니다. 따라서 개행을 해야하는데 결국 한 라인에 함수나 변수가 몇개 못 들어간다는... ㅠㅠ 크윽... 이거 최악의 조합인 것 같습니다. ㅠㅠ 소스코드가 좀 희한한 트리 모양으로 보이는 군요. ㅠㅠ

 어떻게 하는게 좋을지는 나중에 한번 의견을 나눠봐야하겠지만, 일단 길게 작업해두는 쪽으로 가야겠습니다. 일단 제가 이해하기 쉬워야 남들한테 설명하기도 쉬우니까요. ㅎㅎ

 어휴 벌써 또 시간이 이렇게 됬군요.
 다들 좋은 밤 되세요 ㅎㅎ ;)


 크윽... 죽는 줄 알았습니다. ㅠㅠ 지난 휴가와 이번주 주말을 모두 투자해서 드디어 Interrupt 처리 부분을 넣고, 나중에 사용할 APIC 와 IO APIC쪽 설정 기능을 넣었습니다. ㅠㅠ 사실 훨씬 더 일찍 끝났어야 하는 일이나... 가상 머신에 불완전함 때문에 코드를 고치고 테스트하다보니 많이 늦어졌습니다.

 현재 테스트 중인 가상머신은 총 3개인데, VMware, Bochs, 그리고 QEMU를 사용하고 있습니다. 재미있는건 각자 BIOS 설정이 조금씩 다르고, 같은 코드라도 동작하는 방식이 다르다는 것이죠. ㅠㅠ 이것 때문에 죽는 줄 알았습니다. 특히 Interrupt 처리부분에서 Bochs와 VMware에 문제가 있는 것 같더군요.

 테스트를 몇시간동안 한 결과 코드에서 Stack을 Align하지 않고 사용하는 경우, 간혹 Stack에 데이터가 덮어써져서 나중에 iretq로 돌아왔을 때 정상적으로 수행되지 않는 문제가 있었습니다. 원래 CPU가 realign을 해주게 되어있습니다만, 정상적으로 동작하지 않는 때가 있는 것 같습니다. 물론 꼼수로 해결했습니다만, 뭔가 좀 씁쓸하네요. ^^;;; 더 웃긴건 QEMU의 경우는 괜찮다는 것이죠. ;) 이 문제 때문에 거의 이틀을 꼬박 날렸습니다. 아흑 내시간...ㅠㅠ

 더 가관인 문제는 VMware에서 생겼습니다. Keyboard Interrupt를 발생하지 못하게 한 뒤에, 키를 마구 입력해서 키보드 버퍼를 체운 후 APIC쪽을 Enable 하니 Interrupt가 발생하지 않는 것이 아니겠습니까? @0@)/~!!! VMware의 APIC쪽하고 PIC쪽에 매치하는 부분에서 문제가 있는 것 같은데, 키보드 버퍼에서 데이터를 다 읽어내면 그때부터 인터럽트가 발생하기 시작합니다. ㅡ_ㅡa;;;;; 이것도 결국 땜빵코드로 해결... ^^;;;

 마지막으로 애교로 넘어가줄 수 있는 문제... Qemu에서 int 13, 즉 Disk Read에 문제가 있더군요. 여러 섹터를 읽도록 요청하면 간혹 덜 읽히고 종료되는 문제가 있었습니다. 이 문제는 어느정도 예상을 하고 있던 것이라 역시나 땜빵으로 해결... 예전에 만들면서 겪었던 문제라서 그냥 애교로 넘어갔습니다. ㅎㅎ

 결국 온갖 삽질 끝에 세가지 에뮬레이터에서 모두 동작시키는데 성공했습니다. 아래는 세가지 에뮬레이터에서 동작시킨 화면입니다.
사용자 삽입 이미지

사용자 삽입 이미지

사용자 삽입 이미지

 이제 대충 멀티 태스킹과 elf64의 재배치(Relocation) 쪽만 해결하면 되겠군요. ㅎㅎ 코어를 하나 더 활성화 시키는 것은 저번에 해봤으니 그냥 붙여넣기만 하면... (설마 이것도 쉽게 안되려나..ㅠㅠ)  그나저나 계속 삽질만 해서 큰일입니다. ㅠㅠ 크윽...

 제발 좀 한번에 가자 ㅠㅠ)/~~

 그럼 다들 좋은 밤 되시길~ ^^)-b



 예비군 내내 Intel Architecture Volume 3을 봤습니다. 그리고 고향에 내려오는 도중에 PSP로 Multile Processor(MP) Spec을 계속 봤었는데... 뭔가 좀 이상하더군요. Intel 문서에는 I/O APIC에 대해서 아주 조금 언급하고 있습니다. 그냥 APIC와 연결되있다는 정도로 말이지요. ^^;;;;
 
 그런데 MP Spec에 보면 PIC, Virtual PIC, Symmetric I/O의 각 방식에 대해 아래와 같이 자세히 그려놓고 있습니다.
사용자 삽입 이미지
<PIC 모드>
사용자 삽입 이미지
<Virtual Wire Mode With Local APIC>
사용자 삽입 이미지
<Virtual Wire Mode With IO APIC>
사용자 삽입 이미지
<Symmetric IO Mode>

  보시면 아시겠지만 CPU의 Boundary 안쪽에 IO APIC가 있는지 바깥쪽에 있는지 좀 불분명합니다. 어렴풋이 버스 배열 상태나 위치를 봐서 바깥쪽에 있다고 추측합니다만... 이것만 봐서는 확실히 알수가 없었습니다. 그래서 이것 저것 뒤지다보니 intel-82093-apic 문서를 찾았습니다. IO APIC 컨트롤러에 대한 문서더군요. ^^;;; 거의 바깥쪽에 있는게 확실한 것 같습니다. 그런데 이게 왜 중요했던건지... ㅡ_ㅡa;;;;

 생각난 김에 VMWare의 부팅했을때 모드가 위의 그림 중에 어떤 것인지 확인을 좀 해봤습니다. 몇가지 테스트가 있는데... 테스트 과정은 좀 복잡해서 생략하고 결론만 이야기하면 Virtual Wire Mode With Local APIC 인 것 같습니다. 왜냐하면 I/O APIC쪽에 Interrupt Remapping Table인가 하는 녀셕이 Default 값으로 되어있거든요. 즉 안쓴다는 이야기지요. ^^;;; 그동안 다른쪽 코어에는 인터럽트쪽 설정을 안했기 때문에 별 신경을 안쓰고 있었는데, 이제 다른 코어에 인터럽트를 활성화하려면 이것이 중요한 단서가 되서 한번 읽어봤습니다.

 음... 이제 몇가지 코딩을해서 Symmetric IO Mode 로 전환하고 테스트를 좀 더 진행해볼 생각인데... 이것 참... 할일이 많네요. ^^;;; 언제쯤 또 할 수 있을지... 요즘 들어 시간이 부쩍 부족한 것 같습니다. ㅎㅎ 그래도 잘 쪼개서 이것 저것 해봐야겠지요 ;)

 그럼 다음에 또 테스트해서 결과가 나오면 올리겠습니다. ㅎㅎ 다들 좋은 하루 되세요 ;)

ps) APIC 관련 자료가 참 없더군요. 참 불모지(?)스럽다는.... ㅡ_ㅡa...


+ Recent posts