웹공부를 시작하기 위해서 설치한 이클립스에서 JSP 파일일 실행 시키니까

 The server does not support version 3.0 of the J2EE Web module specification 이와 같은 에러가 발생 하였다.

버젼 정보가 맞지 않아서 발생하였다.

Dynamic Web Project를 생성할 때 Dynamic Web Module version 정보가 3.0으로 되어 있을 경우 톰켓 버젼 7.0을 사용해야 한다.

자바 버젼 1.6으로 구성되어 있고, 톰켓 6.0을 사용하면 Dynamic web module version을 2.4로 설정하여 생성하면 된다.

프로세스(Processes)

프로세스는 리눅스와 Windows에서 모두 실행 프로그램을 뜻한다. Windows에서는 .exe 확장자가 있는 파일 이름이 실행파일이다. 프로세스는 소스 파일을 컴파일 하고 실행 파일을 생산함으로서 만들어진다. 컴파일 단계는 Windows와 리눅스 모두 비슷하다:

 


프로그램 컴파일하기
    WINDOWS:   cl -O2 create-pt2.cpp
    LINUX  :   gcc -O2 create-pt2.cpp -lpthread -o create-pt2

위 두개의 컴파일 결과는 create-pt2.exe (Windows)와, create-pt2 (리눅스)라는 실행파일이다.

프로세스는 오픈 파일 핸들을 상속 받을 수 있다. Windows에서, 핸들이 작은 정수가 아니기 때문에 정확한 값은 알 수 없다. Windows에서 기존의 오픈 파일을 위해 핸들을 정확한 값으로 초기화 하는 것은 문서화된 프로시져가 아니다. 리눅스에서 파일 디스크립터(descriptor)를 20으로 설정하고 사용하는 것은 매우 쉽다.

일단 프로세스가 만들어지면 시작할 때 명령행에서 프로그램 이름을 타이핑 하면 된다. 리눅스와 Windows 모두 같다:


프로그램 실행하기
    create-pt2

Windows 프로그램의 경우, .exe 서픽스를 타이핑 할 필요가 없다. 프로그램이 실행권한이 있고 합법적인 Windows 바이너리 형식의 프로그램이라면 Windows는 모든 프로그램 이름을 실행 파일로서 인식한다. 따라서 나는 마이크로소프트 컴파일러의 아웃풋을 리눅스와 마찬가지로 create-pt2 라고 이름을 붙였다.


리눅스 프로세스 만들기

리눅스는 매개변수가 없는 단일 시스템 호출을 사용하여 새로운 프로세스를 만든다. fork 시스템 호출로 정확한 부모 프로세스 카피(copy)를 만들고 부모에게는 자식 프로세스 ID를, 자식에게는 0(zero)을 리턴한다:


리눅스 프로세스 생성
        #include <sys/types.h>
        #include <unistd.h>

        pid_t child_pid;

        child_pid = fork();

        if(child_pid == -1) {
            ERROR;
        }
        else if(child_pid == 0) {
            do_child();
        }
        else {
            do_parent();
        }

Fork는 새로운 프로세스를 만들지만 어떻게 이것이 새로운 프로그램을 실행하는가? 표준 수행 방법은 다음과 같다:


리눅스 상에서 새로운 프로그램 실행하기
        child_pid = fork();
        if(child_pid == -1) ERROR

        if(child_pid == 0) { // Child executes a new program image
            execl("/bin/view", "view", "/etc/hosts");
            ERROR;
        }
        else {               // Parent waits for child to finish (exit).
            int status;

            while(wait(&status) != child_pid);
        }

프로그램을 만들고 실행하는 코드는 깊은 의미를 가지고 있다. 프로그램은 main 으로 부터 리턴하거나 시스템 호출을 사용하여 종료한다.


Windows 프로세스 만들기

Windows 프로세스는 CreateProcess() API를 사용하여 만들어진다. 리눅스의 단순한 fork 호출과는 달리, CreateProcess API는 시도하기에 앞서 공부를 해두는 것이 필요하다.


