시스템 프로그래밍 11장 - 쓰레드의 이해

김주현·2021년 10월 12일
0

시스템 프로그래밍

목록 보기
11/21

01 . 쓰레드란 무엇인가?

-멀티 프로세스 기반 프로그램

위 그림에서 이니셜 P는 프로세스를 의미한다. 일반 사용자는 서버 접속용 클라이언트 프로그램을 이용해서 서버에 접속하고, 특정 서비스를 요구한다. 서버는 이러한 요구를 처리하기 위해사 요청이 있을 때마다 자식 프로세스를 생성한다. 동시에 둘 이상의 접속자에게 원할한 서비스를 제공해주기 위함이다.

-멀티 프로세스 운영체제 기반 프로그램의 문제점과 새로운 제안

하나의 프로그램이 둘 이상의 프로세스 생성을 요구한다면 이는 정말로 부담스로운 일이다. 많은 수의 프로세스 생성은 빈번한 컨텍스트 스위칭으로 이어져 성능에 영향을 미치기 떄문이다.

저장하고 복원하는 컨텍스트 정보의 개수를 줄여주면 컨텍스트 스위칭에 소요되는 시간을 줄일 수 있다!

컨텍스트 정보란 프로세스의 상태 정보와 관련이 있으므로 결국은 프로세스 상태 정보를 줄여야 한다는 결론이 나온다.

컨텍스트 스위칭이 필요한 가장 근본적인 이유는 프로세들이 서로 완전히 독립되기 때문이다. 부모 자식 관게가 있다 하더라도, 일단 생성되고 나면 완전히 별개의 프로세스이다.

그렇다면 A프로세스와 B 프로세스가 완전히 별개가 아닌 50%정도만 별개이고, 나머지 50 %는 공유하는 구조라면, 컨텍스트 스위칭 발생 시 저장하고 복원하는 정보도 반으로 줄지 않을까? 이러한 생각을 기반으로 쓰레드라는 것이 탄생하기에 이른다.

-해결책 쓰레드

프로세스는 완전히 독립된 두 개의 프로그램 실행을 위해서 사용된다. 그러나 쓰레드는 하나의 프로그램 내에서 둘 이상의 프로그램 흐름을 만들어 내기 위해서 디자인된 것이다. 그리고 프로세스와 달리 쓰레드간에는 공유하는 상태 정보들이 있다. 이것이 쓰레드의 컨텍스트 스위칭을 빠르게 하는 요인이된다.

1.쓰레드는 하나의 프로그램 내에서 여러 개의 실행 흐름을 두기 위한 모델이다.
2.쓰레드는 프로세스처럼 완벽히 독립적인 구조가 아니다. 쓰레드들 사이에는 공유하는 요소들이 있다.
3.쓰레드는 공유하는 요소가 있는 관계로 컨텍스트 스위칭에 걸리는 시간이 프로세스보다 짧다.

-메모리 구조 관점에서 본 프로세스와 쓰레드

프로세스와 쓰레드의 차이점을 이해하는 가장 좋은 방법은 메모리 구조 관점에서 비교하는 것이다. 이것을 통해서 쓰레드 기반 프로그래밍 시 고려해야할 특성들도 도출해 낼 수 있다.

자식 프로세스가 생성되고 난 다음에는 모든 것이 부모 프로세스와 독립적이다. 물론 부모 프로세스는 자신이 가지고 있는 핸들 테이블을 상속하는 등 부모로서의 역할에 충실하지만 이는 어디까지나 새성되는 과정에서 발생하는일이다.
메모리 구조상에서 보면 생성 이후에는 아무런 관계가 없다.(이떄문에 프로세스간에 데이터를 주고 받기 위해서 IPC라는 메커니즘이 필요)

쓰레드를 생성할 때마다, 해당 쓰레드만을 위한 스택을 생성할 뿐 그이외의 영역은 부모 프로세스 영역을 공유하고 있다.

쓰레드의 특성 1 : 쓰레드마다 스택을독립적으로 할당해준다

프로세스와 마찬가지로 쓰레드도 독립적으로 스택을 할당한다. 스택은 함수 호출 시 전달되는 인자 , 되돌아갈주소값 및 함수 내에서 선언하는 변수 등을 저장하기 위한 메모리 공간이다. 간단히 말해서 함수 호출 시 필요한 메모리 영역이다. 따라서 이 메모리 공간이 독립적이라는 뜻은 추가적인 실행 흐름을 만들 수 있다는 의미가 된다. 다시 말하면 실행 흐름의 추가를 위한 최소 조건이 독립된 스택의 제공이다.

