Process

🗓️

프로세스

  • 초기의 컴퓨터 시스템은 한번의 하나의 프로그램만 실행하도록 허용했다.
  • 오늘날 컴퓨터는 메모리에 다수의 프로그램들이 적재되어 병행 실행되는 것을 허용한다.
  • 제어와 구획화의 필요성에 따라 프로세스의 개념이 생겼다.
  • 프로세스란 실행중인 프로그램이다.

프로세스 개념

  • CPU활동을 지칭하는 이름
    • 일괄처리 시스템 : Job
    • 시분할 시스템 : 사용자 프로그램, Task
  • 이러한 명칭들이 있지만 모두 프로세스라고 부른다.

프로세스

  • 프로세스는 텍스트 섹션으로 알려진 프로그램 코드 이상의 어떤것이다.
  • 프로세스는 다음을 포함한다
    • 프로그램 카운터
    • 함수의 매개변수
    • 프로세스 지역변수의 스택
    • 전역변수의 데이터 섹션
    • 동적 할당되는 메모리
  • 프로그램 그 자체는 프로세스가 아니다
  • 프로세스는 다음에 실행할 명령어를 지정하는 프로그램 카운터와 관련자원의 집합을 가진 Activity entity다. (능동적인)
  • 실행파일이 메모리에 적재될 떄 프로그램은 프로세스가 된다.
  • 같은 프로그램을 두 번 실행한다면 그들은 다른 프로세스로 취급된다.
  • 프로세스 자체가 다른 코드를 위한 실행환경으로 동작할 수 있다 – 예) JVM

프로세스 상태

  • process state diagram
  • 프로세스는 실행되면서 상태가 변한다. 다음의 상태 중 하나에 존재한다.
    • new : 프로세스가 생성 중이다.
    • running : 명령어들이 실행되고 있다.
    • waiting : 프로세스가 이벤트를 기다린다.
    • ready : 프로세스가 프로세서에 할당되길 기다린다.
    • terminate : 프로세스의 실행이 종료됐다.

PCB; 프로세스 제어 블록

  • PCB는 프로세스와 연관된 여러 정보를 수록한다.
    • 프로세스 상태
    • 프로그램 카운터 : 프로세스가 다음에 실행할 명령어의 주소를 가리킨다.
    • CPU 레지스터 : 누산기, 인덱스 레지스터, 스택 레지스터, 범용 레지스터, 상태코드 정보가 포함된다. 프로그램 카운터와 함께 다뤄진다. 인터럽트 발생시 저장된다.
    • CPU 스케줄링 정보 : 프로세스 우선순위, 스케줄링 큐의 포인터를 포함한다.
    • 메모리 관리 정보 : 페이지 테이블, 세그먼트 테이블, 기준 레지스터, 한계 레지스터를 포함한다.
    • Account 정보 : CPU사용시간, 실시간(real-time), 시간제한, PID등을 포함한다.
    • 입출력 상태 정보 : 프로세스에 할당된 입출력 장치들과 열림 파일의 목록을 포함한다.

스레드

  • 하나의 프로세스가 실행중이면 하나의 명령어 스레드가 실행중이라고 할 수 있다.
  • 하나의 스레드만 실행중이라면 프로그램 내 여러가지 기능을 동시에 실행할 수 없다.
  • 프로세스의 개념을 확장해 한 프로세스가 다수의 실행 스레드를 가질 수 있다.
  • 스레드는 병렬로 실행될 수 있다.

Process scheduling

  • 다중 프로그래밍의 목적은 CPU 이용을 최대화 하기 위해 프로세스가 항상 실행되도록 한다.
  • 시분할의 목적은 각 프로그램이 실행되는 동안 사용자가 상호작용할 수 있도록 CPU를 빈번하게 교체하는 것이다.
  • 프로세스 스케줄러는 이 목적을 달성하기 위해 CPU에서 실행가능한 여러 프로세스들 중에서 하나의 프로세스를 선택한다.

