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 heap | MEM_SIZE | mem_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_SIZE | 128 | 풀 타입 pbuf를 최대 128개 준비 |
MEM_SIZE | 65535 | lwIP mem_malloc() 힙 약 64 KiB |
MEMP_NUM_TCP_PCB | 32 | 활성 TCP PCB 최대 32개 |
MEMP_NUM_TCP_SEG | 200 | 전체 TCP 송신/재전송 큐의 segment 메타데이터 최대 200개 |
TCP_MSS | 1460 | TCP payload 기준 MSS |
TCP_SND_BUF | 14600 | 연결 1개당 송신 큐 byte budget, 약 10 MSS |
TCP_WND | 14600 | 연결 1개당 수신 윈도우, 약 10 MSS |
TCPIP_MBOX_SIZE | 255 | tcpip_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_SIZE와 MEMP_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_SIZE | struct pbuf + 내부 payload buffer | RX 패킷 수신용 풀 |
MEMP_NUM_PBUF | struct 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 |
+---------------------------+
중요한 정정:
- TCP TX에서 항상
PBUF_POOL을 쓰는 것은 아니다. TCP_WRITE_FLAG_COPY경로는 보통PBUF_RAM을 만들며, 이때MEM_SIZE힙이 압박을 받는다.MEMP_NUM_TCP_SEG는 payload가 아니라 segment 메타데이터 개수다.- payload 저장소와
tcp_seg둘 중 하나만 부족해도ERR_MEM이 날 수 있다.
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 또는 EWOULDBLOCK | MEMP_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 = 255는 tcpip_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_MEM | MEMP_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|
+----------------------+---------------------------------------------------------+