[혼공컴운 10] 프로세스와 스레드

uuuu.jini·2023년 9월 9일
0

혼공컴운

목록 보기
2/5

프로세스: 실행중인 프로그램

  • 프로그램은 실행되기 전에 그저 보조기억장치에 있는 데이터 덩어리일 뿐이다. 보저기억 장치에 저장된 프로그램을 메모리에 적재하고 실행하는 순간 그 프로그램은 프로세스가 된다. 이러한 과정을 '프로세스를 생성한다'라고 표현한다.

✅   프로세스 직접 확인하기


  • 윈도우: 작업 관리자의 [프로세스] 탭에서 확인가능

  • 유닉스: ps 명령어로 확인 가능

  • 포그라운드 프로세스foreground process: 사용자가 보는 앞에서 실행되는 프로세스, 사용자가 직접 실행한 프로세스

  • 백그라운드 프로세스background process: 사용자가 보지 못하는 뒤편에서 실행되는 프로세스, 사용자가 실행하지 않는 알 수 없는 프로세스

    • 백그라운드 프로세스 중에는 사용자와 직접 상호작용할 수 있는 프로세스와 상호작용하지 않고 정해진 일만 수행하는 프로세스가 존재
    • 데몬 (유닉스), 혹은 서비스 (윈도우)라고 불림

✅   프로세스 제어 블록


모든 프로세스는 실행을 위해 CPU를 필요로 하지만, CPU 자원은 한정되어 있다. 즉 모든 프로세스가 CPU를 동시에 사용할 수 없다. 프로세스는 차례로 돌아가며 한정된 시간만큼만 CPU를 사용한다. (자신의 차례가 되면 정해진 시간만큼 이용하고, 시간이 끝났음을 알리는 인터럽트인 타이머 인터럽트가 발생하면 자신의 차례를 양보하고 다음 차례가 올때까지 기다린다.)

타이머 인터럽트는 클럭 신호를 발생시키는 장치에 의해 주기적으로 발생하는 하드웨어 인터럽트이다. 타임아웃 인터럽트라고도 불린다.

운영체제는 빠르게 번갈아 수행되는 프로세스의 실행 순서를 관리하고, 프로세스에 CPU를 비롯한 자원을 배분한다. 이를 위해 운영체제는 프로세스 제어 블록PCB: Process Control Block 을 이용한다.

프로세스 제어 블록은 프로세스와 관련된 정보를 저장하는 자료 구조이다. 해당 프로세스를 식별하기 위해 꼭 필요한 정보들이 저장된다. (식별, 처리하는데 필요한 정보)

  • PCB는 커널 영역에 생성된다.
  • PCB는 프로세스 생성 시에 만들어지고 실행이 끝나면 폐기된다.
  • 새로운 프로세스가 생성되었다. -> 운영체제가 PCB를 생성했다.
  • 프로세스가 종료되었다. -> 운영체제가 해당 PCB를 폐기했다.

🔎   프로세스 ID (PID)

프로세스 ID(PID)는 특정 프로세스를 식별하기 위해 부여하는 고유번호이다. (학교의 학번, 회사의 사번) 같은 일을 수행하는 프로세스여도 두번 실행시 PID가 다른 두 개의 프로세스가 생성된다. (윈도우 작업관리자에서 확인이 가능)

🔎   레지스터 값

프로세스는 자신의 실행 차례가 돌아오면 이전까지 진행했던 작업들을 그대로 이어 실행할 수 있도록 이전까지 사용했던 레지스터의 중간값들을 모두 복원한다. PCB 안에는 해당 프로세스가 실행하며 사용했던 프로그램 카운터를 비롯한 레지스터 값들이 담긴다.

🔎   프로세스 상태

현재 프로세스가 기다리고 있는 상태인지, 이용하고 있는 상태인 지 등의 프로세스 상태 정보가 PCB에 저장된다.

🔎   CPU 스케줄링 정보

프로세스가 언제, 어떤 순서로 CPU를 할당받을지에 대한 정보 PCB에 저장

🔎   메모리 관리 정보

프로세스가 어느 주소에 저장되어 있는지에 대한 정보, 베이스 레지스터, 한계 레지스터 값과 같은 정보 저장, 페이지 테이블 정보 저장

🔎   사용한 파일과 입출력장치 목록

프로세스가 실행과정에서 특정 입출력장치나 파일을 사용하면 PCB에 해당 내용이 명시된다. 즉, 어떤 입출력장치가 이 프로세스에 할당되었는지, 어떤 파일들을 열었는지에 대한 정보들이 PCB에 기록된다.

✅   문맥 교환

