지난 내용
[강좌로 보는 서비스 프로그래밍] 서비스란 무엇인가? [1/?]
[강좌로 보는 서비스 프로그래밍] 외적으로 보여지는 서비스 [2/?]
[강좌로 보는 서비스 프로그래밍] 설치와 제거 [3/?]
[강좌로 보는 서비스 프로그래밍] 가끔 쓸모있는 관련 함수들 [4/?]
[강좌로 보는 서비스 프로그래밍] 시작/중지/일시정지 [5/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 본체 코딩 [6/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 본체 코딩 Cont. [6/?]
[강좌로 보는 서비스 프로그래밍] 뼈다귀 서비스 코드 [7/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 디버깅 [8/?]
[강좌로 보는 서비스 프로그래밍] 내컴에 누가 왔는지 다알아 [9/?]
[강좌로 보는 서비스 프로그래밍] 환영 인사 [10/?
서비스로 좀더 깊게 들어가기 이전에, 컴퓨터가 켜져서 꺼질때까지 서비스가 어떻게 동작하는지 알아보자.
서비스는 OS에 따라 동작 순서와 특성에 따른 차이가 존재한다.
Windows 2000 및 95, 98 등에 관해서는 다루지 않을 것이며, 최소 Windows XP 를 기준으로 다룬다.
Windows XP와 Windows Server 2003은 _WIN32_WINNT 의 버전 표기가 0x0500 부터 시작한다.
Windows Vista, Windiow 7, Windows 2008은 표기가 0x0600 부터 시작한다.
이렇게 두가지로 나누어서 동작을 살펴보고자 한다. XP나 2003을 구형 OS, 그 이상을 신형 OS 라고 표기할것이다.
동작은 전체적으로 머신 부팅 -> 서비스 로딩 -> 콘솔세션 로그온 -> 머신 종료 순으로 다룰 것이다.
(로그온 과정이 빠지면 아래중에 로그온과 로그오프는 배제될 것이다.)
그리고, 동작 순서의 표기는 서비스가 받을 수 있는 메시지를 기준으로 표기한다.
왼쪽은 구형 OS이고, 오른쪽은 신형 OS의 동작 순서이다.
[강좌로 보는 서비스 프로그래밍] 서비스란 무엇인가? [1/?]
[강좌로 보는 서비스 프로그래밍] 외적으로 보여지는 서비스 [2/?]
[강좌로 보는 서비스 프로그래밍] 설치와 제거 [3/?]
[강좌로 보는 서비스 프로그래밍] 가끔 쓸모있는 관련 함수들 [4/?]
[강좌로 보는 서비스 프로그래밍] 시작/중지/일시정지 [5/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 본체 코딩 [6/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 본체 코딩 Cont. [6/?]
[강좌로 보는 서비스 프로그래밍] 뼈다귀 서비스 코드 [7/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 디버깅 [8/?]
[강좌로 보는 서비스 프로그래밍] 내컴에 누가 왔는지 다알아 [9/?]
[강좌로 보는 서비스 프로그래밍] 환영 인사 [10/?
서비스로 좀더 깊게 들어가기 이전에, 컴퓨터가 켜져서 꺼질때까지 서비스가 어떻게 동작하는지 알아보자.
서비스는 OS에 따라 동작 순서와 특성에 따른 차이가 존재한다.
Windows 2000 및 95, 98 등에 관해서는 다루지 않을 것이며, 최소 Windows XP 를 기준으로 다룬다.
Windows XP와 Windows Server 2003은 _WIN32_WINNT 의 버전 표기가 0x0500 부터 시작한다.
Windows Vista, Windiow 7, Windows 2008은 표기가 0x0600 부터 시작한다.
이렇게 두가지로 나누어서 동작을 살펴보고자 한다. XP나 2003을 구형 OS, 그 이상을 신형 OS 라고 표기할것이다.
동작은 전체적으로 머신 부팅 -> 서비스 로딩 -> 콘솔세션 로그온 -> 머신 종료 순으로 다룰 것이다.
(로그온 과정이 빠지면 아래중에 로그온과 로그오프는 배제될 것이다.)
그리고, 동작 순서의 표기는 서비스가 받을 수 있는 메시지를 기준으로 표기한다.
왼쪽은 구형 OS이고, 오른쪽은 신형 OS의 동작 순서이다.
시작하는 거야, 컴퓨터가 켜질때 같이 시작하거나 ( StartAuto), 나중에 사용자가 켜주거나 (Manual)
하는 방식의 차이만 있을뿐, 무언가가 서비스를 시작 시키면 전혀 차이점이 존해하지 않는다.
하지만, 종료하는 것에는 차이가 있다.
위의 그림을 살펴보면 SERVICE_CONTROL_STOP 이 않보인다. 서비스 매니저를 이용해서 서비스를 중지 시키면
SERVICE_CONTROL_STOP 메시지가 전달되는데, 시스템이 종료될 때는 발생을 하지 않는것이다.
어짜피 시스템이 종료되기 때문에, 서비스의 정상종료 여부에 크게 관여치 않는 경우는 문제가 없겠지만
서비스가 종료될 때 중요한 정보를 저장해야 하거나 어딘가에 통보해야 하거나 할 경우, 정확한 종료 시점에
필요한 작업을 할수 있는 충분한 시간을 확보해야 한다면 난감해 질 수 있다.
우선 살펴본바와 같이 서비스는 아래의 3개의 이벤트를 받으면 종료되는 것으로 인식하면 된다.
그리고, 아래의 이벤트는 절대 종료시점에 중복되어 호출되지 않으므로, 겹치는 문제는 생각하지 않아도 된다.
1. SERVICE_CONTROL_STOP
서비스가 SERVICE_ACCEPT_STOP 플래그를 가지고 있어야 한다.
사용자나 특정 프로그램에 의하여 서비스를 임의로 중지 시켰을 경우 받는 메시지이다.
당연히 프로세스를 강제로 킬하거나 하면 발생하지 않는다.
머신이 종료될 때는 이 이벤트는 발생하지 않는다.
이 이벤트 핸들러에서는 아무리 많은 시간을 잡아먹어도 OS가 불평하지 안는다. 관여도 하지 않는다.
(단 종료를 오래할 경우 외부에서 보기 좋도록 SERVICE_STOP_PENDING 상태만 출력해주면 된다.)
2. SERVICE_CONTROL_SHUTDOWN
서비스가 SERVICE_ACCEPT_SHUTDOWN 플래그를 가지고 있어야 한다.
하는 방식의 차이만 있을뿐, 무언가가 서비스를 시작 시키면 전혀 차이점이 존해하지 않는다.
하지만, 종료하는 것에는 차이가 있다.
위의 그림을 살펴보면 SERVICE_CONTROL_STOP 이 않보인다. 서비스 매니저를 이용해서 서비스를 중지 시키면
SERVICE_CONTROL_STOP 메시지가 전달되는데, 시스템이 종료될 때는 발생을 하지 않는것이다.
어짜피 시스템이 종료되기 때문에, 서비스의 정상종료 여부에 크게 관여치 않는 경우는 문제가 없겠지만
서비스가 종료될 때 중요한 정보를 저장해야 하거나 어딘가에 통보해야 하거나 할 경우, 정확한 종료 시점에
필요한 작업을 할수 있는 충분한 시간을 확보해야 한다면 난감해 질 수 있다.
우선 살펴본바와 같이 서비스는 아래의 3개의 이벤트를 받으면 종료되는 것으로 인식하면 된다.
그리고, 아래의 이벤트는 절대 종료시점에 중복되어 호출되지 않으므로, 겹치는 문제는 생각하지 않아도 된다.
1. SERVICE_CONTROL_STOP
서비스가 SERVICE_ACCEPT_STOP 플래그를 가지고 있어야 한다.
사용자나 특정 프로그램에 의하여 서비스를 임의로 중지 시켰을 경우 받는 메시지이다.
당연히 프로세스를 강제로 킬하거나 하면 발생하지 않는다.
머신이 종료될 때는 이 이벤트는 발생하지 않는다.
이 이벤트 핸들러에서는 아무리 많은 시간을 잡아먹어도 OS가 불평하지 안는다. 관여도 하지 않는다.
(단 종료를 오래할 경우 외부에서 보기 좋도록 SERVICE_STOP_PENDING 상태만 출력해주면 된다.)
2. SERVICE_CONTROL_SHUTDOWN
서비스가 SERVICE_ACCEPT_SHUTDOWN 플래그를 가지고 있어야 한다.
하위나, 상위 OS 모두 지원하는 이벤트로, 특별한 일이 없으면 머신이 종료될 때 저 이벤트를 발생시킨다.
서비스가 종료되거나, 머신이 꺼질 때 먼가 해야하는 작업이 있다면 저 이벤트 핸들러에서 처리한다.
(즉, SERVICE_CONTROL_STOP 서 하는 작업을 이 이벤트에서도 처리해 주어야 한다.)
현재 머신이 종료중에 있기 때문에 여기서는 오랜시간 작업을 할 수 없다. 대략 20초 정도가 최대인데
시스템 상황에 따라 다소 차이가 나기는 하지만 30초를 넘기지 못한다. 시간이 지나면 강제로 Kill 된다.
3. SERVICE_CONTROL_PRESHUTDOWN (상위 OS 부터 지원 됨)
서비스가 SERVICE_ACCEPT_PRESHUTDOWN 플래그를 가지고 있어야 한다.
상위 OS 부터 지원되기 시작한 이벤트이다. 시스템이 종료에 들어가자 마자 저 이벤트 핸들러를 등록해 놓은
서비스들 부터 먼저 이벤트를 보낸다. 즉, SERVICE_CONTROL_SHUTDOWN 만 등록해 놓은 것들은
저것들이 다 스스로 혹은 강제로 끝나야 호출되기 시작한다.
내가 작업을 진행하는데 오래걸리면, 시간이 오래걸린다고 지연시간을 시스템에 요청할 수 있으며
해당 시스템은 저 요청을 받으면 해당 서비스가 종료중임을 알리고, 강제 종료를 지연 시킨다.
서비스가 종료되거나, 머신이 꺼질 때 먼가 해야하는 작업이 있다면 저 이벤트 핸들러에서 처리한다.
(즉, SERVICE_CONTROL_STOP 서 하는 작업을 이 이벤트에서도 처리해 주어야 한다.)
현재 머신이 종료중에 있기 때문에 여기서는 오랜시간 작업을 할 수 없다. 대략 20초 정도가 최대인데
시스템 상황에 따라 다소 차이가 나기는 하지만 30초를 넘기지 못한다. 시간이 지나면 강제로 Kill 된다.
3. SERVICE_CONTROL_PRESHUTDOWN (상위 OS 부터 지원 됨)
서비스가 SERVICE_ACCEPT_PRESHUTDOWN 플래그를 가지고 있어야 한다.
상위 OS 부터 지원되기 시작한 이벤트이다. 시스템이 종료에 들어가자 마자 저 이벤트 핸들러를 등록해 놓은
서비스들 부터 먼저 이벤트를 보낸다. 즉, SERVICE_CONTROL_SHUTDOWN 만 등록해 놓은 것들은
저것들이 다 스스로 혹은 강제로 끝나야 호출되기 시작한다.
내가 작업을 진행하는데 오래걸리면, 시간이 오래걸린다고 지연시간을 시스템에 요청할 수 있으며
해당 시스템은 저 요청을 받으면 해당 서비스가 종료중임을 알리고, 강제 종료를 지연 시킨다.
신형 OS에 해당하는 Windows Vista, Windows Server 2008, Windows 7은 서비스의 동작 상태가 몇가지 추가되었다.
위의 내용과 연계하여 추가된 내용을 살펴보면 다음과 같다.
1. Delayed AutoStart Service
보통은 서비스가 시작될 때, 커널이 올라온후, 적당한 디펜던시에 의해 순서가 정해지고 서비스란 서비스는
몽땅 한꺼번에 올라온다. 이렇게 되면 초기 시작되는 시점에 상당한 부하가 발생하고 부팅 시간이 길어진다.
저 자동 시작 지연 기능을 이용하면, 지연되지 않은 서비스들이 모두 올라오고 대략적인 부팅이 마무리된 후
10 - 30 초 정도 지연된 이후에 서비스가 시작된다.
(MSDN 설명을 살펴보면 스레드 우선순위를 낮추었다가, 나중에 서비스가 시작되면 순위를 복원한다는 표현을 사용한다.)
해당 기능은 매니저.SetServiceStartType(SERVICE_DELAYED); 와같이 사용할 수 있다.
2. Clear Service Shutdown
서비스를 중지 시킬경우는 SERVICE_CONTROL_STOP 를 받아서 정상적인 종료 처리를 충분한 시간 동안 할 수 있다.
하지만 머신이 종료될 경우는 SERVICE_CONTROL_STOP 는 받지 못하고, SERVICE_CONTROL_SHUTDOWN 를
받게 되는데, 만약 서비스 종료시 디스크에 먼가를 기록하는등 많은 시간을 소요하는 작업을 진행한다면...
종료 작업중에 머신이 강제 종료될 것이다. 대략 디폴트로 주는 시간이 20초 정도 되고, 넘어가면 강제 종료 당한다.
서비스의 상태를 변경할 때 SERVICE_ACCEPT_PRESHUTDOWN 를 SERVICE_STATUS::dwControlsAccepted 에
추가하는 작업으로 SERVICE_CONTROL_PRESHUTDOWN 이벤트를 받을 수 있게된다. 해당 이벤트를 받으면
시스템의 화면이 다음과 같이 변경된다.
이 때는 SERVICE_CONTROL_SHUTDOWN 는 더이상 받지 않게된다.
위의 내용과 연계하여 추가된 내용을 살펴보면 다음과 같다.
1. Delayed AutoStart Service
보통은 서비스가 시작될 때, 커널이 올라온후, 적당한 디펜던시에 의해 순서가 정해지고 서비스란 서비스는
몽땅 한꺼번에 올라온다. 이렇게 되면 초기 시작되는 시점에 상당한 부하가 발생하고 부팅 시간이 길어진다.
저 자동 시작 지연 기능을 이용하면, 지연되지 않은 서비스들이 모두 올라오고 대략적인 부팅이 마무리된 후
10 - 30 초 정도 지연된 이후에 서비스가 시작된다.
(MSDN 설명을 살펴보면 스레드 우선순위를 낮추었다가, 나중에 서비스가 시작되면 순위를 복원한다는 표현을 사용한다.)
해당 기능은 매니저.SetServiceStartType(SERVICE_DELAYED); 와같이 사용할 수 있다.
2. Clear Service Shutdown
서비스를 중지 시킬경우는 SERVICE_CONTROL_STOP 를 받아서 정상적인 종료 처리를 충분한 시간 동안 할 수 있다.
하지만 머신이 종료될 경우는 SERVICE_CONTROL_STOP 는 받지 못하고, SERVICE_CONTROL_SHUTDOWN 를
받게 되는데, 만약 서비스 종료시 디스크에 먼가를 기록하는등 많은 시간을 소요하는 작업을 진행한다면...
종료 작업중에 머신이 강제 종료될 것이다. 대략 디폴트로 주는 시간이 20초 정도 되고, 넘어가면 강제 종료 당한다.
서비스의 상태를 변경할 때 SERVICE_ACCEPT_PRESHUTDOWN 를 SERVICE_STATUS::dwControlsAccepted 에
추가하는 작업으로 SERVICE_CONTROL_PRESHUTDOWN 이벤트를 받을 수 있게된다. 해당 이벤트를 받으면
시스템의 화면이 다음과 같이 변경된다.
이 때는 SERVICE_CONTROL_SHUTDOWN 는 더이상 받지 않게된다.
지연시간이 기본적으로 3분이라고 하는데, 서비스가 특정작업을 진행하지 않으면 보통 1분 정도에 강제 종료된다.
해당 지연시간을 설정하기 위하여 매니저.SetPreShutdownInfo(지연시간 /*밀리초*/); 를 지원한다.
해당 함수에 0을 넣으면 비활성화 시키고, 값을 입력하면 활성화 시키도록 설계 되었다.
3. Service Shutdown Order
세번째로 서비스를 종료시킬 때, 어떤 순서대로 종료시킬 지를 설정할 수 있는 방법을 제공한다.
보통 디펜던시가 걸려있을 경우는 해당 순서대로 처리가 되지만, 그렇지 못할 경우는 무작위로 종료가 되는데...
이를 좀더 개선된 방식으로 지원해주는 것이다. (보통 디펜던시를 걸게되면 까다로운 작업이 생긴다. ==;;;)
해당 지연시간을 설정하기 위하여 매니저.SetPreShutdownInfo(지연시간 /*밀리초*/); 를 지원한다.
해당 함수에 0을 넣으면 비활성화 시키고, 값을 입력하면 활성화 시키도록 설계 되었다.
3. Service Shutdown Order
세번째로 서비스를 종료시킬 때, 어떤 순서대로 종료시킬 지를 설정할 수 있는 방법을 제공한다.
보통 디펜던시가 걸려있을 경우는 해당 순서대로 처리가 되지만, 그렇지 못할 경우는 무작위로 종료가 되는데...
이를 좀더 개선된 방식으로 지원해주는 것이다. (보통 디펜던시를 걸게되면 까다로운 작업이 생긴다. ==;;;)
위의 그림처처럼 해당 레지스트리키에 서비스의 이름을 기록하게 되면, 저 순서대로 종료된다.
서비스의 종료 순서에 영향을 받을 경우, 2번과 병행하여 처리하면 안전하게 서비스를 종료할 수 있다.
(해당 작업은 단순히 레지스트리에 기입하는 방식이므로 별도의 함수를 제공하지는 않는다.)
아래는 추가된 함수의 목록이다. 물론 위에서 나열된 기능은 신형 OS 에서만 동작한다.
서비스의 종료 순서에 영향을 받을 경우, 2번과 병행하여 처리하면 안전하게 서비스를 종료할 수 있다.
(해당 작업은 단순히 레지스트리에 기입하는 방식이므로 별도의 함수를 제공하지는 않는다.)
아래는 추가된 함수의 목록이다. 물론 위에서 나열된 기능은 신형 OS 에서만 동작한다.
// change start : auto, demand, disable, boot, system, delayed
// if select delayed: windows vista and server 2008 over
/* #define SERVICE_BOOT_START 0x0000
#define SERVICE_SYSTEM_START 0x0001
#define SERVICE_AUTO_START 0x0002
#define SERVICE_DEMAND_START 0x0003
#define SERVICE_DISABLED 0x0004
#define SERVICE_DELAYED 0x0010 */
DWORD SetServiceStartType(DWORD dwControl);
/*
typedef struct _QUERY_SERVICE_CONFIGA {
DWORD dwServiceType;
DWORD dwStartType;
DWORD dwErrorControl;
LPSTR lpBinaryPathName;
LPSTR lpLoadOrderGroup;
DWORD dwTagId;
LPSTR lpDependencies;
LPSTR lpServiceStartName;
LPSTR lpDisplayName;
} QUERY_SERVICE_CONFIGA, LLPQUERY_SERVICE_CONFIGA; */
DWORD GetServiceConfig(QUERY_SERVICE_CONFIG*& pConfig);
VOID FreeServiceConfig(QUERY_SERVICE_CONFIG*& pConfig);
// change service desc string
DWORD SetServiceDescription(LPTSTR lpszStr);
// change service startup user
// user : SetServiceUser(".\\aaa", "aaa");
// system : SetServiceUser(".\\LocalSystem", NULL);
DWORD SetServiceUser(LPTSTR lpszUser, LPTSTR lpszPass);
// change interact mode
DWORD SetServiceInteractMode(BOOL bActive);
// this value enabled, service receive SERVICE_CONTROL_PRESHUTDOWN message
// and, get 3 (default) minutes wired.
DWORD SetPreShutdownInfo(DWORD dwPreshutdownTimeout /* ms <default:18000> */);
// if select delayed: windows vista and server 2008 over
/* #define SERVICE_BOOT_START 0x0000
#define SERVICE_SYSTEM_START 0x0001
#define SERVICE_AUTO_START 0x0002
#define SERVICE_DEMAND_START 0x0003
#define SERVICE_DISABLED 0x0004
#define SERVICE_DELAYED 0x0010 */
DWORD SetServiceStartType(DWORD dwControl);
/*
typedef struct _QUERY_SERVICE_CONFIGA {
DWORD dwServiceType;
DWORD dwStartType;
DWORD dwErrorControl;
LPSTR lpBinaryPathName;
LPSTR lpLoadOrderGroup;
DWORD dwTagId;
LPSTR lpDependencies;
LPSTR lpServiceStartName;
LPSTR lpDisplayName;
} QUERY_SERVICE_CONFIGA, LLPQUERY_SERVICE_CONFIGA; */
DWORD GetServiceConfig(QUERY_SERVICE_CONFIG*& pConfig);
VOID FreeServiceConfig(QUERY_SERVICE_CONFIG*& pConfig);
// change service desc string
DWORD SetServiceDescription(LPTSTR lpszStr);
// change service startup user
// user : SetServiceUser(".\\aaa", "aaa");
// system : SetServiceUser(".\\LocalSystem", NULL);
DWORD SetServiceUser(LPTSTR lpszUser, LPTSTR lpszPass);
// change interact mode
DWORD SetServiceInteractMode(BOOL bActive);
// this value enabled, service receive SERVICE_CONTROL_PRESHUTDOWN message
// and, get 3 (default) minutes wired.
DWORD SetPreShutdownInfo(DWORD dwPreshutdownTimeout /* ms <default:18000> */);
토요일이네요.. 주말 즐겁게 보내세요.. ^^;;;