Windows에서 프로세스를 만드는 API
BOOL CreateProcess(
    LPCTSTR lpApplicationName,                 // name of executable module
    LPTSTR lpCommandLine,                      // command line string
    LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
    LPSECURITY_ATTRIBUTES lpThreadAttributes,  // SD
    BOOL bInheritHandles,                      // handle inheritance option
    DWORD dwCreationFlags,                     // creation flags
    LPVOID lpEnvironment,                      // new environment block
    LPCTSTR lpCurrentDirectory,                // current directory name
    LPSTARTUPINFO lpStartupInfo,               // startup information
    LPPROCESS_INFORMATION lpProcessInformation // process information
);

CreateProcess API는 리눅스의 execl() 시스템 호출과 비교되어야 공정하다. execl() 는 프로그램 이름과 인자는 API에 있는 스트링으로 지정되어야 하는 반면, CreateProcess()는 많은 옵션들을 제공한다.

create-pt2.cpp 프로그램은 프로세스 또는 쓰레드를 만들 수 있다. 프로세스를 만드는 코드는 다음과 같다:


Windows에서 프로세스 만들기
    extern char *applname;
    extern char *progname;
    HANDLE t1;
    char buf[1024];
    BOOL b;
    #define errno   GetLastError()

    sprintf(buf, "%s-child", applname);
    buf[sizeof(buf)-1] = 0;

    STARTUPINFO         startInfo;
    PROCESS_INFORMATION pidInfo;

    //
    //    child process
    //
    startInfo.cb          = sizeof(STARTUPINFO);
    startInfo.lpReserved  = NULL;
    startInfo.lpTitle     = buf;
    startInfo.lpDesktop   = NULL;
    startInfo.dwX         = 0;
    startInfo.dwY         = 0;
    startInfo.dwXSize     = 0;
    startInfo.dwYSize     = 0;
    startInfo.dwXCountChars    = 0;
    startInfo.dwYCountChars    = 0;
    startInfo.dwFlags     = STARTF_USESTDHANDLES;
    startInfo.wShowWindow = 0; //SW_SHOWDEFAULT;
    startInfo.lpReserved2 = NULL;
    startInfo.cbReserved2 = 0;
    startInfo.hStdInput   = GetStdHandle(STD_INPUT_HANDLE);
    startInfo.hStdOutput  = GetStdHandle(STD_OUTPUT_HANDLE);
    startInfo.hStdError   = GetStdHandle(STD_ERROR_HANDLE);

    b = CreateProcess(
            progname,
            buf,
            NULL,
            NULL,
            TRUE,
            0,//CREATE_NEW_CONSOLE,
            NULL,
            NULL,
            &startInfo,
            &pidInfo);

    if(!b) {
        printf("Creation of child process failed: err=%d\n", errno);
        printf("applname=<%s>, buf=<%s>\n",applname,buf);
        return;
    }
    t1 = pidInfo.hProcess;

보다시피 필요한 코드의 양이 많다. 이와 유사한 리눅스의 wait() 호출을 보자:


Windows에서 프로세스 기다리기
    if(WaitForSingleObject(t1,INFINITE) == WAIT_FAILED) {
        printf("WaitForSingleObject FAILED: err=%d\n", GetLastError());
        return;
    }

Windows 프로그램은 종료 방식이 여러가지 이다. 두 가지는 리눅스와 유사하다. main 에서 리턴하거나 표준 C 라이브러리에서 exit() 루틴을 호출할 수 있다. 세 번째 옵션은 ExitProcess() API를 호출하는 것이다. 여기에서는 exit 루틴을 사용한다.


쓰레드(Threads)

쓰레드는 실행 콘텍스트(context)이다. 처음에 각 프로세스는 하나의 실행 콘텍스트를 갖는다. 이 실행 콘텍스트를 쓰레드라고 한다. 프로세스가 다른 실행 콘텍스트를 필요로 한다면 간단히 다른 프로세스를 만들 수 있다. 하지만 프로세스 생성은 프로세서 사이클과 메모리 사용의 관점에서 볼 때 사치스러운 일이다. 쓰레드는 다중 실행 콘텍스트를 만드는데 경량의(lightweight) 메커니즘을 제공하도록 되어있다. Windows와 리눅스는 한 쌍의 실행 환경을 제공할 목적으로 오퍼레이팅 시스템에서 쓰레드를 스케쥴링한다.

