JVM Green Thread vs Native Thread
* 커널
커널은 운영체제의 핵심 기능을 수행하고 시스템 전반을 관리 감독하며 하드웨어와 관련된 작업을 수행한다.
간단히 말하면 커널은 운영체제의 메모리에 상주하고있는 프로그램이라 할 수 있다.
프로세스가 실행되는 동안 커널모드와 유저모드를 반복적으로 넘나든다.
*커널모드
커널모드에서 실행되는 코드는 시스템 자원에 대한 접근 권한이 허용되며 하드웨어 제어, 메모리 관리, 인터럽트 처리 등에 중요 작업이 수행된다.
* 유저모드
우리 프로그램이나 소스는 유저모드에서 실행되며,
실행중에 인터럽트가 발생하거나 시스템콜을 호출하게되면 커널모드로 전환된다.
커널모드에서 인터룹트나 시스템콜을 직접 처리한다.
* 커널모드와 유저모드를 나누는 이유는 시스템을 보호하기 위해서이다. 우리가 작성한 프로그램이 다른 프로세스에 접근하고 자원을 사용하게 되면 안되고, 하드웨어와 관련된 부분 또한 안정적으로 실행하기 위해 커널모드를 통해 하드웨어를 사용하고 시스템 기능을 사용함으로 써 시스템이 안정적으로 동작할 수 있도록 한다.
*컨텍스트 스위칭
when? 주어진 타임슬라이스를 다 사용했을경우, IO작업을해야하는 경우, 인터룹트 시 발생한다.
what? 인터룹트나 시스템콜로 인해 현재 실행되던 프로세스를 중단하고 다음 실행할 프로세스 사이의 교체를 의미하며
교체를 위해 실행중인 프로세스의 상태 정보를 저장하고 다음 실행할 프로세스의 상태 정보를 읽어오는 작업
* PCB ( Process Control Block)
위의 컨텍스트 스위칭이 발생하면서 프로세스의 중요 정보를 저장해놓을 필요가 생겼다.
즉, 프로세스들의 정보가 저장되있는 곳이다.
각 프로세스가 생성될 때마다 고유의 PCB가 생성되고 프로세스가 완료되면 PCB는 제거된다.
프로세스 식별자,
프로세스 상태(생성/준비/실행/대기/완료 상태),
다음 실행할 명령어의 주소를 가르키는 프로그램 계수기,
CPU 레지스터 일반 레지스터 정보,
CPU 스케줄링정보(우선순위,CPU점유시간 등),
메모리관리 정보(프로세스주소),
프로세스 계정 정보,
입출력 상태 정보,
포인터(프로세스가 위치한 메모리주소에 대한 포인터 등)
* 인터룹트
인터룹트는 시스템에서 발생한 다양한 종류의 이벤트 혹은 그런 이벤트를 알리는 메커니즘으로
on/off 등의 프로세스에 문제가 생겼을때,
디바이스 키보드/마우스 등의 IO 작업이 완료되었을때
0으로 나누었을 때,
처리 시간이 다 되었을 때
*시스템콜
커널에 접근하기위한 운영체제가 제공하는 인터페이스를 시스템 콜이라고 한다.
즉 응용프로그램이 시스템 콜을 통해서 제공된 기능을 수행할 수 있다.
시스템콜의 종류는 프로세스/스레드 제어, 파일 IO, 소켓통신, 장치, 프로세스 통 관련등이다.
운영체제는 커널이 제공하는 서비스를 시스템콜을 사용해야만 사용할 수 있도록 제한함으로써 컴퓨터의
자원을 보호하면서 사용자 혹은 응용프로그램에 서비스를 제공할 수 있다.
*JVM 의 시스템 콜 ?
=시스템콜이 필요한 네이티브 메소드가 호출될 경우 JVM은 커널 모드로 전환하여 운영체제에 시스템 콜을 요청함
JVM 스택프레임에 쌓고 있던 실행중인 메소드가 메모리 접근이 필요한 네이티브 메소드라면
네이티브메소드는 네이티브메소드 스택에 적재되게 된다.
JVM은 JVM Native Interface JNI를 통해 네이티브 메소드 라이브러리를 호출해 해당 메소드를 실행한다.
이때 네이티브 메소드가 운영체제에 접근할 경우 네이티브 메소드는 시스템 콜을 호출하여 운영체제에게 해당 기능을 요청한다. 시스템 콜은 OS커널에 제공하는 서비스를 실행하며
OS는 요청된 기능을 수행하고 결과를 네이티브 메소드로 반환한다.
JVM은 자바프로그램에 결과를 전달한다.
*여기서 추측? 4월5일에 질문해야겠다.
*JVM의 스레드 객체와 커널의 user-level 스레드 간의 관계 ? user-level 스레드가 jvm 스레드 객체가아닌가?
user-level 스레드와 kernel-level 스레드 간의 관계 ?
유저 스레드가 cpu에서 실행되려면 커널 스레드와 반드시 연결돼야 한다.
유저 스레드는 직접 접근하지 못하기 때문이다. cpu에서 실행되는 단위는 커널 스레드이기 때문
약간 상상인데..
저렇게 jvm>stack frame > 메모리접근이 필요한 네이티브 메소드?Y > 네이티브메소드스택적재>JNI를 통해 네이티브메소드라이브러리를 호출하고 메소드를 실행 > 이때의 네이티브메소드가 있던 그림상 thread1이 유저스레드일까? 헷갈린돠
위의 내용 기반으로 싱글코어의 CPU일 때의 파일READ를 할때 예시를 들어보자.
t1 (스레드1), t2 (스레드2)가있다.
1. t1이 cpu에서 running 중이고 t2는 ready상태이다.
2. 유저모드에서 t1이 실행되다가 파일읽기를 위한 시스템콜을 호출한다.
3. 커널모드로 전환된다.
4. 커널모드에서 t1의 cpu 상태를 PCB 에 저장하고 파일을 읽을 준비한다, t1의 상태를 running-> wating, t2를 ready->runnig 상태로로 바꿔준다.=> 컨텍스트 스위칭
5. t2가 유저모드에서 실행된다.
6. t1 파일을 읽을 준비가 되어 인터룹트가 발생된다.
7. 인터룹트 처리를 위해 커널모드로 전환된다.
8. 커널모드에서 t2의 cpu상태를 PCB에 저장한다. t1을 wating->ready상태로 전환, t2의 cpu상태를 다시 복원한다.(이런게 컨텍스트 스위칭 맞)
9. 유저모드로 전환되고 t2가 실행된다.
10. t2가 타임슬라이스를 다쓰고 인터룹트가 발생한다.(각 스레드마다 사용시간이있음)
11. 커널모드에서 t2 cpu상태가 저장되고 t1을 ready->running , t2를 running->wadting 상태로 바꿔준다.
t1의 상태를 복원한다.<= 4번에서 저장되었던
12. t1실행 파일읽는다.
이제 스레드에 대해서 알아보자.
스레드는 프로세스내에서 실행되는 흐름의 단위이다.
한 프로그램은 최소 하나의 스레드를 가지고 있으며 프로그램 환경에 따라 둘 이상의 스레드를 동시에 사용할 수 있는데 이를 멀티스레드라고한다.
커널레벨스레드와 유저레벨스레드에 대해서 알아보자.
User-Level Thread( 사용자 레벨 스레드 =유저스레드 )
스레드 개념을 프로그래밍 레벨에서 추상화한 것
커널영역의 상위에서 지원되며, 일반적으로 사용자레벨의 라이브러리를 통해 구현된다.
커널에 개입 없이 동일한 메모리 영역에서 스레드가 생성, 교체, 동기화 등 관리되므로 속도가 빠른 장점이
Kernel-Level Thread (커널 레벨 스레드=OS레벨스레드)
커널 스레드는 운영체제가 지원하는 스레드 기능으로 구현되며, 커널이 스레드의 생성 및 스케줄링 등을 관리한다.
커널 스레드는 커널이 직접 제공해주기 때문에 안정성과 다양한 기능이 제공된다.
커널이 개입하고 컨텍스트 스위칭에 따른 비용이 발생한다.
유저모드에서 커널모드로 전환이 빈번하게 이루어져 성능이 저하가 발생한다.
사용자 수준 스레드보다 생성 및 관리 속도가 느리다.
프로세스 내에 스레드 라이브러리가 없어서 커널 스레드를 스케줄 하여 매핑된 사용자 스레드를 동작시킨다.
두 유형의 차이는 누가 스레드를 제어하느냐에 있다.
사용자 수준 스레드는 스레드가 생성 된 프로세스 자체에 의해 제어되며, 커널은 프로세스 내의 사용자 스레드에 대해 알지 못한다. 커널 수준 스레드는 OS에 의해 직접 관리된다.
*JVM의 스레드 객체와 커널의 user-level 스레드 간의 관계
user-level 스레드와 kernel-level 스레드 간의 관계
유저 스레드가 cpu에서 실행되려면 커널 스레드와 반드시 연결돼야 한다.
멀티쓰레딩 모델을 알아보자.
멀티쓰레딩 모델은 아래 세가지 범주 중 하나에 속한다.
- Many-to-One
- One-to-One
- Many-to-Many
Many-to-One Model (Green Threads)
자바 1.3이전인 자바 스레드의 초기 구현은 다대일 이었다.
vm가상머신(Virtual Machine)이나 Library 등의 어플리케이션 수준에서 구현되고
사용자 공간에서 관리된다. user-level 쓰레드라고도 불린다.
동기화 및 자원 공유가 용이하여 실행시간이 단축된다.
커널 개입 없이 유저 스레드끼리의 스위칭이 발생하기 때문에 1대1모델보다 빠르다.
다대일 모델은 여러 사용자 스레드(User Thread)를 하나의 커널 스레드에(Kernel Thread) 매핑한다.
결국 실제로 동작하는 건 하나의 커널 스레드이기 때문에 멀티코어를 활용하지 못한다.
사용자 스레드 중 하나의 스레드가 block되어도 사용자 스레드 전체가 block 된다.
One-to-One Model
1:1 관계로 유저 레드와 커널 스레드를 연결한 것이다.
스레드 관리를 커널에 위임하기 때문에
이 모델의 단점은 사용자 스레드를 만들기 위해서는 커널 스레드를 만들어야 한다는 것이다.
하나의 스레드가 Blocking 되더라도 다른 스레드가 실행될 수 있기 때문에 병렬 수행에 용이하다.
많은 수의 커널 스레드는 시스템 성능에 부담을 줄 수 있기 때문에 Window 나 Linux 는 스레드 수의 증가를 제한한다.
자바 1.2 이후부터 사용 중인 방식이다.
경쟁상태(race condition) 발생 가능성이 있다.
Many-to-Many Model (Native Threads)
자바1.3 이후로 다대다 모델이다. OS단에서 관리한다.(OS수준에서 구현되고 커널 공간에서 관리)
다대다 모델(많은 유저 스레드, 많은 커널 스레드)은 멀티스레딩 기능을 더욱 확장한다.
여러 개의 사용자 스레드를 그보다 작은 수 혹은 같은 수의 커널 스레드로 멀티플렉스한다.
커널 스레드의 수는 응용 프로그램에 따라 다르다. 4코어 시스템보다 8코어 시스템에서 더 많은 커널 스레드를 할당받는다. 개발자는 필요한 만큼 사용자 스레드를 생성하고 그에 상응하는 커널 스레드가 병렬로 수행될 수 있다. 가장 높은 정확도의 동시성 처리를 제공하는 모델로, 하나의 스레드가 Blocking 되었을 때 커널은 다른 스레드의 수행을 스케쥴할 수 있다. 사용자 스레드가 하나의 커널 스레드에만 연결되는 일대일 모델로 동작하는 것도 허용한다.
단점으로 스레드 동기화 및 자원공유가 복잡해 실행시간이 증가됨
위의 솔라리스 스레드 라이브러리
말고
리눅스 운영체제에서는 posix thread Native POSIX Thread Library (NPTL) 라이브러리를 사용한다.
그러면 자바에서 new Thread 했을 때 JVM에서 그 뉴스레드가 유저 스레드와 어떻게 매핑될까 ???
jvm의 스레드는 리눅스의 스레드 하나와 매핑되는 경향이 있다.
그러면 스레드는 언제 생길까??
네이티브 스레드와 우리가 new Thread 의 타이밍은? new 할 때 일까?
아니다 run() 할때이다.
왜냐하면 스레드를 미리 만들어놓으면 스레드는 잠깐 멈추지 못한다. 즉 소멸만 할 수 있다. 중간에 몇초 쉬라고 할 수 없는 노릇이다.
제어권이 jvm이 아닌 커널에 있기 때문이다. 커널이 스레드를 관리하기때문이다.
스레드의 단점이기도하다. 제어권이 없기 때문에 네이티브 스레드가 자기들 끼리 돌아가기 때문에
우리가 말하는 동시성 이슈도 마찬가지로 제어할 수 없으니 나타난 것이다.
참고
https://www.youtube.com/watch?v=v30ilCpITnY
https://zu-techlog.tistory.com/122#%EC%-D%B-%ED%--%B-%EB%-F%BD%ED%-A%B--Interrupt-https://kdhyo98.tistory.com/103