인프라/운영체제 2019. 7. 27. 22:29

2019/07/27 - [운영체제] - 운영체제 - 프로세스(Process)란? 프로세스상태,Context Switching

 

운영체제 - 프로세스(Process)란? 프로세스상태,Context Switching

프로세스의 개념 프로세스는 다양한 정의가 있다. 실행 중인 프로그램 비동기적 행위 실행 중인 프로시저 실행 중인 프로시저의 제어 추적 운영체제에 들어 있는 프로세스 제어 블록(PCB) 프로세서에 할당하여 실..

coding-start.tistory.com

 

쓰레드(Thread)란 간단히 말해 프로세스 내에서 실행되는 실행 단위이다. 프로세스는 이러한 쓰레드를 한 개 이상으로 나눌 수 있다. 쓰레드는 프로그램 카운터와 스택 포인터 등을 비롯한 쓰레드 실행 환경 정보(Context 정보), 지역 데이터, 스택을 독립적으로 가지면서 코드, 전역 데이터, 힙을 다른 쓰레드와 공유한다.

그림을 보면 프로세스 내에서 쓰레드는 별도의 Stack(+ 스레드 실행 환경 정보, 지역데이터, 레지스터 등)을 할당받고 Code, Data, Heap 영역은 같은 프로세스 내의 다른 쓰레드와 공유한다.

 

프로세스 하나에 포함된 쓰레드들은 공동의 목적을 달성하려고 병렬로 수행한다. 이러한 쓰레드를 이용하면 아래와 같은 이점들이 있다.

  • 사용자 응답성 증가
  • 프로세스 자원과 메모리 공유가능
  • 경제성이 좋음(프로세스 컨텍스트 스위칭보다 쓰레드 컨텍스트 스위칭이 오버헤드가 더 적다.)
  • 다중 처리로 성능과 효율 향상

현대 시스템은 대부분 다중 쓰레드 운영체제이다. 다중 쓰레드는 프로그램 하나를 여러 실행 단위로 쪼개어 실행한다는 측면에서 다중 처리(다중 프로세싱)와 의미가 비슷하다. 하지만 동일 프로세스의 쓰레드는 자원을 공유하므로 자원 생성과 관리의 중복성을 최소화하여 실행 능력을 향상시킬 수 있다. 그리고 각 쓰레드는 커널이 개입하지 않고도 독립적으로 실행할 수 있어 서버에서 많은 요청을 효과적으로 처리할 수 있다.

 

이러한 쓰레드의 특성 때문에 프로세스보다 쓰레드를 생성하는 것이 더 빠르고, 동일한 프로세스에 있는 쓰레드 간의 교환이나 쓰레드 종료도 훨씬 빠르다.

 

보통 프로세스를 생성하면 해당 프로세스의 쓰레드도 함께 생성한다. 단, 쓰레드 생성에서는 운영체제가 부모 프로세스와 공유할 자원을 초기화할 필요가 없다. 해당 프로세스가 스택과 레지스터를 직접 제공하기 때문이다. 그러므로 프로세스의 생성과 종료보다는 오버헤드가 훨씬 적다. 여기서 스레드의 장점 하나는 쓰레드 한개가 대기 상태로 변할 때 전체 프로세스를 대기 상태로 바꾸지 않는다는 것이다. 실행 상태의 쓰레드가 대기 상태가 되면 다른 쓰레드를 실행할 수 있다. 그러나 프로세스와 달리 서로 독립적이지는 않다. 프로세스 하나에 있는 전체 쓰레드는 프로세스의 모든 주소에 접근할 수 있으므로 쓰레드 한 개가 다른 쓰레드의 스택을 읽거나 덮어쓸 수 있다.

 

쓰레드 제어 블록(TCB)

프로세스가 프로세스 제어 블록에 정보를 저장하듯이 쓰레드도 쓰레드 제어 블록에 정보를 저장한다. 그런데 프로세스는 쓰레드를 한 개 이상 가질수 있으므로, 결국 프로세스 제어블록은 스레드 제어 블록의 리스트를 가리킨다.

 

쓰레드의 구현