하나의 프로세스에서 다른 프로세스로 실행 순서가 넘어가는 경우 이때까지 수행하였던 내용을 저장하여 다음 차례시에 이전까지 실행한 내용에 이어 다시 실행을 재개할 수 있게 해주어야 한다. 이때 하나의 프로세스 수행을 재개하기 위해 기억해야할 정보를 문맥이라고 한다. 하나의 프로세스 문맥은 해당 프로세스의 PCB에 저장되어 있다.

문맥 교환: 기존 프로세스의 문맥을 PCB에 백업하고, 새로운 프로세스를 실행하기 위해 문맥을 PCB에서부터 복구하여 새로운 프로세스를 실행하는 것

문맥 교환은 여러 프로세스가 끊임없이 빠르게 번갈아 가며 실행되는 원리이다. 문맥 교환이 자주 일어나면 프로세스는 그만큼 빨리 번갈아 가며 수행되기 떄문에 프로세스들이 동시에 실행되는 것처럼 보인다.

문맥교환을 너무 자주 하면 오버헤드가 발생할 수 있기 때문에 문맥교환이 자주 일어난다고 해서 반드시 좋은것은 아니다.

✅   프로세스의 메모리 영역


프로세스는 사용자 영역에 크게 코드 영역, 데이터 영역, 힙 영역, 스택 영역으로 나뉘어 저장된다.

🔎   코드 영역

코드 영역텍스트 영역 이라고도 부른다. 실행할 수 있는 코드, 즉 기계어로 이루어진 명령어가 저장된다. 코드 영역에는 데이터가 아닌 CPU가 실행할 명령어가 담겨 있기 때문에 쓰기가 금지되어 있다. 읽기 전용이다.

🔎   데이터 영역

데이터 영역은 잠깐 썼다가 없앨 데이터가 아닌 프로그램이 실행될 동안 유지할 데이터가 저장되는 공간이다. 전역 변수가 대표적이다. (프로그램이 실행되는 동안 유지되며, 프로그램 전체에서 접근할 수 있는 변수)

코드 영역과 데이터 영역은 그 크기가 변하지 않는다. ➡️ 크기가 고정된 영역인 정적 할당 영역이라고도 불린다.

🔎   힙 영역

힙 영역은 프로그램을 만드는 사용자, 즉 프로그래머가 직접 할당할 수 있는 저장공간이다. 프로그래밍 과정에서 힙 영역에 메모리 공간을 할당했다면 언젠가는 해당 공간을 반환해야 한다. 메모리 공간을 반환하지 않으면 메모리 누수가 발생한다. (할당한 공간이 메모리 내에 계속 남아 메모리 낭비 초래)

🔎   스택 영역

스택 영역은 데이터를 일시적으로 저장하는 공간이다. 데이터 영역에 담기는 값과는 달리 잠깐 쓰다가 말 값들이 저장된다. 매개변수, 지역변수가 대표적이다. 일시적으로 저장할 데이터는 스택 영역에 PUSH되고, 더 이상 필요하지 않은 데이터는 POP 되어 스택 영역에서 사라진다.

힙 영역과 스택 영역은 그 크기가 변할 수 있기 때문에 동적 할당 영역이라고 불린다.

  • 일반적으로 힙 영역은 메모리의 낮은 주소에서 높은 주소로 할당
  • 스택 영역은 높은 주소에서 낮은 주소로 할당
  • 두 영역이 겹치지 않도록

✅   프로세스 상태

컴퓨터를 사용할 때 여러 프로세스들이 번갈아가며 실행된다. 그 과정에서 하나의 프로세스는 여러 상태를 거치며 실행된다. 운영체제는 프로세스의 PCB를 통해 인식하고 관리한다. (생성, 준비, 실행, 대기, 종료)

🔎   프로세스 상태 다이어그램

🔎   생성 상태 (NEW)

프로세스를 생성 중인 상태를 생성 상태라고 한다. 이제 막 메모리에 적재되어 PCB를 할당받은 상태를 말한다. 생성 상태를 거쳐 실행할 준비가 완료된 프로세스는 곧바로 실행되지 않고 준비 상태가 되어 CPU의 할당을 기다린다.

🔎   준비 상태 (READY)

준비 상태는 CPU를 할당받아 실행할 수 있지만, 아직 차례가 아니어서 기다리고 있는 상태를 말한다. 준비 상태 프로세스는 차례가 되면 CPU를 할당받아 실행 상태가 된다.

준비 상태인 프로세스가 실행 상태로 전환되는 것을 디스패치라고 한다.

🔎   실행 상태 (RUN)

실행 상태는 CPU를 할당받아 실행 중인 상태를 말한다. 할당된 일정 시간만 CPU를 사용할 수 있다. 이때 프로세스가 할당된 시간을 모두 사용한다면 다시 준비 상태가 되고, 실행 도중 입출력 장치를 사용하여 입출력장치의 작업이 끝날 때까지 기다려야 한다면 대기 상태가 된다.

