Skip to content
오늘을살자
Go back

lwIP 메모리 옵션 구조 정리: PBUF_POOL, MEM_SIZE, MEMP_NUM_*를 한 번에 보기

Edit page
임베디드 보드와 패킷 버퍼, 타이머, 네트워크 흐름을 표현한 lwIP 손그림 스타일 삽화

lwIP 메모리 옵션은 처음 보면 전부 같은 “버퍼 크기”처럼 보인다.

하지만 실제로는 PBUF_POOL_SIZE, MEM_SIZE, MEMP_NUM_*, TCPIP_MBOX_SIZE가 서로 다른 층을 담당한다. 이 차이를 구분하지 못하면 RX가 막힌 문제를 heap으로 잡으려 하거나, MEMP_NUM_TCP_SEG가 부족한 문제를 pbuf 개수로 해결하려고 하면서 튜닝이 길어진다.

이 글은 기존 질의응답 메모를 lwIP 메모리 구조를 처음부터 따라갈 수 있는 형태로 다시 정리한 글이다.

기준: lwIP 2.x, FreeRTOS 포팅, MEM_LIBC_MALLOC == 0, MEMP_MEM_MALLOC == 0인 일반적인 정적 풀 구성 기준.
포팅 옵션이 다르면 실제 메모리 소모 위치가 달라질 수 있다.


1. 먼저 전체 지도를 잡기

lwIP 메모리는 하나의 큰 버퍼가 아니라 여러 층으로 나뉜다.

+--------------------------------------------------------------------------------+
|                              Application tasks                                  |
|  socket/netconn API, user buffers, recv loop, send loop                         |
+-----------------------------------+--------------------------------------------+
                                    |
                                    | API request / recv / send
                                    v
+--------------------------------------------------------------------------------+
|                               sys_arch / RTOS IPC                               |
|  tcpip_mbox, recvmbox, acceptmbox, semaphores                                   |
|  queue item = pointer-sized handle, not packet payload                          |
+-----------------------------------+--------------------------------------------+
                                    |
                                    | message points to pbuf/netbuf/api_msg
                                    v
+--------------------------------------------------------------------------------+
|                                  lwIP core                                      |
|  TCP PCB / UDP PCB / tcp_seg / netconn / netbuf / tcpip_msg                     |
|  normally allocated from fixed-size MEMP pools                                  |
+-----------------------------------+--------------------------------------------+
                                    |
                                    | pbuf chain
                                    v
+--------------------------------------------------------------------------------+
|                            packet / data storage                                |
|  PBUF_POOL: RX packet buffers                                                   |
|  MEM heap: PBUF_RAM, copied TCP TX data, general mem_malloc users               |
|  External/user buffer: PBUF_REF/PBUF_ROM, DMA buffers may live outside lwIP      |
+--------------------------------------------------------------------------------+

핵심은 다음 네 가지를 분리해서 보는 것이다.

계층대표 옵션의미주로 담는 것
패킷 데이터 풀PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE고정 크기 패킷 버퍼 개수와 크기RX 패킷 데이터
lwIP heapMEM_SIZEmem_malloc()용 동적 힙PBUF_RAM, 복사 송신 데이터 등
고정 객체 풀MEMP_NUM_*타입별 구조체 개수TCP PCB, UDP PCB, tcp_seg, netconn, tcpip_msg
OS 큐/세마포어TCPIP_MBOX_SIZE, DEFAULT_*_MBOX_SIZE포인터 메시지를 담는 RTOS 큐 길이pbuf 자체가 아니라 메시지 포인터

2. 옵션들의 관계 한 장 요약

                             +----------------------+
                             | TCP_MSS = 1460       |
                             +----------+-----------+
                                        |
             +--------------------------+--------------------------+
             |                                                     |
             v                                                     v
+-----------------------------+                      +----------------------------+
| PBUF_POOL_BUFSIZE           |                      | TCP_SND_BUF / TCP_WND      |
| align(MSS + headers)        |                      | send/receive byte window   |
+-------------+---------------+                      +-------------+--------------+
              |                                                    |
              v                                                    v