쓰레드는 운영체제에 따라 다양하게 구현할 수 있는데, 대부분 다음 세 가지 형태로 구현한다. 사용자 수준 쓰레드는 쓰레드 라이브러리를 이용하여 작동하는 형태이고, 커널 수준 쓰레드는 커널(운영체제)에서 지원하는 형태이다. 그리고 이 둘을 혼합한 형태가 혼합형 쓰레드이다.

 

  1. 사용자 수준 쓰레드 - 다대일 매핑
  2. 커널 수준 쓰레드 - 일대일 매핑
  3. 혼합형 쓰레드 - 다대다 매핑

 

<사용자 수준 쓰레드>

사용자 수준 쓰레드는 사용자 영역의 쓰레드 라이브러리로 구현하고, 쓰레드와 관련된 모든 행위를 사용자 영역에서 하므로 커널이 쓰레드의 존재를 알지 못한다. 여기서 쓰레드 라이브러리는 쓰레드의 생성과 종료, 쓰레드 간의 메시지 전달, 쓰레드의 스케줄링과 컨텍스트 등 정보를 보관한다. 

사용자 수준 쓰레드에서는 쓰레드 교환에 커널이 개입하지 않아 커널에서 사용자 영역으로 전환할 필요가 없다. 그리고 커널은 쓰레드가 아닌 프로세스를 한 단위로 인식하고 프로세서를 할당한다. 다수의 사용자 수준 쓰레드가 커널 수준 쓰레드 한 개에 매핑되므로 다대일 쓰레드 매핑이라고 한다. 

 

 

사용자 수준 쓰레드의 장점

  • 이식성이 높음 : 커널에 독립적으로 스케쥴링을 할 수 있어 모든 운영체제에 적용할 수 있다.
  • 오버헤드가 적음 : 스케쥴링이나 동기화를 하려고 커널을 호출하지 않으므로 커널 영역으로 전환하는 오버헤드가 줄어든다.
  • 유연한 스케쥴링이 가능 : 커널이 아닌 쓰레드 라이브러리에서 쓰레드 스케줄링을 제어하므로 응용 프로그램에 맞게 스케줄링할 수 있다.

사용자 수준 쓰레드의 단점

  • 시스템의 동시성을 지원하지 않음 : 쓰레드가 아닌 프로세스 단위로 프로세서를 할당하여 다중 처리 환경을 갖춰도 쓰레드 단위로 다중 처리를 하지 못한다. 동일한 프로세스의 쓰레드 한개가 대기 상태가 되면 이 중 어떤 쓰레드도 실행하지 못한다.
  • 확장에 제약이 따름 : 커널이 한 프로세스에 속한 여러 쓰레드에 프로세서를 동시에 할당할 수 없어 다중 처리 시스템에서 규모를 확장하기가 어렵다.
  • 쓰레드 간 보호 불가능 : 쓰레드 간 보호에 커널의 보호 방법을 사용할 수 없다. 쓰레드 라이브러리에서 쓰레드 간 보호를 제공해야 프로세스 수준에서 보호가 가능하다.

 

<커널 수준 쓰레드>

커널 수준 쓰레드는 사용자 수준 쓰레드의 한계를 극복하는 방법으로, 커널이 쓰레드와 관련된 모든 작업을 관리한다. 한 프로세스에서 다수의 쓰레드가 프로세서를 할당받아 병행으로 수행하고, 쓰레드 한 개가 대기 상태가 되면 동일한 프로세스에 속한 다른 쓰레드로 교환이 가능하다. 이때도 커널이 개입하므로 사용자 영역에서 커널 영역으로 전환이 필요하다. 커널 수준 쓰레드는 사용자 수준 쓰레드와 커널 수준 쓰레드가 일대일로 매핑된다. 따라서 사용자 수준 쓰레드를 생성하면 이에 대응하는 커널 쓰레드를 자동으로 생성한다.

 

