뭉근 : 느긋하게 타는 불

이번 포스트는 Windbg에서 프로세스 리스트를 볼 때 왜 유휴 프로세스(Idle Process)는 없는가? 에 대한 내용이다. Process Explorer로 보면 아래와 같이 Idle Process가 나오는데, 왜 프로세스 리스트를 직접 출력해보면 유휴 프로세스를 찾을 수 없는 것일까? 궁금하면 아래의 글을 읽어보기 바란다.



들어가기

윈도우는 시스템 프로세스 중 커널 영역에서만 동작하는 프로세스로 Idle Process(유휴 프로세스)와 System Process(시스템 프로세스)를 사용한다. 이 두 프로세스들은 유저 모드에서 실행되지 않기 때문에 커널 영역에서만 존재한다.   

System Process는 커널 모드 시스템 스레드(kernel-mode system thread)들을 포함한다. 이 스레드들은 오직 커널 주소 공간의 코드들만을 실행하여 커널 레벨에서만 동작한다.

Idle Process는 CPU의 각 프로세서의 유휴 상태를 나타내기 위한 Idle Thread(유휴 스레드)를 포함한다. 프로세서에 더 이상 실행 가능한 스레드가 없을 때, 윈도우는 해당 프로세서의 유휴 스레드를 실행한다. 이는 멀티프로세서 시스템에서 하나의 CPU가 실행될 때, 다른 CPU들이 실행할 스레드들을 가지지 않는 상황이 있기 때문이다. 따라서 Idle Thread는 프로세서의 수만큼 생성된다.

Idle Process와 Idle Thread들은 특수한 케이스로 EPROCESS / ETHREAD 구조체로 표현되지만 익스큐티브 오브젝트 관리자에 의해 생성되는 프로세스와 스레드 오브젝트들이 아니기 때문에 시스템의 프로세스 리스트에 포함되지 않는다[0].


---------------------------------------------------------------------------------

0: kd> !process 0 0

**** NT ACTIVE PROCESS DUMP ****

PROCESS fffffa800ccc2990

SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000

DirBase: 00187000 ObjectTable: fffff8a000001740 HandleCount: 479.

Image: System

   

PROCESS fffffa800d2fcb30

SessionId: none Cid: 00e8 Peb: 7fffffde000 ParentCid: 0004

DirBase: 2495c000 ObjectTable: fffff8a0005c07d0 HandleCount: 30.

Image: smss.exe

   

… (생략)

   

PROCESS fffffa800f2b3b30

SessionId: 1 Cid: 0948 Peb: 7fffffd4000 ParentCid: 08d8

DirBase: 3091b000 ObjectTable: fffff8a0017fdea0 HandleCount: 134.

Image: vmtoolsd.exe

   

PROCESS fffffa800f2e6b30

SessionId: 1 Cid: 0954 Peb: 7efdf000 ParentCid: 08d8

DirBase: 30cce000 ObjectTable: fffff8a0018be990 HandleCount: 41.

Image: runonce.exe

---------------------------------------------------------------------------------


위의 결과를 보면 프로세스 리스트가 System Process부터 시작한다는 것을 알 수 있다. 물론 언급했던 것과 같이 Idle Process는 나오지 않는다.

커널은 프로세스 리스트를 유지하기 위해 이중 연결 리스트(double-linked list)를 사용한다. 프로세스들은 생성된 순서대로 이 이중 연결 리스트에 들어가고, 프로세스가 종료된다면 역시 이중 연결 리스트에서 제거된다.

따라서 !process 0 0 명령어를 사용하지 않고도 직접 이 이중 연결 리스트를 따라가 리스트에 있는 각각의 프로세스를 추출할 수 있다. (* 아마도 !process 명령어도 이 리스트를 따라가지 않나 싶다. 실제로 확인해보지는 않았다 :) )   