+-----------------------------+                      +----------------------------+
| PBUF_POOL_SIZE              |                      | TCP_SND_QUEUELEN           |
| number of pool buffers      |                      | queued pbuf/segment limit  |
+-------------+---------------+                      +-------------+--------------+
              |                                                    |
              v                                                    v
+-----------------------------+                      +----------------------------+
| RX packet memory budget     |                      | MEMP_NUM_TCP_SEG pressure  |
| full-size frame capacity    |                      | global queued TCP metadata |
+-----------------------------+                      +----------------------------+

+-----------------------------+                      +----------------------------+
| MEMP_NUM_TCP_PCB            |                      | TCPIP_MBOX_SIZE            |
| active TCP connection objs  |                      | pending message pointers   |
+-----------------------------+                      +----------------------------+

현재 메모에서 다루는 주요 값은 다음처럼 읽으면 된다.

옵션읽는 법
PBUF_POOL_SIZE128풀 타입 pbuf를 최대 128개 준비
MEM_SIZE65535lwIP mem_malloc() 힙 약 64 KiB
MEMP_NUM_TCP_PCB32활성 TCP PCB 최대 32개
MEMP_NUM_TCP_SEG200전체 TCP 송신/재전송 큐의 segment 메타데이터 최대 200개
TCP_MSS1460TCP payload 기준 MSS
TCP_SND_BUF14600연결 1개당 송신 큐 byte budget, 약 10 MSS
TCP_WND14600연결 1개당 수신 윈도우, 약 10 MSS
TCPIP_MBOX_SIZE255tcpip_thread 앞의 메시지 큐 슬롯 수

3. lwIP의 세 가지 메모리 공급원

기본 구성에서는 MEM_SIZE, MEMP_NUM_*, RTOS 큐 메모리를 같은 것으로 보면 안 된다.

Default lwIP memory model

+-----------------------------+   +-----------------------------+   +-----------------------------+
| MEM heap                    |   | MEMP fixed pools            |   | sys_arch / RTOS objects     |
|-----------------------------|   |-----------------------------|   |-----------------------------|
| controlled by MEM_SIZE      |   | controlled by MEMP_NUM_*    |   | controlled by mbox sizes    |
| used by mem_malloc/free     |   | used by memp_malloc/free    |   | xQueueCreate, semaphores    |
| variable-sized allocation   |   | fixed-sized typed objects   |   | usually outside lwIP heap   |
+-----------------------------+   +-----------------------------+   +-----------------------------+

단, 아래처럼 포팅 옵션을 바꾸면 관계가 달라진다.

If MEMP_MEM_MALLOC == 1

+-------------------------+
| MEMP typed allocation   |
+------------+------------+
             |
             v
+-------------------------+
| mem_malloc()            |
| controlled by MEM_SIZE  |
+-------------------------+

따라서 분석할 때는 먼저 다음 옵션을 확인해야 한다.

#define MEM_LIBC_MALLOC     ...
#define MEMP_MEM_MALLOC     ...
#define MEM_USE_POOLS       ...

이 문서는 일반적인 임베디드 기본값인 MEMP_MEM_MALLOC == 0 기준으로 설명한다.


4. PBUF_POOL_SIZE: 실제 패킷 데이터 예산

PBUF_POOL_SIZE는 byte 크기가 아니라 “풀 pbuf element 개수”다.

PBUF_POOL

+-----------------------------+  +-----------------------------+  +-----------------------------+
| pool element #0             |  | pool element #1             |  | pool element #127           |
|-----------------------------|  |-----------------------------|  |-----------------------------|
| struct pbuf                 |  | struct pbuf                 |  | struct pbuf                 |
| payload buffer              |  | payload buffer              |  | payload buffer              |
+-----------------------------+  +-----------------------------+  +-----------------------------+

일반적으로 PBUF_POOL은 RX 경로에서 가장 많이 사용된다. 이더넷 드라이버가 프레임을 받으면 pbuf_alloc(PBUF_RAW, len, PBUF_POOL) 형태로 풀 버퍼를 잡아 상위 계층으로 넘기는 구조가 흔하다.

