OS - 프로세스 2

Thread

스레드에 배우기 전 프로세스에 대해 다시 알아보자

os_4_1

그림처럼 프로세스는 메모리에 저장되는 code, data, stack, PCB로 구성된다.

만약 웹브라우저를 여러 개 띄울 때 마다 각각 하나의 프로세스로 메모리에 올릴경우
똑같은 내용의 code 부분이 중복으로 메모리에 여러 개 올라갈 것이다.
data 영역 관리도 힘들 것이다.

메모리 낭비! 그래서 만들어 진 것이 Thread 이다.

Thread 를 Light Weight Process 라고도 한다.

os_4_2

프로세스가 사용하는 각종 자원들 data, code 을 여러 개의 스레드가 공유하고 있다.

이런 공유 부분은 Task 라고 한다. 스레드가 여러 개라도 Task 는 1개이다.

stack, PC, register 같은 건 같은 프로세스라도 서로 다른 코드를 실행 할 수 있기 때문에 CPU수행과 관련된 부분은 별도로 가진다.

CPU제어권이 Thread 1 에서 Thread 2 로 넘어갈 때 문맥교환보다 overhead가 작다.
그리고 다중으로 실행되는 것이 아닌 단독으로 실행되는 프로세스도 스레드가 여러 개인 것이 더 효율적이다.

프로세스에 Thread 하나를 두는 전통적인 프로세스를 Heavy Weight Process 라고 한다.

스레드를 사용하면 생기는 장점

Thread 1Blocked 상태 일 때 다른 Thread N 이 CPU를 잡고 Running 상태 가 되어 빠른 응답을 할 수 있다.

  • 웹브라우저에 네이버 검색
  • 네트워크를 통해 웹 페이지를 읽어 옴(I/O작업)
  • 이 동안에는 오래 걸리기 때문에 웹브라우저는 Blocked상태가 된다.
  • 이렇게 되면 다 읽어올 동안 아무것도 못한다? 사용자 입장에선 답답
  • 만약 여러 개의 스레드를 사용해 구성하게 되면 네이버에서 그림을 불러오는 동안 또 다른 스레드가 이미 읽어온 텍스트를 표시(빠른 응답)

같은 일을 해도 하나의 Task 안에서 수행하기 때문에 위에서 말했던 중복 메모리가 제거된다.

스레드를 사용하면 병렬성을 높일 수 있다
예를 들어 100x100 행렬을 곱해서 더하는데 CPU가 하나밖에 없으면 처음부터 끝까지 다 수행 해야겠지만 여러 개의 스레드를 사용하면 병렬적으로 결과를 빨리 구할 수 있다.

스레드를 사용하면 생기는 장점2

  1. Responsivenees (응답성)
  2. Resource Sharing (자원 공유)
  3. Economy (경제성)
    속도와 관련 있다. 프로세스 안에서 스레드 생성과 스레드 간 CPU Switching이 프로세스 생성과 스위치보다 쉬움, 이에 따른 Overhead도 적다. (30배 가량 차이)
  4. Utilization Of MP(Multi Processer) Architecture

코어가 여러 개 있는 환경에서의 장점이다. 각 스레드가 서로 다른 CPU에서 병렬적으로 실행된다.

스레드의 구현(Implementation)

  • Kernel Thread: OS커널에 의해 구성되는 Thread
  • User Thread: Library에 의해 구성되는 Thread

커널 Thread 가 여러개의 Thread를 구동시키는것을 OS 가 알고있다.
이를 염두하고 OS가 CPU스케줄링한다(ThreadCPU Switching)

User Thread 가 여러개의 Thread 를 구동시키는것을 OS 가 모른다.
유저 프로그램이 CPU제어권을 가지면 내부 Thread 하나가 I/O를 하러 가면 비동기식 입출력을 통해 다른 Thread 가 CPU를 연속으로 사용한다
즉 CPU사용권을 주면 프로세스 내부에서 알아서 실행 단위를 여러 개로 두고 관리한다

