윈도우 5. PhoenixPE 부팅이 시작 화면 "Initializing..." 에서 멈출 때: 시간 제한 없는 RunProgramWait
원격 수리 중 고객 PC 를 PhoenixPE 로 한 번 부팅시키는데, 노트북 시험 중에 한 번은 PhoenixPE 시작 화면에서 멈췄습니다. “Initializing …” 글자가 겹쳐 찍힌 채 더 진행이 안 됐습니다. 다시 한 번 부팅하니 멀쩡히 넘어갔고요.
원인을 보려고 해도 로그가 없었습니다. PE 의 로그는 RAM 디스크(X:)에 있어서 전원을 끄면 사라집니다. 고객 PC 앞에 제가 있는 것도 아니고요.
그래서 코드부터 읽었습니다. PhoenixPE 의 시작 스크립트는 AutoIt 으로 되어 있고, 우리 원격 지원 창은 셸을 띄우기 직전 단계(PreShellConfig)에서 뜹니다. 창이 아예 안 떴다면 그 앞에서 멈춘 겁니다. 그 앞에는 화면 드라이버와 해상도 설정, 오디오 드라이버 설치(pnputil 로 hdaudio), 네트워크 시작(net start Wlansvc) 같은 단계가 있습니다. 이것들은 RunProgramWait 라는 함수로 외부 프로그램을 실행하는데, 이 함수에 시간 제한이 없었습니다.
Do
$sOutput = StdoutRead($iPID)
If Not $sOutput = "" Then WriteLog(" " & $sOutput)
Until @error
프로그램 하나가 안 끝나면 영원히 기다립니다. 게다가 Sleep 없이 도는 루프라 기다리는 동안 CPU 한 코어를 다 씁니다. 해상도 목록을 로그에 남기려고 부르는 WMI 조회도 제한이 없었습니다.
PhoenixPE 는 빌드 때마다 이 스크립트를 새로 만들기 때문에, 빌드 결과물을 고치는 파이썬 스크립트를 따로 두었습니다. 고칠 원문이 예상과 다르면 assert 로 멈추게 해서, PhoenixPE 가 바뀌었을 때 엉뚱한 곳을 고치지 않게 했습니다.
바꾼 것은 이렇습니다.
RunProgramWait에 시간 제한 변수를 넣었습니다. 기본은 0(무제한)이라 다른 PhoenixPE 도구는 그대로이고, 시작 순서 동안에만 120초로 둡니다. 시간이 지나면 기다리기만 그만두고 프로그램은 그대로 둔 채 다음으로 갑니다. 루프에는Sleep(20)을 넣었습니다.- 우리에게 필요 없는 오디오 초기화는 건너뛰고, 로그용 WMI 해상도 목록은 지웠습니다.
- 시작 스크립트 맨 앞에서 감시 스크립트를 하나 띄웁니다.
감시 스크립트는 5초마다 PE 의 시작 로그들을 고객 PC 의 윈도우 드라이브(\PCM-PE-Boot\logs\<부팅 시각>\)로 복사합니다. 이제 다음에 멈추면 어느 단계였는지 남습니다. 그리고 원격 수리 예약이 걸린 PC 에서만, 10분 안에 원격 지원 창이 안 뜨거나 20분 안에 저를 부르지 못하면 30초 안내 뒤에 PE 를 재부팅합니다. PE 부팅은 한 번만 걸어 둔 거라 재부팅하면 원래 윈도우로 돌아가고, 원래 윈도우에서 자동으로 다시 저를 부릅니다. 재부팅 이유는 따로 남겨서 알림에 붙입니다. 고객이 직접 USB 로 부팅했거나 예약이 없는 PC 는 건드리지 않습니다.
멈춤, 네트워크 없음, 정상 호출, 예약 없음 네 경우를 시간만 줄인 사본으로 돌려 확인했고, 고친 로더 전체를 PE 안과 같은 AutoIt3 로 파싱해 봤습니다. 아직 실제 PC 에서 멈춤을 다시 만나지는 못했습니다. 그때 남는 로그를 보고 진짜 원인을 적겠습니다.
그 전에 같은 증상을 만나면, PhoenixPE 시작 화면에서 F11 로 스플래시를 숨기고 F12 로 X:\Windows\System32\PhoenixPE.log 를 열어 보면 어디서 멈췄는지 알 수 있습니다.
참고: PhoenixPE, https://github.com/PhoenixPE/PhoenixPE