- LwIP支持在 Mainloop 模式(即没有目标板上没有 OS/RTOS)和 OS模式。宏 NO_SYS控制模式选择,NO_SYS设置为0,表示OS模式。
- 以下描述都是基于 OS 模式。在OS模式下,LwIP会创建一个 tcpip_thread 线程。
- 不同线程访问同一变量存在不一致的问题,为了解决这个问题,有2种解决方式:(1)保证只在一个线程(假设A线程)内访问变量,如果其它线程要访问该变量,通过线程间通信让A线程访问,再告知访问线程。(2)通过互斥量做排他性访问。 LwIP通过宏 LWIP_TCPIP_CORE_LOCKING 控制采用哪种方式。
如果 LWIP_TCPIP_CORE_LOCKING 定义为0,采用第(1)种方式;socket层的API函数的主体运行与 tcpip_thread 线程,调用者线程挂起直到函数在 tcpip_thread 线程执行完毕。如果 LWIP_TCPIP_CORE_LOCKING 定义为1,采用第(2)种方式;socket层的API函数先获取 lock_tcpip_core这个互斥量,在调用者线程执行API函数主体。
代码请查看 api_lib.c -> netconn_apimsg() -> tcpip_send_msg_wait_sem()
- 在LwIP中,socket使用 struct lwip_sock表示,每个 struct lwip_sock 都包含一个 struct netconn,这个netconn是内部处理网络连接通信最主要的结构体。netconn中包含pcb。在创建socket时,先获得一个netconn,再分配一个 struct lwip_sock,然后返回 lwip_sock数组索引。通过宏 MEMP_NUM_NETCONN 定义最多支持的socket数量。
- TCP接收数据代码流:
tcpip_input() -> ip_input() -> tcp_input() -> TCP_EVENT_RECV() -> recv_tcp() ,这是将接收到数据放入 recvmbox中。如果要实现为异步通知方式,可以在recv_tcp()之后通知关注者。如果 LWIP_TCPIP_CORE_LOCKING = 1, 该流程运行的网络设备驱动线程中;如果 LWIP_TCPIP_CORE_LOCKING = 0, ip_input()就运行于tcpip_thread。
lwip_read() -> lwip_recvfrom() -> lwip_recv_tcp() -> netconn_recv_tcp_pbuf_flags() -> netconn_recv_data_tcp() -> netconn_recv_data() (从 recvmbox 中获取数据)。如果 LWIP_TCPIP_CORE_LOCKING = 1, 那全部都运行于调用者线程。如果 LWIP_TCPIP_CORE_LOCKING = 0, 该流程大部分运行于调用者线程,少部分运行于 tcpip_thread,比如netconn_recv_data_tcp() 中 netconn_tcp_recvd_msg()和netconn_tcp_recvd_msg()的主体。
- 整体来看,如果 LWIP_TCPIP_CORE_LOCKING = 1,在 tcpip_thread 中运行的内容很少。来自网络端上行(接收方向)数据处理基本运行于网络设备驱动线程中;下行(发送方向)数据处理基本运行于调用者线程。
UIS8910DM平台移值LwIP的方式和推荐的不太一样。
(1)没有创建 tcpip_thread 线程,把需要在 tcpip_thread 执行的代码,移植到 net_thread 线程上。该工作通过修改 tcpip_thread() 函数和异步调用函数(如tcpip_inpkt、tcpip_callback_with_block、tcpip_send_msg_wait_sem、tcpip_api_call)的实现来完成。
(2)设置 LWIP_TCPIP_CORE_LOCKING = 1
(3)对网络设备的数据处理,通过线程 callback的方式,运行在 net_thread 线程上。
综上所述,UIS8910DM平台上,socket层的主动调用基本都运行在调用者的线程上,而接收数据的协议栈内的处理运行在 net_thread 线程上,然后通过消息队列将异步事件通知给使用者。