LWIP之启动流程

启动流程图: 

启动时序:

启动程序会创建2个线程:tcpip_thread负责LWIP的绝大部分工作(主要是协议栈的解析和系统运行),ethernetif_thread负责从网口接收数据包再交付给tcpip_thread线程进行处理。

 

路径:lwip-2.1.2\src\api\tcpip.c

static tcpip_init_done_fn tcpip_init_done;
static void *tcpip_init_done_arg;
static sys_mbox_t tcpip_mbox;
sys_mutex_t lock_tcpip_core;

void tcpip_init(tcpip_init_done_fn initfunc, void *arg)
{
	/* 初始化LwIP */
	lwip_init();
	/* 初始化完毕后调用的回调函数,可传NULL,在tcpip_thread开头调用 */
	tcpip_init_done = initfunc;
	tcpip_init_done_arg = arg;
	/* 创建message box,实际上是FreeRTOS的Queue
	 * 每个项目大小为一个指针的大小(4B),队列长度为TCPIP_MBOX_SIZE */
	if (sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
		LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
	}
	#if LWIP_TCPIP_CORE_LOCKING
	/* 创建互斥锁:用户可以通过这个锁在代码中实现LwIP的一些操作,而不需要在tcpip_thread的callback中实现 */
	if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
		LWIP_ASSERT("failed to create lock_tcpip_core", 0);
	}
	#endif /* LWIP_TCPIP_CORE_LOCKING */
	/* 创建FreeRTOS任务tcpip_thread */
	sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
}

其中lwip_init函数内容如下(各个初始化需要打开相应的宏定义

void lwip_init(void)
{
	 /* 统计模块初始化,这里只是初始化lwip_stats.mem.name为MEM */
	 stats_init();
	 /* 带操作系统时的初始化:函数中暂时没有内容 */
	 sys_init();
	 /* 初始化内存堆的起始地址、结束地址以及空闲列表 */
	 mem_init();
	 /* 初始化LwIP内存池 */
	 memp_init();
	 /* 初始化pbuf:函数中暂时没有内容 */
	 pbuf_init();
	 /* 初始化netif:主要是环回的ip,网关,子网掩码以及netif的添加和配置 */
	 netif_init();
	 /* 兼容老版本:函数中暂时没有内容 */
	 ip_init();
	 /* 兼容老版本:函数中暂时没有内容 */
	 etharp_init();
	 /* 兼容老版本:函数中暂时没有内容 */
	 raw_init();
	 /* 初始化UDP端口号(随机分配),范围:0xc000~0xffff */
	 udp_init();
	 /* 初始化TCP端口号(随机分配),范围:0xc000~0xffff */
	 tcp_init();
	 /* 初始化组播IP */
	 igmp_init();
	 /* 初始化DNS解析:设置UDP PCB并配置默认服务器 */
	 dns_init();
	 /* 初始化PPP:根据配置分配相关的结构体内存和初始化魔术字(PPP连接需要用到) */
	 ppp_init();
	 /* 初始化软件定时器 */
	 sys_timeouts_init();
}

可以看到tcpip_init最终调用sys_thread_new创建了一个tcpip_thread任务,看看它做了什么:

static void
tcpip_thread(void *arg)
{
  struct tcpip_msg *msg;  // 用于存储接收到的消息
  LWIP_UNUSED_ARG(arg);   // 表示参数 arg 未被使用

  LWIP_MARK_TCPIP_THREAD();

  LOCK_TCPIP_CORE();
  // 如果有初始化完成的回调函数,执行它
  if (tcpip_init_done != NULL) {
    tcpip_init_done(tcpip_init_done_arg);
  }

  while (1) {                          /* MAIN Loop */
    LWIP_TCPIP_THREAD_ALIVE();
    /* wait for a message, timeouts are processed while waiting */
    // 从消息邮箱中获取消息
    TCPIP_MBOX_FETCH(&tcpip_mbox, (void **)&msg);
    // 如果获取到的消息为空
    if (msg == NULL) {
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
      LWIP_ASSERT("tcpip_thread: invalid message", 0);
      continue;
    }
    // 处理消息
    tcpip_thread_handle_msg(msg);
  }
}

带操作系统时是用 tcpip_timeouts_mbox_fetch(mbox, msg)函数

/**
 * Wait (forever) for a message to arrive in an mbox.
 * While waiting, timeouts are processed.
 *
 * @param mbox the mbox to fetch the message from
 * @param msg the place to store the message
 */
/**
 * 从指定的邮箱中获取消息,并处理超时情况
 *
 * @param mbox 要操作的邮箱
 * @param msg 用于存储获取到的消息的指针
 */
static void tcpip_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
{
    u32_t sleeptime, res;

again:
	/* 计算下一个到期的定时器的时间和当前时间的差 */
    sleeptime = sys_timeouts_sleeptime();
    if (sleeptime == SYS_TIMEOUTS_SLEEPTIME_INFINITE) {
    	/* 系统中没有的定时器 */
        UNLOCK_TCPIP_CORE();
        /* 阻塞等待mbox的消息:若阻塞的时候有新的定时器创建,
         * 是否也会有一个mbox消息,否则一直阻塞?(todo:后续阅读相关代码) */
        sys_arch_mbox_fetch(mbox, msg, 0);
        LOCK_TCPIP_CORE();
        return;
    } else if (sleeptime == 0) {
    	/* 有定时器已经到期 */
        sys_check_timeouts();
        /* 回去重新检查是否有多个定时器同时到期 */
        goto again;
    }
	/* 没有定时器到期:这段时间(sleeptime)来检查mbox有没有消息 */
    UNLOCK_TCPIP_CORE();
    res = sys_arch_mbox_fetch(mbox, msg, sleeptime);
    LOCK_TCPIP_CORE();
    if (res == SYS_ARCH_TIMEOUT) {
        /* 等待sleeptime后还是没有mbox到期,此时肯定有定时器到期,去检查 */
        sys_check_timeouts();
        /* 再回去检查mbox中是否有数据 */
        goto again;
    }
}

tcpip_timeouts_mbox_fetch 的作用检查系统timeout的定时器,因为下一个定时器到期的时间是确定的,所以在这个间隔内可以等待mbox的消息。
由于tcpip_thread中需要tcpip_timeouts_mbox_fetch中返回一个msg去处理,所以该函数的退出时机是等到了mbox中的消息。

 

对于这边的互斥锁,tcpip_thread一上来就调用LOCK_TCPIP_CORE上锁,在TCPIP_MBOX_FETCH中等待消息队列阻塞时释放这个锁,等待完这个队列,无论有没有消息到来,sys_arch_mbox_fetch返回后就立即上锁,因为在此期间tcpip_thread已经阻塞死等,此时用户可以调用部分LwIP内核函数。

整体框架图: 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值