쓰레드의 특성 2 : 코드 영역을 공유한다.

프로세스의 경우 완전히 독립된 구조이기 때문에 다음과 같은 형태의 실행이 불가능하다.

위 그림에서 프로세스 A는 사칙연산에 관련된 함수밖에 없고 연산결과를 출력하기위해 B에있는 print함수를 호출하려고 하지만 이는 불가능하다.
그러나 쓰레드는 자신을 생성한 프로세스가 가지고 있는 함수를 호출할 수 있다. 왜냐하면 코드 영역을 공유하기 떄문이다.


위 그림은 코드 영역을 공유한다는 것에 대한 본질을 설명하고 있다.

프로세스이므로 프로그램 흐름의 첫 시작인 main함수가 있다. 그리고 이 영역에 또 다른 실행 흐름을 의미하는 쓰레드의 main함수가 있다. 이 쓰레드 main 함수는 코드 영역에 존재하는 모든 함수를 호출할 수 있다. 프로세스 main 함수처럼 말이다.

쓰레드가 있는 프로그램은 시작과 동시에 3개의 흐름을 형성하는 것이 아닌 쓰레드 생성을 위해 마련된 함수호출을 통해서 쓰레드를 생성하게 된다.

쓰레드의 특성 3 : 데이터 영역과 힙을 공유한다

IPC가 필요 없어졌다. 쓰레드간에 힙과 데이터 영역을 공유하기떄문이다. 따라서 힙이나 데이터 영역에 메모리 공간을 할당해서 서로 통신하는 것이 가능하다. IPC와 같은 복잡한 통신기법은 필요가 없다.

전역변수와 malloc 함수를 통해서 동적 할당된 메모리 공간은 공유가 가능하다.

데이터 영역과 힙의 공유가 장점만 있는 것은 아니더. 메모리 영역을 공유하다 보면 문제 가 발생할수도 있다.

컨텍스트 스위칭이 빨라진 쓰레드

pc 는 프로그램 실행 흐름과 관련이 있는 것이다. 쓰레드별로 main 함수를 독립적으로 가지고 있고, 또 함수의 호출도 독립적으로 진행되기 때문에 쓰레드별로 main 함수를 독립적으로 가지고 있고, 또 함수의 호출도 독립적으로 진행되기 때문에 쓰레드별로 현재 pc가 가져야 할 갑슨 다르기 마련이다. 코드 영역을공유함으로 인해서 호출할 수 있는 함수들을 공유하게 되는 것이지, pc 정보까지 공유하는 것은 아니다. 따라서 이부분은 프로세스의 컨텍스트 스위칭과 별발 다를바 없다.

fp - 스택은 어차피 쓰레드별로 독립적이다. 따라서 스택의 정보를 저장하는 데 사영되는 fp와 sp가 공유될리 만무하다.

범용 레지스터 또한 연산을 위해서 임시적으로 사용되는 것이 보통이고 연산이라는 것 자체가 프로그램의 흐름에 따라 진행되는 것이므로 이것들도 공유한다는 것이 그리 쉬운 일은 아니다.

결국 몇 십배의 속도 향상은 레지스터에서만 답을 찾으면 곤란하다.

메모리 관리에서 이야기를 풀어야함.

캐쉬는 CPU에서 한 번 이상 읽어 들인 메인 메모리의 데이터를 저장하고 있다가 CPU가 다시 그 메모리에 저장된 데이터를 요구할 때, 메인 메모리의 통하지 않고 바로 값을 전달해주는 용도로 사용된다,

프로세스 컨텍스트 스위칭은 캐쉬정보를 다지우고 다시 쌓게함
하지만 쓰레드라면 캐쉬를 비울필요가 없다. -> 큰 속도향상을 가져옴

Windows에서의 프로세스와 쓰레드

Windows입장에서 프로세스는 단순히 쓰레드를 담는 상자에 지나지 않는다.

사실 Windows 운영체제에 있어서 프로세스는 상태를 지니지 않는다.
상태를 지니는 거은 프로세스가 아니라 쓰레드이다. 뿐만 아니라. 스케줄러가 실행의 단위로 선택 하는 것도 프로세스가 아닌 쓰레드이다.

