반응형

본 글은  컴퓨터 밑바닥의 비밀  2.7 장 내용을 요약한 내용입니다.

 

블로킹와 논블로킹

  • 함수를 호출할 때 주로 사용됨

함수 A, B가 있다고 가정

함수 A가 B를 호출할 때, 함수 B를 호출함과 동시에 OS가 함수 A가 실행중인 스레드나 프로세스를 일시 중지 시킨다 → 블로킹

그렇지 않으면 → 논-블로킹

  • 블로킹 호출 핵심은 스레드/프로세스가 일시 중지되는 것

모든 함수 호출이 호출자의 스레드를 일시 중지 시키는 것은 아님

int sum(int a, int b)
{
	return a + b;
}

void func()
{
	int r = sum(1, 1);
}
  • 위 코드는 운영체제가 func 함수 호출 시 해당 스레드를 중지시키지 않음. 핵심은 입출력

 

블로킹의 핵심 문제: 입출력

  • 입출력 요청 완료시간은 CPU의 클럭 주파수보다 훨씬 느림
  • 입출력 과정이 실행되는 동안 CPU 제어권을 다른 스레드로 넘겨 다른 작업을 할 수 있도록 해야 함
  • 이후 입출력 작업이 완료되면 다시 CPU제어권을 우리 쓰레드/프로세스로 받아와 다음 작업을 실행할 수 있도록 함

  • CPU 제어권을 상실했다가 되찾는 시간 동안 블로킹되어 일시 중지됨
    • 스레드 A가 입출력 작업을 실행하여 블로킹되면 CPU는 스레드 B에 할당됨
    • 스레드 B가 실행되는 동안 OS는 입출력 작업이 완료된 것을 확인하면 다시 스레드 A에 CPU 할당

 

논블로킹과 비동기 입출력

네트워크 데이터 수신을 예로 설명. 데이터를 수신하는 함수인 recv가 논블로킹이면 이 함수를 호출할 때 OS는 스레드를 일시 중지시키는 대신 recv 함수를 즉시 반환

  • 호출 스레드는 자신의 작업을 계속 진행하고 데이터 수신 작업은 커널이 처리

데이터를 언제 수신했는지 알 수 있는 방법은?

  1. 논블로킹 방식의 recv 함수 외에 결과를 확인하는 함수를 함께 제공하고, 해당 함수를 호출하여 수신된 데이터가 있는지 확인
  2. 데이터가 수신되면 스레드에 메시지나 신호등을 전송하는 알림 작동 방식
  3. recv 함수를 호출 할 때 데이터 수신 처리를 담당하는 함수를 콜백 함수에 담아 매개변수로 전달할 수 있음(recv 함수가 콜백 함수를 지원해야 함)

논블로킹 호출이며, 이런 유형의 입출력 작업을 비동기 입출력 이라고도 함

피자 주문에 비유하기

  • 피자를 직접 주문하러 피자가게에 가서 주문하고 기다려 받는것: 블로킹
  • 피자 전화로 주문 후 받는 것: 논 블로킹
    • 피자 완성됬는지 계속 체크하면? 동기
    • 일반적으로는 비동기

동기와 블로킹

  • 블로킹 호출은 확실한 동기 호출
  • 동기호출은? 블로킹 호출이 아닐 수 있음
    • sum 함수에 대한 호출은 동기이지만 funcA가 sum 함수 호출을 했다고 해서 블로킹되거나 하지 않음
int sum(int a, int b)
{
	return a + b;
}

void func()
{
	int r = sum(1, 1);
}

 

비동기와 논블로킹

네트워크 데이터 수신을 예로 들어 설명

  • 데이터를 수신하는 recv 함수를 논블로킹 호출로 설정하기 위해 flag 값 추가(NON_BLOCKING_FLAG)
void handler(void *buf)
{
	//수신된 네트워크 데이터를 처리
}

while(true)
{
	fd = accept();
	recv(fd, buf, NON_BLOCKING_FLAG, handler); // 호출 후 바로 반환, 논블로킹
}
  • recv 함수는 논블로킹 호출 → 네트워크 데이터를 처리해주는 handler 함수를 콜백 함수로 전달, 즉 비동기이자 논 블로킹

하지만 데이터 도착을 감지하는 전용 함수인 check 함수를 제공해서 아래와 같이 구성한다면,

void handler(void *buf)
{
	//수신된 네트워크 데이터를 처리
}