윈도우 커널은 프로세스 리스트의 헤드를 전역 변수로 관리하며, 이에 대한 심볼 정보를 PsActiveProcessHead로 공개하고 있다. 간단히 말해서 전역 변수인 PsActiveProcessHead를 시작으로 실행 중인 프로세스들의 정보에 접근한다.   

Windbg의 dq 명령을 통해 직접 순회하여 볼 수도 있지만, !list 명령을 통해 다음과 같이 각각의 리스트 요소에 대해 정보를 일괄적으로 얻을 수 있다. 아래의 !list 명령어는 리스트의 각 요소인 ActiveProcessLinks를 참조한다.


---------------------------------------------------------------------------------

0: kd> !list "-t nt!_EPROCESS.ActiveProcessLinks.Flink -e -x \"dd @$extret L1; dt nt!_EPROCESS @$extret ImageFileName\" poi(nt!PsActiveProcessHead)-0x188"

dd @$extret L1; dt nt!_EPROCESS @$extret ImageFileName

fffffa80`0ccc2990 00580003

+0x2e0 ImageFileName : [15] "System"

   

dd @$extret L1; dt nt!_EPROCESS @$extret ImageFileName

fffffa80`0d2fcb30 00580003

+0x2e0 ImageFileName : [15] "smss.exe"

   

… (생략)

   

dd @$extret L1; dt nt!_EPROCESS @$extret ImageFileName

fffffa80`0f2b3b30 00580003

+0x2e0 ImageFileName : [15] "vmtoolsd.exe"

   

dd @$extret L1; dt nt!_EPROCESS @$extret ImageFileName

fffffa80`0f2e6b30 00580003

+0x2e0 ImageFileName : [15] "runonce.exe"

---------------------------------------------------------------------------------

* 0x188은 Windows 7 x64 시스템의 ActiveProcessLinks 오프셋이다. 이는 버전마다 다를 수 있다.

  

!process 명령어를 사용했을 때와 마찬가지로 System Process는 나타나지만, Idle Process는 나타나지 않는다. 즉 Idle 프로세스는 프로세스의 이중 연결 리스트에 포함되지 않는다.

그렇다면 Idle Process는 어떻게 생성되길래 System Process도 포함되어 있는 프로세스 리스트에는 존재하지 않는건가?

   

Idle Process 찾아가기

일단 Idle Process의 존재부터 알아본다. 윈도우 인터널스[0]에 Idle Process로 접근하는 방법이 나와있지만, Windbg로 디버깅시에는 전역 심볼인 PsIdleProcess을 이용하여 더 간단하게 접근할 수 있다.

   

---------------------------------------------------------------------------------

0: kd> dt _EPROCESS poi(PsIdleProcess) ImageFileName ActiveProcessLinks

nt!_EPROCESS

+0x188 ActiveProcessLinks : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]

+0x2e0 ImageFileName : [15] "Idle"

---------------------------------------------------------------------------------

   

이 Idle Process의 ActiveProcessLinks 필드를 살펴보면 Flink와 Blink가 모두 NULL로 처리되어 있음을 확인할 수 있다. 즉 Idle Process는 다른 프로세스들을 가리키지 않는다.

* KiInitialProcess 로도 접근할 수 있다. PsIdleProcess는 EPROCESS 구조체의 주소에 대한 주소를 가진 반면 KiInitialProcess는 _EPROCESS 구조체의 주소를 가진다.


Idle Process는 시스템에서 반드시 생성하는 정적인 데이터이기 때문에 전역 심볼로 관리된다. 생성 및 파괴를 반복하는 일반적인 프로세스와 다르게 한번 생성되면 시스템이 실행되는 동안 파괴되지 않는다. 그렇다면 System 프로세스는 어떨까? System 프로세스 또한 아래와 같이 전역 변수인 PsInitialSystemProcess로 관리된다.

   

---------------------------------------------------------------------------------

0: kd> dt _EPROCESS poi(PsInitialSystemProcess) ImageFileName