커널 수준 쓰레드는 커널이 직접 스케줄링하고 실행하기에 사용자 수준 쓰레드의 커널 지원이 부족한 문제를 해결할 수 있지만, 커널이 전체 프로세스와 쓰레드 정보를 유지하여 오버헤드가 커진다. 대신 커널이 각 쓰레드를 개별적으로 관리할 수 있어 동일한 프로세스의 쓰레드들을 병행으로 수행할 수 있다. 동일한 프로세스에 있는 쓰레드 중 한 개가 대기 상태가 되더라도 다른 쓰레드를 실행할 수 있다는 장점이 있다. 하지만 이 과정에서도 커널 영역으로 전환하는 오버헤드가 발생하고 스케쥴링과 동기화를 하려면 더 많은 자원이 필요하다.

 

<혼합형 쓰레드>

혼합형 쓰레드는 사용자 수준 쓰레드와 커널 수준 쓰레드를 혼합한 구조이다. 이는 시스템 호출을 할 때 다른 쓰레드를 중단하는 다대일 매핑의 사용자 수준 쓰레드와 쓰레드 수를 제한하는 일대일 매핑의 커널 수준 쓰레드 문제를 극복하는 방법이다. 즉, 사용자 수준 쓰레드는 커널 수준 쓰레드와 비슷한 경량 프로세스에 다대다로 매핑되고, 경량 프로세스는 커널 수준 쓰레드와 일대일로 매핑된다. 결국 다수의 사용자 수준 쓰레드에 다수의 커널 쓰레드가 다대다로 매핑된다. 

 

 

프로세스 하나에는 경량 프로세스가 하나 이상 있고, 경량 프로세스에는 이에 대응하는 커널 쓰레드가 한 개 있다. 그리고 자원과 입출력 대기를 하려고 경량 프로세스 단위로 대기하므로 프로세스는 입출력을 완료할 때까지 대기할 필요가 없다.(다른 경량 프로세스는 대기상태가 아니기 때문) 어떤 경량 프로세스가 입출력 완료를 기다리더라도 동일한 프로세스에서 다른 경량 프로세스를 실행할 수 있기 때문이다.

 

[쓰레드 풀링]

시스템이 관리하는 쓰레드의 풀을 응용 프로그램에 제공하여 스레드를 효율적으로 사용할 수 있게 하는 방법이다. 즉, 미리 생성한 스레드를 재사용하도록 하여 스레드를 생성하는 시간을 줄여 시스템의 부담을 덜어준다. 또 동시에 생성할 수 있는 스레드수를 제한하여 시스템의 자원 소비를 줄여서 응용 프로그램의 전체 성능을 일정 수준으로 유지한다.

posted by 여성게
:

Java - ThreadLocal 이란? 쓰레드로컬 사용법!



ThreadLocal(쓰레드로컬)이란?





쓰레드로컬이란 간단히 얘기하면 하나의 스레드의 작업 흐름동안에 전역변수처럼 무엇인가를 저장하여 사용할수 있다.
일반 변수의 수명은 특정 코드 블록(예, 메서드 범위, for 블록 범위 등) 범위 내에서만 유효하다.

{
    int a = 10;
    ...
   // 블록 내에서 a 변수 사용 가능
}
// 변수 a는 위 코드 블록이 끝나면 더 이상 유효하지 않다. (즉, 수명을 다한다.)


반면에 ThreadLocal을 이용하면 쓰레드 영역에 변수를 설정할 수 있기 때문에, 특정 쓰레드가 실행하는 모든 코드에서 그 쓰레드에 설정된 변수 값을 사용할 수 있게 된다. 아래 그림은 쓰레드 로컬 변수가 어떻게 동작하는 지를 간단하게 보여주고 있다. 



위 그림에서 주목할 점은 동일한 코드를 실행하는 데, 쓰레드1에서 실행할 경우 관련 값이 쓰레드1에 저장되고 쓰레드2에서 실행할 경우 쓰레드2에 저장된다는 점이다.



ThreadLocal의 기본 사용법

ThreadLocal의 사용방법은 너무 쉽다. 단지 다음의 네 가지만 해 주면 된다.
  1. ThreadLocal 객체를 생성한다.
  2. ThreadLocal.set() 메서드를 이용해서 현재 쓰레드의 로컬 변수에 값을 저장한다.
  3. ThreadLocal.get() 메서드를 이용해서 현재 쓰레드의 로컬 변수 값을 읽어온다.
  4. ThreadLocal.remove() 메서드를 이용해서 현재 쓰레드의 로컬 변수 값을 삭제한다.

