다들 즐거운 추석 연휴를 보내시고 계신가요? ㅎㅎ 저는 할머니댁에 당일치기로 갔다오는 바람에 시간이 많이 남아서 오늘도 어김없이 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



 

+ Recent posts