🔎   대기 상태 (BLOCKED)

프로세스는 실행 도중에 입출력 장치를 사용하는 경우가 있다. 입출력 작업은 CPU에 비해 처리 속도가 느리기에, 입출력 작업을 요청한 프로세스는 입출력장치가 입출력을 끝낼 때까지 (입출력 완료 인터럽트를 받을 때까지) 기다려야 한다. 이렇게 입출력장치의 작업을 기다리는 상태를 대기 상태라고 한다. 입출력 작업 완료 시 다시 준비상태로 CPU할당을 기다린다.

대기 상태가 되는 이유는 입출력 작업만 있는 것은 아니다. 특정 이벤트 발생 시 해당 이벤트 의 작업을 기다리는 대기 상태가 된다. 하지만 대부분의 원인이 입출력 작업이라고 봐도 무방하다.

🔎   종료 상태 (TERMINATED)

종료 상태는 프로세스가 종료된 상태이다. 프로세스 종료시 운영체제는 PCB와 프로세스가 사용한 메모리를 정리한다.

✅   프로세스 계층 구조


프로세스는 실행 도중 시스템 호출을 통해 다른 프로세스를 생성할 수 있다. 이때 새 프로세스를 생성한 프로세스를 부모 프로세스, 부모 프로세스에 의해 생성된 프로세스를 자식 프로세스라고 한다.

각각 다른 PID를 가진다. (일부 OS에서는 자식 프로세스의 PCB에 부모 프로세스의 PID인 PPID가 기록되기도 한다.)

많은 운영체제가 프로세스가 프로세스를 낳는 계층적인 구조로써 프로세스를 관리한다. 컴퓨터 부팅 시 실행되는 최초의 프로세스가 자식 프로세스들을 생성하고, 생성된 자식 프로세스들이 새로운 프로세스를 낳는 형식으로 여러 프로세스가 동시에 실행된다.

데몬이나 서비스 또한 최초 프로세스의 자식 프로세스이다.

최초 프로세스

최초의 프로세스는 유닉스 운영체제에서는 inti, 리눅스 운영체제에서는 systemd, macOS에서는 launchd라고 한다. 최초의 프로세스 PID는 항상 1번이며, 모든 프로세스 최상단에 있는 부모 프로세스이다.

pstree 명령어는 프로세스 계층 구조를 보여주는 명령어이다. 리눅스에서 pstree 명령어를 입력하면 systemd가 최상단에 있다는 것을 확인할 수 있다.

✅   프로세스 생성 기법


부모 프로세스를 통해 생성된 자식 프로세스들은 복제와 옷 갈아입기를 통해 실행된다. 부모 프로세스는 fork를 통해 자신의 복사본을 자식 프로세스로 생성하고 만들어진 복사본(자식 프로세스)는 exec를 통해 자신의 메모리 공간을 다른 프로그램으로 교체한다.

fork와 exec는 시스템 호출이다. 부모 프로세스는 fork 시스템 호출을 통해 자신의 복사본을 자식 프로세스로 생성한다. 즉, fork는 자기 자신 프로세스의 복사본을 만드는 시스템 호출이다. 자식 프로세스는 부모 프로세스의 복사본이기 때문에 부모 프로세스의 자원들, 이를 테면 메모리 내의 내용, 열린 파일의 목록등이 자식 프로세스에 상속된다. (복사된 자식 프로세스라 할지라도 PID값이나 저장된 메모리 위치는 다르다.)

fork를 통해 복사본이 만들어진 뒤에 자식 프로세스는 exec 시스템 호출을 통해 새로운 프로그램으로 전환된다. exec는 자신의 메모리 공간을 새로운 프로그램으로 덮어쓰는 시스템 호출이다. 새로운 프로그램 내용으로 전환하여 실행하는 시스템 호출이다.

exec 를 호출하면 코드 영역과 데이터 영역의 내용이 실행할 프로그램의 내용으로 바뀌고, 나머지 영역은 초기화 된다.

부모가 자식 프로세스를 실행하며 프로세스 게층 구조를 이루는 과정은 fork과 exec가 반복되는 과정이라고 볼수 있다.

부모에서 fork 한 뒤 exec를 수행하지 않는 경우 동일한 코드를 병행하여 실행하는 프로세스가 된다.

✅   프로세스와 스레드


시작하기 전에

스레드thread는 실행의 단위이다. (프로세스를 구성하는 실행의 흐름 단위이다.) 하나의 프로세스는 여러 개의 스레드를 가질 수 있다. 스레드를 이용하면 하나의 프로세스에서 여러 부분을 동시에 실행할 수 있다.

  • 단일 스레드 프로세스: 프로세스가 하나의 실행 흐름을 가지고 한 번에 하나의 부분만 실행되는 프로세스, 실행의 흐름 단위가 하나