nt!_EPROCESS

+0x2e0 ImageFileName : [15] "System"

---------------------------------------------------------------------------------

   

사실 커널 디버깅이 아닌, 실제 커널 코드 코딩(ex. 드라이버)를 할 때에 PsInitialSystemProcess는 전역 변수로 export[1]되어 있어 프로그래머가 접근할 수 있다. 하지만 Idle Process의 디버깅 심볼인 PsIdleProcess는 MS에서 심볼 정보만 제공하기 때문에 커널 코드 작성 시에 사용할 수 없다. 사실 생각해보면 유휴 프로세스에 드라이버가 접근할 필요가 없다. 인터널스에서도 언급한대로, 유휴 프로세스는 유휴 스레드를 위한 프로세스로 내부 객체를 살펴보면 일반적인 프로세스들과 달리 대부분의 필드들이 사용할 필요가 없기 때문에 NULL(0)으로 채워져 있다.

또한 구현 측면에서 보면 인터널스에 업근된 대로, 초기 유휴 스레드(PsInitialSystemThread)와 유휴 프로세스(PsInitialSystemProcess) 구조체는 정적으로 할당돼 프로세스 관리자와 객체 관리자가 초기화되기 전에 시스템 부팅을 위해 사용된다. 그 이후 유휴 스레드 구조체는 추가 프로세서가 동작할 때 동적으로 할당된다.

그렇다면 위의 말이 진짜일까? 궁금해진다.

   

Idle Process 생성

디버깅 심볼 정보인 PsIdleProcess와 PsInitialSystemProcess가 사용되는 부분을 찾으면 아래와 같이 PspInitPhase0 함수에서 Idle Process와 System Process의 EPROCESS.ImageFileName에 각 프로세스 이름을 쓰는 것을 볼 수 있다. 다른 프로세스들은 파일 이름을 기반으로 프로세스 이름이 정해지는데 비해, 이 두 프로세스들은 특수한 프로세스로서 프로세스 이름이 정해진 커널 코드에 의해 정해진다.

   

* 0x16c는 윈도우 7 32비트 상에서 _EPROCESS.ImageFileName 필드의 오프셋이다.

   

PspInitPhase0 함수는 커널을 초기화할 때 실행되는 함수 중 하나로, 커널은 KiSystemStartup 함수를 시작으로 다음과 초기화 함수를 호출한다. PsActiveProcessHead를 참조하는 모든 코드를 살펴보면 이 PspInitPhase0 함수에서 아래와 같이 PsActiveProcessHead를 초기화하는 것을 볼 수 있다.

   

   

코드를 살펴보면 KeGetCurrentThread 함수를 이용하여 현재 쓰레드가 속한 프로세스를 얻는다. 프로세스의 몇몇 필드를 초기화한 후 전역 변수 PsIdleProcess에 해당 프로세스의 주소를 대입한다. EPROCESS 구조체 필드는 이게 다가 아닌데 나머지는 어디서 초기화하는 걸까? 또한 KeGetCurrentThread는 어떤 쓰레드를 얻어오는건가?

   

커널 초기 쓰레드의 생성

KiSystemStartup 함수부터 살펴보면 다음과 같은 순서로 커널 초기화 함수가 실행된다.

   

KiSystemStartup()

  - KiInitialThread를 스택에 저장

  - KiInitilizePcr()

  - KiIntializeKernel()

    - KiInitializeProcess()

    - KiInitializeThread()

    - InitBootProcessor()

      - PsInitSystem()

      - PspInitPhase0()

   

KiSystemStartup 함수는 전역 변수 KiInitialThread를 스택에 저장한다.

   

   

이후 코드에서 스택에 저장한 값을 커널 프로세서 관리 구조체(Kernel Processor Control Region)인 KPCR의 IdleThread에 그 값을 등록한다.

   

   

   

