Linux Kernel 네트워크 스택의 수신 과정(RX)과 튜닝 효과
커널의 네트워크 스택은 상대적으로 느리다고 평가됩니다. IT 분야에 있다면 이런 이야기를 여러 번 들어봤을 것입니다. 이 때문에 고성능 네트워크 처리가 필요한 기업에서는 커널의 네트워크 스택을 거치지 않고, eBPF 등을 활용해 커널 진입 이전 단계에서 패킷을 처리하기도 합니다.
그러나 이러한 방식은 구현과 운영 모두에서 많은 비용이 듭니다. 개발자와 운영자는 새로운 네트워크 처리 방식을 이해해야 하며, 처리 과정이 복잡해질수록 관리 부담도 커집니다. 따라서 많은 경우 커널 파라미터 튜닝을 통해 성능 요구사항을 충족합니다. 이러한 튜닝 방법은 Red Hat의 Linux 성능 최적화 오픈소스 프로젝트인 redhat-performance/tuned혹은 각 기업의 기술블로그등에서 확인할 수 있습니다.
본 글에서는 커널 네트워크 스택에서 병목이 발생할 수 있는 주요 지점을 살펴보고, 이를 관측하는 방법을 다룹니다. NIC(Network Interface Card) 오프로딩 등 커널 외부 영역은 범위에서 제외하며, 커널 내부의 처리 과정을 중심으로 설명합니다. 진행 순서는 OSI 7Layer 순으로 구성됩니다. 드라이버의 경우에는 벤더사별로 다른 관계로 여기서는 다루지 않습니다. 해당 글에서는 수신만을 서술합니다.
이 글의 목적은 리눅스 커널 문서와 Red Hat Performance Tuned 프로젝트의 내용을 기반으로, 커널 파라미터가 병목 구간에 어떤 영향을 왜 미치는지를 설명합니다. 먼저 각 네트워크 스택별 대략적인 개요를 살펴봅시다.
# 수신 경로 요약
NIC → DMA 버퍼 → 인터럽트/SoftIRQ → NAPI Poll → sk_buff 생성 → L2 → L3 → L4 → 소켓
L2 (Ethernet...)
Ethernet 계층의 핵심은 NIC(Network Interface Card) 에서 수신한 전기적 신호를 운영체제가 이해할 수 있는 데이터 버퍼(sk_buff) 형태로 가공하는 과정입니다. 이 과정은 크게 하드웨어 수신 → 인터럽트 처리 → 소프트웨어 수신 → 상위 계층 전달의 순서로 진행됩니다.
-
하드웨어 수신: NIC는 네트워크로부터 프레임을 수신하면, 미리 할당된 DMA 버퍼 영역에 데이터를 기록합니다. 이때 각 버퍼는 커널이 관리하는
sk_buff와 연결되어 있으며, 이를 통해 이후의 소프트웨어 처리가 가능해집니다. -
인터럽트 발생 및 NAPI 스케줄링: 패킷이 도착하면 NIC는 CPU에 인터럽트를 발생시킵니다. 그러나 트래픽이 많을 경우 인터럽트가 과도하게 발생하므로, NAPI(New API)가 이를 완화합니다. NAPI는 초기 인터럽트 이후에 poll 기반 처리로 전환하여, 커널이 일정한 주기로 여러 패킷을 한 번에 처리할 수 있도록 합니다.
-
소프트웨어 수신(poll) 단계: NAPI poll 과정에서 NIC 드라이버는 장치 큐에 저장된 여러
sk_buff를 한꺼번에 꺼내 커널 네트워크 스택으로 전달합니다. 이 단계에서 패킷마다 타임스탬프 기록, CPU 코어 배정, 큐 분배 등의 전처리가 수행됩니다. -
패킷 전달 및 분류: 수신된
sk_buff는 L2 처리 경로를 거쳐, VLAN, 브리징, Netfilter 등 필요한 기능을 통과한 뒤 최종적으로 L3(IP) 계층으로 전달됩니다. 이 단계의 목적은 “이 패킷이 어디로 가야 하는가”를 결정하는 것입니다 — 즉, 로컬로 처리할지, 브리지를 통해 전달할지, 혹은 상위 계층으로 넘길지를 판별합니다.
Ethernet 계층의 전체 흐름은 하드웨어에서 커널로의 데이터 이동과 필터링 과정이며, 실제 패킷 내용이 해석되기 전 단계의 전달 효율과 CPU 부하 관리가 핵심입니다.
L3 (IP, ARP...)
L3 계층은 L2가 넘겨준 패킷을 IP 단위로 해석하고, 목적지에 따라 라우팅하거나 상위 계층(L4)에 전달하는 과정을 담당합니다. 여기서는 주로 헤더 검증 → 라우팅 결정 → 로컬 처리 또는 포워딩의 순서로 진행됩니다.
-
헤더 파싱 및 검증: IP 계층은 Ethernet 프레임의 페이로드를 받아 IP 헤더를 분석합니다. 헤더의 유효성을 확인하고, 잘못된 패킷이면 즉시 폐기하거나 ICMP 오류 메시지를 반환합니다.
-
라우팅 결정: 유효한 패킷이면 커널은 라우팅 테이블을 참조해 패킷의 목적지를 결정합니다.
- 목적지가 현재 호스트라면 로컬로 전달하고,
- 다른 네트워크에 있다면 다음 홉(게이트웨이)으로 포워딩합니다.
- 멀티캐스트라면 복제 후 다수의 인터페이스로 전송할 수도 있습니다.
-
로컬 전달: 목적지가 로컬인 경우, 커널은 패킷을 상위 계층(L4)로 넘깁니다. 이 과정에서 raw 소켓, TCP, UDP 등 각 프로토콜에 맞는 핸들러가 호출되어 데이터를 분배합니다.
-
포워딩: 목적지가 외부 네트워크일 경우, IP 계층은 해당 패킷을 적절한 네트워크 인터페이스로 재전송합니다. 이때 TTL 감소, 체크섬 재계산 등의 수정이 이루어집니다.
결국 L3 계층의 워크플로우는 “어디서 온 패킷을, 어디로 보낼 것인가”를 결정하는 경로 제어 과정이며, L2가 물리적 전송을 담당한다면 L3는 논리적 주소 기반의 데이터 흐름 제어를 담당합니다.
L4 (TCP...)
TCP 계층은 L3(IP)에서 전달된 데이터를 연결 단위로 관리하고 신뢰성 있게 전달하는 단계입니다. 이 과정은 단순한 데이터 수신이 아니라, 연결 상태 추적 → 순서 정렬 → 흐름 제어 → 응답 처리 → 사용자 공간 전달의 일련의 워크플로우로 구성됩니다.
-
연결 식별 및 상태 확인: 수신된 패킷은 TCP 헤더의 IP 주소, 포트 번호, 시퀀스 번호를 기반으로 기존 연결을 식별합니다. 커널은 이 정보를 이용해 적절한 소켓 구조체(
sock)를 찾고, 해당 연결의 상태(예: LISTEN, ESTABLISHED 등)를 확인합니다. -
ACK 및 흐름 제어 처리: 패킷에 ACK가 포함된 경우, 커널은 이를 통해 송신 측 버퍼의 데이터를 확인하고 필요 시 다음 데이터를 전송합니다. 동시에 수신 윈도우 크기를 갱신하여 송신 측의 전송 속도를 제어(flow control) 합니다.
-
데이터 정렬 및 큐잉: 수신된 데이터는 순서가 어긋날 수 있으므로, 커널은 이를 시퀀스 번호 기준으로 정렬합니다. 도착 순서에 맞지 않는 데이터는 임시 큐(
out_of_order_queue)에 저장되며, 순서가 맞는 경우에는 수신 큐(sk_receive_queue)로 이동합니다. -
사용자 공간 전달: 사용자 프로세스가 해당 소켓을 통해 데이터를 기다리고 있다면, 커널은 즉시 데이터를 복사(
skb_copy_datagram_iovec)하여 전달합니다. 프로세스가 대기 중이 아니라면, 데이터는 소켓 큐에 보관되어 나중에 읽힐 때 전달됩니다. -
프로세스 깨우기: 데이터가 도착하면 커널은 소켓의
sk_data_ready콜백을 호출하여, 대기 중인 프로세스(예:recv()호출 중인 프로세스)를 깨워 데이터가 준비되었음을 알립니다.
Latency 튜닝
네트워크 스택의 이더넷 계층은 NIC로부터 DMA를 통해 데이터를 수신할 때, 초기 알림을 위해 softirq를 수행하고 큐에 데이터를 적재합니다. 이 과정에서 레이턴시(latency)가 증가할 수 있습니다.
이때 조정 가능한 주요 옵션은 net.core.busy_read와 net.core.busy_poll로, 일반적으로 바쁜 폴링(Busy Polling) 설정과 관련됩니다.
바쁜 폴링은 NIC 인터럽트를 대체하여, 설정된 주기에 따라 지속적으로 데이터를 폴링하는 방식입니다. 이 방식은 인터럽트 지연을 줄여 레이턴시를 낮출 수 있지만, CPU 사용률과 전력 소모가 증가하는 단점이 있습니다.
또한 L3(IP) 계층에서는 net.ipv4.tcp_fastopen 옵션이 대표적입니다.
이 기능은 RFC 7413에서 정의된 기술로, RTT(Round-Trip Time)를 줄이기 위해 3-way handshake 과정 중 SYN-RCVD 패킷에 데이터를 포함해 전송할 수 있도록 합니다.
Throughput 튜닝
Throughput 향상을 위한 대표적인 커널 파라미터는 net.ipv4.tcp_rmem과 net.ipv4.tcp_wmem입니다.
이는 TCP 송수신 버퍼 크기를 설정하는 옵션으로, 버퍼 크기를 늘리면 한 번에 전송할 수 있는 데이터량이 증가하여 Throughput 개선에 도움이 됩니다.
여담 - XDP
설명된바와 같이 커널의 복잡한 네트워크 스택을 거치는 동안 데이터는 여러 단계의 가공과 처리를 거치며, 유저 레벨로 이동할 때 데이터 복사가 발생합니다. 데이터 복사는 대부분 CPU를 거쳐 RAM까지 접근해야 하므로 큰 오버헤드를 유발합니다. 반면, zero-copy 방식은 이러한 복사를 최소화하여 효율을 크게 향상시킵니다.
최근에는 커널을 거치는 트래픽 처리가 점점 느리고 비효율적으로 인식되면서, NIC의 DMA 버퍼를 직접 매핑하거나 L3(IP) 계층에서 패킷을 처리하는 XDP와 같은 기술이 주목받고 있습니다. 이러한 방식은 특정 상황에서 일반적인 네트워크 처리보다 최대 8배 이상 빠른 성능을 보이기도 하며, 대표적인 활용 예로 패킷 드롭 처리가 있습니다.
댓글