A LWIP memory management LWIP memory management uses two methods: memory pool memp and memory heap mem, as shown in Figure 1. The memory pool is characterized by pre-opening a plurality of sets of fixed-size memory blocks organized into a linked list, which is simple to implement, fast in allocation and recovery, does not generate memory fragments, but has a fixed size and needs to be pre-estimated accurately. The essence of the memory heap is to organize and manage a pre-defined memory block reasonably and effectively. It is mainly used for memory allocation of any size. The implementation is more complicated, the allocation needs to be searched, the recovery needs to be merged, and the memory fragment is easy to generate. The total size of the memory heap. Figure 1 memory pool and memory heap Packet management There are four types of packet management structures pbuf. Their characteristics and usage are shown in Table 1. Table 1 pbuf types and characteristics Each pbuf allocates memory differently, as shown in Figure 2. Figure 2 four packet management structures Only choose the appropriate pbuf type to play the maximum performance of LWIP. A data packet may be a combination of multiple pbufs, connected by a linked list, as shown in Figure 3. Figure 3 pbuf linked list 2. Set the memory size It is appropriate to open a dedicated memory heap for LWIP, so that LWIP's mem_alloc() and mem_free() will be allocated and reclaimed based on the heap memory, without affecting the use of other system memory. As shown on the left of Figure 1, the macro MEM_SIZE in the lwipopt.h file defines the size of the heap area. For a heavily loaded system heap area, a larger allocation is required. Figure 4 heap and PBUF_ROM memory area LWIP uses the memory pool of the PBUF_ROM type to send "read-only" data (which is not modifiable in ROM or other processes). The macro MEMP_NUM_PBUF defines the number of buffer pools, as shown in the right side of Figure 2. In the ISR (Interrupt Service Routine), it is often necessary to quickly allocate a portion of the memory for data storage, which is implemented by a buffer of the PBUF_POOL type. To do this, you need to define two macros: PBUF_POOL_SIZE defines the number of buffer pools, and PBUF_POOL_BUFSIZE defines the size of a single buffer, as shown in Figure 5. Figure 5 PBUF_POOL memory area 3 macro compile switch If MEM_LIBC_MALLOC=1 is defined, use malloc and free in the C library to allocate dynamic memory; otherwise, use the functions such as mem_malloc and mem_free that are provided by LWIP. If MEMP_MEM_MALLOC=1 is defined, then all the contents of memp.c will not be compiled, and the memory heap is allocated by the memory heap. In this way, it is considered whether the time delay caused by the memory heap allocation can be tolerated. If MEM_USE_POOLS=1 is defined, then all the contents of mem.c will not be compiled. The memory pool is used to allocate the memory heap. In this way, it is considered whether it can tolerate the memory waste caused by the fixed size of POOL memory. In addition, the user needs to define the macro MEM_USE_CUSTOM_POOLS=1, and also needs to implement a header file lwippools.h, and open some memory pool POOL for the memory heap allocation function. The format of the development space is as follows: LWIP_MALLOC_MEMPOOL_START LWIP_MALLOC_MEMPOOL(20, 256) LWIP_MALLOC_MEMPOOL(10, 512) LWIP_MALLOC_MEMPOOL_END Two LWIP startup timing Figure 6 shows the LWIP startup sequence. Most of the functions are provided by LWIP. The main porting code is eth_init() to initialize the Ethernet interface. The startup program will create 2 threads: tcpip_thread is responsible for most of the work of LWIP (mainly It is the resolution of the protocol stack and the system operation.) ethernetif_thread is responsible for receiving data packets from the network port and then delivering them to the tcpip_thread thread for processing. Figure 6 LWIP startup function Three LWIP running logic 1 Receive packet Figure 7 receives the packet When the Ethernet port receives a data packet, the EMAC_IRQ interrupt service routine notifies the ethernetif thread by the semaphore. The ethernetif thread calls low_level_input() to receive the data packet and deliver it to the tcpip_thread thread through the mailbox. The tcpip_thread performs corresponding processing according to the type of the data packet. . It is based on messaging, as shown in Figure 8. Figure 8 Generation and processing of packet messages 2 SequentialAPI function call The core of API design is to let the user process be responsible for as much work as possible, such as calculation, copying, etc. of the data; and the protocol stack process is only responsible for simple communication work, which is very reasonable, because there may be multiple applications in the system, they Both use the communication services provided by the protocol stack process to ensure that the efficiency and real-time performance of the kernel process is an important guarantee for improving system performance. Communication between processes uses IPC technology, including mailboxes, semaphores, and shared memory, as shown in Figure 9. Figure 9 protocol stack API implementation Take the function netconn_bind() as an example to see how the API is implemented. First, the user program calls the function netconn_bind() to bind a connection. When this function is implemented, it sends a message of type TCPIP_MSG_API to the kernel process to tell the kernel process to execute. Do_bind function: After the message is sent, the function is blocked on the semaphore, waiting for the kernel to process the message; when the kernel processes the message, it will call do_bind according to the message content, and do_bind will call the kernel function udp_bind, tcp_bind or raw_bind according to the type of connection; When do_bind is executed, it releases the semaphore, which causes the blocked netconn_bind to continue. The whole process is shown in Figure 10. Figure 10 API function implementation Four TCP/IP core knowledge points Sliding window Figure 11 sliding window In the field related to the receive window, rcv_nxt is the next byte number that you expect to receive, rcv_wnd indicates the size of the receive window, and rcv_ann_wnd indicates the window size value to be advertised to the other party. This value will be filled in the header when the message is sent. The window size field in the rcv_ann_right_edge records the value of the right edge of the window when the last window was advertised. The above four fields will change dynamically as the data is sent and received, as shown in Figure 12. Figure 12 receiving window In the send window, lastack records the highest sequence number confirmed by the receiver, snd_nxt indicates the start number of the next data to be sent, and snd_wnd records the current send window size, which is often set as the receive window of the receiver's announcement. The value, snd_lbb, records the starting number of the next data cached by the application, as shown in Figure 10. The values ​​of the above four fields are also dynamically changed. When a valid ACK of the receiver is received, the value of lastack is incremented accordingly, pointing to the number of the next data to be acknowledged. After sending a message, snd_nxt The value of the corresponding increase, pointing to the next data to be sent, the difference between snd_nxt and lastack can not exceed the size of snd_wnd. Since the actual data is sent in the form of a message segment, there may be a case where even if the transmission window allows, not all the data in the window can be sent to fill the window, as shown in FIG. The data of ~13 may not be sent because they are too small to be organized into a valid segment. The sender will wait until a new acknowledgment arrives, causing the send window to slide to the right, causing more data to be included in the window, thus initiating the transmission of the next segment. Figure 13 send window 2. Three-way handshake Figure 14 connection establishment process 3. Disconnect Figure 15 connection disconnection process 4. TCP state transition Figure 16 TCP state transition diagram 5. Open at the same time Figure 17 both sides open simultaneously 6. Close at the same time Figure 18 both sides are closed at the same time Five correctly use LWIP Generally speaking, the LWIP protocol stack is relatively stable. In particular, V1.3.2 has experienced extensive use and engineering applications in the industry and can be applied to embedded products. Then why do many people still report that LWIP is unstable? Mainly the following reasons: 1. Network hardware driver Ensures stable and reliable EMAC port reception and transmission 2. Porting LWIP OS-based porting code is stable and stable 3. Configure LWIP to be properly configured according to the device RAM size. 1) The value (PBUF_POOL_SIZE * PBUF_POOL_BUFSIZE) must be greater than TCP_SND_BUF and TCP_WND, otherwise it is easy to cause an error; 2) TCP can't be sent too fast when memory is limited (the specific value depends on the size of the allocated memory), otherwise it will cause tcp_enqueue() logic error; 4. Call the LWIP API function Proper use of API functions, especially to prevent memory leaks. 5. Insufficient resources Open alarm reminder, remind the designer when resources are not enough Six LWIP Frequently Asked Questions NIC driver First, the protocol stack must be fully initialized to enable the network receive function. The receive interrupt must encapsulate the data in the PBUF and then hand it over to the protocol stack kernel for processing. Second, LWIP's global variables (arp_table, netif_list, udp_pcbs, etc.) ensure that the initial value is 0, otherwise it will be easy to crash as soon as it runs. 2. Memory leak The first principle (responsibility system): Whoever allocates memory is responsible for recycling. The second principle (symmetry): the one that allocates the memory and the one that reclaims the memory form a closed loop. In addition, you need to pay special attention to the call of some system functions, they also bring memory leaks, such as: example 1 Newconn = netconn_accept(conn); Do_something_for(newconn); Netconn_close(newconn); Netconn_delete(newconn); /* must release newconn or it will cause memory leak */ Example 2 Inbuf = netconn_recv(conn); Do_something_for(inbuf); Netbuf_delete(inbuf); /* must release inbuf or it will cause memory leak */ 3. The PC cannot establish a TCP connection with LWIP. Problem: The PC can successfully operate with the LWIP device PING, but cannot establish a TCP connection. Cause: Through code tracking, it was found that LWIP sent a SYN+ACK packet, but the PC could not receive the handshake packet, which is 60 bytes, which is smaller than the minimum length of Ethernet (64 bytes), and the LWIP device EMAC does not set the short packet filling function, which causes the PC to not receive the short packet. Solution: Enable short packet filling for EMAC. SHENZHEN CHONDEKUAI TECHNOLOGY CO.LTD , https://www.szfourinone.com