while(true)
{
	fd = accept();
	recv(fd, buf, NON_BLOCKING_FLAG, handler); // 호출 후 바로 반환, 논블로킹
	
	while (!check(fd))
	{
		// 순환 감지
		;
	}
	
	handler(buf);
}
  • while 문에서 끊임없이 데이터를 도착하기 전까지 체크하기 때문에 그 전에 handler 함수를 사용할 수 없음 ⇒ recv 함수는 논블로킹이지만 전체적인 관점에서 이 코드는 동기

정리하면, 논블로킹이더라도 전체적으로 반드시 비동기라는 의미는 아님

반응형

'OS' 카테고리의 다른 글

[책리뷰] 컴퓨터 밑바닥의 비밀: ch3.1 메모리의 본질, 포인터와 참조  (0) 2024.08.25
Event loop와 coroutine  (0) 2024.08.11
동기/비동기  (0) 2024.08.10
파일 시스템  (0) 2024.07.28
가상 메모리(Virtual memory)  (0) 2024.07.28
반응형

본 글은 책 컴퓨터 밑바닥의 비밀  2.6 장 내용을 요약한 내용입니다.

고된 프로그래머 예시를 통한 동기/비동기 비교

동기 요청(왼쪽)과 비동기 요청(오른쪽)의 예

 
  • 상사가 프로그래머에게 일을 시킬 때, 옆에서 계속 기다리고 있으면 동기, 일을 시키고 다른 일을 하면 비동기라고 예를 들어 설명

또 다른 예로 전화통화와 이메일을 비교해보면,

  • 전화통화 : 상대방이 말할 때 들으며 기다려야 함(동기)
  • 이메일: 작성하는 동안 다른 사람들은 다른 일 처리 가능(비동기)

 

동기 호출

  • 일반적인 함수 호출
funcA()
{
	// funcB 함수가 완료될 때까지 기다림
	funcB();
	
	// funcB 함수는 프로세스를 반환하고 계속 진행
}

funcA와 funcB 함수가 동일한 스레드에서 실행됨

입출력 작업의 경우,

...
read(file, buf); // 여기에서 실행이 중지됨
...
// 파일 읽기가 완료될 때가지 기다렸다가 계속 실행함
  • 최하단 계층은 실제로 system call로 운영체제에 요청을 보냄
    • 파일 읽기 작업을 위해 read 호출 스레드를 일시 중지하고, 커널이 디스크 내용을 읽어 오면 일시 중지 되었던 스레드가 다시 깨어남 (Blocking input/output 이라고 함)

Blocking input/output

 

비동기 호출

  • 디스크의 파일 읽고 쓰기, 네트워크 IO, 데이터 베이스 작업처럼 시간이 많이 걸리는 입출력 작업을 백그라운드 형태로 실행
read(file, buf); // 여기에서 실행이 중지되지 않고 즉시 반환
// 이후 내용의 실행을 블로킹하지 않고 바로 
  • read 함수가 비동기 호출되면 파일 읽기 작업이 완료되지 않은 상태에서도 read 함수는 즉시 반환될 수 있음

  • 호출자가 블로킹 되지 않고 read 함수가 즉시 반환되기 때문에 다음 작업을 실행할 수 있음

 

그럼 비동기 호출에서 파일 읽기 작업이 언제 완료되었는지 어떻게 알 수 있을까? 이 경우, 처리에 대한 2가지 상황이 있을 수 있음

  1. 호출자가 실행 결과를 전혀 신경쓰지 않을 때
  2. 호출자가 실행 결과를 반드시 알아야 할 때

 

1. 호출자가 실행 결과를 전혀 신경쓰지 않을 때

void handler(void* buf)
{
	... // 파일 내용 처리 중
}

read(buf, handler)

"계속해서 파일을 읽고, 작업이 완료되면 전달된 함수(handler)를 이용해 파일을 처리해주세요."
⇒ 파일 내용은 호출자 스레드가 아닌 콜백 함수가 실행되는 다른 스레드(호출되는 스레드) 또는 프로세스 등에서 처리

2. 호출자가 실행 결과를 반드시 알아야 할 때

  • notification 작동 방식을 사용하는 것
    • 작업 실행이 완료되면 호출자에게 작업 완료를 알리는 신호나 메시지를 보내는 것
    • 결과 처리는 이전과 마찬가지로 호출 스레드에서 함

 

웹 서버에서 동기와 비동기 작업

아래의 작업을 한다고 가정

  • A, B, C 세 단계를 거친 후 데이터 베이스를 요청
  • 데이터 베이스 요청 처리가 완료 되면 D, E, F 세 단계를 거침
  • A, B, C, D, E, F 단계에는 입출력 작업이 포함되어 있지 않음
// 사용자 요청을 하는 단계
A;
B;
C;
데이터 베이스 요청;
D;
E;
F;

 

먼저 가장 일반적인 동기 처리 방식