4.1 PBUF_POOL_BUFSIZE 계산

lwIP 기본 공식은 보통 다음 형태다.

PBUF_POOL_BUFSIZE =
  LWIP_MEM_ALIGN_SIZE(TCP_MSS
                      + 40
                      + PBUF_LINK_ENCAPSULATION_HLEN
                      + PBUF_LINK_HLEN)

현재 메모의 값으로 계산하면:

TCP_MSS                         1460
TCP/IP header reserve             40
PBUF_LINK_ENCAPSULATION_HLEN       0   assumed
PBUF_LINK_HLEN                    14   Ethernet header
------------------------------------------------
raw size                        1514
align to 4 bytes                1516

즉 풀 pbuf 하나의 payload 영역은 대략 1516 byte다.

One PBUF_POOL element

+------------------------------------------+
| struct pbuf                              |
| metadata: next, payload, len, ref, flags |
+------------------------------------------+
| payload buffer                           |
| 1516 bytes                               |
+------------------------------------------+
| optional pool/alignment overhead         |
+------------------------------------------+

대략 메모리 예산은 다음과 같다.

payload only:
  1516 * 128 = 194,048 B = 189.5 KiB

metadata rough estimate:
  28 * 128   =   3,584 B =   3.5 KiB

rough total:
  197,632 B  = 193.0 KiB

정확한 값은 타깃 ABI, sizeof(struct pbuf), pool overhead, alignment에 따라 달라진다. 최종 확인은 map file 또는 빌드 타깃에서 sizeof(struct pbuf)로 보는 것이 가장 안전하다.

4.2 MEMP_NUM_PBUF와의 차이

PBUF_POOL_SIZEMEMP_NUM_PBUF는 이름이 비슷하지만 역할이 다르다.

PBUF_POOL_SIZE

+-----------------------------+
| struct pbuf + payload data  |
+-----------------------------+
| struct pbuf + payload data  |
+-----------------------------+
| ...                         |
+-----------------------------+

MEMP_NUM_PBUF

+-------------+     +-------------------+
| struct pbuf | --> | external payload  |
+-------------+     +-------------------+

+-------------+     +-------------------+
| struct pbuf | --> | ROM/user buffer   |
+-------------+     +-------------------+
옵션담는 것대표 사용
PBUF_POOL_SIZEstruct pbuf + 내부 payload bufferRX 패킷 수신용 풀
MEMP_NUM_PBUFstruct pbuf 메타데이터만PBUF_REF, PBUF_ROM, 외부 payload 참조

따라서 MEMP_NUM_PBUF = 40은 “추가 1516 byte 데이터 버퍼 40개”가 아니다. 외부 버퍼를 가리키는 pbuf 메타데이터 40개에 가깝다.


5. MEM_SIZE: lwIP 동적 힙

MEM_SIZE는 lwIP 내부 mem_malloc() 힙 크기다.

MEM heap

+---------------------------------------------------------+
| MEM_SIZE = 65535                                        |
|---------------------------------------------------------|
| mem_malloc() / mem_free()                               |
|                                                         |
| examples:                                               |
|   - PBUF_RAM                                            |
|   - copied TCP TX payload                               |
|   - variable-sized allocations in some lwIP paths       |
|   - MEMP objects only if MEMP_MEM_MALLOC == 1           |
+---------------------------------------------------------+

주의할 점은 MEMP_NUM_TCP_PCB, MEMP_NUM_TCP_SEG, MEMP_NUM_NETCONN 같은 MEMP_NUM_* 객체들이 기본 설정에서 반드시 MEM_SIZE를 소모하는 것은 아니라는 점이다. 기본값에서는 타입별 정적 pool로 잡히는 경우가 많다.

Common embedded default

MEMP_NUM_TCP_SEG  ---> static MEMP_TCP_SEG pool
MEMP_NUM_TCP_PCB  ---> static MEMP_TCP_PCB pool
MEMP_NUM_NETCONN  ---> static MEMP_NETCONN pool