추가적으로 Real Time Thread 라고 실시간 환경에서만 특별히 구현되는 스레드도 있다(알아만 두자).

프로세스 생성

프로세스를 만드는 행위는 사용자 프로그램이 할까 운영체제가 할까?
아무래도 PID, PCB 같은 중요 데이터가 필요하기 때문에 운영체제가 하는 것이 맞다.

부모프로세스가 fork 라는 커널 함수를 사용해 OS에게 만들어 달라고 요청한다

프로세스 생성의 특징

프로세스 생성의 특징은 아래 3가지

  1. 부모프로세스가 자식프로세스를 생성
  2. 프로세스 트리 형성
  3. 프로세스는 자원을 필요로 함

부모, 자식 프로세스간 자원 공유 방식 아래 3가지

  1. 모든 자원을 공유
  2. 일부만 공유
  3. 전혀 공유하지 않음

프로세스가 만들어지면 그 프로세스만의 독자적인 주소공간이 생기는데(code, data, stack)
fork함수를 통해 자식 프로세스가 태어나면 이 code, data, stackbinary 그대로 복사한다.

전역변수, 지역변수, 현재 수행중인 함수의 return주소까지 복사하고 부모가 실행하던 PC까지 복사되기 때문에 바로 그 위치부터 실행하게 된다.

이렇게 되면 시스템에 있는 모든 프로세스가 다 똑같이 동작하게 된다.
다른 방식으로 프로세스를 돌리고 싶으면 exec(System Call) 을 통해 프로그램을 덮어씌워야 한다.

프로세스가 자발적으로 종료될 때는 exit 함수를 통해 부모프로세스에게 종료알림(output data)을 알려주고 모든 자원을 반납하고 종료한다.

부모프로세스가 자식프로세스를 강제적으로 종료시킬 때는 abort함수를 통해 종료시킨다. 이런 경우는 총 3가지가 있다.

  1. 자식이 할당된 자원의 한계치를 넘어설 경우(나쁜 짓)
  2. 자식에게 할당된 태스크(작업)가 더 이상 필요하지 않을 경우
  3. 부모프로세스 자신이 종료(exit)하는 경우, 가장 말단에 있는 자식부터 단계적으로 종료한다. 윈도우 창을 끄면 그 안에서 돌아가는 프로세스도 같이 종료 되는 것과 같은 개념.

프로세스의 시스템콜 함수

fork, exec, wait, exit 에 대해 알아보자.

fork

os_4_3

위의 fork가 시스템콜 함수로 자식을 만들어 달라고 요청하는 코드이다.

fork 함수가 실행되는 순간 복제생성이기 때문에 위의 코드와 똑같은 코드를 가진, 똑같은 문맥을 가진 프로세스가 하나 더 생기는 것이다.
문맥까지 복사하기 때문에 자식프로세스도 fork다음 코드부터 수행한다.

복제된 프로세스는 자신이 부모인지 자식인지 모른다.
자식도 방금 전 fork함수를 호출한 프로세스다 라고만 알고 있다.

구분은 fork 의 return 값인 PID로 구분한다.
부모는 양수를, 자식은 0을 return받는다.

보통 위의 코드처럼 부모와 자식이 하는 일이 다르게 구분해 놓는다.

사실 위처럼 조잡한 방식으론 자식프로세스를 운용할 수 없고 프로그램을 새로 덮어씌우는 작업, exec 시스템콜이 필요하다.

exec

os_4_4

만약 자식프로세스라면 I am child! 라는 문구를 출력하고
/bin/data라는 프로그램을 실행한다는 내용이다.

execlp 함수의 매개변수는 프로그램명 두번, 그리고 프로그램에 전달할 매개변수를 다 입력하면 제로 포인터를 입력한다.

os_4_5