// 메인 스레드
main_thread()
{
	while(1)
	{
		요청 수신;
		A;
		B;
		C;
		데이터베이스 요청을 전송하고 결과가 반환될 때까지 대기;
		D;
		E;
		F;
		결과 반환;
	}
}

// 데이터베이스 스레드
database_thread()
{
	while(1)
	{
		요청 수신;
		데이터베이스 처리;
		결과 반환;
	}
}
  • 데이터 베이스 요청 후 주 스레드가 블로킹 되어 일시 중지됨
  • 데이터 베이스 처리가 완료된 시점에서 D, E, F가 계속 실행됨

  • 주 스레드에서 빈공간은 유휴 시간(idle time)으로 기다리는 과정
  • 유휴 시간을 줄이기 위해서 비동기 작업을 활용할 수 있다

 

1. 주 스레드가 데이터 베이스 처리 결과를 전혀 신경 쓰지 않을 때

  • 주 스레드는 데이터 베이스 처리 완료 여부 상관하지 않음
  • 데이터베이스 스레드가 D, E, F 세 단계를 자체적으로 직접 처리
  • 데이터 베이스 처리 후 DB 스레드가 D, E, F 세 단계를 알 수 있는 방법은?
    • 콜백 함수
void handle_DEF_after_DB_query()
{
	D;
	E;
	F;
}

주 쓰레드가 데이터베이스 처리 요청을 보낼 때 위 함수를 매개변수로 전달

DB_query(request, handle_DEF_after_DB_query);
  • 데이터 베이스 쓰레드는 데이터 베이스 요청을 처리한 후 handle_DEF_after_DB_query 함수 호출하기만 하면 됨

이 함수를 데이터 베이스 쓰레드에 정의하고 직접 호출하는 대신 콜백 함수를 통해 전달받아 실행하는 이유은?

⇒ 소프트웨어 조직 구조 관점에서 볼 때 데이터베이스 쓰레드에서 해야 할 작업이 아니기 때문

 

2. 주 쓰레드가 DB 작업 결과에 관심을 가질 때

  • 알림 작동 방식을 이용하여 작업 결과를 주 스레드로 전송
  • 주 스레드는 사용자 요청의 후반부를 계속 처리
  • 주 스레드에는 유휴시간이 없음
    • 비 동기 호출만큼 극단적으로 효율적이진 않지만 동기 호출에 비하면 여전히 효율적
반응형

'OS' 카테고리의 다른 글

Event loop와 coroutine  (0) 2024.08.11
블로킹/논블로킹  (0) 2024.08.10
파일 시스템  (0) 2024.07.28
가상 메모리(Virtual memory)  (0) 2024.07.28
교착상태(Deadlock)  (0) 2024.07.19
반응형

본 글은 인프런 강의 "대세는 쿠버네티스 [초급~중급]" 를 듣고 요약 및 실습 정리한 내용입니다.

 

Node Schedule

  • pod는 결국 여러 node들 중 한 node에 올라가져야 함
  1. 직접선택
  • Node에 라벨을 달고 Pod를 만들때 노드를 지정

apiVersion: v1
kind: Pod
metadata:
  name: pod-3
spec:
    nodeSelector:
    hostname: node1
  containers:
  - name: container
    image: tmkube/init

 

2. 쿠버네티스의 스케쥴러가 판단해서 자동 지정

  • memory나 cpu 자원을 보고 판단

apiVersion: v1
kind: Pod
metadata:
  name: pod-4
spec:
  containers:
  - name: container
    image: tmkube/init
    resources:
      requests:
        memory: 2Gi # 필요한 memory 명시 가능
      limits: # 최대 허용
        memory: 3Gi 
  • memory : 초과시 Pod 종료 시킴
  • limits
    • memory: 초과시 pod 종료
    • cpu: Over시 종료되지 않고 requests에 명시된 수치까지 낮춤
반응형

'Kubernetes' 카테고리의 다른 글

Service - ClusterIP, NodePort, LoadBalancer  (0) 2024.10.13
Pod - Container  (0) 2024.08.04
Pod - Label  (0) 2024.08.04
반응형

본 글은 인프런 강의 "대세는 쿠버네티스 [초급~중급]" 를 듣고 요약 및 실습 정리한 내용입니다.

Pod

 
  • 파드 내에는 하나의 독립적인 서비스를 구성할 수 있는 여러개의 Container가 있을 수 있음