프로세스와 쓰레드의 가장 뚜렷한 차이는 프로세스의 모든 쓰레드가 같은 메모리 공간과 시스템 정의의 "도구(facility)"를 공유한다는 점이다. 오픈 파일 핸들(파일 디스크립터), 공유 메모리, 프로세스 동기화 프리머티브(process synchronization primitives), 현재 디렉토리 등이 "도구"이다. 글로벌 메모리가 공유되고 새로운 메모리가 할당되지 않기 때문에 쓰레드를 만드는 것은 프로세스를 만드는 것보다 간단하고 빠르다.


리눅스 쓰레드 만들기

리눅스는 POSIX 쓰레드를 지원한다. 리눅스 쓰레드는 다음의 코드로 만들어진다:


리눅스에서 쓰레드 만들기
        #    define DEC    ( void *(*)(void*) )

        if(pthread_create(&t1, NULL, DEC  threadwork, NULL)) {
            printf("pthread_create A failed: err=%d\n", errno);
            return;
        }

여기에서, t1은 쓰레드 ID를 받기위해 사용되는 매개변수이다. 두 번째 인자는 많은 스케쥴링 옵션을 지원하는 쓰레드 속성 인자이다. 우리는 여기서 디폴트 세팅을 사용할 것이다. 세 번째 인자는 쓰레드를 생성할 때 실행 될 서브루틴(subroutine) 이다. 네 번째 인자는 서브루틴으로 전달된 포인터이다. 이것은 쓰레드나 새로 만들어진 쓰레드에 필요한 것을 위해 저장한 메모리를 지시할 수 있다.

리눅스 쓰레드는 서브루틴에서 리턴하거나 pthread_exit() 루틴을 호출하여 종료한다. 쓰레드가 가지고 있는 리소스는 부모 쓰레드가 pthread_join() 루틴을 호출할 때 회복된다. pthread_join()는 프로세스용 wait() 함수와 비슷하다. pthread_join()는 쓰레드에 의해 할당된 힙 메모리(heap memory)를 회복하지 않는다. 전역으로(globally) 할당된 메모리가 프로그램 또는 쓰레드에 의해 자유로워 져야 한다.


Windows 쓰레드 만들기

Windows 쓰레드는 Windows 프로세스를 만드는 것보다 훨씬 간단하다. 쓰레드를 만드는 데에는 리눅스에서 사용한 매개변수를 비롯하여 보안 디스크립터와 초기 스택 크기도 포함된다.


Windows에서 쓰레드 만들기
        t1 = CreateThread(NULL,4096,(THRDFN)threadwork,"L",NULL,&threadid);
        if(t1 == NULL) {
            printf("CreateThread FAILED: err=%d\n", errno);
            return;
        }

첫 번째 인자는 보안 디스크립터 로서 쓰레드 핸들이 상속 될 수 있을 지의 여부를 결정한다. NULL 은 이것이 상속될 수 없다는 것을 의미한다. 두 번째 인자는 스택 사이즈이다. 세 번째와 네 번째 인자는 서브루틴과 패스된 매개변수이다. 다섯 번째 인자는 플래그 인자이다. 쓰레드는 플래그가 CREATE_SUSPENDED로 설정될 때 중지 상태에서 만들어진다. 여섯 번째 인자는 결과 쓰레드 ID를 시스템에 저장한 위치를 가리킨다.

Windows 쓰레드는 호출된 서브루틴에서 리턴하거나 ExitThread() API를 호출하여 종료한다. 부모는 WaitForSingleObject(threadid)를 호출하여 쓰레드를 정리하거나 WaitForMultipleObjects() API를 사용하여 다중 이벤트를 기다릴 수 있다.


퍼포먼스 결과

프로세스와 쓰레드의 생성과 리눅스와 Windows에서 그들의 유사성을 설명하기 위해 create-pt2.cpp를 작성했다. "시간 측정 (timed-test)" 방식을 사용한다.