MEM_SIZE          ---> mem_malloc heap

그래서 “lwIP 전체 메모리 = MEM_SIZE”가 아니다.

Total lwIP-related RAM, simplified

+--------------------+   +--------------------+   +--------------------+   +--------------------+
| MEM heap           | + | MEMP fixed pools   | + | PBUF_POOL          | + | RTOS queues/sems   |
| MEM_SIZE           |   | MEMP_NUM_*         |   | PBUF_POOL_SIZE     |   | *_MBOX_SIZE        |
+--------------------+   +--------------------+   +--------------------+   +--------------------+

6. RX 경로: PBUF_POOL과 mbox가 같이 쓰이는 방식

수신 경로에서는 “데이터”와 “데이터를 가리키는 메시지”가 분리된다.

RX packet path

       Ethernet frame arrives
                 |
                 v
       +-------------------+
       | ETH DMA / driver  |
       | descriptors       |
       +---------+---------+
                 |
                 | low_level_input()
                 | pbuf_alloc(..., PBUF_POOL)
                 | consumes PBUF_POOL element(s)
                 v
       +-------------------+
       | pbuf payload      |
       | actual packet     |
       +---------+---------+
                 |
                 | tcpip_input(p, netif)
                 | allocate tcpip_msg from MEMP_NUM_TCPIP_MSG_INPKT
                 | post pointer to tcpip_mbox
                 v
       +-------------------+
       | tcpip_mbox        |
       | pointer slots     |
       +---------+---------+
                 |
                 | tcpip_thread fetch
                 v
       +-------------------+
       | ethernet_input()  |
       | ip_input()        |
       | tcp_input()       |
       +---------+---------+
                 |
                 | TCP payload accepted
                 v
       +-------------------+
       | recvmbox/socket   |
       | waits for app     |
       +---------+---------+
                 |
                 | app recv/read
                 | pbuf free, tcp_recved()
                 v
       +-------------------+
       | pool returned     |
       +-------------------+

여기서 병목은 여러 곳에서 생길 수 있다.

RX backlog capacity is limited by:

min(available PBUF_POOL elements,
    MEMP_NUM_TCPIP_MSG_INPKT,
    TCPIP_MBOX_SIZE,
    recvmbox capacity,
    application recv speed)

따라서 TCPIP_MBOX_SIZE = 255라고 해서 실제 패킷 255개를 모두 담을 수 있는 것은 아니다. 풀 pbuf가 128개이고 input message pool이 64개라면, 그보다 먼저 다른 제한에 걸릴 수 있다.


7. TX 경로: tcp_seg, pbuf, MEM heap의 역할

TCP 송신은 “실제 payload 저장소”와 “TCP segment 메타데이터”를 같이 쓴다.

TCP TX path

App task
  |
  | netconn_write() / send() / tcp_write()
  v
+---------------------------+
| copy or reference data?   |
+-------------+-------------+
              |
      +-------+-----------------------------+
      |                                     |
      v                                     v
+---------------------------+       +---------------------------+
| COPY path                 |       | NOCOPY / REF path         |
| allocate PBUF_RAM         |       | pbuf references app data  |
| usually from MEM heap     |       | app buffer lifetime matters|
+-------------+-------------+       +-------------+-------------+
              |                                     |
              +------------------+------------------+
                                 |
                                 v
                    +---------------------------+
                    | allocate tcp_seg          |
                    | from MEMP_NUM_TCP_SEG     |
                    +-------------+-------------+
                                  |
                                  v
                    +---------------------------+
                    | pcb->unsent               |
                    | queued for output         |
                    +-------------+-------------+
                                  |
                                  v
                    +---------------------------+
                    | pcb->unacked              |
                    | waits for ACK             |
                    +-------------+-------------+
                                  |
                                  | ACK received
                                  v
                    +---------------------------+
                    | free tcp_seg and pbuf     |
                    +---------------------------+

중요한 정정:


