
스레드 : 프로세스의 코드에 정의된 절차에 따라 CPU에 작업 요청을 하는 실행 단위
CPU가 처리하는 작업의 단위는 프로세스로부터 전달 받은 스레드가 됩니다. 운영체제 입장의 작업 단위는 프로세스이고 CPU 입장의 작업 단위는 스레드입니다.
작업을 상대적인 크기순으로 나열하면 job>task>operation이고, 이를 프로세스와 스레드의 관계에 대입하면 처리(job)>프로세스(task)>스레드(operation)가 됩니다. 여러 개의 스레드가 모여 프로세스를 이루고 여러 개의 프로세스가 모여 처리가 됩니다. 여러 개의 프로세스를 모아서 한꺼번에 처리하는 방법을 일괄 작업(batch job)이라고 합니다.
프로세스는 순서를 바꿔도 큰 영향을 미치지 않지만, 스레드는 프로세스 내부에서 서로 강하게 연결되어 있습니다.
초기의 운영체제에서는 작업의 단위가 프로세스 하나뿐이었습니다. 당시에는 프로세스가 위에서부터 아래로 순차적으로 실행되었고, 프로세스의 내부 작업을 여러 개로 나누기가 어려웠습니다. 지금의 스레드 개념에 비교하면 1개의 스레드를 가진 프로세스만 있는 셈이라 프로세스와 스레드를 따로 구분할 필요가 없었습니다.
CPU 프로그래밍 기술이 발전하면서 여러 개의 코어를 가진 CPU가 생겨나 멀티스레드를 지원하기 시작했습니다. 이런 상황에서 프로세스에 하나의 스레드만 있다면 여러 코어에 나누어 동시에 작업하는 것이 불가능하므로 멀티스레드의 장점을 살릴 수 없습니다. 따라서 오늘날의 운영체제는 프로세스를 다양한 스레드로 나누어 여러 개의 코어에 배분함으로써 시스템의 효율을 높입니다.
멀티스레드 : 프로세스 내 작업을 여러 개의 스레드로 분할함으로써 작업의 부담을 줄이는 프로세스 운영 기법
멀티태스킹 : 운영체제가 CPU에 작업을 줄 때 시간을 잘게 나누어 배분하는 기법
이렇게 여러 스레드에 시간을 잘게 나누어 주는 시스템을 시분할 시스템(time-sharing system)이라고 합니다. 시분할 시스템에서 운영체제가 CPU에 전달하는 작업은 프로세스가 아니라 스레드입니다.
멀티 프로세싱 : CPU를 여러 개 사용하여 여러 개의 스레드를 동시에 처리하는 작업 환경
컴퓨터 하나에 CPU가 여러 개 설치되어 동시에 작동하는 것이 멀티 프로세싱입니다. 하나의 CPU 내 여러 개의 코어에 스레드를 배정하여 동시에 작동하는 것도 멀티 프로세싱입니다.
CPU 멀티스레드 : 한 번에 하나씩 처리해야 하는 스레드를 잘게 쪼개어 동시에 처리하는 명령어 병렬 처리 기법
운영체제가 소프트웨어적으로 프로세스를 작은 단위의 스레드로 분할하여 운영하는 기법은 멀티스레드, 하나의 CPU에서 여러 스레드를 동시에 처리하는 것은 CPU 멀티스레드라고 부릅니다.
C언어와 같은 초기의 프로그래밍 언어는 순차적으로 실행되기 때문에 프로세스로 여러 개의 작업을 동시에 처리하기가 불편했습니다. 그래서 여러 개의 작업을 동시에 처리하기 위해 fork() 시스템 호출을 사용하여 부모와 똑같은 프로세스를 생성하거나, exec() 시스템 호출로 프로세스를 전환하는 방법을 이용했습니다. 그러나 fork() 세스템 호출은 낭비 요소가 많았습니다. fork() 시스템 호출로 프로세스를 복사하면 코드 영역과 데이터 영역의 일부가 메모리에 중복되어 존재하며, 부모-자식 관계이지만 서로 독립적인 프로세스이므로 이러한 낭비 요소를 제거할 수 없습니다.
스레드는 이러한 멀티태스킹의 낭비 요소를 제거하기 위해 사용됩니다. 비슷한 일을 하는 2개의 프로세스를 만드는 대신 코드, 데이터 등을 공유하면서 여러 개의 일을 하나의 프로세스 내에서 하는 것입니다.
프로세스는 크게 정적인 영역과 동적인 영역으로 구분됩니다. 과거에는 프로세스 하나에 스레드가 하나였기 때문에 여러 작업을 하려면 fork() 시스템 호출로 여러 개의 프로세스를 만들어야 했습니다. 그러나 오늘날에는 fork() 시스템 호출 대신 하나의 프로세스에 여러 개의 스레드를 만들어 사용합니다.
응답성 향상 : 한 스레드가 입출력으로 인해 작업이 진행되지 않더라도 다른 스레드가 작업을 계속하여 사용자의 작업 요구에 빨리 응답할 수 있습니다.
자원 공유 : 한 프로세스 내에서 독립적인 스레드를 생성하면 프로세스가 가진 자원을 모든 스레드가 공유하게 되어 작업을 원활하게 진행할 수 있습니다.
효율성 향상 : 여러 개의 프로세스를 생성할 필요가 없어 불필요한 자원의 중복을 막음으로써 시스템의 효율이 향상됩니다.
다중 CPU 지원 : 2개 이상의 CPU를 가진 컴퓨터에서 멀티스레드를 사용하면 다중 CPU가 멀티스레드를 동시에 처리하여 CPU 사용량이 증가하고 프로세스의 처리 시간이 단축됩니다.
앞서 여러 개의 프로세스를 사용하면 낭비 요소가 발생하므로 멀티스레드를 사용한다고 설명했는데 이는 단점으로 작용하기도 합니다. 멀티스레드의 경우 모든 스레드가 자원을 공유하기 때문에 한 스레드에 문제가 생기면 전체 프로세스에 영향을 미칩니다. 반면 프로세스를 여러 개 만드는 방식의 경우 각 프로세스가 독립적이기 때문에 한 프로세스의 문제가 다른 프로세스에 전달되지 않습니다.
커널 스레드는 커널이 직접 생성하고 관리하는 스레드, 사용자 스레드는 라이브러리에 의해 구현된 일반적인 스레드 입니다. 사용자 스레드가 커널 스레드를 사용하려면 시스템 호출을 통해야 합니다. 커널 스레드와 사용자 스레드의 대응 방식은 1 to N 모델, 1 to 1 모델, M to N 모델로 분류됩니다.
사용자 스레드(user-level thread)는 커널이 멀티스레드를 지원하지 않을 때 사용하는 방법으로 초기의 스레드 시스템에서 사용되었습니다. 사용자 스레드는 사용자 레벨에서 라이브러리를 사용하여 스레드를 구현합니다. 이때 사용자 스레드 라이브러리는 커널이 지원하는 스케줄링이나 동기화 같은 기능을 대신 구현해 줍니다. 커널 입장에서 사용자 스레드는 하나의 프로세스처럼 보입니다.
사용자 스레드는 커널 입장에서는 일반 프로세스이지만 커널이 하는 일을 라이브러리가 대신 처리하여 여러 개의 스레드로 작동합니다. 따라서 사용자 프로세스 내에 여러 개의 스레드가 존재하지만 커널의 스레드 하나와 연결되기 때문에 1 to N 모델이라고 합니다.
사용자 스레드는 라이브러리가 직접 스케줄링하고 작업에 필요한 정보를 처리하기 때문에 문맥 교환이 필요 없습니다. 커널에서 문맥 교환을 하는 이유는 시간을 나누어 서로 다른 프로세스를 처리하기 위해서 입니다. 이처럼 사용자 스레드에서는 문맥 교환과 같은 부가적인 작업이 줄어들어 속도가 빠릅니다.
사용자 스레드의 단점은 여러 개의 스레드가 하나의 커널 스레드와 연결되기 때문에 커널 스레드가 입출력 작업을 위해 대기 상태에 들어가면 모든 사용자 스레드가 같이 대기하게 된다는 것입니다. 커널 입장에서는 하나의 프로세스이므로 일부 스레드만 대기 상태로 보낼 수 없습니다.
또 다른 단점은 한 프로세스의 타임 슬라이스를 여러 스레드가 공유하기 때문에 여러 개의 CPU를 동시에 사용할 수 없다는 것입니다. 그러나 커널 입장에서 사용자 스레드는 하나의 프로세스로 인식되므로 작업을 나눌 수 없습니다.
또한 사용자 스레드는 보안에 취약합니다. 커널 레벨에서는 공유 변수를 보호하는 장치가 있으나 이러한 서비스를 커널이 아닌 라이브러리에서 구현해야 하기 때문입니다.
커널 스레드(kernel-level thread)는 커널이 멀티스레드를 지원하는 방식으로, 하나의 사용자 스레드가 하나의 커널 스레드와 연결되기 때문에 1 to 1 모델이라고 부릅니다. 커널 스레드는 독립적으로 스케줄링되므로 특정 스레드가 대기 상태에 들어가도 다른 스레드는 작업을 계속할 수 있습니다.
커널 스레드의 장단점은 사용자 스레드의 장단점과 반대라고 생각하면 됩니다. 커널 스레드는 문맥 교환을 할 때 오버헤드 때문에 느리게 작동하기도 합니다.
멀티레벨 스레드(multi-level thread) 또는 하이브리드 스레드(hybrid thread)는 사용자 스레드와 커널 스레드를 혼합한 방식이므로 M to N 모델이라고 합니다.
멀티레벨 스레드는 사용자 스레드와 커널 스레드 방식을 혼용하기 때문에 사용자 스레드와 커널 스레드의 장단점을 모두 가지고 있습니다. 하나의 커널 스레드가 대기 상태에 들어가면 다른 커널 스레드가 대신 작업을 하여 사용자 스레드보다 유연하게 작업을 처리할 수 있습니다. 하지만 커널 스레드를 같이 사용하기 때문에 여전히 문맥 교환 시 오버헤드가 있어 사용자 스레드만큼 빠르지 않습니다. 따라서 빠르게 움직여야 하는 스레드는 사용자 스레드로 작동하고, 안정적으로 움직여야 하는 스레드는 커널 스레드로 작동합니다.