잡(Job)
July 14, 2019
목표
- 잡 내의 프로세스에 대한 제한사항 설정
- 잡 내에 프로세스 배치하기
- 잡 내의 모든 프로세스 종료하기
- 잡 통지
잡
마이크로소프트 윈도우는 잡 커널 오브젝트를 이용하여 프로세스들을 하나의 그룹으로 묶어 관리할 수 있다. 일종의 프로세스 컨테이너 처럼 말이다. 예를 들면 visual studio는 C++을 빌드하기 위해 cl.exe를 실행하고 순차적으로 모든 차일드 프로세스를 추가 실행하게 된다. 하지만 도중에 빌드를 취소하는 경우에 관련된 모든 프로세스가 종료되는 것도 보장되어야 한다. 프로세스는 부모 프로세스가 종료된다고 해서 차일드 프로세스가 종료되지는 않으므로 이런 문제를 해결하는 것이 쉽지 않은데, 이 때 잡 커널 오브젝트를 사용하면 유용하다.
잡 관련 함수
- IsProcessInJob
BOOL IsProcessInJob( HANDLE ProcessHandle, HANDLE JobHandle, PBOOL Result );
- 현재 프로세스가 해당 잡 내에 포함되어 있는지 알아보는 함수
- 이미 프로세스가 잡 내에 배치된 경우 다른 잡으로 이동될 수 없음.
- 위 제약을 통해 프로세스가 잡 내에서 임의로 빠져나오는 일을 방지
- CreateJobObject
HANDLE CreateJobObjectA( LPSECURITY_ATTRIBUTES lpJobAttributes, LPCSTR lpName );
- 다른 커널 오브젝트와 마찬가지로 첫 번째 매개변수는 오브젝트에 대한 보안정보를 전달
-
두번째 인자로 잡 오브젝트의 이름 지정(OpenJobObject 함수로 다른 프로세스에서 해당 오브젝트 사용 가능)
- OpenJobObject
HANDLE OpenJobObjectA( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName );
- 이미 생성된 잡 오브젝트의 이름을 통해 잡 오브젝트 핸들을 가져오는 함수
1. 잡 내의 프로세스에 대한 제한사항 설정
잡을 생성하고 나면 잡 내에서 수행될 프로세스들에 대한 샌드박스(제한사항의 집합)을 설정하고 싶을 때가 있다. 이러한 제한 사항은 SetInformationJobObject 함수를 사용하여 설정한다.
BOOL SetInformationJobObject(
HANDLE hJob, // 잡 핸들
JOBOBJECTINFOCLASS JobObjectInformationClass, // 어떤 제한사항을 설정할지
LPVOID lpJobObjectInformation, // 제한사항 설정 구조체 주소
DWORD cbJobObjectInformationLength // 구조체의 크기
);
- 각 파라메터에 대한 자세한 내용
- SetInformationJobObject를 통해 잡에 설정 가능한 내용들
- 프로세스별로 사용 가능한 유저 모드 시간
- 제한사한 설정
- 프로세스별 최소 워킹셋(working set)
- 잡 내에서 동시에 수행 가능한 프로세스의 최대 개수
- 잡 내 프로세스를 수행할 CPU 부분집합 설정
- 잡 내 프로세스들에 대한 우선순위 클래스 지정
- 잡 내 스레드에게 상대적으로 다른 퀀텀 시간 설정
working set: 프로세스 주소 공간에서 현재 사용되고 있는 물리 페이지의 리스트 정보로 구성된 프로세스의 데이터 구조체. 시스템은 워킹 셋을 이용해서 각 프로세스의 물리 메모리 사용 상황과 일정 기간 사용되지 않은 메모리 페이지가 무엇인지 판단할 수 있다.
2. 잡 내에 프로세스 배치하기
AssignProcessToJobObject 함수를 이용하면 잡 내에 프로세스를 배치 가능하다.
BOOL AssignProcessToJobObject(
HANDLE hJob,
HANDLE hProcess
);
- 매개 변수로 전달한 프로세스가 다른 잡 내에 포함되지 않는 경우에만 성공
- 이미 잡 내에 포함된 프로세스를 다른 잡으로 옮기거나 제거하는 것은 불가능
- 잡 내 프로세스가 차일드 프로세스를 생성하면 자동으로 잡 내 배치
- JOBOBJECT_BASIC_LIMIT_INFORMATION의 설정을 변경하는 것으로 새로 생성될 프로세스를 잡에서 분리하는 것은 가능(링크)
3. 잡 내의 모든 프로세스 종료
TerminateJobObject 함수를 호출하여 잡 내의 모든 프로세스를 종료
BOOL TerminateJobObject(
HANDLE hJob,
UINT uExitCode
);
- 잡 내의 모든 프로세스의 TerminateProcess 를 호출하는 것처럼 동작
잡 통계 정보 조회
QueryInformationJobObject 함수를 통해 잡의 제한 사항과 상태 정보를 알 수 있음
BOOL QueryInformationJobObject(
HANDLE hJob,
JOBOBJECTINFOCLASS JobObjectInformationClass,
LPVOID lpJobObjectInformation,
DWORD cbJobObjectInformationLength,
LPDWORD lpReturnLength
);
- 2, 3번째 전달인자를 변경하여 여러 가지 정보를 얻을 수 있다. (링크)
- 잡 내 프로세스들의 커널/유저 모드 CPU 사용 시간
- 발생한 페이지 폴트 횟수
- 잡 내에 포함되었던 프로세스의 전체 개수
- 잡 내에 현재 포함된 프로세스의 전체 개수
- 허용된 CPU 시간을 초과하여 강제 종료된 프로세스의 개수
- 잡 내 프로세스가 수행한 읽기, 쓰기, 기타 동작의 수행 횟수
- 잡 내 프로세스들의 ID
4. 잡 통지
잡 내의 프로세스가 종료되거나 또는 허락된 CPU 시간을 초과할 때, 아니면 잡 내에서 새로운 프로세스가 생성되었을 때 잡 통지를 이용하면 사실을 전달 받을 수 있다.
- 대부분의 잡들은 하나의 페어런츠 프로세스에 의해 실행되고, 페어런츠 프로세스는 차일드 프로세스들이 종료될 때까지 기다리는 것이 일반적
- 따라서 모든 잡들이 종료되었는지 알고 싶으면 단순히 페어런츠 프로세스가 시그널 상태가 될 떄까지 기다리면 됨
I/O Completion 포트를 생성하고 잡에 연결하기
- I/O 컴플리션 포트를 생성한 후 SetInformationJobObject를 호출하면 잡과 I/O 컴플리션 포트를 연결할 수 있다. (링크)
JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp;
joacp.CompletionKey = 1; // 잡을 구분할 고유값
joacp.CompletionPort = hIOCP; // 통지 수신을 위한 I/O 컴플리션 포트의 핸들
SetInformationJobObject(hJob, JobObjectAssociateCompletionPortImformation,
&jocap, sizeof(joacp));
위와 같은 코드는 시스템은 잡의 상태를 감시하고 있다가 잡 통지가 발생하면 I/O 컴플리션 포트를 통해 그 사실을 알려준다.
I/O Completion 포트에서 발생한 통지 가져오기
- GetQueuedCompletionStatus 함수를 사용 (링크)
BOOL GetQueuedCompletionStatus(
HANDLE CompletionPort,
LPDWORD lpNumberOfBytesTransferred,
PULONG_PTR lpCompletionKey,
LPOVERLAPPED *lpOverlapped,
DWORD dwMilliseconds
);
- 이 함수는 잡으로부터 통지가 발생하면 발생한 통지를 가져온다.
- lpCompletionKey로 어떤 잡에서 발생한 통지인지 구분
- lpNumberOfBytesTransferred를 통해 어떤 종류의 통지인지 구분한다.