8. TCP_SND_BUF, TCP_SND_QUEUELEN, MEMP_NUM_TCP_SEG

현재 값:

#define TCP_MSS              1460
#define TCP_SND_BUF          14600
#define TCP_SND_QUEUELEN     (4 * TCP_SND_BUF / TCP_MSS)
#define MEMP_NUM_TCP_SEG     200

계산:

TCP_SND_BUF / TCP_MSS = 14600 / 1460 = 10
TCP_SND_QUEUELEN      = 4 * 10       = 40

TCP_SND_BUF는 연결 1개가 큐에 둘 수 있는 송신 byte 예산이다. TCP_SND_QUEUELEN은 큐에 연결될 pbuf/segment 수에 대한 별도 제한이다. 단순 계산에서는 “연결 1개가 최대 40개 정도의 송신 큐 항목을 가질 수 있다”고 보면 된다.

하지만 MEMP_NUM_TCP_SEG = 200은 전체 시스템 global pool이다.

Worst-case full send queue

connection #1   40 tcp_seg
connection #2   40 tcp_seg
connection #3   40 tcp_seg
connection #4   40 tcp_seg
connection #5   40 tcp_seg
--------------------------
total          200 tcp_seg

connection #6   needs more tcp_seg -> allocation can fail

즉 32개 TCP 연결을 열 수 있더라도, 32개가 동시에 최대 송신 큐를 채울 수 있다는 뜻은 아니다.

Average queued segments available

active senders =  5  -> 200 /  5 = 40 each
active senders = 10  -> 200 / 10 = 20 each
active senders = 20  -> 200 / 20 = 10 each
active senders = 32  -> 200 / 32 =  6 each

이 값이 부족하면 다음 증상이 나온다.

증상가능한 원인
tcp_write() / send()ERR_MEM 또는 EWOULDBLOCKMEMP_NUM_TCP_SEG 부족
송신이 순간적으로 멈췄다가 ACK 후 재개TCP_SND_BUF 또는 TCP_SND_QUEUELEN 포화
COPY 송신에서만 자주 실패MEM_SIZE 힙 부족 가능

9. TCP_WND와 RX pbuf 압박

현재 값:

#define TCP_WND 14600

이는 연결 1개가 원격 peer에게 광고하는 수신 가능 byte 수다.

TCP_WND / TCP_MSS = 14600 / 1460 = 10

연결 1개가 app에서 아직 읽히지 않은 full-size TCP payload를 약 10 MSS까지 들고 있을 수 있다는 뜻이다.

If application recv is slow

remote peer
    |
    | sends up to TCP_WND
    v
+-----------------------+
| pbufs held by TCP RX  |
| about 10 MSS per conn |
+-----------+-----------+
            |
            | app recv/read
            v
+-----------------------+
| tcp_recved()          |
| window opens again    |
+-----------------------+

PBUF_POOL_SIZE = 128일 때 대략적인 감각:

full RX window per connection ~= 10 full-size pbufs

 5 slow RX connections  -> about  50 pbufs
10 slow RX connections  -> about 100 pbufs
12 slow RX connections  -> about 120 pbufs
16 slow RX connections  -> about 160 pbufs, exceeds 128-pbuf pool

실제 값은 패킷 크기, pbuf chain 여부, driver 방식, recvmbox 크기, app 처리 속도에 따라 달라진다. 그래도 이 계산은 “동시 연결 수를 늘리면 RX pbuf가 먼저 고갈될 수 있다”는 감각을 잡는 데 유용하다.


10. TCPIP_MBOX_SIZE: 데이터 버퍼가 아니라 메시지 큐

TCPIP_MBOX_SIZE = 255tcpip_thread 앞의 큐 슬롯 수다. 여기에 Ethernet frame payload가 직접 들어가는 것이 아니다.

tcpip_mbox

