내가 만드는 프로그램에 문제점이 있다.
이 프로그램의 목적은 도스에서도 여러개의 화면 전환이 이루어지게끔 하는 것이다. 프로그램이 수행되지 않는 상황에서 화면 전환은 쉽게 이루어진다. 하지만, 프로그램이 이미 수행중에 있는 상태에서 화면 전환을 시도하면 어떻게 되는가?
간단히 계속 Blocking 되고 있는 것이다. 입력을 해도, 이전에 수행되고 있는 프로그램의 입력으로 쏙 들어가 버린다. 이것은 리눅스의 기본 Console이나 screen 명령에서 지원되는 수준에 훨씬 못 미치는 수준으로 밖에 안보인다. 스크립트 수준...

그래서, 이 프로그램은 태스크 전환을 시도하며, 그것을 위해서 두개의 태스크를 생성한다. Task1과 Task2가 있는데, 간단히 말하면, Task1은 화면에 무한적으로 '2'라는 값을 출력하는 것이고, Task2는 입력되는 문자를 화면에 바로 뿌려주며, 그에 맞춰서 ASCII 코드를 0부터 차례대로 출력을 한다.

본 프로그램을 수행하면 Task1을 수행하게 된다. 그리고 F2키를 누르면, Task2로 전환이 이루어지는데, 화면과 프로그램이 동시에 전환이 되는 것이다.
이 상황에서 F1키를 눌러, Task1로 전환을 시도하면 잘 변환이 된다. 그리고 다시 F2키를 눌러, Task2로 전환을 시도하면 되지 않는다.

이것의 문제점을 파악하기 위해서 SoftIce를 이용하였다. F1이나 F2키에 의해서 수행되는 소스 위치에 break point를 지정하여 간단히 살펴볼 수 있었다.

결과는 내가 임의로 지정한 스택의 공간을 사용하지 않는 데서 따른 문제였다. 하지만 ISR을 수행하게 될 때는, 인터럽트가 발생되는 시점에 수행중인 프로그램의 CS:IP를 현재 사용중인 SS:SP에 저장을 하는 것이라서, 예측이 불가능한 상황에 ISR로 들어가는 것이 된다.

다시 처음부터 코드 레벨로 설명을 하면,

Task1는 CS=2A8E인 공간에 있고, Task2는 CS=4000인 공간에 할당되어 있다. Task1이 수행되는 상황에서 F2키에 의해서 ISR로 들어가게 되는데, 이 때는 SS=2A8E이기 때문에, Task1이 위치한 공간에 CS:IP(복귀주소)가 저장된다. 그리곤 Task2로 스위칭이 되는데, 이 때는 Task2를 생성하면서 미리 저장해놓은 스택공간(SS=4000)을 사용하여 pop이 이뤄지기 때문에, 복귀주소인 CS:IP도 SS=4000에 위치하게 된다. 결국, SS=4000, CS=4000 인 공간으로 전환이 이루어져 Task2는 수행되게 되는 것이다.

이젠 CPU는 Task2를 수행하고 있으며, CS=4000인 공간에 있다. 이 상황에서 Task1로 가기 위해서는 F1키를 눌러서 ISR로 들어가게 되는데, 인터럽트 발생되는 시점이 코드 수행 시점인데, CS가 4000이 아니라, 00C9가 되어 있다. 어쩔 수 없이 SS=00C9가 되며, 따라서 그 공간에 복귀주소인 CS:IP가 저장되게 된다. 결국 Task2가 ISR로 들어올 때는 CS=00C9이며, Task1로 전환된 후에, 다시 Task2로 전환되려고 할 때, 이 공간 CS=00C9의 스택공간 SS=00C9를 사용하게 된다. 근데, 문제는 주소가 아니라, 그 안에 내용이었다.

바껴서는 안될 스택 공간 내용이 어느새 바뀌어져 있는 것이다. 이 결과는 당연히 메모리 접근 문제로 발생될 수 밖에 없는 것이다.

직접 노트에 기록한 것을 보면 다음과 같다.

Task1에서 F2키에 의해서 Task2로 전환이 된 후,
F1키 눌렀을 때 Break Point에 의해서 멈춤
( CS:IP = 11CD:0397 [_my_proc 처음위치] )
이 때의 스택값
( SS:SP = 00C9:0A7A [내가 지정한 위치가 아님] )
---------------------------------------------------------
          00 01 02 03 04 05 06 07-08 09 0A 0B 0C 0D 0E 0F
00C9:0A70 .......................... C9 00 8F 84 97 2E
00C9:0A80 C8 FC 02 32 F7 2F B2 FC-01 01 02 01 F0 FF 00 40
00C9:0A90 B5 60 00 ......................................
---------------------------------------------------------
아무튼, 그래도 Task1로 전환이 되었다. 이것은 복귀값이 Task1의 스택공간을 사용했기 때문에, 문제가 없는 것이다.
Task2로의 전환을 위해,
F2키를 눌렀을 때 Break Point에 의해서 멈춤
 ( CS:IP = 11CD:0397 [_my_proc 처음위치] )
 이 때의 스택값 또한 내가 예상한 값들로 채워져 있었다.
 ( SS:SP = 2A8E:FFEE [내가 지정한 위치임] )
 TASK_SW 소스 수행....
 Task sw 준비
  ( SS:SP = 11CD:0260 [예상한 위치임] )
  pushf
  pusha
  .....
  ( SS:SP = 11CD:0244 [예상한 위치임] )
 SS:SP 교체로 Task sw 유도함
  ( saved_sp1 <- 11CD:0244
    SS:SP <- saved_sp2 (11CD:0344) )
  .....
  popa
  popf
  ( SS:SP = 11CD:0360 [예상한 위치임] )
 ISR 종료를 위해 코드수행 복귀주소의 반환을 위해서
 SS과 SP를 설정하는데, 이것은 저장되어 있는 SS:SP값으로 00C9:0A74가 되었다.
  pop dx ; ffff
  pop bx ; 3816
  pop cx ; 0000
---------------------------------------------------------
          00 01 02 03 04 05 06 07-08 09 0A 0B 0C 0D 0E 0F
00C9:0A70 00 00 63 06 FF FF 16 38-00 00 00 00 12 09 00 40
00C9:0A80 0D 01 32 02 4E 60 42 FF-46 32 C9 00 00 00 00 61
---------------------------------------------------------

스택값은 어느새 이렇게 바껴있었다. 위에 그려진 메모리와는 너무나 다르지 않은가...
나는 저 공간(00C9 Segment)을 사용하고 싶지 않다. 그렇지만, 키 입력이 들어가 있는 프로그램에서 인터럽트가 걸리면, 수행중인 프로그램이 아닌, 내부적인 코드 위치에서 바로 ISR로 들어가 버리는 것 같다. 그래서 cli나 sti 명령을 이용해서 키 입력 부분 AH=01/INT 21H에 감싸서 해봤지만, 소용이 없었다. 어차피 소프트웨어 인터럽트이기 때문인 것 같다. cli 명령에 의해서 영향을 받지 않기 때문이다. 또한 내부적으로 sti 해버리면 소용이 없지 않은가...

저 공간을 사용하지 않고 바로 Task1에서와 같이 전환이 된다면 문제가 없을텐데... 실제로 Task1과 비슷한 Task2를 수행했을 때에는 태스크 전환이 잘 이루어졌다.

http://asmlove.co.kr/zBdC7/viewtopic.php?t=1326 에도 질문을 방금 올렸었는데, 아직 답변이 없다. 결국 해결이 안될까?

Posted by wbhacker :