Linux 2.4.2 커널( Red Hat 7.2), Windows 2000 Advanced Server, Windows XP Professional에서 프로그램을 실행했다. 세 개의 오퍼레이팅 시스템은 같은 Thinkpad 600X (320 MB메모리)에서 실행되었다. 그림1과 2는 오퍼레이팅 시스템의 퍼포먼스를 나타낸다.


그림 1. 쓰레드 생성 속도
Figure 1. Thread creation speed

그림 2. 프로세스 생성 속도
Figure 2. Process creation speed

쓰레드와 프로세스를 만들 때 리눅스가 Windows(2000, XP)보다 상당히 빠르다는 것을 알 수 있다.

create-pt2.cpp 는 주어진 시간 간격에서 가능한 오랜 시간동안 쓰레드(프로세스)를 만들고 소멸시키는 단순한 프로그램이다. 다음과 같이 테스트를 실행했다:


테스트 스크립트
    create-pt 2         # create threads for 2 seconds
    create-pt 4
    create-pt 8
    create-pt 16

    create-pt -p 2      # create processes for 2 seconds
    create-pt -p 4
    create-pt -p 8
    create-pt -p 16

두 개의 Windows 결과는 프로그램 실행이 길어질수록 퍼포먼스가 악화되었음을 나타냈다. 그림 3은 이 문제를 보여주고 있다.


그림 3. 쓰레드 생성 속도
Figure 3. Thread creation speed decay

Windows가 쓰레드를 생성할 때 동시에 핸들도 생성한다. 이 핸들은 쓰레드를 만들었던 프로세스 상에서 열려있다. 프로세스의 경우, Windows는 두 개의 핸들을 만든다. 하나는 프로세스 용이고 다른 하나는 그 프로세스의 실행 쓰레드용이다. 이 핸들들은 나중에 OpenProcess()이나 OpenThread() API를 이용하여 얻을 수 있기 때문에 불필요한 것들이다. 그럼에도 불구하고 그들이 닫히지 않는다면 프로그램은 계속해서 핸들을 얻을 것이고 시스템 전역의 리소스를 소비하는 결과가 된다.

create-pt2.cpp 의 첫 번째 버전은 생성된 쓰레드와 프로세스를 위해 핸들을 닫지 않았다. 쇠퇴하는 퍼포먼스는 메모리 유출을 증명하고 있다. Task Manager 를 사용하거나 프로세스 탭에 나타내기 위해 칼럼으로서 "핸들" 을 선택함으로서 create-pt2.exe 프로그램이 가진 핸들의 수는 증가한다. 핸들의 유출은 Windows 프로그램의 퍼포먼스가 실행 시간이 길어질수록 쇠퇴하는 이유가 된다.

문제를 고쳤을 때 Task Manager는 create-pt2a.cpp가 고정된 수의 핸들을 가지고 있다는 것을 나타냈다. 퍼포먼스 결과는 테스트 실행이 길어져도 더 이상 쇠퇴하지 않았다.

Windows XP는 쓰레드 생성 속도에 있어서 Windows 2000를 능가했다. 하지만 리눅스 속도의 60% 에 그치는 수준이다. 프로그램이나 측정 기술에 이상이 없는 한, Windows XP의 프로세스 생성은 Windows 2000보다 현저하게 느리고, Windows 2000의 프로세스 생성은 리눅스 보다 느리다.


요약

프로세스와 쓰레드에 대해 간단히 설명했고 내가 작성한 프로그램에서 두 가지의 프로그래밍을 설명했다. 프로세스와 쓰레드의 퍼포먼스를 측정한 결과 리눅스가 Windows 2000 이나 Windows XP 보다 빨랐다. 여러분도 테스트 스크립트 (create-pt2a-sh.sh) 와 create-pt2a.cpp용 소스 코드를 다운로드 하여 직접 실행해보기 바란다.

Windows 프로세스와 쓰레드 생성속도를 높이는 코드를 만드는 방법을 알고있다면 discussion forum에서 의견을 나눠주기 바란다.