이후 KiInitializeKernel 함수를 실행한다. 이 때 스레드 초기화를 위해 앞서 스택에 저장한 KiInitialThread를 호출 인자로 넘겨준다. KiInitilizeKernel을 호출할 때 KiInitialProcess 또한 인자로 같이 넘긴다.

   


KiInitializeProcess 함수를 먼저 실행하여 Idle Process의 나머지 필드들을 초기화한다. 사실 이 함수의 내부를 보면 복잡한 초기화가 아니라 앞에서 0으로 채웠던 것처럼 다른 필드들도 NULL 값으로 채운다.

   

   

Idle Process의 필드를 초기화한 후, 시스템 스레드를 초기화하는 KiInitializeThread 함수를 실행한다.

   

   

이 함수에서는 크게 KeInitThread 함수와 KeStartThread 함수를 호출한다. KeInitThread 함수를 호출하는 코드를 찾아보면 일반적인 스레드를 생성할 때 호출하는 용도로도 사용되는 것을 알 수 있는데, 이 때 입력받는 인자가 다르다. 실제로 PspAllocateThread를 찾아가보면 아래와 같이 인자가 일반적인 스레드와 다르게 들어간다는 것을 알 수 있다.

   

   

   

다시 KiInitilizeThread 함수로 돌아와 생각해보면 이 함수가 호출될 때 입력 받는 인자는 Idle Thread와 Idle Process이다. KiInitializeThread 함수에서 실행되는 KeInitThread 함수는 다음 코드와 같이 Idle Thread가 속한 프로세스를 Idle Process라고 명시한다.

   

   

앞서 커널의 첫 쓰레드는 언제 어떻게 생성되어 이 스레드로부터 Idle Process를 어떻게 얻어올 수 있는지 궁금했었는데 이제 해결되었다. KeGetCurrentThread 함수를 호출하기 전에 첫번째로 KPCR.Prcb.CurrentThread에KiInitialThread (Idle Thread)를 등록하고 KiInitializeThread 함수에서 Idle Thread가 속한 프로세스로 Idle Process를 등록하는 것이다.

   

프로세스 리스트에 Idle Process가 포함되지 않는 이유

IdleProcess는 PspCreateProcess에 의해 생성되지 않기 때문에 PsActiveProcessHead의 리스트에 등록되지 않는다. 물론 다음 코드처럼 System 프로세스부터는 PspCreateProcess에 의해 생성되고, 이 PspCreateProcess 함수 내부에서 PsActiveProcessHead를 참조하여 프로세스 리스트에 새로 생성되는 프로세스를 등록한다.

   

   

PspCreateProcess 함수의 내부에서 호출하는 PspInsertProcess 함수는 다음 코드와 같이 이중 연결 리스트의 마지막에 새로운 프로세스를 추가한다.

   

   

이 때까지의 Idle Process를 초기화하는 과정을 정리하면 아래와 같다.

   

   

결론

Idle Process는 커널에 _EPROCESS 구조체 자체, 즉 C언어의 변수 선언으로 "EPROCESS KiInitialProcess"처럼 정적으로 존재하기 때문에 PspCreateProcess와 같은 프로세스 할당 함수를 사용하지 않는다. 따라서 PsActiveProcessHead로부터 얻을 수 있는 프로세스 리스트에는 Idle Process가 없다. 더욱이 Idle Process는 다른 여타 프로세스와 달리 실행 파일이 없기 때문에 커널 코드 내에서 프로세스 이름이 정해진다. (System Process 또한 커널 코드 내에서 프로세스 이름이 정해진다. )

   

참조

[0] Windows Internals, 마크 러시노비치 등, 5장, p616

[1] https://msdn.microsoft.com/en-us/library/windows/hardware/ff559943(v=vs.85).aspx

   

커널 함수에서 사용하는 구조체 분석에 사용된 문서들

http://www.nirsoft.net/kernel_struct/vista/LOADER_PARAMETER_BLOCK.html

