LwIP源码分析(3):内存堆和内存池代码详解


在嵌入式系统中,内存池有助于快速有效地分配内存。LwIP提供了两个灵活的方式来管理和组织内存池的大小:内存堆和内存池。当然它还支持C库中的 mallocfree来申请和释放内存,但是这种分配方式可能会产生很多堆碎片,最后造成没有内存可以分配,而且运行地越久寻找新内存的时间可能也越长,这里不建议使用这种方式。下面通过代码来详细地看一下内存堆和内存池的实现

1 内存堆

来看看LwIP中实现的堆内存管理的实现:

1.1 mem_init

先来看看mem_init函数,相关结构体和宏定义写在前面:

struct mem {
  /** index (-> ram[next]) of the next struct */
  mem_size_t next;
  /** index (-> ram[prev]) of the previous struct */
  mem_size_t prev;
  /** 1: this area is used; 0: this area is unused */
  u8_t used;
#if MEM_OVERFLOW_CHECK
  /** this keeps track of the user allocation size for guard checks */
  mem_size_t user_size;
#endif
};

#define MEM_ALIGNMENT 4
#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1U) & ~(MEM_ALIGNMENT-1U))
#define SIZEOF_STRUCT_MEM    LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))

/* 用户自定义的堆内存大小 */
#define MEM_SIZE (32 * 1024)
#define MEM_SIZE_ALIGNED     LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[LWIP_MEM_ALIGN_BUFFER(size)]
LWIP_DECLARE_MEMORY_ALIGNED(ram_heap, MEM_SIZE_ALIGNED + (2U * SIZEOF_STRUCT_MEM));
#define LWIP_RAM_HEAP_POINTER ram_heap

static struct mem * ptr_to_mem(mem_size_t ptr)
{
  return (struct mem *)(void *)&ram[ptr];
}

static struct mem * LWIP_MEM_LFREE_VOLATILE lfree;
static u8_t *ram;
---------------------------------------------------------------
void mem_init(void)
{
    struct mem *mem;
	/* 检查struct mem是否是四字节对齐 */
    LWIP_ASSERT("Sanity check alignment",
    (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT - 1)) == 0);

    /* 将ram变量的指针指向上面声明的ram_heap数组,其大小为(MEM_SIZE+2*mem结构体大小) */
    ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
    /* 上面ram_heap数组的大小多了两个mem结构体,分别填充在ram_heap的起始和结尾 */
    /* 初始化堆的起始 */
    mem = (struct mem *)(void *)ram;
    /* 下一个内存的偏移量为MEM_SIZE_ALIGNED,即结束 */
    mem->next = MEM_SIZE_ALIGNED;
    /* 前一个内存的偏移量 */
    mem->prev = 0;
    /* 当前内存没有被使用 */
    mem->used = 0;
    /* 初始化堆的结束:指向上一个内存的结束位置 */
    ram_end = ptr_to_mem(MEM_SIZE_ALIGNED);
    /* 由于当前结构体为标记堆内存的结束,无实际大小,标记使用 */
    ram_end->used = 1;
    /* 标记下一个内存的偏移量:下一个还是自己表示到头了 */
    ram_end->next = MEM_SIZE_ALIGNED;
    /* 标记前一个内存的偏移量 */
    ram_end->prev = MEM_SIZE_ALIGNED;
    /* 合理性检验:一般在每次mem_free()后调用,确保堆链表结构体内内存的合法性和是否对齐 */
    MEM_SANITY();

    /* 将堆内存中第一块可用内存的指针赋值给lfree */
    lfree = (struct mem *)(void *)ram;
	/* 内存统计相关:略 */
    MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
	/* 创建一个内存堆分配时的互斥量:避免多个任务/线程同时申请和释放内存造成错误 */
    if (sys_mutex_new(&mem_mutex) != ERR_OK) {
    	LWIP_ASSERT("failed to create mem_mutex", 0);
    }
}

初始化后ram_heap数组的内存分配如下所示:

在这里插入图片描述

1.2 mem_malloc

首先来看看内存分配的函数,它的原理是First Fit,即遍历空闲内存,找到第一个满足大小的堆内存进行分配,并将剩下的返回内存堆。

