-
[컴퓨터 밑바닥의 비밀] 2.7 블로킹과 논블로킹Study/컴퓨터 밑바닥의 비밀 2025. 3. 2. 16:11
들어가기 전에
- 동기, 비동기를 이야기할 때 항상 두 가지 대상을 언급한다.
- 프로그래밍에서는 서로 상호 작용하는 모듈 두 개 또는 함수를 의미한다.
1) 동기
- A와 B 두 대상이 강하게 결합된 것을 의미함
- 위와 같이 작업 A가 작업 B에 의존하는 경우 A와 B는 동기
2) 비동기
- A와 B가 강한 결합과 같은 제약 없이 각자 자신의 작업을 독립적으로 실행할 수 있는 경우 A와 B는 비동기
더보기- 동기 방식
- 정의
- 작업이 순차적으로 실행되는 것
- 한 작업이 시작되면 해당 작업이 완료될 때까지 다른 작업은 기다려야 한다.
- 장점
- 간단하고 직관적임
- 단점
- 작업의 완료를 기다려야 하므로 처리 성능에 영향을 줄 수 있음
- 한 작업이 지연되면 다른 작업에도 영향을 줌
- 정의
- 비동기 방식
- 정의
- 작업이 독립적으로 실행되는 것
- 작업의 완료 여부를 기다리지 않고 다른 작업을 실행할 수 있다.
- 장점
- 주로 디스크 읽기/쓰기, 네트워크 통신 등의 시간이 오래 소요되는 I/O 작업에 유용함
- 작업의 완료를 기다리지 않고 다른 작업을 수행하므로 처리 성능을 향상시킬 수 있음
- 단점
- 예외 처리 어려움
- 동기 방식에서는 try-catch로 예외를 잡을 수 있지만, 비동기 방식으로 작업이 병렬로 실행될 경우 특정 작업에서 발생한 예외 추적 어려움
- 실행 순서가 보장되지 않으므로 예외 추적 어려움
- 예외 처리 어려움
- 정의
2.7.1 블로킹과 논블로킹
- 프로그래밍에서 함수를 호출할 때 주로 사용됨
- 함수 호출 --> 호출자 스레드를 블로킹 시키는지/안 시키는지가 기준
- 블로킹 방식
- 함수 A가 함수 B를 호출할 때, 함수 B를 호출함과 동시에 운영체제가 함수 A를 호출한 스레드나 프로세스를 일시 중지 시킨 경우 (함수 B에 호출 방식은 블로킹 방식)
- 논블로킹 방식
- 위와 반대의 경우
- 블로킹 방식
ex)
- 블로킹 호출 핵심은 함수를 호출한 스레드나 프로세스가 일시 중지되는 것
2.7.2 블로킹의 핵심 문제: 입출력
- 모든 함수 호출로 인해 호출자의 스레드나 프로세스가 운영 체제에 의해 일시 중지되는 것은 아님
- 블로킹은 대부분 입출력과 관련이 있음
- I/O 작업 소요 시간이 CPU 작업 소요 시간보다 훨씬 오래걸리기 때문에 스레드가 I/O 작업을 할 때 CPU 제어권을 다른 스레드에 넘겨 CPU가 다른 작업을 할 수 있도록 함
- I/O 작업이 완료되면 다시 CPU 제어권을 넘겨받아 다음 작업을 이어갈 수 있도록 함
- CPU 제어권을 상실했다가 되찾는 시간 동안 스레드는 블로킹되어 일시 중지됨
2.7.3 논블로킹과 비동기 입출력
- 호출자 스레드를 블로킹하지 않으면서 입출력 작업을 시작할 수 있는 방법은 없을까? --> 논블로킹 호출 사용
예시) 네트워크 데이터 수신
- 데이터를 수신하는 함수인 recv가 논블로킹이면, 이 함수를 호출할 때 운영체제는 호출자 스레드를 일시 중시 시키지 않고 recv 함수를 즉시 반환함
- 이후 호출자 스레드는 자신의 작업을 계속 진행하고, 데이터 수신 작업은 커널이 처리함 (두 가지 작업은 병행 처리됨)
- Q. 데이터를 수신했는지 어떻게 알 수 있을까?
- 논블로킹 방식의 recv 함수 외에 결과를 확인하는 함수 함께 제공 (해당 함수를 호출해 수신된 데이터가 있는지 확인)
- 데이터가 수신되면 스레드에 메시지나 신호 등을 전송하는 알림 작동 방식 사용
- recv 함수 호출할 때, 데이터 수신 처리를 담당하는 함수를 콜백 함수에 담아 매개변수로 전달 (이때 recv 함수가 콜백 함수를 지원해야 함)
- 이러한 호출이 '논블로킹 호출'이며, 이런 유형의 입출력 작업을 비동기 입출력(asynchronous I/O)라고 함
- I/O 작업 시작할 때 호출자 스레드가 블로킹되지 않고, 호출자 스레드의 작업과 I/O 작업이 독립적으로 동시에 처리되기 때문에 비동기 방식임
2.7.5 동기와 블로킹
- 프로그래밍 관점에서, 동기 호출은 반드시 블로킹이 아니지만 블로킹 호출은 모두 확실한 동기 호출이다.
예시)
int sum(int a, int b) { return a + b; } void funcA() { sum(1, 1); }
- sum 함수에 대한 호출은 동기이지만, funcA 함수가 sum 함수를 호출했다고 해서 해당 스레드가 블로킹되지 않음 (동기 + 논블로킹)
- 반면 어떤 함수가 블로킹 방식으로 호출된 경우 이는 반드시 동기 호출임
- 호출자 스레드가 블로킹되어 작업이 순차적으로 실행됨
2.7.6 비동기와 논블로킹
- 논블로킹이더라도 전체적으로 반드시 비동기는 아니며, 이는 코드 구현 방식에 따라 달라진다.
예시)
void handler(void *buf) { // 수신된 네트워크 데이터를 처리한다. ... } while (true) { fd = accpet(); recv(fd, buf, NON_BLOCKING_FLAG, handler); // 호출 후 바로 반환, 논블로킹 }
- recv 함수는 논블로킹 호출이므로, 네트워크 데이터를 처리해 주는 handler 함수를 recv 함수에 콜백으로 전달함
- 위의 코드는 비동기이자 논블로킹
void handler(void *buf) { // 수신된 네트워크 데이터를 처리한다. ... } while (true) { fd = accpet(); recv(fd, buf, NON_BLOCKING_FLAG); // 호출 후 바로 반환, 논블로킹 while (!check(fd)) { // 순환 감지 ... } handler(buf); }
- recv 함수는 여전히 논블로킹으로 호출되지만, while 반복문에서 감지를 시도하여 데이터가 도착하기 전까지 handler 함수를 사용할 수 없음
- 즉, recv 함수는 논블로킹이지만, 전체적인 관점에서 위 코드는 동기임
'Study > 컴퓨터 밑바닥의 비밀' 카테고리의 다른 글
[컴퓨터 밑바닥의 비밀] 6. 입출력 (0) 2025.03.24 [컴퓨터 밑바닥의 비밀] 3.3 스택 영역: 함수 호출은 어떻게 구현될까? (0) 2025.03.09 [컴퓨터 밑바닥의 비밀] 2.3 스레드 안전 코드 (0) 2025.03.01 [컴퓨터 밑바닥의 비밀] 2.2 스레드 간 공유되는 프로세스 리소스 (0) 2025.03.01 [컴퓨터 밑바닥의 비밀] 2.1 운영체제, 프로세스, 스레드 (0) 2025.03.01