/**

얼마 전 리눅스의 데몬(Daemon)이라는 것에 대해 약간이나마 공부를 해야 할 일이 있었습니다. 그리고 그 과정에서 윈도우에서는 그와 비슷한 서비스라는 것이 있다는 것을 알았습니다. 이번 포스팅에서는 서비스 프로그램을 작성하는 방법에 대해 알아 보도록 하겠습니다. 원문은 코드구루(http://www.codeguru.com)의 'Writing a Service Program(Jonathan Ng)' 을 기본으로 하고 있습니다.

*/

서비스(services)라는 것은 일반적으로 시스템 부팅시점 부터 시작해 시스템이 끝나는 시점 까지 그 수명을 같이 하는 프로세스를 가리키기도 합니다. 예를 들어 윈도우 베이스 프로그램과 컴포넌트가 남기는 로그를 기록해주는 '로그 이벤트'같은 프로세스 같은 것이 있습니다. 리눅스(혹은 유닉스) 시스템에서 윈도우의 서비스와 비슷한 역할을 하는 것으로는 데몬(daemon) 프로세스가 있습니다.

서비스 에플릿은 Control Panel > Administrator tools > Service에서 찾아 볼 수 있으며, 새로운 서비스 설치는 이 애플릿과 Registry(HKEY_LOCAL_MACHINE\System\CurrentControlSet\Service)에 앤트리를 생성합니다. 이렇게 해서 시스템은 부트타임에 어떤 서비스들이 등록 되어야 하는지 알 수 있게 되는 것이지요.

모든 서비스들은 반드시 아래의 standard event에 응답하도록 디자인 되어야만 합니다. 아래의 이벤트들은 윈도우 서비스에서 버튼의 형태로 보여지게 됩니다 :

  • START : 서비스를 수동으로 시작하거나, 서비스가 중단 되었다면 서비스를 시작한다.
  • STOP : 서비스를 중단한다.
  • PAUSE : 서비스를 일시 중지한다.
  • CONTINUE : 중단되었던 서비스를 재 시작한다.

모든 서비스는 Service Control Manager 시스템에 의해 관리 됩니다. Service Control Manager는 레지스트리에 서비스 목록을 유지하며, 부트타임이나 아니면 따로 매뉴얼하게 스타트가 요구 될 경우 레지스트리에 등록되어 있는 서비스 프로그램을 실행하게 됩니다.

일반적으로 서비스 프로그램은 일반 exe 확장자 형태를 가지고 있지만 Service Sonctrol Manager(SCM)에 정상적으로 접속하기 위해서는 아래의 특정한 요구사항들을 만족 시켜야 합니다 :

  1. EXE 는 반드시 main 또는 WinMain 함수를 가지고 있어야하고, StartServiceCtrlDiespatcher 함수를 호출 해야 한다. StartServiceCtrlDiespatcher 함수는 EXE 파일을 SCM에 등록하고, SCM이 ServiceMain 함수에게 포인터를 넘겨 주는 역할을 한다.
    void main(int argc, char *argv[])
    {
        SERVICE_TABLE_ENTRY serviceTable[] =
        {
            { ServiceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
            { NULL, NULL}
        };

        BOOL success;
        if(argc == 2)
           //..check arguments
        else
        {
            //register with SCM
            success = StartServiceCtrlDispatcher(serviceTable);
            if (!success)
                ErrorHandler("StartServiceCtrlDispatcher", GetLastError());
        }
    }
  2. SCM은 서비스 시작 요청이 들어 오면 ServiceMain 함수를 호출한다. ServiceMain 함수는 그 즉시 RegisterServiceCtrlHandler 함수를 호출하여 Handler 함수를 SCM에 등록한다.
    void ServiceMain(DWORD argc, LPTSTR *argv)
    {
        BOOL success;

        //레지스터 함수 호출
        serviceStatusHandle =
             RegisterServiceCtrlHandler(ServiceName, (LPHANDLER_FUNCTION)ServiceCtrlHandler);

        //SCM에게 통보 및 종료 이벤트 생성
        terminateEvent = CreateEvent (0, TRUE, FALSE, 0);
        .
        .
        //notify SCM
        .
        .
        //startup parameter 체크
        //start service
        //notify SCM service is runnning
        SendStatusToSCM(SERVICE_RUNNING, NO_ERROR, 0 , 0, 0);

        //wait for stop signal and then terminate
        WaitForSingleObject(terminateEvent, INFINITE);

        terminate(0);
    }
  3. Handler 함수는 SCM으로 부터 넘어오는 요청을 처리하는 switch 구문으로 이루어진다.
    void ServiceCtrlHandler(DWORD controlCode)
    {
        DWORD currentState = 0;
        BOOL success;

        switch(controlCode)
        {
            // START = ServiceMain()

            // STOP
            case SERVICE_CONTROL_STOP:
                currentState = SERVICE_STOP_PENDING;
                //notify SCM
                SendStatusToSCM(SERVICE_STOP_PENDING,
                                            NO_ERROR,
                                            0,
                                            1,
                                            5000);
                //stop service
                StopService();
                return;

            // PAUSE
            case SERVICE_CONTROL_PAUSE:
                if (runningService && !pauseService)
                {
                     //notify SCM
                     success = SendStatusToSCM(SERVICE_PAUSE_PENDING,
                                                                 NO_ERROR,
                                                                 0,
                                                                 1,
                                                                 1000);
                     PauseService();
                     currentState = SERVICE_PAUSED;
                }
                break;

            // RESUME
            case SERVICE_CONTROL_CONTINUE:
                if (runningService && pauseService)
                {
                     //notify SCM
                     success = SendStatusToSCM(SERVICE_CONTINUE_PENDING,
                                                                 NO_ERROR,
                                                                 0,
                                                                 1,
                                                                 1000);
                     ResumeService();
                     currentState = SERVICE_RUNNING;
                }
                break;

            // UPDATE
            case SERVICE_CONTROL_INTERROGATE:
                //update status out of switch()
                break;

            case SERVICE_CONTROL_SHUTDOWN:
                //do nothing
                return;
            default:
                break;
        }
        //notify SCM current state
        SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0);
    }
  4. 서비스 프로그램은 main, ServiceMain, Handler 함수를 뿐만 아니라 서비스 자신을 위한 쓰레드를 갖추었을때 비로소 서비스 프로그램으로써 모든 것을 다 갖추었다고 말 할 수 있습니다.