스케줄링 큐

  • 프로세스가 시스템에 들어오면 잡 큐에 들어간다. 이 잡 큐는 시스템 안의 모든 프로세스로 구성된다.
  • 잡 큐는 메인메모리에 존재한다.
  • 준비 완료 큐에는 준비 완료 상태에서 실행 대기하는 프로세스들이 유지된다. (연결리스트)
  • 준비 완료 큐의 헤더는 첫번째와 마지막 PCB를 가리킨다. 각 PCB는 다음 PCB를 가리킨다.
  • 시스템에는 준비완료 큐 뿐만 아니라 장치 큐, 입출력 큐 등이 있다 (이건 문장이 무슨말인지 모르겠다)
  • 프로세스는 처음 준비 완료 큐에 놓인다. CPU를 dispath할 때 까지 대기한다. CPU가 할당되면 다음중 하나가 발생할 수 있다.
    • 입출력 요청을 해 입출력 큐에 들어갈 수 있다.
    • 자식 프로세스를 생성하고 자식 프로세스의 종료를 기다릴 수 있다.
    • 인터럽트 결과에 의해 강제로 CPU로부터 제거되고 준비 완료 큐에 다시 대기할 수 있다.
    • 종료되면 모든 큐에서 삭제되고 PCB와 자원을 반납한다.

스케줄러

  • 프로세스는 라이프사이클 동안 다양한 스케줄링 큐에 이동한다. 이 과정을 스케줄러가 수행한다.
  • 일괄처리 시스템에서는 즉시 실행될 수 있는 것보다 더 많은 프로세스들이 종종 제출된다. 이들은 메모리에 저장되어 나중에 실행될때 까지 유지된다.
  • 장기 스케줄러 (잡 스케줄러) : 프로세스를 실행하기 위해 메모리로 적재한다.
    • 실행 빈도 수가 작다. 시스템에서 새로운 프로세스를 생성하는 간격이 수 분까지 될 수 있다.
    • 입출력 중심 프로세스와 CPU 중심 프로세스가 있다. 장기 스케줄러는 이들간의 프로시스 혼합을 선택하는것이 중요하다.
  • 단기 스케줄러 (CPU 스케줄러) : 실행준비가 완료되어있는 프로세스 들 중에서 선택해 CPU를 할당한다.
    • 단기 스케줄러는 CPU를 위해 자주 새로운 프로세스를 선택한다. 100 밀리초 마다 한번씩 실행된다. 매우 빨라야 한다.
  • UNIX와 Windows는 시분할 시스템으로 장기 스케줄러가 없다. 모든 프로세스를 단기 스케줄러를 위해 메모리에 할당한다.
  • 시분할 시스템같은 운영체제들은 중기 스케줄러를 도입했다. 메모리에서 CPU의 자원을 획득하기 위한 프로세스를 제거함으로써 완화하는 방법이다. 차후 메모리에서 다시 프로세스를 불러와 중단 지점부터 다시 실행한다. 이것을 Swapping이라고 한다.

컨텍스트 스위칭

  • 인터럽트는 운영체제가 CPU를 현재 작업에서 빼앗아 커널 루틴을 실행 할 수 있게 한다.
  • 인터럽트가 발생하면 시스템은 인터럽트 처리가 끝난 후 컨텍스트를 복구할 수 있도록 현재 컨텍스트를 저장할 필요가 있다.
  • 컨텍스트는 PCB에 표현된다.
  • 컨텍스트는 CPU레지스터의 값, 프로세스 상태, 메모리 관리 정보 등을 포함한다.
  • 컨텍스트 스위칭은 CPU를 다른 프로세스로 교환 하려면 이전의 프로세스 상태를 보관하고 새로운 프로세스의 보관된 상태를 복구하는 작업이다.
  • 컨텍스트 스위칭이 일어나는 시간은 순수한 오버헤드다.

프로세스 운영

  • 시스탬 내의 프로시스들은 병행 실행될 수 있다.
  • 반드시 동적으로 실행되고 제거되야 한다.
  • 운영체제는 프로세스 생성 및 종료를 위한 기법을 제공해야 한다.