https://translate.google.co.kr/translate?hl=ko&sl=ru&tl=en&u=http%3A%2F%2Fhex.pp.ua%2Fntstatus%2Fpage024.php

http://www.osronline.com/showThread.cfm?link=3787

http://doxygen.reactos.org/d3/d0c/ntoskrnl_2include_2internal_2powerpc_2ke_8h_a88a282c034ffdbc59b89bab1b67676bb.html

http://www.osronline.com/showThread.cfm?link=72000

   

이외에 커널 분석에 도움될만한 내용

이번에 커널 코드를 살펴보면서 _LIST_ENTRY 구조체의 초기화 방식을 알게 되었다. 대부분이 자기 자신을 가리키도록 해놓았다. 이렇게 해놓으면 리스트 엔트리가 초기화 되었는지 되지 않았는지를 바로 파악할 수 있구나 생각하였다.

   

   

따라서 커널 구조체 분석시 LIST_ENTRY 구조체로 이루어진 필드 멤버가 자기 자신을 가리킨다면 별 다른 의미를 가진 것이 아니라 단순히 초기화된 상태라는 것이다.

'디버깅' 카테고리의 다른 글

MS 심볼 구성에 관하여  (0) 2014.12.17
Windows 10 _KPCR and _KPRCB  (0) 2014.11.14
8바이트 변수 초기화 문제  (0) 2014.11.14
Windbg에서 KDBG 찾기  (0) 2014.06.19
Window8 Remote Kernel Debugging on Vmware using WinDbg  (0) 2014.04.11

사용하는 USB가 점점 늘어나고 USB 충전이 필요한 장치도 많아져, USB 3.0 제품 허브를 알아보고 그 때 싸게 팔던 NEXT-UH308 허브를 구입하였습니다. 싼 맛에 구입하였죠.

   

NEXT-UH308 제품 사진

   

제품을 6개월 이상 사용하였는데, 그 동안 USB와 다른 장치들에 중대한 문제를 끼친 문제들이 있었고 이번 포스팅은 이를 다루고자 합니다. 


1.외장하드 인식 불가

NEXT 허브를 구입하기전부터 Seegate Freeagent GoFlex 제품을 사용하였습니다. 따라서 구입한 NEXT 허브에 꽂고 사용하려는데 인식불가 상태가 되더군요. 내 외장하드에 이상이 생긴건가 싶어 외장하드를 그냥 본체에 꽂았더니 잘 됩니다. 네 허브 자체에서 인식이 안되더군요.

인식이 안되는 상황에 몇 가지 실험을 해봤는데, 직장 동료의 SONY 외장하드를 빌려서 허브에 꽂았더니 잘 됩니다. 그러면 Seegate Freeagent GoFlex 제품이 국제표준을 지키지 않은건가 싶어서 직장 동료의 NEXT 4포트 허브(UH305)에 꽂아보았더니 잘되더군요. -_-;;

이런 현상과 관련하여 A/S 센터에 관련 전화 문의를 해보니 USB 외장하드 제품에 따라 인식할 수 있는게 다르다는 답변이 왔습니다. 일단 USB들은 잘 인식되니 계속 사용하였습니다.


2. USB 먹통

기존 잘 사용하던 USB가 NEXT 제품을 사용하면서부터 제 컴퓨터에선 인식이 가능한데 다른 컴퓨터에서는 인식이 안되더군요. 제 컴퓨터에서도 종종 인식이 안되서 chkdsk 도구로 디스크 포맷을 다시 고치기도 했습니다. 그런데 다른 USB도 먹통이 되니까 개인적으로 "이 제품을 계속 사용하면 내 남은 USB들이 모두 먹통이될 것이다"라 판단하였습니다. 이후로 이 USB 허브는 먹통된 USB 전용 허브로 바뀌었고 나머지 용도는 충전용으로만 사용하였습니다.


