Multi-Threaded application의 한 쓰레드가
fork()
를 호출하면 새로운 프로세스는 모든 쓰레드를 복제해야 할까요?!?! fork()는 호출한 쓰레드만 복제 ?! 아님 전부 다 ?! 유닉스는 두가지 버전이 있습니다.
exec()
실행 시 모든 쓰레드를 포함하여 새 프로세스로 대체해야 할까요 !?!?
일반적인 대처로는
fork() 이후 exec() 하면 exec 에서 지정한 프로그램이 모든 메모리 영역을 대체할 것이므로 호출한 쓰레드만 복제, 그렇지 않으면 모든 쓰레드를 복제합니다.
Signal = 시스템 이벤트 입니다. 유닉스 시스템에서 프로세스에게 특정 이벤트가 발생했음을 알리기 위한 수단입니다. 동기식 혹은 비동기식으로 전달될 수 있습니다.
시그널은 특정 이벤트로 인해 생성되고, 시그널은 프로세스에게 전달됩니다. 이 때 프로세스는 2가지 방식으로 처리할 수 있습니다.
user-defined 는 default 를 override 하여 정의할 수 있습니다.
쓰레드는 일반적으로 루프 혹은 무한 루프를 돌리는 용도로 주로 사용합니다. 그런데 이걸 종료하고 싶을 때는 ?! cancellation 을 사용합니다. 캔슬은 굉장히 조심히 사용해야 합니다. 리소스를 다뤄야 하기 때문에 비동기로 진행해야 합니다.
캔슬에는 2가지 방식이 있습니다.
- asynchronous cancellation
해당 메모리를 즉시 해제합니다. 이 경우에는 shared resource problem 이 발생합니다.
- Deferred cancellation
타겟 쓰레드가 주기적으로 종료 여부를 스스로 체크한 후 종료가 결정되면 종료 이전에 자원을 반납합니다.
쓰레드 취소를 호출하면, 취소가 요청되지만 실제로 취소는 쓰레드 상태에 따라 달라집니다.
쓰레드의 취소가 disabled 된 경우, 쓰레드가 취소를 enable할 때까지 취소가 pending 상태로 유지됩니다.
기본적으로 deffered가 기본 상태입니다. 쓰레드 캔슬은 cancellation point 에 도달했을 때에만 발생합니다.
interrupt()
메소드를 사용합니다.
while (!Thread.currentThread().isInterrupted()) {}
쓰레드의 고유한 전역/정적 변수를 사용할 수 있게 해줍니다. 쓰레드들 사이 공유가 가능합니다. 약간 static data 와 비슷한 느낌입니다. 다른 점이라면, TLS는 각 쓰레드에서 유니크하게 존재합니다. 쓰레드 생성 프로세스를 제어할 수 없을 때 유용하게 쓰이는 방식입니다. (예를 들어 쓰레드 풀 사용 시에)
앞서 Multi-Threading Model 에서 M:M 그리고 Two-level Model 에서는 유저-쓰레드와 커널-쓰레드의 개수가 1:1 매핑이 아니므로 application에게 할당된 kernel threads를 적정 개수로 유지하기 위해 통신을 요구합니다. 이러한 통신은 어플리케이션이 커널 쓰레드의 개수를 알맞게 유지할 수 있도록 해줍니다.
따라서 보통 유저 쓰레드와 커널 쓰레드 사이에 intermediate data structure을 둬서 사용합니다. 이를 스케쥴 하기 위한 가상의 프로세스를 하나 두는데, 이를 Lightweight Process (LWP) 라고 합니다. LWP는 커널 쓰레드에 1:1 매핑됩니다.
upcall은 커널이 특정 이벤트에 대해 어플리케이션에 알리는 프로세스를 말합니다. scheduler activation은 upcall을 생성합니다. 커널은 어플리케이션에게 LWP Set (1개 이상의 LWP)을 제공합니다. 어플리케이션은 사용자 쓰레드를 available virtual processor (LWP)로 예약 가능합니다.
upcall은 쓰레드 라이브러리에서 upcall handler를 통해 처리되며, 이는 LWP에서 실행되어야 합니다. 이러한 통신으로 어플리케이션이 적정 수의 커널 쓰레드를 유지할 수 있도록 해줍니다.