+---------+---------+---------+---------+---------+
| slot 0  | slot 1  | slot 2  |   ...   | slot254 |
| pointer | pointer | pointer |         | pointer |
+----+----+----+----+----+----+---------+----+----+
     |         |         |                   |
     v         v         v                   v
  tcpip_msg tcpip_msg tcpip_msg           tcpip_msg
     |         |         |                   |
     v         v         v                   v
   pbuf      API msg    timeout             pbuf

FreeRTOS 기준으로 queue item은 보통 pointer 크기다.

Approx queue storage, 32-bit target:

TCPIP_MBOX_SIZE * sizeof(void*)
255             * 4
= 1020 bytes + RTOS queue control block overhead

하지만 실제 대기 가능한 input packet 수는 다음 값의 최소치에 가깝다.

effective tcpip input backlog <=
  min(TCPIP_MBOX_SIZE,
      MEMP_NUM_TCPIP_MSG_INPKT,
      available PBUF_POOL elements,
      driver RX descriptors,
      tcpip_thread processing speed)

따라서 TCPIP_MBOX_SIZE를 255에서 128로 줄여도 절약되는 RAM은 보통 pointer 배열 약 512 byte 수준일 수 있다. 대신 tcpip_thread가 잠깐 밀리는 burst 상황에서는 drop 가능성이 늘어난다.


11. MEMP_NUM_TCP_PCB를 줄이면 무엇이 줄어드는가

MEMP_NUM_TCP_PCB는 활성 TCP PCB 개수다.

TCP PCB pool

+--------------------+  +--------------------+  +--------------------+
| tcp_pcb #0         |  | tcp_pcb #1         |  | tcp_pcb #31        |
| state              |  | state              |  | state              |
| local/remote port  |  | local/remote port  |  | local/remote port  |
| snd/rcv window     |  | snd/rcv window     |  | snd/rcv window     |
| timers/callbacks   |  | timers/callbacks   |  | timers/callbacks   |
+--------------------+  +--------------------+  +--------------------+

32 -> 16으로 줄이면 다음이 줄어든다.

saved memory ~= (32 - 16) * sizeof(struct tcp_pcb)
              + pool overhead

정확한 sizeof(struct tcp_pcb)는 lwIP 옵션에 따라 크게 달라진다. TCP SACK, timestamp, callback, IPv6, debug 옵션 등에 영향을 받으므로 “항상 250 byte”처럼 고정해서 보면 위험하다.

운영 관점에서는 이렇게 잡는 것이 안전하다.

실제 필요한 동시 TCP 연결권장 MEMP_NUM_TCP_PCB이유
최대 10개가 명확함12-16순간 재접속, TIME-WAIT류 여유
최대 10개지만 burst 불확실16보수적인 절충
실제 연결 수 모름20 이상먼저 계측 후 축소

PCB 개수를 줄이면 RAM은 줄지만, 11번째 또는 17번째 연결이 실패하는 식의 명확한 상한이 생긴다. 메모리 절감 효과는 보통 PBUF_POOL_SIZE, TCP_WND, TCP_SND_BUF를 줄이는 것보다 작을 수 있다.


12. 현재 설정을 숫자로 다시 보기

12.1 PBUF_POOL

PBUF_POOL_BUFSIZE ~= 1516 bytes
PBUF_POOL_SIZE    = 128

payload area      = 1516 * 128
                  = 194,048 B
                  = 189.5 KiB

with pbuf metadata and pool overhead:
                  ~= 193 KiB class

12.2 TCP send queue

TCP_SND_BUF       = 14600 = 10 MSS
TCP_SND_QUEUELEN  = 40
MEMP_NUM_TCP_SEG  = 200 global

full send queues possible at once:
  200 / 40 = 5 connections

12.3 TCP receive window

TCP_WND           = 14600 = 10 MSS

slow-receive pbuf budget:
  10 connections * 10 MSS ~= 100 full-size pbufs
  leaves about 28 pool pbufs for other RX/ARP/UDP/control traffic

12.4 tcpip_mbox

TCPIP_MBOX_SIZE   = 255 pointer slots

32-bit queue item memory:
  255 * 4 = 1020 bytes + RTOS overhead

not equal to:
  255 Ethernet frames
  255 pbuf payload buffers