즉 Windows에 있어서 실행의 중심에 있는 것은 프로세스가 아닌 쓰레드이다.

대상이 프로세스에서 쓰레드로 바뀌었을 뿐 달라진것은 없다.

그래도 서로 다른 프로세스 내에 존재하는 쓰레드들 사이에서 발생하는 컨텍스트 스위칭은 존재함.

Window에서는 쓰레드가 존재하지 않는 프로세스란 있을 수 없다.
무조건 main 쓰레드가 생김

02 . 쓰레드 구현 모델에 따른 구분

  • 커널 레벨 쓰레드와 유저 레벨 쓰레드

첫 번째 경우로, 쓰레드를 생성해 주는 대상은 커널일 수 있다. 이러한 경우 운영체제가 제공하는 시스템 함수 호출을 통해서 쓰레드 생성을 요구해야 한다.

프로그래머 요청에 따라 쓰레드를 생성 및 스케줄링하는 주체가 커널인 경우, 이를 가리켜 커널 레벨 쓰레드라 한다. 커널 레벨에서 쓰레드가 지원된다는 뜻이다.

위 그림은 커널 레벨 쓰레드의 특성을 보여주고 있다. 그림에 나타난 유저 영역(User 영역)은 사용자에 의해서 할당되는 메모리 공간을 의미한다.

프로세스에 메모리공간이 할당되면 일부 영역은 프로그램 코드를 올리는데 사용됨. 이렇게 프로그램이 동작하기 위해 사용되는 메모리 공간을 가리켜 유저 영역이라 한다.

간단히 말해서 코드,데이터,스택,힙 영역을 가리켜 유저영역이라 한다.

커널영역 : 프로세스에게 할당된 총 메모리 공간 중에서 유저 영역을 제외한 나머지 영역
운영체제역시 메모리에 올라가야하고 또 일반 프로그램 처럼 실행되는 과정에서 변수 선언도 하고 메모리를 동적할당하기도 한다. 이렇게 운영체제라는 하나의 소프트웨어를 실행 시키기 위해서 필요한 메모리 공간을 커널 영역이라 한다.

유저영역과 커널영역을 분리하지 않으면 관리하는 측면에서 엄청난 곤란을 겪을수 있음

그림으로 돌아가서. 쓰레드에게 일을 시키기 위한 프로그램 코드는 프로그래머가 개발하므로 쓰레드 A,B,C의 실행코드는 유저 영역에 존재할 것이다. 그러나 스케줄러와 쓰레드 정보(스케줄링을 하는데 필요한 쓰레드 정보)는 커널 영역에 존재한다. 이것이 바로 커널 레벨 쓰레드의 유형이다.

두 번째 경우로, 유저 레벨 쓰레드 모델이다.

커널에서 쓰레드 기능을 지원하지 않을 때 생각해 볼 수 있는 것이 유저 레벨 쓰레드이다.

커널에서 제공하는 기능이 아미므로 당연히 실행 시 유저 영역에서 실행된다. 그래서 유저 레벨 쓰레드라 한다.

유저 레벨 쓰레드와 커널 레벨 쓰레드는 기능의 제공 주체가 누구냐에 달려있다.

그림은 유저 레벨 쓰레드 모델을 보여준다. 쓰레드를 지원하지 않기 때문에 스케줄러가 스케줄링 하는 대상은 프로세스이다.그리고 쓰레드를 스케줄링 하는 스케줄러는 유저 영역에서 실행된다.

아래쪽 커널영역을 보면, 커널에는 쓰레드에 대한 아무런 정보도 존재하지 않음을 알 수 있다. 유저 레벨 쓰레드 모델을 적용할 경우, 운영체제는 쓰레드의 존재를 알지도 확인하지도 못한다.

커널 모드와 유저 모드

"메모리는 활용 대상에 따라서 유저 영역과 커널 영역으로 나뉜다. 유저 졍역은 사용자가 구현한 프로그램 동작 시 사용하게 되는 메모리 영역이고, 커널 영역은 운영체제 동작 시 사용하게 되는 메모리 영역이다. 그리고 커널이 쓰레드를 지원할 경우 쓰레드 관리가 커널 영역에서 이뤄지기 때문에 커널 레벨 쓰레드 모델이라 하고, 커널이 지원하지 않을 경우에 라이브러리를 통해서 제공 받아야 하는데 이러한 경우에는 유저 영역에서 쓰레드의 관리가 이뤄지기 때문에 유저 레벨 쓰레드 모델이라 한다."

