终于开始这部分的工作,计划了很久,但一直都没有实施。以前一直只使用TCP/IP但对其处理的流程确一知半解。计划拿出几天的时间好好的学习下,理解其运行的基本原理。
一个嵌入式的网络架构一般都由3部分构成:1、应用层。2、协议层。3、网卡层驱动。为了较好的理解lwip的架构。我们从应用层开始一层一层的剥开整个过程,了解整个系统时怎样串联起来的,
首先是应用层,这里会初始化网络参数。参见下面用户初始化网络的代码:
tcpip_init(tcpip_init_done_signal,(void *) &tcpipdone);
IP4_ADDR(&gw, 10, 1, 10, 1);
IP4_ADDR(&ipaddr,10, 1, 10, 234);
IP4_ADDR(&netmask,255, 255, 255, 0);
/* Add netif interfacefor lpc17xx_8x */
if(!netif_add(&lpc_netif, &ipaddr, &netmask, &gw, NULL,lpc_enetif_init,
tcpip_input)) {
LWIP_ASSERT("Netinterface failed to initialize\r\n", 0);
}
netif_set_default(&lpc_netif);
netif_set_up(&lpc_netif);
这是初始化网络代码的一部分。其中最重要的是tcpip_init()和net_add()函数。
串联其中的就是lpc_netif变量,它是一个struct netif结构体,一般一个网卡对应一个struct netif结构,并用一个list将所有网卡链接起来构成一个链表。首先先看看这个接口的定义:
struct netif {
struct netif *next; /** pointer to next in linked list;指向下一个网卡 */
ip_addr_t ip_addr;/**网卡的配置参数**/
ip_addr_t netmask;
ip_addr_t gw;
/** This function iscalled by the network device driver
* to pass a packet up the TCP/IP stack. */
netif_input_fn input;//这是个函数指针,由设备驱动程序调用,传递一个网络数据包到TCP/IP层。
/** This functionis called by the IP module when it wants
* to send a packet on the interface. Thisfunction typically
* first resolves the hardware address, thensends the packet. */
netif_output_fn output; //这也是一个函数指针,由IP模块调用发送一个网卡。
/** This function is called by the ARP modulewhen it wants
* to send a packet on the interface. Thisfunction outputs
* the pbuf as-is on the link medium. */
netif_linkoutput_fn linkoutput;//这个函数由ARP协议调用
};
在tcpip_init()中会有一个重要的调用既创建了tcpip_thread()的线程。 我们来看看这个函数到底做了些什么呢?
void tcpip_init(tcpip_init_done_fninitfunc, void *arg)
{
lwip_init();
if(sys_mbox_new(&mbox,TCPIP_MBOX_SIZE) != ERR_OK) {
LWIP_ASSERT("failedto create tcpip_thread mbox", 0);
}
#if LWIP_TCPIP_CORE_LOCKING
if(sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
LWIP_ASSERT("failed to createlock_tcpip_core", 0);
}
#endif /* LWIP_TCPIP_CORE_LOCKING */
sys_thread_new(TCPIP_THREAD_NAME ,tcpip_thread,NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
}
我从中可以看到调用了3个函数:
1、lwip_init();初始化了lwip需要要用到的一些参数。
2、sys_mutex_new()创建了一个新的互斥锁。
3 sys_thread_new()新建了一个线程处理tcpip协议栈。在这个线程中你会看到线程会阻塞在读mbox的函数上,直到网卡接收到数据才会继续执行下去。详细的代码请参看tcpip.c文件。
下面我们在来看看另一函数net_add(),我们先看它的第六个参数:lpc_enetif_init(),这个参数是一个函数指针,它是lpc mac层的驱动程序中的一个函数。我们在来看看这个函数中到底干了些什么。
{
err= low_level_init(netif);//初始化MAC以及Phy芯片
netif->output= lpc_etharp_output;//设置网卡arp输出函数到网卡接口
netif->linkoutput= lpc_low_level_output;//设置网卡IP数据输出函数网卡接口
sys_thread_new("receive_thread",vPacketReceiveTask, netif->state,
DEFAULT_THREAD_STACKSIZE,tskRECPKT_PRIORITY);
//底层的接收线程
sys_thread_new("txclean_thread",vTransmitCleanupTask, netif->state,
DEFAULT_THREAD_STACKSIZE,tskTXCLEAN_PRIORITY);
//底层的发送线程
}
我们再看看接收线程:
首先线程信号量:/* Wait for receive task to wakeup*/
sys_arch_sem_wait(&lpc_netifdata->RxSem,0);
然后读取网卡数据:
lpc_enetif_input(lpc_netifdata->netif); à pbuf * p=lpc_low_level_input(netif);//读一个包到pbuf包中 à netif->input(p, netif)//将数据传递给注册的input函数(即tcpip_input()函数)。
看完上面的,我们在看看我们在netif_add注册的tcpip_input();
lpc_netif.input=tcpip_input() 及网卡收到网络数据包调用该input函数指针。tcpip_input()函数会根据协议调用sys_mbox_trypost(&mbox, msg)邮箱发送函数,这个函数会将接收到的pbuf数据发给TCP/IP协议栈的处理进程处理(即阻塞的tcpip_thread进程);
看到这儿,我都感觉有点凌乱了。在重新的串一下整个流程:首先在主函数中,调用了tcpip_init()函数建立了tcpip的协议栈线程。接着又调用了net_add()函数,特别需要注意的是其两个传入的函数指针(这两个函数是在网卡驱动中实现的(在这里驱动与tcpip栈建立了联系)init参数和input参数。init()函数会调用网卡驱动的初始化函数err_t lpc_enetif_init(struct netif *netif)。在这个函数会新建两个线程,一个是网卡的接收线程(从网卡中读取一个数据包,并调用我们在netif中注册的input函数将数据包交给tcpip协议栈线程处理了),一个是网卡的发送线程(它会阻塞在一个信号量上,当有数据需要发送时,将数据发送出去)。讲到这里应该大概明白了lwip的驱动基本架构了吧!。