본문 바로가기

예전/API

[API] 프로세스


프로세스실행중인 프로그램이다.

프로그램이라고 말할 수도 있지만, 사실 프로세스는 실행중인 프로그램의 한 인스턴스이다.


NotePad.exe 라는 실행 파일이 실행되어 메모리에 적재되면 메모장 프로세스가 된다. 만약 사용자가 2개의 메모장을 실행시켰다면 이 둘은 같은 프로그램이지만, 다른 프로세스로 인식된다. 이것이 프로그램과 프로세스의 차이이다.



운영체제는 실행된 프로그램을 프로세스 단위로 관리한다.


프로세스는 실행중인 프로그램이지만 실제로 작업을 하는 주체는 아니다. 작업은 프로세스 내의 스레드가 담당한다.

프로세스는 단지 메모리 상에만 존재하고, 실행과 동시에 스레드를 하나 만들고 스레드를 호출하므로써 모든 작업을 맡긴다.


정리하면, 프로세스는 스레드를 담는 껍데기이며, 실제 일을 하는 것은 스레드 이다.



실제 일을 하는 것이 스레드이기 때문에 프로세스는 하나 이상의 스레드를 갖는다. 실행과 동시에 생성되는 스레드를 '주 스레드'라고 하며, 필요에 따라 여러 스레드를 더 만들어 사용할 수도 있다. 이런 것을 멀티 스레드라고 부른다.




이제 프로세스를 생성하는 함수들에 대해 알아보겠다.


1. WinExec



lpCmdLine 은 실행하고자 하는 프로그램의 이름을 전달하되 완전 경로를 줄 수 있다.


만약 

WinExec("Notepad.exe",SW_SHOW);

를 하면 그냥 메모장이 뜬다. 



디렉토리 명을 생략하고 실행 파일 이름만 지정해도 된다. 프로그램은 아래의 순서대로 실행 파일의 위치를 검색하여 발견된 파일을 실행한다.


① 프로그램이 실행된 디렉토리

② 현재 디렉토리

③ 시스템 디렉토리

④ 윈도우즈 디렉토리

⑤ PATH 환경변수가 지정하는 디렉토리 들


하지만 이 위치에 없는 실행파일이라면 절대 주소를 정확히 입력해줘야 한다.


uCmdShow 는 실행된 프로그램이 보이는 것을 결정하는데, SW_SHOW 혹은 SW_SHOWNORMAL 은 일반적으로 보인다는 것이고, SW_SHOWMAXIMIZED 나 SW_SHOWMINIMIZED 를 하면 최대화 혹은 최소화 상태로 실행시킬 수도 있다.



실행시킬 프로그램에 명령인수 까지 같이 전달하고 싶다면, lpCmdLine 위치에 명령행 인수까지 같이 적으면 된다.


외부 프로그램을 실행할 경우 에러가 발생할 소지가 높으므로 이 함수는 항상 리턴값을 체크해야 한다.

만약 WinExes 함수가 성공했을 경우 31보다 큰 값을 리턴한다.


실패시 0을 리턴한 경우는 메모리나 리소스가 부족해 프로세스를 생성하지 못한 것이다.

그외에도 ERROR_BAD_FORMAT (지정 파일이 실행파일이 아님), ERROR_FILE_NOT_FOUND(파일 없음), ERROR_PATH_NOT_FOUND(경로 없음) 등이 있다.



리턴 값보다도 중요한 것은 이 함수가 리턴하는 시기이다. 


프로세스는 주 스레드를 생성하고, 관련 DLL을 모두 읽어오고 초기화 작업까지 완료해야 생성된다. 아주 복잡하다.


WinExes 는 생성된 프로세스가 처음으로 GetMessage 를 호출할 때 리턴한다. 즉, 새 프로세스가 일련의 초기화 작업을 마무리하고 스스로 메시지를 처리할 수 있을 때 리턴한다. 메시지 구동 시스템에서는 리턴시기가 아주 중요한 의미를 가지기 때문에 이 시점이 중요하다.


WinExes 가 프로세스 생성 완료 후에 리턴하기 때문에 이 함수 호출 후에 에러만 없었다면 바로 FindWindow로 생성된 프로세스의 메인 윈도우를  찾을 수도 있고, SendMessage로 메시지를 보낼 수도 있다. 즉, 프로세스 생성 직후에 두 프로세스 끼리 안전하고 완벽한 상호 작용이 가능하다는 것이다. 하지만 뒤에 나올 CreateProcess 는 그렇지 못하다.


이 WinExes 는 사용이 편리하지만, 프로세스를 제어하는 옵션이 부족하며, 유니코드를 지원하지 않는다. 