위와 같이 서비스 프로그램을 만들었다고 해서 모든 것이 끝난 것은 아닙니다. 서비스는 특별한 프로세스로 취급되기에 일반 exe 처럼 더블클릭이나 커맨드 창에 이름을 적어 주는 것으로 실행 되지 않습니다. 서비스 프로세스는 서비스 매니저에 의해서 실행 되어져야 하며, 서비스 매니저에 의해 관리 되기 위해서는 등록 과정이 필요합니다. 이런 등록을 위해서 'sc'라는 바이너리가 기본적으로 제공되어지고 있습니다. 프로그램 내에서 특별한 인자를 받아 스스로 등록하도록 하는 방법도 있지만 여기서는 sc 에 대해서만 다루고 나머지는 따로 자리를 마련하도록 하겠습니다.

c:\> sc create [service name] [binPath= ] ..

기본 형태는 위와 같습니다. 서비스 등록을 위해서 sc 뒤에 create라는 인자를 주고 등록할 서비스의 이름과 실행할 바이너리 경로를 적어줍니다. 이때 주의할 점은 binPath= 와 뒤에 들어가는 패스 사이에는 반드시 스페이스 문자가 있어야 한다는 것입니다. 예를 들어 binPath= C:\bin\path\some.exe 와 같은 형식으로 말입니다. sc /? 를 커맨드 창에 입력하시면 보다 자세한 설명이 출력 됩니다.

원문 보기 : http://www.codeguru.com/Cpp/W-P/system/services/article.php/c5785/

'진리는어디에' 카테고리의 다른 글

const vs mutable  (4) 2006.12.21
PHP - 문자열 취급하기  (0) 2006.12.14
가변인자를 이용한 함수(va_list)  (10) 2006.12.13
SQL command  (0) 2006.12.13
Writing a Windows Service Program  (8) 2006.12.11
Python 정규 표현식  (1) 2006.12.11
Posted by kukuta

댓글을 달아 주세요

  1. Favicon of http://blog.ggamsso.wo.tc/ BlogIcon 깜쏘 2006.12.13 17:55  댓글주소  수정/삭제  댓글쓰기

    선배, 오늘 zdnet에서 기사를 봤는데, 선배 경력 관리해야 할 것 같아요.
    볼 때마다 새로운 걸 하고 있으니...
    아직 취업한지 얼마 안되었지만 관리 하세욧!!!

  2. Favicon of https://kukuta.tistory.com BlogIcon kukuta 2006.12.14 10:08 신고  댓글주소  수정/삭제  댓글쓰기

    음...이거는 그냥 취미 생활이야..
    내가 원래 윈도우였으니까 윈도우를 잊지 않기 위해서 공부하는거지..

    나의 주 임무는...잡무다...-_-

  3. Favicon of https://kukuta.tistory.com BlogIcon kukuta 2006.12.15 14:28 신고  댓글주소  수정/삭제  댓글쓰기

    한번 댓글 단 글은 더 이상 댓글을 달 수 없나??

  4. Favicon of http://www.thesispaperswriting.com/assignments_writing.htm BlogIcon Custom Assignment Writing 2011.05.26 00:47  댓글주소  수정/삭제  댓글쓰기

    yes...:) great work ! I definitely enjoying every little bit of it I have you bookmarked to check out new stuff you post