프로세스 생성

  • 실행되는 동안 프로세스는 여러개의 프로세스를 생성할 수 있다.
  • 생성하는 프로세스를 부모 프로세스, 새로운 프로세스는 자식 프로세스라고 부른다.
  • 새로운 프로세스들은 또 다른 프로세스를 생성할 수 있다.
  • 결과적으로 프로세스 트리를 형성한다.
  • 운영체제는 PID(Process identifier)를 사용해 프로세스를 구분한다.
  • PID는 고유한 값을 가지며 정수다.
  • Linux
    • init 프로세스는 언제나 PID가 1이다.
    • 시스템이 부팅되면 init은 다양한 사용자 프로세스를 생성한다.
  • 자식 프로세스를 생성할 때 자신의 임무를 위해 자원이 필요하다. 자원은 운영체제로부터 할당 받거나 부모 프로세스의 자원만을 사용하도록 제한당할 수 있다.
  • 부모 프로세스와 자식프로세스간의 관계
    1. 부모는 자식과 병행하게 실행을 지속한다
    2. 부모는 일부 또는 모든 자식이 실행을 종료할 때 까지 기다린다.
  • 새로운 프로세스들은 주소 공간 측면에서 두가지 가능성이 있다.
    1. 자식 프로세스는 부모 프로세스의 복사본이다 (fork())
    2. 자식 프로세스가 자신에게 적재될 새로운 프로그램을 갖고 있다.
  • 새로운 프로세스는 원래 프로세스의 주소 공간의 복사본으로 구성된다. 그래서 부모 프로세스가 쉽게 자식 프로세스와 통신할 수 있다.
  • fork()의 반환값으로 부모 프로세스는 자식 프로세스의 PID를 반환받고, 자식 프로세스는 0을 반환받는다.
  • fork()이후 exec()를 호출해 자신의 메모리 공간을 새로운 프로그램으로 교체한다. 프로세스의 주소 공간을 새 프로그램으로 덮어 쓰기 때문에 시스템 호출은 오류가 발생하지 않는 한 제어를 반환하지 않는다.
  • 자식 프로세스가 exec()호출하지 않고 부모 프로세스의 복사본을 실행하는것을 막을 방법은 없다. 이 경우 병행 프로세스가 된다.
  • 부모 프로세스는 wait()으로 자식 프로세스가 종료될 때 까지 기다린다.

프로세스 종료

  • 프로세스가 마지막 문장의 실행을 끝내고 exit() 시스템 호출을 사용해 운영체제에 자신의 삭제를 요청하면 종료한다.
  • 이 시점에서 프로세스는 자신의 부모 프로세스에게 상태 값을 반환할 수 있다 (wait() 호출을 통한)
  • 모든 자원이 운영체제로 반납된다.
  • 부모 프로세스는 적당한 시스템 호출을 통해 자식 프로세스의 종료를 유발할 수 있다. 자식의 PID를 알아야 한다.
    • 자식이 자신에게 할당된 자원을 초과하여 사용할 때
    • 자식에게 할당된 작업이 더이상 필요 없을 때
    • 부모가 exit를 하는데 운영체제가 자식의 실행을 허용하지 않을때
  • 부모프로세스가 종료된 이후에 자식 프로세스가 종료 될 수 없다.
  • 프로세스가 종료되면 자식 프로세스들도 연쇄적으로 종료된다 (cascading termination)
  • 프로세스를 종료하면 사용하던 자원은 운영체제가 되찾아간다. 그러나 프로세스의 종료 상태가 저장되는 프로세스 테이블의 해당 항목은 부모 프로세스가 wait()를 호출할 때 까지 남아있게 된다. 이런 프로세스를 좀비 프로세스라고 칭한다.
  • 모든 프로세스는 종료시 아주 짧은 상태의 좀비 프로세스가 된다. 운영체제에 곧 반환된다.
  • 부모 프로세스가 wait()대신 종료를 해버리면 자식 프로세스는 고아 프로세스가 된다.
  • Linux에서는 고아프로세스의 부모를 init으로 지정함으로써 이 문제를 해결한다.

IPC; Interprocess communication

  • 프로세들은 서로 협력할 수 있다.
  • 다른 프로세스와 통신을 허용하는 환경을 제공하는 이유
    • 정보 공유 Information sharing : 여러 사용자가 동일한 정보를 원할 수 있다. 병행적으로 접근할 수 있어야 한다.
    • 계산 가속화 Computation speedup : 복수개의 코어처리를 가진 경우 서브태스크로 나눠 작업을 병렬로 실행할 수 있어야 한다.
    • 모듈성 Modularity : 시스템 기능을 별도의 프로세스 또는 스레드로 나눠 모듈식 형태로 시스템을 구성하기 원할 수 있다.
    • 편의성 Convenience : 사용자들은 다수의 태스크를 병렬로 가질 수 있다.
  • 협력적인 프로세스들은 데이터와 정보를 교환할 수 있는 Interprocess communication 기법을 필요로 한다.
  • 프로세스간 통신에는 공유 메모리메시지 전달 두가지 모델이 있다. 운영체제는 두가지를 모두 구현한다.
  • 일반적으로 메시지 전달이 더 나은 성능을 보인다
    • 공유 메모리는 공유데이터가 여러 캐시사이에서 이주하기 때문에 캐시 일관성 문제로 성능 저하가 발생한다.