13. 어떤 값을 줄이거나 늘릴 때의 영향

Change impact map

+----------------------+-------------------------+-----------------------------+
| Option               | Increase helps          | Increase costs              |
+----------------------+-------------------------+-----------------------------+
| PBUF_POOL_SIZE       | RX burst, slow app recv | large static RAM            |
| PBUF_POOL_BUFSIZE    | larger frame per pbuf   | large static RAM per item   |
| MEM_SIZE             | PBUF_RAM, copy TX       | heap RAM, fragmentation     |
| MEMP_NUM_TCP_SEG     | concurrent TX queues    | static pool RAM             |
| MEMP_NUM_TCP_PCB     | concurrent connections  | static pool RAM             |
| TCP_SND_BUF          | per-conn TX throughput  | more queued TX data         |
| TCP_WND              | per-conn RX throughput  | more pbufs held by RX       |
| TCPIP_MBOX_SIZE      | tcpip_thread backlog    | RTOS queue RAM              |
+----------------------+-------------------------+-----------------------------+

증상별로 보면 다음 순서로 의심한다.

증상먼저 볼 것
RX drop, pbuf_alloc(PBUF_POOL) 실패PBUF_POOL_SIZE, app recv 속도, TCP_WND, driver RX task
tcpip_input() 실패MEMP_NUM_TCPIP_MSG_INPKT, TCPIP_MBOX_SIZE, tcpip_thread 우선순위
send()/tcp_write()ERR_MEMMEMP_NUM_TCP_SEG, MEM_SIZE, TCP_SND_BUF, TCP_SND_QUEUELEN
새 TCP 연결 실패MEMP_NUM_TCP_PCB, MEMP_NUM_TCP_PCB_LISTEN, backlog
메모리는 많은데 throughput 낮음task priority, zero-copy/copy 정책, window/buffer, ACK 지연

14. 흔한 오해 정리

Misconception #1
  "MEM_SIZE가 lwIP 전체 메모리다."

Correction
  기본 구성에서는 MEM heap, MEMP pools, PBUF_POOL, RTOS queue memory가 따로 있다.
Misconception #2
  "TCPIP_MBOX_SIZE = 255면 패킷 데이터 255개를 저장한다."

Correction
  mbox는 포인터/메시지 큐다. 실제 데이터는 pbuf에 있고,
  input message pool이나 pbuf pool이 먼저 고갈될 수 있다.
Misconception #3
  "MEMP_NUM_TCP_SEG = 200은 연결마다 200개다."

Correction
  global pool이다. 모든 TCP PCB가 나눠 쓴다.
Misconception #4
  "MEMP_NUM_PBUF는 PBUF_POOL_SIZE의 추가 데이터 버퍼다."

Correction
  보통 외부 payload를 참조하는 pbuf metadata pool이다.
  PBUF_POOL처럼 payload 1516 byte를 같이 갖는 버퍼가 아니다.
Misconception #5
  "TCP 연결 32개면 32개 모두 최대 속도로 송수신 가능하다."

Correction
  연결 객체 수와 active throughput 예산은 다르다.
  `MEMP_NUM_TCP_SEG`, `PBUF_POOL_SIZE`, `MEM_SIZE`, `TCP_WND`,
  `TCP_SND_BUF`가 동시에 받쳐줘야 한다.

15. 튜닝할 때의 실전 순서

15.1 먼저 요구사항을 숫자로 바꾸기

Questions before tuning

1. 동시 TCP 연결은 최대 몇 개인가?
2. 그중 동시에 송신 중인 연결은 몇 개인가?
3. 그중 동시에 수신 backlog가 생길 연결은 몇 개인가?
4. app recv loop가 밀릴 수 있는 최악 시간은 얼마인가?
5. TCP 송신은 COPY인가, NOCOPY인가?
6. RX driver는 PBUF_POOL을 쓰는가, 별도 DMA buffer를 쓰는가?
7. MEMP_MEM_MALLOC 옵션은 기본값인가?