Container

  • 서비스가 연결될 수 있도록 포트를 가지고 있음
  • 한 컨테이너가 하나 이상의 포트를 가질 수는 있지만 파드 내에서 다른 컨테이너와 중복된 포트를 가질 수 는 없음

  • 두 container는 하나의 host로 묶여있어서 container들끼리 localhost로 접근 가능

  • pod가 생성될 떄 고유한 ip 주소가 할당되는데 재생성시 변경됨
  • 쿠버네티스 클러스터에서만 접근가능, 외부에서 접근 불가
  • 파드에 문제가 생기면 재생성되는데 ip가 변경됨

Pod 생성을 위한 yaml 파일 내용

apiVersion: v1
kind: Pod
metadata:
  name: pod-1
spec:
  containers:
    - name: container1
      image: tmkube/p8000
      ports:
      - containerPort: 8000 # container의 노출된 포트
    - name: container2
      image: tmkube/p8080
      ports:
      - containerPort: 8080

 

실습

  • dashboard에서 아래 yaml파일 통해 파드 생성
    • JSON로 변경하면 오른쪽과 같이 생성됨 (container2는 제외)
apiVersion: v1
kind: Pod
metadata:
  name: pod-1
spec:
  containers:
  - name: container1
    image: tmkube/p8000
    ports:
    - containerPort: 8000 # container의 노출된 포트
  - name: container2
    image: tmkube/p8080
    ports:
    - containerPort: 8080
{
  "apiVersion": "v1",
  "kind": "Pod",
  "metadata": {
    "name": "pod-1"
  },
  "spec": {
    "containers": [
      {
        "name": "container1",
        "image": "tmkube/p8000",
        "ports": [
          {
            "containerPort": 8000
          }
        ]
      }
    ]
  }
}

  • IP를 확인할 수 있고 이는 쿠버네티스 클러스터 내에서만 접근 가능
  • master node에서 아래 명령어를 통해 pod 내의 container에 접근 가능
[root@k8s-master ~]# curl 20.109.131.1:8000
containerPort : 8000
[root@k8s-master ~]# curl 20.109.131.1:8080
containerPort : 8080

한 pod 내에 동일한 포트가 노출된 2개의 container를 띄우는 경우

apiVersion: v1
kind: Pod
metadata:
  name: pod-2
spec:
  containers:
  - name: container1
    image: tmkube/p8000
    ports:
    - containerPort: 8000 # container의 노출된 포트
  - name: container2
    image: tmkube/p8000
    ports:
    - containerPort: 8000

  • Container2 내의 로그를 보면 Error: listen EADDRINUSE: address already in use :::8000 즉, 동일한 port가 이미 사용중이라는 에러가 발생했음

 

 
반응형

'Kubernetes' 카테고리의 다른 글

Service - ClusterIP, NodePort, LoadBalancer  (0) 2024.10.13
Node schedule  (0) 2024.08.04
Pod - Label  (0) 2024.08.04
반응형

본 글은 인프런 강의 "대세는 쿠버네티스 [초급~중급]" 를 듣고 요약 및 실습 정리한 내용입니다.

 

Label

  • Pod 뿐만 아니라 모든 object에 추가할 수 있음 (pod에 가장 많이 사용됨)
  • 목적에 따라 object를 분류하고 따로 연결하기 위해서임
  • 구성은 Key와 Value이고 한 pod에 여러개의 label을 달 수 있음

  • web, db, server type인 pod들이 dev, production에 있음
  • 웹 개발자가 웹화면만 보고 싶다면 type이 web이 pod만 Service에 연결
  • 상용 환경 운영자에게는 lo:production label이 붙은 아래 pod들을 Service에 연결하여 사용

apiVersion: v1
kind: Pod
metadata:
  name: pod-2
  labels:
    type: web
    lo: dev
spec:
  containers:
  - name: container
    image: tmkube/init

 

실습

아래에서 labels 의 type과 lo를 변경하여 6개의 pod를 띄움

apiVersion: v1
kind: Pod
metadata:
  name: pod-1
  labels:
    type: web
    lo: dev
spec:
  containers:
  - name: container
    image: tmkube/init

 

web을 위한 Service를 띄움

apiVersion: v1
kind: Service
metadata:
  name: svc-for-web
spec:
  selector:
    type: web
  ports:
    - port: 8080

 

서비스가 만들어짐

  • selector에 따라 label이 type: web인 pod들만 연결시켜줌

production을 위한 service를 띄우면 3개의 pod가 연결된걸 확인할 수 있음

apiVersion: v1
kind: Service
metadata:
  name: svc-for-production
spec:
  selector:
    lo: production
  ports:
    - port: 8080

반응형

'Kubernetes' 카테고리의 다른 글

Service - ClusterIP, NodePort, LoadBalancer  (0) 2024.10.13
Node schedule  (0) 2024.08.04
Pod - Container  (0) 2024.08.04

+ Recent posts