공유 메모리 시스템

  • 공유메모리 영역은 공유 메모리 세그먼틀르 생성한느 프로세스의 주소 공간에 위치한다.
  • 공유메모리 세그먼트를 이용하고자 하는 다른 프로세스들은 이 세그먼트를 자신의 주소공간에 추가해야 한다.
  • 일반적으로 하나의 주소 공간에 두 프로세스가 접근하는것을 허용하지 않기 때문에 제약을 해제하는 작업이 필요하다.
  • 데이터 형식과 위치는 프로세스에 의해 결정된다. (운영체제 소관X)
  • 생산자 프로세스와 소비자 프로세스가 병행으로 실행되게 하려면 공유 메모리 모델이 있다.
  • 정보가 오가는 항목에 버퍼가 있어야 한다.
  • 생산자와 소비자가 동기화 되어야 있지도 않은 항목을 소비하지 않을 수 있다.
  • 무한버퍼 : 버퍼 크기에 제한이 없어 생산자는 항상 새로운 항목을 생산할 수 있으나 소비자는 새로운 항목을 기다려야 할 수도 있다.
  • 유한버퍼 : 버퍼의 크기가 고정되어 있고 소비자는 반드시 대기 해야하고 모든 버퍼가 채워져 있을때는 생산자가 대기해야 한다.

메시지 전달 시스템

  • 공유메모리 외에 협력하는 방법으로 메시지 전달 설비를 통해 프로세스간 통신 수단을 운영체제가 제공한다.
  • 메시지 전달 방식은 동일한 주소 공간을 공유하지 않고도 프로세슫르이 통신을 하고, 그들의 동작을 동기화할 수 있도록 허용하는 기법을 제공한다.
  • 메시지 전달 방식은 분산환경에서 유용하다.
  • 메시지 전달 시스템은 최소한 send()와 receive()가 존재한다.
  • 통신하기 위해 Communication link가 반드시 필요하다.
  • send()와 receive()를 구현하는 방법
    • 직접 또는 간접 통신
    • 동기식 또는 비동기식 통신
    • 자동 또는 명시적 버퍼링

직접 통신

  • 통신을 원하는 각 프로세스의 쌍들 사이에 연결이 자동적으로 구축된다. 프로세스들은 통신하기 위해 서로의 id만 알면 된다.
  • 연결은 두 프로세스 사이에서만 이루어진다.
  • 하나의 쌍에 하나의 연결만 존재한다.
  • 주소 방식에서 대칭성을 보인다.
  • 프로세스 지정으로 인해 프로세스 식별자를 바꾸면 지정된 다른 부분을 모두 검사해야 한다.

간접 통신

  • 한쌍의 프로세스들 사이의 연결은 이들 프로세스가 공유 메일박스를 가질때만 구축된다.
  • 연결은 두개 이상의 프로세스들과 연관될 수 있다.
  • 통신하고 있는 각 프로세스들 사이에는 다수의 서로 다른 연결이 존재할 수 있다.
  • 각 연결은 하나의 메일 박스에 대응한다.

동기화

  • 프로세스는 send()와 receive() 원형을 호출해 서로 통신한다. 각 원형을 구현하기 위한 설계 옵션이 있다.
  • 메시지 전달은 blocking과 non-blocking방식으로 전달된다.
  • 동기식 송신 : 송신하는 프로세스는 메시지가 수신 프로세스 또는 메일 박스에 의해 수신될 때 까지 블로킹 한다.
  • 비동기식 송신 : 송신하는 프로세스가 메시지를 보내고 작업을 재시작한다.
  • 동기식 수신 : 수신자는 메시지가 도착할 때 까지 블로킹 된다.
  • 비동기식 수신 : 수신자는 유효한 메시지를 받거나 널을 받는다.
  • send()와 receive()간에 다른 조합도 가능하다.
  • send()와 receive()가 모두 동기식일때 둘 간에 랑데뷰를 이룬다.