'나는 다른 사람들이 생각하는 것처럼 천재는 결코 아니다. 어린 시절엔 무엇 하나 뚜렷하게 잘한다는 말을 들어보지 못했다. 오히려 나는 공부나 운동 어느 것도 잘하지 못하고 너무나 내성적인 내 자신에 실망하면서 지냈다. 천재들의 이야기를 들을 때면 도저히 그들을 따라갈 수 없는 내 자신이 서글퍼지기도 했다. '

《별난 컴퓨터의사 안철수》는 스스로에 대해 이렇게 생각했던 안철수씨(48)가 어떻게 의학박사 · 컴퓨터백신 전문가 · 벤처기업인 · KAIST 석좌교수 및 베스트셀러 저자가 됐는지 들여다보게 해준다. 그의 대표작인 《CEO 안철수,영혼이 있는 승부》보다 이 책을 더 좋아하는 이유다.

1995년 펴낸 이 첫 책에서 그는 자신의 성격과 생각,의대 입학 및 졸업 과정,컴퓨터 백신프로그램 공개 이유까지 솔직하게 털어놨다. 책에 따르면 그는 어린 시절 외톨이였다. 내성적인데다 얼굴이 유독 하얗고 머리도 노란 편이어서 또래들에게 흰둥이란 놀림을 받은 통에 밖에 나가지 않고 혼자 지냈다는 것이다.

병원집 장남으로 태어났지만 중학교 때까진 성적도 그저 그렇고 피만 봐도 무서워 의사보다 과학자가 되고 싶었다고도 했다. 그러나 고2 때 부모님의 사랑에 보답해야 한다는 생각에 마음을 바꿔 의대에 입학,우수한 성적으로 졸업했지만 결국 기초의학인 생리학 쪽으로 전공을 바꾸게 돼 부모님께 죄송하다고 고백했다.

그는 자신의 특징 중 하나로 뭐든 기초부터 시작하는 점과 뛰어난 집중력을 들었다. 바둑만 해도 의대 2학년 때 처음 배우기로 작정한 뒤 책을 50권쯤 사서 읽은 뒤에야 기원에 갔다는 것이다. 컴퓨터와 의학 공부도 마찬가지.'기계를 사기 전에 책부터 봤다. 모르는 게 많아도 소처럼 읽어나가다 보면 결국 통째로 이해할 수 있었다. 의대에서도 족보 대신 교과서만 봤다. 취미도 본업도 기초부터 하다 보니 처음 한 단계 올라서는 데 남보다 많은 시간이 걸렸지만 나중엔 가속도가 붙었다. '

적응력과 책임감도 강점으로 꼽았다. 서울에 온 뒤 한동안 광화문에서 동대문까지 걸어가며 골목골목 죄다 들어가 봤더니 점차 모르는 길도 척척 찾게 되더란 얘기다. 결혼 후 아내가 양말을 아무 데나 벗어던지면 어쩌느냐고 했을 때 당황했지만 곧 어머니가 다 정돈해주던 시절은 끝났다는 걸 깨닫고 치우게 됐다고도 했다.

본과 1학년 때인 1982년 가을 하숙집 친구가 가져온 컴퓨터를 보고 반한 뒤 고생 끝에 컴퓨터 백신 프로그램을 만들어 무료로 공개한 이후 겪은 어려움에 대해서도 밝혔다. 박사논문 준비 등 개인적 사정 때문에 백신 개정 작업이 늦어지면 어김없이 상용화에 들어간다는 소문과 함께 항의가 쇄도했다는 것이다.

그는 그러나 자신의 생각을 이렇게 못 박았다. '칭찬과 비난을 포함해 남이 나를 어떻게 생각하느냐에 귀를 막으려 애쓰면서 내가 생각하는 값진 일에 최선을 다하는 것만 염두에 둔다. 누군가 내게 도움을 받았다는 이야기를 들으면 내게 끊임없이 도움을 주는 사람들을 생각하지 않을 수 없다. 결국 사회에서 맡은 자신의 자리를 충실히 지키는 가운데 내가 남을 돕고 남이 나를 도우며 살아가게 돼 있는 것이다. ' 언제 읽어도 가슴이 뻐근해지는 대목이다.

+ Recent posts