对于mem_malloc有三种方式,第一种为C库的malloc,一种为使用内存池(memory pool)方式,还有一种就是内存堆方式,这里对内存堆分配方式进行介绍(具体实现见代码注释):

  • 该函数中有一个宏定义:LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT,这是允许用户可以在中断或不允许等待信号量的上下文中释放pbuf,它的原理就是使用信号量和SYS_ARCH_PROTECT保护mem_malloc函数,使用SYS_ARCH_PROTECT保护mem_free函数。只有mem_mallocSYS_ARCH_UNPROTECTs后,mem_free才可以允许。

    • 很明显,这种方式要经常开关中断,如果在内存很少的嵌入式系统中,mem_malloc的速度会非常慢,一般都不会打开,所以这里不对相关宏定义的代码进行解析。
  • 函数中对于overflow检查的相关代码也将省略

void* mem_malloc(mem_size_t size_in)
{
    mem_size_t ptr, ptr2, size;
    struct mem *mem, *mem2;
    /* 声明一个unsigned long的变量lev用于同步 */
    LWIP_MEM_ALLOC_DECL_PROTECT();
    /* 分配的内存大小为0则直接返回 */
    if (size_in == 0)
    {
        return NULL;
    }

    /* 保证分配的内存是字节对齐的 */
    size = (mem_size_t)LWIP_MEM_ALIGN_SIZE(size_in);
    /* 分配的内存的大小必须比MIN_SIZE_ALIGNED(12)大:防止分配过多小内存造成内存碎片 */
    if (size < MIN_SIZE_ALIGNED)
    {
        /* every data block must be at least MIN_SIZE_ALIGNED long */
        size = MIN_SIZE_ALIGNED;
    }
    /* 如果size大于总的堆内存的大小或者小于待分配内存的大小,则返回NULL */
    if ((size > MEM_SIZE_ALIGNED) || (size < size_in))
    {
        return NULL;
    }

    /* 获得在mem_init中创建的互斥量,以并发地使用内存管理函数 */
    sys_mutex_lock(&mem_mutex);
    /* 在有OS的情况下:如果当前函数在中断中运行,则lev=basepri并设置优先级高于lev的中断才能打断;
       否则进入临界区 */
    LWIP_MEM_ALLOC_PROTECT();

    /* 从lfree指针开始用first fit方式寻找第一个足够大的内存 */
    for (ptr = mem_to_ptr(lfree); ptr < MEM_SIZE_ALIGNED - size;
            ptr = ptr_to_mem(ptr)->next)
    {
        mem = ptr_to_mem(ptr);
        /* 如果该内存没有used且大小满足(size+内存头mem结构体)的大小 */
        if ((!mem->used) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size)
        {
            /* 如果该内存除去我们申请的内存后,剩余的大小还比(struct mem + 最小申请内存MIN_SIZE_ALIGNED)大
               则将剩下的内存返回内存堆中 */
            if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED))
            {
                ptr2 = (mem_size_t)(ptr + SIZEOF_STRUCT_MEM + size);
                LWIP_ASSERT("invalid next ptr",ptr2 != MEM_SIZE_ALIGNED);
                /* create mem2 struct */
                mem2 = ptr_to_mem(ptr2);
                mem2->used = 0;
                mem2->next = mem->next;
                mem2->prev = ptr;
                /* and insert it between mem and mem->next */
                mem->next = ptr2;
                mem->used = 1;

                if (mem2->next != MEM_SIZE_ALIGNED)
                {
                    ptr_to_mem(mem2->next)->prev = ptr2;
                }
            }
            else
            {
                /* 剩下的内存不足以分配一个新的内存堆,只好浪费,标记整块内存used */
                mem->used = 1;
            }
            /* 如果分配的内存为第一块,则需要更新lfree指针(指向第一块空闲的堆的指针) */
            if (mem == lfree)
            {
                struct mem *cur = lfree;
                /* Find next free block after mem and update lowest free pointer */
                while (cur->used && cur != ram_end)
                {
                    cur = ptr_to_mem(cur->next);
                }
                lfree = cur;
                LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used)));
            }
            /* 对应前面的protect,若在中断中则使能中断并清空basepri寄存器;
               若不在中断中,则退出临界区 */
            LWIP_MEM_ALLOC_UNPROTECT();
            /* 释放互斥锁 */
            sys_mutex_unlock(&mem_mutex);
            /* 返回申请的内存的地址,这里不打开完整性检查,即MEM_SANITY_OFFSET为0 */
            return (u8_t *)mem + SIZEOF_STRUCT_MEM + MEM_SANITY_OFFSET;
        }
    }
    /* 找不到足够大的内存,释放锁 */
    LWIP_MEM_ALLOC_UNPROTECT();
    sys_mutex_unlock(&mem_mutex);
    return NULL;
}