execlp 는 프로그램을 덮어씌우기 때문에 위 그림처럼 execlp함수 호출 후의 코드에 대해서는 싹 잊혀 진다. 아예 다른 프로그램이 되어버리는 것이다.

wait

os_4_6

그림처럼 fork 함수가 실행되면 PID만 다른 똑같은 자식프로세스가 생성된다
왼쪽 구문을 보면 자식프로세스가 아닐 경우 else문에 wait함수가 있다.
이는 자식프로세스가 종료될 때 까지 sleep 상태(block상태)가 되는 함수이다.

자식이 종료되면 다시 ready상태로 돌아온다.

프로세스는 I/O요청 같은 하드웨어의 작업 때문에 block될 수도 있지만 공유데이터나 자식프로세스 종료 같은 소프트웨어 작업으로 인해 block될 수도 있다.

wait() = Sleep until child is done.

리눅스 커맨드에서도 wait함수를 많이 사용한다.
쉘 프로세스에서 vi라는 자식프로세스를 실행하면 vi가 종료 될 때 까지 쉘은 block상태이다.

즉 부모프로세스는 CPU경쟁에서 빠지기 위해 wait를 사용한다.

exit

딱 봐도 프로세스 종료 코드.

exit 함수는 프로그래밍 할 때 굳이 코드로 명시하지 않아도 컴파일러가 알아서 main 함수가 끝날 때 쯤 exit 함수를 넣어준다.

exit 를 직접 호출해 자발적으로 종료 될 때만 있는 것은 아니다.

부모프로세스가 강제로 자식프로세스를 종료시키거나 사람이 kill, break 명령을 통해 종료할 수도 있다.

운영체제가 경우에 따라 강제 종료 시킬 수도 있다.
exit함수도 시스템콜이기 때문에 운영체제가 실행해 준다.

프로세스 간 협력

독립적 프로세스(Independent Process)

일반적으로 프로세스는 생성되면 경쟁을하지 협력하거나 간섭하는 일은 거의 없다.
서로 OS나 컴퓨터 자원을 공유하는 경쟁관계가 원칙이다.

그리고 프로세스 각각의 주소 공간(virtual memory)을 가지고 있기 때문에 서로 접근할 수 없다.
즉 프로세스들끼리는 직접적으로 절대 협력할 수 없다.

협력 프로세스(Cooperating Process)

경우에 따라선 프로세스끼리 협력하도록 개발해야 할 수도 있다.

운영체제한테 시스템콜 호출하는 것이 다인 프로세스들이 어떻게 협력할까.
IPC(Interprocess Communication)을 통해 협력한다.

IPC(Interprocess Communication)

Message Passing

os_4_7

메시지 패싱은 말 그대로 프로세스A 가 프로세스B 에게 메시지를 전달하는 것이다.
방법은 두 가지가 있다.

  1. Direct Communication
    os_4_8
    그림처럼 직접적으로 프로세스A가 B에게 메시지를 줄 순 없고 시스템콜을 통해 OS를 매개로 전달가능하다. 이를 위해선 통신하려는 프로세스의 이름을 명시하여야 한다.

  2. Indirect Communication
    os_4_9
    Indirect 는 프로세스 이름을 명시하지 않고 자신의 Mailbox에 메시지를 넣어두면 자신과 협력하는 프로세스들이 접근해서 메시지를 꺼내가는 것이다.
    Q가 아닌 다른 프로세스가 먼저 가져갈 수도 있다.

DirectIndirect 건 시스템콜을 통해 커널의 메시지 박스에 저장하고 가져가는 방식이다.

Shared Memory

os_4_10

서로 다른 프로세스 간에도 일부 주소공간을 공유하는 방식이다.
공유메모리 또한 원칙적으론 안 되기 때문에 시스템콜을 통해 OS에게 공유메모리를 할당받는다.
공유메모리는 서로 신뢰할 수 있는 프로세스 간 사용해야 안전하다.