스레드라는 개념이 도입되며 하나의 프로세스가 한 번에 여러 일을 동시에 처리할 수 있게 되었다. 즉, 프로세스를 구성하는 여러 명령어를 동시에 실행할 수 있게 되었다.

스레드는 프로세스를 구성하는 실행 단위 라고 볼 수 있다. 스레드는 프로세스 내에서 각기 다른 스레드 ID, 프로그램 카운터 값을 비롯한 레지스터 값, 스택으로 구성된다. 각자 프로그램 카운터 값을 비롯한 레지스터 값, 스택을 가지고 있기에 스레드마다 각기 다른 코드를 실행할 수 있다.

프로세스의 스레드들은 실행에 필요한 최소한의 정보(프로그램 카운터를 포함한 레지스터, 스택)만을 유지한 채 프로세스 자원을 공유하며 실행된다. 각 스레드가 각자의 코드/데이터/힙 영역이 있는 것이 아니다.

프로세스가 실행되는 프로그램이라면, 스레드는 프로세스를 구성하는 실행의 흐름 단위이다.

실제로 최근 많은 운영체제는 CPU에 처리할 작업을 전달할 때 프로세스가 아닌 스레드 단위로 전달한다. 그리고 스레드는 프로세스 자원을 공유한 채 실행에 필요한 최소한의 정보만으로 실행된다.

리눅스는 프로세스와 스레드 간에 명확한 구분을 짓지 않는다. 리눅스는 프로세스와 스레드 모두 실행의 문맥이라는 점에서 동등하다고 간주하고 이 둘을 크게 구분 짓지 않는다. 프로세스와 스레드라는 말 대신 태스크라는 이름으로 통일하여 명명한다.

✅   멀티프로세스와 멀티스레드


컴퓨터는 실행 과정에서 여러 프로세스가 동시에 실행될 수 있고, 그 프로세스를 이루는 스레드는 여러 개 있을 수 있다.

  • 멀티프로세스 multiprocess : 여러 프로세스를 동시에 실행
  • 멀티스레드 multithread : 여러 스레드로 프로세스를 동시에 실행하는 것

🔎  멀티프로세스 vs. 멀티스레드

동일한 작업을 수행하는 단일 스레드 프로세스 여러 개를 실행하는 것과 하나의 프로세스를 여러 스레드로 실행하는 것의 차이점?

프로세스끼리는 기본적으로 자원을 공유하지 않지만, 스레드끼리는 같은 프로세스 내의 자원을 공유한다.

프로세스를 fork하여 같은 작업을 하는 동일한 프로세스 두 개를 동시에 실행하면 코드 영역, 데이터 영역, 힙 영역 등을 비롯한 모든 자원이 복제되어 메모리에 적재된다. fork를 여러 번 하는 경우 메모리에 같은 프로세스가 적재되므로 메모리의 낭비이다. 같은 프로그램을 실행하기 위해 메모리에 동일한 내용들이 중복되어 존재하기 때문이다.

스레드는 프로세스가 가진 자원을 공유한다. 즉, 같은 프로세스 내의 모든 스레드는 동일한 주소 공간의 코드, 데이터, 힙 영역을 공유하고, 프로세스의 자원을 공유한다. 여러 프로세스를 병행 실행하는 것보다 메모리를 더 효율적으로 사용할 수 있다.

또한 서로 다른 프로세스들은 기본적으로 자원을 공유하지 않기 때문에 서로가 남남처럼 독립적으로 실행되는 반면, 스레드는 프로세스의 자원을 공유하기 때문에 서로 협력과 통신에 유리하다.

다만 멀티프로세스 환경에서는 하나의 프로세스가 문제가 생겨도 다른 프로세스에는 문제가 없지만, 멀티스레드 환경에서는 하나의 스레드에 문제가 생기면 프로세스 전체에 문제가 생길 수 있다. 모든 스레드는 프로세스의 자원을 공유하고, 하나의 스레드에 문제가 생기면 다른 스레드도 영향을 받기 때문이다.

🔎   프로세스 간 통신

프로세스끼리는 기본적으로 자원을 공유하지 않지만, 프로세스끼리도 자원을 공유하고 데이터를 주고받을 수 있다. 프로세스 간의 자원을 공유하고 데이터를 주고 받는 것을 프로세스 간 통신 IPC: Inter-Process Communication 이라고 부른다.

  • 파일을 통한 프로세스 간 통신: 파일 속 데이터를 주고받는 통신
  • 공유 메모리: 서로 공유하는 메모리 영역을 두어 데이터 주고 받음
  • 소켓, 파이프 통신
profile
멋쟁이 토마토

0개의 댓글