1.3 mem_free

mem_free函数也很好理解,具体见注释:

void mem_free(void *rmem)
{
  struct mem *mem;
  /* 和mem_malloc一样,声明一个lev变量 */
  LWIP_MEM_FREE_DECL_PROTECT();

  if (rmem == NULL) {
    return;
  }
  /* 由于分配的内存都是四字节对齐的,若不对齐则传入的参数错误 */
  if ((((mem_ptr_t)rmem) & (MEM_ALIGNMENT - 1)) != 0) {
    LWIP_MEM_ILLEGAL_FREE("mem_free: sanity check alignment");
    return;
  }

  /* 获取该段内存前面的结构体的内容 */
  mem = (struct mem *)(void *)((u8_t *)rmem - (SIZEOF_STRUCT_MEM + MEM_SANITY_OFFSET));
  /* 如果结构体的首地址不在整个内存堆的范围内则直接返回 */
  if ((u8_t *)mem < ram || (u8_t *)rmem + MIN_SIZE_ALIGNED > (u8_t *)ram_end) {
    return;
  }

  /* 痛mem_malloc中的LWIP_MEM_ALLOC_PROTECT() */
  LWIP_MEM_FREE_PROTECT();
  /* 如果mem->used为0,说明参数出错,直接返回 */
  if (!mem->used) {
    return;
  }
  /* 该函数检查mem->prev和mem->next的参数合法性 */
  if (!mem_link_valid(mem)) {
    LWIP_MEM_FREE_UNPROTECT();
    return;
  }

  /* 标记该mem为not used */
  mem->used = 0;
  /* 如果释放的mem为第一块内存,则更新lfree */
  if (mem < lfree) {
    /* the newly freed struct is now the lowest */
    lfree = mem;
  }

  /* 最后,如果释放的内存的prev和next有not used的话,则合并 */
  plug_holes(mem);
  LWIP_MEM_FREE_UNPROTECT();
}

2 内存池

在LwIP的数据段中保留了一个固定大小的静态内存,这段内存被分为多个内存池(pools),用来描述各种不同的数据结构,比如TCP、UDP、IP等报文的首部,它们的长度是固定的,这样每次分配和释放都不会产生内存碎片。比如有一个内存池用于TCP控制结构体struct tcp_pcb,另一个内存池用于UDP控制结构体struct udp_pcb。每个内存池都可以配置它可以容量的最大数量的数据结构,比如上面的TCP和UDP控制块的个数可以通过MEMP_NUM_TCP_PCBMEMP_NUM_UDP_PCB进行修改。

下面来对内存池的代码进行分析,代码中去掉了统计分析和overflow检查的相关代码。

2.1 memp_init

首先是内存池的初始化函数:

void memp_init(void)
{
  u16_t i;
  /* for every pool: */
  for (i = 0; i < LWIP_ARRAYSIZE(memp_pools); i++) {
    memp_init_pool(memp_pools[i]);
  }
}

可以看到内存池的初始化函数非常简单,就是对每一个定义的内存池进行初始化。现在来看看memp_pools是什么:

const struct memp_desc *const memp_pools[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
#include "lwip/priv/memp_std.h"
};

其中MEMP_MAX的定义如下:

typedef enum {
#define LWIP_MEMPOOL(name,num,size,desc)  MEMP_##name,
#include "lwip/priv/memp_std.h"
  MEMP_MAX
} memp_t;

可以看出来,在memp_std.h中定义了多个内存池,每个内存池的参数包括:名字(name)、个数(num)、大小(size)和描述(desc),下面来看一下这个文件:

#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[LWIP_MEM_ALIGN_BUFFER(size)]