15.2 현재 값으로 빠르게 sanity check

Given:
  PBUF_POOL_SIZE     = 128
  TCP_WND            = 10 MSS
  MEMP_NUM_TCP_SEG   = 200
  TCP_SND_QUEUELEN   = 40

RX:
  10 slow RX conns ~= 100 pbufs
  12 slow RX conns ~= 120 pbufs
  16 slow RX conns ~= 160 pbufs, too high for pool 128

TX:
  5 full TX conns  ~= 200 tcp_seg
  10 TX conns      ~= average 20 tcp_seg each

15.3 줄이고 싶을 때

If RAM pressure is high

1. Measure first:
   - max pbuf pool usage
   - max MEMP_TCP_SEG usage
   - max MEM heap usage

2. Reduce the item that has proven headroom:
   - no RX burst pressure      -> reduce PBUF_POOL_SIZE
   - few active senders        -> reduce MEMP_NUM_TCP_SEG
   - few active connections    -> reduce MEMP_NUM_TCP_PCB
   - low throughput required   -> reduce TCP_SND_BUF / TCP_WND

3. Keep burst margin:
   - do not size exactly to the normal case
   - leave room for retransmit, ARP, DHCP, DNS, reconnect timing

15.4 늘리고 싶을 때

If throughput or burst handling is poor

RX-heavy:
  increase PBUF_POOL_SIZE
  check TCP_WND
  ensure app drains recvmbox quickly
  raise tcpip_thread / driver RX task priority if needed

TX-heavy:
  increase MEMP_NUM_TCP_SEG
  increase MEM_SIZE if COPY path fails
  tune TCP_SND_BUF and TCP_SND_QUEUELEN together
  verify ACK/retransmission behavior

Connection-heavy:
  increase MEMP_NUM_TCP_PCB
  increase MEMP_NUM_TCP_PCB_LISTEN if server accept pressure exists
  check acceptmbox / recvmbox sizes

16. 계측 포인트

가능하면 lwIP stats를 켠 뒤 실제 peak를 보고 조정한다.

#define LWIP_STATS       1
#define MEM_STATS        1
#define MEMP_STATS       1
#define PBUF_STATS       1
#define TCP_STATS        1
#define LINK_STATS       1

볼 값:

MEM heap:
  used, max, err

MEMP pools:
  MEMP_TCP_SEG.used/max/err
  MEMP_TCP_PCB.used/max/err
  MEMP_PBUF.used/max/err
  MEMP_TCPIP_MSG_INPKT.used/max/err
  MEMP_TCPIP_MSG_API.used/max/err

PBUF:
  pbuf pool allocation failure

LINK / driver:
  rx drop
  tx drop
  checksum error

로그에 남기기 좋은 에러:

ERR_MEM       allocation failed
ERR_BUF       buffer/full queue condition
ERR_TIMEOUT   connect or retransmission timeout
ERR_RST       peer reset

17. 한 줄 결론

PBUF_POOL_SIZE  -> RX packet memory count
MEM_SIZE        -> lwIP dynamic heap
MEMP_NUM_*      -> typed object pool counts
TCPIP_MBOX_SIZE -> pointer message queue depth

These are related, but they are not the same memory bucket.

현재 설정은 대략 다음 성격이다.

+----------------------+---------------------------------------------------------+
| Strong point         | PBUF_POOL 128 gives decent RX burst capacity             |
| TX limit             | MEMP_NUM_TCP_SEG 200 allows about 5 full send queues     |
| RX limit             | TCP_WND 10 MSS means 10 slow conns can hold ~100 pbufs   |
| connection setting   | TCP PCB 32 is generous if actual max is around 10        |
| easy safe reduction  | MEMP_NUM_TCP_PCB 32 -> 16, after confirming max conns    |
| needs measurement    | PBUF_POOL_SIZE and MEMP_NUM_TCP_SEG before aggressive cut|
+----------------------+---------------------------------------------------------+

Edit page
Share this post on:

Next Post
Secure Password and Salt Generator, 임시 비밀번호와 salt 만들기