충전용으로만 사용하다가 불편함을 느껴 새로운 USB 3.0 허브 제품을 구매하려고 안정성있는 제품이 무엇인지 알아보았습니다. 국내에선 오리코 제품이 가장 안정성있는 제품이고 추천이 많더군요. 추천하는 사이트를 살펴보면 안정성있는 USB 3.0 허브를 사용하려면 USB-IF 인증받은 칩셋을 사용하는 제품을 찾아봐라 라는 내용을 볼 수 있습니다. 쉽게 말하면 인증을 받은 VL810, VL812 등의 칩셋을 사용하는 제품이 안정성이 있다는 것입니다. 2014년 1월에 적힌 내용이라 지금은 어떤지 제품 스펙을 찾아보니 오리코는 다음과 같이 VL812 칩셋을 사용한다는 것을 알 수 있습니다.

   

(http://www.oricoonline.us/goods.php?id=5544)

   

반면 NEXT 제품군은 컨트롤러를 찾아볼 수 없더군요.

   

   

(http://www.ez-net.co.kr/new_2012/product/view.php?cid=3&sid=&q=&seq=335&page=&q=&PHPSESSID=52adcfc3b7e3362fb6c6df0548e8f0c3)

   

다른 회사(IPTIxx, NETMAxx) 제품들 또한 USB-IF 인증받은 칩셋을 사용하는지에 대해서 전혀 적혀있지 않더군요. 국내 USB 제품들을 보면 주로 속도(5Gbps)와 편의성(포트 개수)를 위주로 설명하는데 안정성에 대해선 전원 유뮤만 어필하고 있습니다. 오리코도 공식사이트 들어가야 저 컨트롤러 내용이 보이고 판매업자가 제공하는 정보에선 컨트롤러 내용이 안보이더군요. 오리코에서 안정성에 대해 어필하면 더 잘 팔리지 않을까 싶습니다. 가격이 두배 정도 비싸서 모르는 사람은 구매하지 않을 것 같네요.

   

어제 오리코 제품을 주문했는데 몇개월 사용하다가 실사용기 남기려 합니다.

   

   

논문 영어 쓰기

생활2015. 4. 20. 19:04

1. with using이냐 그냥 using이냐 by using이냐?

 

논문을 작성하던 중 "그 도구를 이용하여 분석한다."라는 표현을 써야했다.

이 표현은 뭐 "그 도구로 분석한다." 정도로도 바꿔 번역할 수 있는데

영어로는 다음과 같은 표현을 쓸 수 있다.

 

we analyzes it with the tool.

we analyzes it using the tool.

we analyzes it by using the tool.

 

검색해보니 모두다 가능한 표현이다. 하지만 미국 영어에서는 강조 정도가 다르다는데

첫번째 예문(with를 사용하는)은 무엇이 분석되었는가? 즉 동사의 목적어를 강조한다.

여기서는 'it'을 강조한다.

즉 "도구로 그것을 분석을 분석한다." 정도의 문장으로 번역할 수 있다.

 

두번째 예문(using을 사용하는)은 무엇이 분석을 하였는가? 즉 using 뒤의 보어 목적어(?)를 강조한다.

여기서는 'the tool'이 되겠다.

또한 분석 가능한 도구(the tool)을 선택할 수 있을 때 사용된다고 한다.

즉 "여러 도구 중에서 이 도구를 사용해서 분석한다." 정도의 문장으로 번역할 수 있다.

 

세번째 예문 또한 'the tool'을 강조한다. 두번째 예문보다 강하게 강조한다.

하지만 이 표현은 자주 사용되지 않는 진짜로 강조하고 싶을 때 쓰는 용어로 보편적으로 쓰이지 않는다.

즉 "여러 도구 중에서 특히 이 도구만을 사용해서 분석한다." 정도의 문장으로 번역할 수 있다.

 

영어 어렵다..

 

참고 http://www.grammarly.com/answers/questions/4579-usingby-using/

 

본 내용은 계속 추가됩니다. 15.04.20