#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \
  LWIP_DECLARE_MEMORY_ALIGNED(memp_memory_ ## name ## _base, ((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))); \
    \
  LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(memp_stats_ ## name) \
    \
  static struct memp *memp_tab_ ## name; \
    \
  const struct memp_desc memp_ ## name = { \
    DECLARE_LWIP_MEMPOOL_DESC(desc) \
    LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(memp_stats_ ## name) \
    LWIP_MEM_ALIGN_SIZE(size), \
    (num), \
    memp_memory_ ## name ## _base, \
    &memp_tab_ ## name \
  };
  
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
------------
#if LWIP_RAW
LWIP_MEMPOOL(RAW_PCB,        MEMP_NUM_RAW_PCB,         sizeof(struct raw_pcb),        "RAW_PCB")
#endif /* LWIP_RAW */

#if LWIP_UDP
LWIP_MEMPOOL(UDP_PCB,        MEMP_NUM_UDP_PCB,         sizeof(struct udp_pcb),        "UDP_PCB")
#endif /* LWIP_UDP */

#if LWIP_TCP
LWIP_MEMPOOL(TCP_PCB,        MEMP_NUM_TCP_PCB,         sizeof(struct tcp_pcb),        "TCP_PCB")
LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN,  sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN")
LWIP_MEMPOOL(TCP_SEG,        MEMP_NUM_TCP_SEG,         sizeof(struct tcp_seg),        "TCP_SEG")
#endif /* LWIP_TCP */

#if LWIP_ALTCP && LWIP_TCP
LWIP_MEMPOOL(ALTCP_PCB,      MEMP_NUM_ALTCP_PCB,       sizeof(struct altcp_pcb),      "ALTCP_PCB")
#endif /* LWIP_ALTCP && LWIP_TCP */

#if LWIP_IPV4 && IP_REASSEMBLY
LWIP_MEMPOOL(REASSDATA,      MEMP_NUM_REASSDATA,       sizeof(struct ip_reassdata),   "REASSDATA")
#endif /* LWIP_IPV4 && IP_REASSEMBLY */
#if (IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF) || (LWIP_IPV6 && LWIP_IPV6_FRAG)
LWIP_MEMPOOL(FRAG_PBUF,      MEMP_NUM_FRAG_PBUF,       sizeof(struct pbuf_custom_ref),"FRAG_PBUF")
#endif /* IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF || (LWIP_IPV6 && LWIP_IPV6_FRAG) */

#if LWIP_NETCONN || LWIP_SOCKET
LWIP_MEMPOOL(NETBUF,         MEMP_NUM_NETBUF,          sizeof(struct netbuf),         "NETBUF")
LWIP_MEMPOOL(NETCONN,        MEMP_NUM_NETCONN,         sizeof(struct netconn),        "NETCONN")
#endif /* LWIP_NETCONN || LWIP_SOCKET */

可以看到一个宏定义LWIP_MEMPOOL就根据参数声明了一个内存堆数组,还有mempmemp_desc结构体。

现在回过头来看MEMP_MAX,前面的#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name就是暂时用一下文件中这个定义,将LWIP_MEMPOOL的宏定义再进行一层替换,作为enum的值的名称,所以MEMP_MAX就是内存堆的总个数。

memp_pools就是将在memp_std.h中用LWIP_MEMPOOL声明的各个内存池的memp_desc结构体的地址包含进来。

最后再回来看memp_init_pool函数:

struct memp {
  struct memp *next;
};

struct memp_desc {
  /** Element size */
  u16_t size;
  /** Number of elements */
  u16_t num;
  /** Base address */
  u8_t *base;
  /** First free element of each pool. Elements form a linked list. */
  struct memp **tab;
};
--------
void memp_init_pool(const struct memp_desc *desc)
{
  int i;
  struct memp *memp;

  *desc->tab = NULL;
  memp = (struct memp *)LWIP_MEM_ALIGN(desc->base);

  /* 初始化为0:如果MEMP_MEM_INIT宏定义为1的话 */
  memset(memp, 0, (size_t)desc->num * (MEMP_SIZE + desc->size));

  /* create a linked list of memp elements */
  for (i = 0; i < desc->num; ++i) {
    memp->next = *desc->tab;
    *desc->tab = memp;
    /* cast through void* to get rid of alignment warnings */
    memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size);
  }
}

其中base就是通过LWIP_MEMPOOL_DECLARE声明的第一项大小为num*(MEMP_SIZE+MEMP_ALIGN_SIZE(size))的数组。除了每个内存池的大小是固定的,它的原理和内存堆类似,内存池的大小包括内存池的实际存数据大小和内存池结构体struct memp的大小。
tab就是通过LWIP_MEMPOOL_DECLARE声明的struct *memp指针变量的地址,它表示内存池中第一个没有使用的pool。

所以这个函数就是将每个声明的内存池通过链表连接起来,其中desc->tab为每个内存池的基地址。

最后,根据memp_init_pool函数,memp_pools中的每个memp_desc初始化后的示意图如下:
在这里插入图片描述

2.2 memp_malloc

来看一下内存池分配函数:

void *memp_malloc(memp_t type)
{
	void*memp;
	LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
	memp = do_memp_malloc_pool_fn(memp_pools[type], file, line);
	return memp;
}

参数type的定义如下:

typedef enum {
#define LWIP_MEMPOOL(name,num,size,desc)  MEMP_##name,
#include "lwip/priv/memp_std.h"
	MEMP_MAX
} memp_t;

memp_t就是根据LWIP_MEMPOOL定义的各个内存池的name而组成的枚举类型,我们就通过这个枚举类型来申请相应的内存池中的内存。

然后来看看do_memp_malloc_pool_fn函数:

static void* do_memp_malloc_pool(const struct memp_desc *desc)
{
	struct memp *memp;
	/* 与内存堆中的作用一样:为了执行关中断/进临界区等操作,这里不再解释 */
	SYS_ARCH_DECL_PROTECT(old_level);
	SYS_ARCH_PROTECT(old_level);
	/*  获取对于memp的tab */
	memp = *desc->tab;
	if (memp != NULL) {
		/* 指向下一个memp结构体 */
		*desc->tab = memp->next;
		SYS_ARCH_UNPROTECT(old_level);
		/* 将内存的首地址返回 */
		return ((u8_t *)memp + MEMP_SIZE);
	} else {
		SYS_ARCH_UNPROTECT(old_level);
	}
	
	return NULL;
}

2.3 memp_free

void memp_free(memp_t type, void *mem)
{
	if (mem == NULL) {
		return;
	}
	do_memp_free_pool(memp_pools[type], mem);
}

同样地来看看do_memp_free_pool函数:

static void do_memp_free_pool(const struct memp_desc *desc, void *mem)
{
	struct memp *memp;
	SYS_ARCH_DECL_PROTECT(old_level);
	/* 获得该内存前面的结构体的地址 */
	memp = (struct memp *)(void *)((u8_t *)mem - MEMP_SIZE);
	SYS_ARCH_PROTECT(old_level);
	/* 将该内存地址加入到desc->tab的最前面,表示空闲 */
	memp->next = *desc->tab;
	*desc->tab = memp;
	SYS_ARCH_UNPROTECT(old_level);
}

3 内存管理宏定义

(1)MEM_LIBC_MALLOC:使用C库中的mallocfree管理内存
(2)MEMP_MEM_MALLOC:使用内存堆方式来分配内存池的内存。上面内存池的分配中我把相关代码注释掉了,如果此宏打开,在分配和释放内存时会直接使用mem_mallocmem_free
(3)MEM_USE_POOLS:使用内存池的方式来分配内存堆的内存。同上。
若此宏打开,还需将宏MEMP_MEM_MALLOC关闭,并将MEMP_USE_CUSTOM_POOLS宏打开,表示创建一个lwippools.h文件,然后用LWIP_MALLOC_MEMPOOL来声明自己的内存。
下面给出一个lwippools.h的配置例子:

/* OPTIONAL: Pools to replace heap allocation
 * Optional: Pools can be used instead of the heap for mem_malloc. If
 * so, these should be defined here, in increasing order according to
 * the pool element size.
 *
 * LWIP_MALLOC_MEMPOOL(number_elements, element_size)
 */
#if MEM_USE_POOLS
LWIP_MALLOC_MEMPOOL_START
LWIP_MALLOC_MEMPOOL(100, 256)
LWIP_MALLOC_MEMPOOL(50, 512)
LWIP_MALLOC_MEMPOOL(20, 1024)
LWIP_MALLOC_MEMPOOL(20, 1536)
LWIP_MALLOC_MEMPOOL_END
#endif /* MEM_USE_POOLS */

/* Optional: Your custom pools can go here if you would like to use
 * lwIP's memory pools for anything else.
 */
LWIP_MEMPOOL(SYS_MBOX, 22, 100, "SYS_MBOX")
  • 除了声明内存池给内存堆分配外,可以在最后声明一个内存池供自己使用
  • 这种方式适合用在SRAM很大的情况下,以空间换取执行速度
  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
INTRODUCTION lwIP is a small independent implementation of the TCP/IP protocol suite. The focus of the lwIP TCP/IP implementation is to reduce the RAM usage while still having a full scale TCP. This making lwIP suitable for use in embedded systems with tens of kilobytes of free RAM and room for around 40 kilobytes of code ROM. lwIP was originally developed by Adam Dunkels at the Computer and Networks Architectures (CNA) lab at the Swedish Institute of Computer Science (SICS) and is now developed and maintained by a worldwide network of developers. FEATURES * IP (Internet Protocol, IPv4 and IPv6) including packet forwarding over multiple network interfaces * ICMP (Internet Control Message Protocol) for network maintenance and debugging * IGMP (Internet Group Management Protocol) for multicast traffic management * MLD (Multicast listener discovery for IPv6). Aims to be compliant with RFC 2710. No support for MLDv2 * ND (Neighbor discovery and stateless address autoconfiguration for IPv6). Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 (Address autoconfiguration) * DHCP, AutoIP/APIPA (Zeroconf), ACD (Address Conflict Detection) and (stateless) DHCPv6 * UDP (User Datagram Protocol) including experimental UDP-lite extensions * TCP (Transmission Control Protocol) with congestion control, RTT estimation fast recovery/fast retransmit and sending SACKs * raw/native API for enhanced performance * Optional Berkeley-like socket API * TLS: optional layered TCP ("altcp") for nearly transparent TLS for any TCP-based protocol (ported to mbedTLS) (see changelog for more info) * PPPoS and PPPoE (Point-to-point protocol over Serial/Ethernet) * DNS (Domain name resolver incl. mDNS) * 6LoWPAN (via IEEE 802.15.4, BLE or ZEP) APPLICATIONS * HTTP server with SSI and CGI (HTTPS via altcp) * SNMPv2c agent with MIB compiler (Simple Network Management Protocol), v3 via altcp * SNTP (Simple network time protocol) * NetBIOS name service responder * MDNS (Multicast DNS) responder * iPerf server implementation * MQTT client (TLS support via altcp) LICENSE lwIP is freely available under a BSD license. DEVELOPMENT lwIP has grown into an excellent TCP/IP stack for embedded devices, and developers using the stack often submit bug fixes, improvements, and additions to the stack to further increase its usefulness. Development of lwIP is hosted on Savannah, a central point for software development, maintenance and distribution. Everyone can help improve lwIP by use of Savannah's interface, Git and the mailing list. A core team of developers will commit changes to the Git source tree. The lwIP TCP/IP stack is maintained in the 'lwip' Git module and contributions (such as platform ports) are in the 'contrib' Git module. See doc/savannah.txt for details on Git server access for users and developers. The current Git trees are web-browsable: http://git.savannah.gnu.org/cgit/lwip.git http://git.savannah.gnu.org/cgit/lwip/lwip-contrib.git Submit patches and bugs via the lwIP project page: http://savannah.nongnu.org/projects/lwip/ Continuous integration builds (GCC, clang): https://travis-ci.org/lwip-tcpip/lwip DOCUMENTATION Self documentation of the source code is regularly extracted from the current Git sources and is available from this web page: http://www.nongnu.org/lwip/ Also, there are mailing lists you can subscribe at http://savannah.nongnu.org/mail/?group=lwip plus searchable archives: http://lists.nongnu.org/archive/html/lwip-users/ http://lists.nongnu.org/archive/html/lwip-devel/ There is a wiki about lwIP at http://lwip.wikia.com/wiki/LwIP_Wiki You might get questions answered there, but unfortunately, it is not as well maintained as it should be. lwIP was originally written by Adam Dunkels: http://dunkels.com/adam/ Reading Adam's papers, the files in docs/, browsing the source code documentation and browsing the mailing list archives is a good way to become familiar with the design of lwIP. Adam Dunkels Leon Woestenberg

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tilblackout

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值