2. LoadModule



이 함수는 tagLOADPARMS32라는 구조체를 사용해 VOID* 인자를 받아 프로그램을 실행시킨다. 
호출 전에 준비할게 많고 번거로우니 그냥 있다는 것만 알아두자. (P.1511)


3. CreateProcess



CreateProcess는 Win32 API에서 프로세스를 생성하는 기본 함수이다. WinExes 도 내부적으로는 이 함수를 호출해 프로세스를 생성한다.

기본 함수인 만큼 많은 인수를 가지고 있다. 이 중 중요한 인수는 주석이 달려있는 4개의 인수이다.



우선 메모장을 띄워보자.



si,pi 구조체는 반드시 선언되야 하며, si구조체는 또 반드시 초기화되어야 한다.


첫 번째 인자가 실행할 프로그램이라고 했는데 메모장 예제에선 2번째 인수에 실행파일 명을 주었다. 그 이유는 첫번째 인자는 현재 디렉토리에서만 파일을 검사하기 때문이다.

첫 번째 인자에 실행파일 명을 주어도 되지만, 그 전에 path의 경로를 MAX_PATH로 설정해 줘야한다. 


(참고. 두 번째 인수만으로도 모든 작업이 가능 하나 POSIX와의 이식성을 확보하기 위해 첫 번째 인수가 필요하다.)


CreateProcess 함수는 프로세스 생성에 관한 세밀한 제어를 할 수 있는 고급함수로 프로세스 생성과 동시에 여러 속성을 지정할 수 있다.

STARTUPINFO 구조체는 생성될 프로세스의 메인 윈도우 속성을 지정한다. 예를 들어 프로세스의 메인 윈도우가 지정한 위치에 지정한 크기로 생성되는 것이 가능하다.
PROCESS_INFORMATION 구조체는 생성한 프로세스의 정보를 돌려받기 위한 출력용이므로 초기화 할 필요가 없다.



CreateProcess 는 복잡해서 사용하기에 불편하다. 단순히 프로세스를 생성할 목적이라면 WinExes 를 사용하는 것이 더 편하다. 하지만 이 두 함수는 중요한 차이가 있다. 위에서 말한 리턴시기이다. 

CreateProcess 는 프로세스를 생성한 후 곧바로 리턴하므로, 이 함수가 리턴된 직후에도 메모장이 아직 초기화 중이며 메인 윈도우가 만들어 있지 않다. 때문에 FindWindow 로 메모장 윈도우를 찾지 못한다. 하지만 WinExes 는 메모장이 초기화될 때까지 기다렸다가 리턴하므로 FindWindow 에 의해 메모장 윈도우를 찾을 수 있다.

만약 프로세스를 생성한 후 메시지를 전달하거나 IPC로 데이터를 교환하고자 한다면 CreateProcess 로는 원하는 결과를 호출 할 수 없다. 이때는 프로세스를 생성한 직후에 WaitForInputIdle 를 호출해야 한다.



WaitForInputIdle 는 hProcess 가 지정하는 프로세스가 사용자의 입력을 받을 수 있을 때까지. 즉, 초기화가 완료될 때 까지 dwMilliseconds 인수가 지정하는 시간까지 대기한다.

이 함수를 사용하면 메모장을 실행한 후 초기화 완료시까지 대기하므로 FindWindow로 메모장을 찾을 수 있다.
이런 일은 디버그시 보이지 않기 때문에 알아두는 것이 좋다.


4. ShellExcute



이 함수를 사용하기 위해서는 #include <ShellAPI.h> 헤더를 추가해야 한다.
ShellExcute는 실행 파일 뿐만 아니라 일반 데이터 파일도 실행할 수 있다. 예를 들어 데이터 파일을 실행하면 연결된 프로그램이 실행되면서 데이터 파일이 열린다. 

hwnd 는 에러발생시 에러 메시지 박스가 이 윈도우의 차일드로 생성된다.
lpOperation 는 해당 파일을 어떻게 열 것인가를 지정하는 동사를 문자열 형태로 지정한다. 예를 들어 "open" 은 해당 파일을 연다.
그 외에도 "edit", "print", "explorer" 등이 있다.
모든 동사가 의미가 있는 것은 아니고, 대게 "open"이 쓰인다.

이 함수는 인터넷 창을 실행할 때 많이 쓰인다.



ShellExcuteEx 함수는 좀 더 상세한 정보를 지정하고, 실행된 프로세스에 대한 정보를 리턴한다.

 

이 함수는 SHELLEXECUTEINFO 구조체를 이용해 사용된다.