일반적인 프로그램은 기본적으로 유저 모드에서 동작한다,그러다가 Windows 커널이 실행되어야 하는 경우에는 커널 모드로의 전환이 일어난다. 다시 말하면, 커널 영역에서 실행이 이뤄져야 할 경우에는 커널 모드로의 전환이 일어난다.

ex) 프로세스들이 실행 중일때 정해진 타임 슬라이스가 지나서 스케줄러 동작하려할때 스케줄러는 커널의 일부이기때문에 커널모드로의 전환이 일어남

유저모드 : 프로세스가 유저모드에서 동작할때 커널영역으로의 접근이 금지

운영체제에서 제공하는 시스템 함수들 중 일부는 호출 시 커널모드로 전환을 요구할것이고 "이러한 모드의 전환(커널 모드 유저모드)은 시스템에 부담을 주는 일이다"

그리고 커널모드와 유저모드는 윈도우즈 운영체제가 아닌 프로세서이다. 즉 메모리 보호 기능이 CPU에 달려 있다.

  • 커널 레벨 쓰레드와 유저 레벨 쓰레드의 장점 및 단점

[커널 레벨 쓰레드의 장점 및 단점]

장점 : 커널에서 직접 제공해 주기 때문에 안전성과 다양한 기능성이 제공된다.

단점 : 커널에서 제공해 주는 기능이기 때문에 유저모드에서 커널 모드로의 전환이 빈번하게 일어난다. 따라서 이는 성능의 저하로 이어지게 된다.

[유저 레벨 쓰레드의 장점 및 단점]

장점 : 커널은 쓰레드의 존재조차 모른다. 오로지 유저 모드로 동작하기 때문에 유저 모드에서 커널 모드로의 전환이 필요 없다. 때문에 성능이 좋다.

단점 : 하나의 프로세스 내에 3개의 쓰레드 A,B,C가 있다고 할때, A쓰레드가 시스템 함수를 호출했는데 커널에 의해서 블로킹 되었을떄 BC도 실행되지않음

운영체제는 프로세스의 존재만 알지 쓰레드의 존재를 모르기때문 때문에 A쓰레드가 속해있는 프로세스 전부가 블로킹되는 문제를 안고있다. 이문제를 해결하기 위한 방법들이 있지만 결국은 프로그래밍하기 어려워지고 커널 레벨 쓰레드에 비해서 결과 예측이 어렵다

속도의 향상이라는 장점이있기때문에 Windows 개발자들과 달리 Linux 개발자들은 라이브러리를 통한 유저 레벨 쓰레드를 활용하기도 한다.

이것만은 알고 갑시다.

1.프로세스 쓰레드의 차이점
프로그램의 실행 관점에서 이해하는 것도 중요하지만, 메모리 관점에서 이 둘의 차이점을 이해는것도 중요하다. 프로세스는 메모리를 공유하지 않지만, 프로세스 내에 존재하는 둘 이상의쓰레드들은 스택을 제외한 나머지 메모리 공간을 공유한다.

  1. 커널 영역 vs 유저 영역

메모리 공간은 커널 영역과 유저 영역으로 나뉜다. 커널 영역은 커널이 올라가 있으며, 커널의 실행을 위한 메모리 영역이고, 유저 영역은 운영체제 이외의 프로그램이 올라가 있으며, 이 프로그램들의 실행을 위한 메모리 영역이다.

  1. 커널 모드 vs 유저 모드

커널 영역의 보호를 위해서 커널 모드와 유저 모드라는 것을 정의하고 있다. 일반적인 응용 프로그램이 실행될 떄 시스템은 유저 모드 상태에 있다. 이 경우 제한된 영역의 메모리 접근만 허용한다. 커널 영역을 보호하기 위해서다. 그러나 커널 모드에서는 메모리 전영역의 접근을 허용한다. 따라서 커널은 커널 모등에서 동작한다.

  1. 커널 레벨 쓰레드 vs 유저 레벨 쓰레드

쓰레드를 지원하는 운영체제의 기능을 통해서 생성된 쓰레드를 가리켜 커널 레벨 쓰레드라 하고 라이브러리 형태로 제공되는 기능을 통해서 생성된 쓰레드를 가리켜 유저 레벨 쓰레드라 한다.

0개의 댓글