아래 코드는 ThreadLocal의 기본적인 사용방법을 보여주고 있다.

// 현재 쓰레드와 관련된 로컬 변수를 하나 생성한다.
ThreadLocal<UserInfo> local = new ThreadLocal<UserInfo>();

// 로컬 변수에 값 할당
local.set(currentUser);

// 이후 실행되는 코드는 쓰레드 로컬 변수 값을 사용
UserInfo userInfo = local.get();


위 코드만으로는 ThreadLocal이 어떻게 동작하는 지 잘 이해가 되지 않을테니, 구체적인 예제를 이용해서 ThreadLocal의 동작 방식을 살펴보도록 하겠다. 먼저 ThreadLocal 타입의 static 필드를 갖는 클래스를 하나 작성해보자.

public class Context {
    public static ThreadLocal<Date> local = new ThreadLocal<Date>();
}

이제 Context 클래스를 사용해서 쓰레드 로컬 변수를 설정하고 사용하는 코드를 작성할 차례이다. 아래는 코드의 예이다.

class A {
    public void a() {
        Context.local.set(new Date());
        
        B b = new B();
        b.b();

        Context.local.remove();
    }
}

class B {
    public void b() {
        Date date = Context.local.get();

        C c = new C();
        c.c();
    }
}

class C {
    public void c() {
        Date date = Context.local.get();
    }
}

위 코드를 보면 A, B, C 세 개의 클래스가 존재하는데, A.a() 메서드를 호출하면 다음 그림과 같은 순서로 메서드가 실행된다.


위 그림에서 1~10은 모두 하나의 쓰레드에서 실행된다. ThreadLocal과 관련된 부분을 정리하면 다음과 같다.

  • 2 - A.a() 메서드에서 현재 쓰레드의 로컬 변수에 Date 객체를 저장한다.
  • 4 - B.b() 메서드에서 현재 쓰레드의 로컬 변수에 저장된 Date 객체를 읽어와 사용한다.
  • 6 - C.c() 메서드에서 현재 쓰레드의 로컬 변수에 저장된 Date 객체를 읽어와 사용한다.
  • 9 - A.a() 메서드에서 현재 쓰레드의 로컬 변수를 삭제한다.

위 코드에서 중요한 건 A.a()에서 생성한 Date 객체를 B.b() 메서드나 C.c() 메서드에 파라미터로 전달하지 않는다는 것이다. 즉, 파라미터로 객체를 전달하지 않아도 한 쓰레드로 실행되는 코드가 동일한 객체를 참조할 수 있게 된다.



ThreadLocal의 활용

ThreadLocal은 한 쓰레드에서 실행되는 코드가 동일한 객체를 사용할 수 있도록 해 주기 때문에 쓰레드와 관련된 코드에서 파라미터를 사용하지 않고 객체를 전파하기 위한 용도로 주로 사용되며, 주요 용도는 다음과 같다.

  • 사용자 인증정보 전파 - Spring Security에서는 ThreadLocal을 이용해서 사용자 인증 정보를 전파한다.
  • 트랜잭션 컨텍스트 전파 - 트랜잭션 매니저는 트랜잭션 컨텍스트를 전파하는 데 ThreadLocal을 사용한다.
  • 쓰레드에 안전해야 하는 데이터 보관

이 외에도 쓰레드 기준으로 동작해야 하는 기능을 구현할 때 ThreadLocal을 유용하게 사용할 수 있다.

ThreadLocal 사용시 주의 사항


쓰레드 풀 환경에서 ThreadLocal을 사용하는 경우 ThreadLocal 변수에 보관된 데이터의 사용이 끝나면 반드시 해당 데이터를 삭제해 주어야 한다. 그렇지 않을 경우 재사용되는 쓰레드가 올바르지 않은 데이터를 참조할 수 있다.


▶︎▶︎▶︎자바캔


posted by 여성게
: