** 프로그램 : 하드디스크 등의 저장 매체에 저장. 실행 파일의 형태
프로세스의 생성에서 종료까지 관리는 모두 커널에 의해 이루어짐
- 커널 영역에 프로세스 테이블을 만들고, 프로세스의 목록 관리
관리내용
- 프로세스 생성, 실행, 일시 중단 및 재개, 정보 관리, 프로세스 통신, 프로세스 동기화, 프로세스 중단, 프로세스 컨텍스트 스위칭
프로그램의 다중 인스턴스
- 한 프로그램을 여러 번 실행시켜 다중 인스턴스를 생성하면 어떻게 될까?
- 운영체제는 프로그램을 실행할 때마다 독립된 프로세스 생성
** CPU가 주소선을 통해 엑세스할 수 있는 전체 메모리 공간
공간 크기
- CPU의 주소선의 수에 의해 결정
- 32비트 CPU -> 32개의 주소선 -> 232개의 주소 -> 4GB 공간
주소공간은 0번지부터 시작
- 1번지의 저장 공간 크기는 1바이트
** 함수 호출 외에 프로세스에서 필요시 사용가능
- 프로세스가 실행 중에 접근할 수 있도록 허용된 주소의 최대 범위
- 프로세스 주소 공간은 논리 공간(가상 공간) - 0번지에서 시작하여 연속적인 주소
** 프로세스 주소 공간은 2부분으로 나뉘어짐
1) 사용자 공간
2) 커널 공간
** 프로세스의 주소 공간은 가상 공간
프로세스의 주소 공간은 사용자나 개발자가 보는 관점
- 자신이 작성한 프로그램이 0번지부터 시작하여,
- 연속적인 메모리 공간에 형성되고,
- CPU가 액세스할 수 있는 최대 크기의 메모리가 설치되어 있다고 상상
실제 상황
- 설치된 물리 메모리의 크기는 주소 공간보다 작을 수 있고,
- 프로세스의 코드, 데이터, 힙, 스택은 물리 메모리에 흩어져 저장됨 (연속적인 메모리 공간이 아님)
[가상 주소 공간의 물리 메모리로의 매핑]
- 사용자는 연속적인 공간(가상 주소 공간)으로 생각 -> 그러나 가상 주소의 데이터가 실제 메모리에 분산되어 있어 어느 번지에 있을지 알 수 없음
- 가상메모리 운영 방식 : LRU (least, recently, uses- 최근 최소 사용) 필요한 것만 RAM으로 가져오고 오래동안 사용하지 않는 건 하드디스크에 내려놓는다.
** 각 프로세스는 독립된 사용자 공간 소유, 커널 공간 공유
프로세스 테이블과 프로세스 제어 블록
1) 프로세스 테이블
2) 프로세스 제어 블록(PCB : Process Control Block)
- nice 값 : Linux 상에서 Process 가 실행될 때, nice 라는 값을 가지고 실행되는데, nice 값은 process 간의 우선순위를 말함
프로세스 스케줄링
- 과거 운영체제에서 실행단위는 프로세스였음
- Ready 상태의 프로세스 중에 실행 시킬 프로세스 선택
오늘날 운영체제는 스레드를 대상으로 스케줄링
- 오늘날 프로세스 스케줄링은 없음
- 오늘날 운영체제에서 실행 단위는 스레드
- Ready 상태의 스레드 중 실행시킬 스레드 선택
프로세스는?
- 프로세스는 스레드들에게 공유 자원을 제공하는 컨테이너로 역할이 바뀌었음
** 프로세스는 일반적으로 부모-자식 관계 (윈도우에서 프로세스는 모두 동등 - 계층 관계 아님)
#0 프로세스가 시스템 부팅시 실행되는 최초의 프로세스, 조상 프로세스
부모 프로세스는 여러 개의 자식 프로세스를 가질 수 있음
모든 프로세스는 부모 프로세스를 가짐 (#0 프로세스 제외)
자식 프로세스 생성
- 모든 프로세스는 프로세스(부모)에 의해 생성
fork(), clone() 등의 커널 코드가 자식 프로세스 생성
예외 : PID 0,1,2 등의 몇몇 조상 프로세스는 시스템 호출이 아닌 수작업으로 생성
리눅스 사례
#0 프로세스 - swapper/idle 프로세스 (hand-crafted)
#1 프로세스 - init 프로세스(hand-crafted)
- 부팅 후 생성되는 모든 사용자 프로세스의 조상
#2 프로세스 - kthreadd 프로세스
- 커널 모드에서 커널 코드로만 실행되는 모든 커널 프로세스의 조상
#0 프로세스
- 최고의 어른(조상) 프로세스
- Unix의 #0 프로세스
Linux의 #0 프로세스
- idle 프로세스, 부팅 관여 없이 아무 일도 하지 않고 루프
Windows의 #0 프로세스 : system idle process(시스템 유휴 프로세스)
프로세스를 다루는 시스템 호출
** 좀비 프로세스 : 종료 후 방치된 자식 프로세스
프로세스가 종료할 때
- PCB에, 종료 코드 저장
- PCB에 프로세스 상태를 Terminated 라고 표시
- 프로세스에게 할당된 모든 메모리 반환
부모 프로세스의 의무
- wait() 시스템 호출을 통해 프로세스의 종료 코드를 읽어야 함
좀비 프로세스
- 종료하였지만, 부모가 종료 코드를 읽지 않은 상태의 프로세스
- 프로세스 리스트를 출력할 때(ps명령으로) 나타남
좀비 프로세스 제거
- 부모에게 SIGCHLD 핸들러가 없다면 좀비는 제거되지 못함
- 부모 프로세스를 강제 종료시키면,
- 좀비는 init 프로세스의 자식이 되고
- init이 wait() 호출하여 좀비 프로세스 제거
- 쉘에서 부모 프로세스에게 SIGCHLD 신호보내기
고아 프로세스 : 부모가 먼저 종료한 자식 프로세스
운영체제에 따라, 혹은 쉘의 경우
1) 백그라운드 프로세스
2) 포그라운드 프로세스
3) CPU 집중 프로세스 vs. I/O 집중 프로세스
CPU 집중프로세스
- 대부분의 시간을 계산 중심의 일(CPU 작업)을 하느라 보내는 프로 세스
- 배열 곱, 인공지능 연산, 이미지 처리
- CPU 속도가 성능 좌우
I/O 집중 프로세스
- 입출력 작업을 하느라 대부분의 시간을 보내는 프로세스
- 네트워크 전송, 파일 입출력에 집중된 프로세스
- 파일 서버, 웹 서버
- 입출력 장치나 입출력 시스템의 속도가 성능 좌우
운영체제의 스케줄링 우선순위 : I/O 집중 프로세스 > CPU 집중 프로세스
- I/O 작업을 하는 동안 다른 프로세스에게 CPU 할당 가능
컴퓨터 시스템에서 프로세스가 생성되는 5가지 경우
- 시스템 부팅과정에서 필요한 프로세스 생성
- 사용자의 로그인 후 사용자와 대화를 위한 프로세스 생성(bash 등 쉘)
- 새로운 프로세스를 생성하도록 하는 사용자의 명령(vi a.c, vi hello.c)
- 배치 작업 실행 시(at, batch 명령, "몇 시에 ~해라")
- 사용자 응용프로그램이 시스템 호출로 새 프로세스 생성
프로세스 생성
- 프로세스가 프로세스를 생성
시스템 호출을 통해서만 프로세스 생성
- 커널만이 프로세스 생성가능
- 리눅스 : fork() 시스템 호출
- windows : CreateProcess() 등 시스템 호출
프로세스 생성 과정
1) 새로운 PID 번호 할당
2) PCB 구조체 생성
3) 프로세스 테이블에서 새 항목 할당
4) 새로운 프로세스를 위한 메모리 공간 할당
-> 프로세스의 코드, 데이터, 스택, 힙 영역
-> 할당받은 메모리 공간에 프로세스의 코드와 데이터 적재
5) PCB에 프로세스 정보 기록
6) PCB에 프로세스 상태를 ready 상태로 표시하고, 준비 큐에 넣어서 차후 스케줄되게 함
- fork() 시스템 호출로 자식 프로세스 생성
int pid = fork();
-> 자식 프로세스 생성
-> 부모 프로세스의 모든 환경, 메모리, PCB 등을 복사
-> 부모와 동일한 모양이지만, 독립된 주소 공간 소유리턴값
-> 부모 프로세스에게는 자식 프로세스의 PID 리턴
-> 자식 프로세스에게는 0 리턴
프로세스 오버레이, exec()
프로세스 오버레이(process overlay)
현재 실행중인 프로세스의 주소 공간에 새로운 응용프로그램을 적재하여 실행시키는 기법
exec 패밀리 시스템 호출
-> execlp(), execv(), execvp() 시스텔 호출들
-> 실행 파일을 로딩하여 현재 프로세스의 이미지 위에 단순히 덮어쓰고 새로운 프로세스의 생성 과정을 거치지 않는다.프로세스의 PID 변경 없음
프로세스의 코드, 데이터, 힙, 스택에 새로운 응용프로그램이 적재됨
보통 fork() 를 통해 생성된 자식 프로세스가 exec() 실행
프로세스 종료
- exit() 시스템 호출
- C프로그램의 main() 에서 리턴
종료 코드
- 부모 프로세스에게 전달하는 값
exit() 시스템 호출로 프로세스 종료 과정
1) 프로세스의 모든 자원 반환
- 코드, 데이터, 스택, 힙 등의 모든 메모리 자원을 반환
- 열어 놓은 파일이나 소켓 등을 닫음
2) PCB에 프로세스 상태를 Terminated 로 변경, PCB 에 종료 코드 저장
3) 자식 프로세스들을 init 프로세스에게 입양
4) 부모 프로세스에게 SIGCHLD 신호 전송
- 부모가 SIGCHLD 신호 처리기를 작성하여 wait() 시스템 호출로 자식의 종료 코드 읽기 실행
- 혹은 언젠가 부모가 자식의 죽음 처리. 그동안 자식은 좀비 상태에 있음
1) 종료 코드
2) 종료 코드 사용 시 유의할 점
int main(){
return 300; // return 44; 와 같음
}
void func(){
exit(300); // exit(44) 와 같음
}
1) 프로세스 종료
2) 프로세스가 종료되면
** 죽은 프로세스가 wait(), 혹은 CloseHandle() 시스템 호출을 통해, 죽은 자식이 남긴 정보를 읽게 되면 자식 프로세스의 PCB 가 완전히 제거
좀비 프로세스
종료할 때 리턴한 정보(main() 함수에서 리턴값)를 부모 프로세스가 읽지 않을 때, 죽었지만 PCB만 남아 완전히 제거되지 못한 상태
이 글이 문제가 된다면 삭제하겠습니다.