CS 통신

  • 클라이언트-서버 환경에서 사용할 수 있는 세가지 다른 통신 전략이 있다.
  • 소켓, RPC, 파이프

Socket

  • 소켓은 통신의 endpoint를 뜻한다.
  • 네트워크를 통해 통신하는 한 쌍의 프로세스는 프로세스당 하나씩, 한쌍의 소켓이 필요하다.
  • 각 소켓은 IP와 port로 식별된다.
public class DataServer {

    public static void main(String[] args) {
        try{
            ServerSocket sock = new ServerSocket(6013);

            while(true){
                Socket client = sock.accept();

                PrintWriter pout = new PrintWriter(client.getOutputStream(), true);
                pout.println(new Date().toString());
                client.close();
            }
        }
        catch (IOException e){
            System.err.println(e);
        }
    }

}

public class DataClient {

    public static void main(String[] args) {
        try{
            Socket sock = new Socket("127.0.0.1", 6013);

            InputStream in = sock.getInputStream();
            BufferedReader bin = new BufferedReader(new InputStreamReader(in));

            String line;
            while ((line = bin.readLine()) != null) {
                System.out.println(line);

                sock.close();
            }
        }
        catch (IOException e){
            System.err.println(e);
        }
    }
}
  • 소켓은 바이트 스트림 기반이기 때문에 해석하는것은 서버와 클라이언트의 몫이다.

RPC; 원격 프로시저 호출

  • RPC는 두 시스템 사이에 통신하기 위해 프로시저 호출 기법을 분리해 생각하기 위한 방편이으로 IPC기반으로 만들어진다.
  • RPC는 IPC와 달리 전달되는 메시지가 구조화 되어있고 데이터의 패킷 수준을 넘어선다.
  • 각 메시지는 원격지 포트에서 리스닝 중인 RPC데몬의 주소가 지정되어있고 함수, 매개변수가 포함되어 있다.
  • 클라이언트와 서버의 데이터 표현방식이 빅엔디안과 리틀엔디안이 다를 경우에 대비해 XDR이라는 중립적인 표현방식으로 변환해 전송한다.
  • 호출의 semantic이 실패하거나 중복되는것을 방지 하기 위해 정확히 한번만 처리되도록 보장해야 한다.
  • RPC는 분산 파일 시스템을 구현하는데 유용하다.

파이프

  • 파이프는 두 프로세스가 통신할 수 있게 하는 전달자다.
  • 통신에 여러 제약이 있다.
  • 고려해야 하는 부분
    • 파이프가 단방향 또는 양방향을 허용하는가?
    • 양방향을 허용한다면 반이중인가? 전이중인가?
    • 부모-자식 같은 특정 관계가 존재해야 하는가?
    • 네트워크 통신이 가능한가? 같은 시스템 내에서만 가능한가?

일반 파이프

  • 생산자-소비자 형태고 두 프로세스간의 통신을 허용한다.
  • 단방향이다.
  • 파이프를 생성한 프로세스 이외에는 접근할 수 없다.
  • 통상 부모 프로세스가 파이프를 생성하고 fork한 자식 프로세스와 통신하기 위해 사용된다.

지명 파이프

  • 지명 파이프는 양방향으로 통신이 가능하다
  • 부모-자식 관계도 필요 없다.
  • 지명 파이프가 구축되면 여러 프로세스들이 이를 사용해 통신할 수 있다.
  • 두 프로세스는 동일한 시스템 내에 존재해야한다.
  • UNIX에서는 지명파이프를 FIFO라고 부른다.

실제 상황에서의 파이프

  • 파이프는 한 명령어의 출력이 두번째 명령어의 입력으로 사용되는 경우, 명령어 라인 환경에서 자주 사용된다. ls는 디렉토리 목록을 생성한다. 특히 긴 디렉토리 목록의 경우 출력은 여러 화면에 걸쳐 스크롤 될 것이다. more 명령어는 한번에 한 화면 단위로 출력되도록 출력을 관리한다. ls와 more의 명령어 사이에 파이프를 구축하면 ls의 출력이 more의 입력으로 전달되도록 하여 사용자가 긴 디렉토리 목록을 한 화면 단위로 볼 수 있게 한다.