9.LwIP一探究竟¶

9.LwIP一探究竟¶

9.4.3. API消息¶

LwIP使用api_msg结构体描述一个API消息的内容,具体见 代码清单9_11。

代码清单 9‑11api_msg结构体(已删减)

struct api_msg

{

struct netconn *conn; //当前连接

err_t err; //执行结果

union

{

struct netbuf *b; //执行lwip_netconn_do_send需要的参数,待发送数据

struct

{

u8_t proto; //执行lwip_netconn_do_newconn需要的参数,连接类型

} n;

//执行lwip_netconn_do_bind 和 lwip_netconn_do_connect需要的参数

struct

{

API_MSG_M_DEF_C(ip_addr_t, ipaddr); //ip地址

u16_t port; //端口号

u8_t if_idx;

} bc;

//执行lwip_netconn_do_getaddr需要的参数

struct

{

ip_addr_t API_MSG_M_DEF(ipaddr);//ip地址

u16_t API_MSG_M_DEF(port); //端口号

u8_t local;

} ad;

//执行lwip_netconn_do_write需要的参数

struct

{

const struct netvector *vector; //要写入的当前向量

u16_t vector_cnt; //未写入的向量的数量

size_t vector_off; //偏移到当前向量

size_t len; //总长度

size_t offset; //偏移量

u8_t apiflags;

} w;

//执行lwip_netconn_do_write需要的参数

struct

{

size_t len; //长度

} r;

} msg;

};

api_msg只包含3个字段,描述连接信息的conn、内核返回的执行结果err、还有msg,msg是一个共用体,根据不一样

的API接口使用不一样的数据结构。在conn中,它保存了当前连接的重要信息,如信号量、邮箱等,lwip_netconn_do_xxx(xxx表示不一样的NETCONN

API接口)类型的函数执行需要用这些信息来完成与应用线程的通信与同步;内核执行lwip_netconn_do_xxx类型的函数返回结果会被记录在err中;msg的各个产业记录各个函数执行时需要的详细参数。

我们了解底层的数据包消息,那么同理对于上层的API函数,想要与内核进行数据交互,也是通过LwIP的消息机制,API消息由用户线程发出,与内核进行交互,因为用户的应用程序并不是与内核处于同一线程中,简单来说就是用户使用NETCONN

API接口的时候,LwIP会将对应API函数与参数构造成消息传递到tcpip_thread线程中,然后根据对应的API函数执行对应的操作,LwIP这样子处理是为了简单用户的编程,这样子就不要求用户对内核很熟悉,与数据包消息类似,也是有独立的API消息投递函数去处理,那就是netconn_apimsg()函数,在NETCONN

API中构造完成数据包,就会调用netconn_apimsg()函数进行投递消息,具体见 代码清单9_12。

代码清单 9‑12NETCONN API构造消息(以netconn_bind为例,已删减)

err_t

netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port)

{

API_MSG_VAR_DECLARE(msg);

err_t err;

if (addr == NULL)

{

addr = IP4_ADDR_ANY;

}

API_MSG_VAR_ALLOC(msg);

API_MSG_VAR_REF(msg).conn = conn;

API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);

API_MSG_VAR_REF(msg).msg.bc.port = port; (1)

err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg)); (2)

API_MSG_VAR_FREE(msg);

return err;

}

static err_t

netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)

{

err_t err;

err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));

if (err == ERR_OK)

{

return apimsg->err;

}

return err;

}

err_t

tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t *sem)

{

TCPIP_MSG_VAR_DECLARE(msg);

TCPIP_MSG_VAR_ALLOC(msg);

TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API;

TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn;

TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg; (3)

sys_mbox_post(&tcpip_mbox, &TCPIP_MSG_VAR_REF(msg)); (4)

sys_arch_sem_wait(sem, 0); (5)

TCPIP_MSG_VAR_FREE(msg);

return ERR_OK;

}

代码清单

9‑12(1):根据netconn_bind()传递的参数初始化api_msg结构体。

代码清单

9‑12(2):调用netconn_apimsg()函数投递这个api_msg结构体,

这个函数实际上是调用tcpip_send_msg_wait_sem()函数投递API消息的,

并且需要等待tcpip_thread线程的回应。

代码清单

9‑12(3):构造API消息,类型为TCPIP_MSG_API,函数为API对应的函数lwip_netconn_do_bind,将msg

的指针指向api_msg结构体。

代码清单 9‑12(4):调用sys_mbox_post()函数向内核进行投递消息。

代码清单

9‑12(5):同时调用sys_arch_sem_wait()函数等待消息处理完毕

总的来说,用户的应用线程与内核也是相互独立的,依赖操作系统的ICP通信机制进行数据交互与同步(邮箱、信号量等),

LwIP提供上层NETCONN API接口,会自动帮我们处理这些事情,只需要我们根据API接口传递正确的参数接口,

当然,NETCONN API的使用我们会在后面的章节具体介绍,此处仅做了解一下即可,

只是为了让大家对LwIP整个内核的运作有个详细的了解,其运作示意图具体见 图9_4。

图 9‑4API消息运作

其实这个运作示意图并不是最优的,这种运作的方式在每次发送数据的时候,会进行一次线程的调度,这无疑是增大了系统的开销,而将LWIP_TCPIP_CORE_LOCKING宏定义设置为1则无需操作系统邮箱与信号量的参与,直接在用户线程中通过回调函数调用对应的处理,当然在这个过程中,内核线程是无法获得互斥量而运行的,因为是通过互斥量进行保护用户线程的处理,当然,LwIP的作者也是这样子建议的。

更多详细内容请看tcpip_send_msg_wait_sem()源码,具体见

代码清单9_13。注意:此处的源码是无删减的,它通过宏定义LWIP_TCPIP_CORE_LOCKING决定运行哪部分代码。

代码清单 9‑13 tcpip_send_msg_wait_sem()源码

err_t

tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t *sem)

{

#if LWIP_TCPIP_CORE_LOCKING

LWIP_UNUSED_ARG(sem);

LOCK_TCPIP_CORE();

fn(apimsg); //调用对应的回调函数去处理

UNLOCK_TCPIP_CORE();

return ERR_OK;

#else /* LWIP_TCPIP_CORE_LOCKING */

TCPIP_MSG_VAR_DECLARE(msg);

LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem));

LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));

TCPIP_MSG_VAR_ALLOC(msg);

TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API;

TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn;

TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg;

sys_mbox_post(&tcpip_mbox, &TCPIP_MSG_VAR_REF(msg));

sys_arch_sem_wait(sem, 0);

TCPIP_MSG_VAR_FREE(msg);

return ERR_OK;

#endif /* LWIP_TCPIP_CORE_LOCKING */

}

相关推荐

核桃是怎么种出来的?
365 体育投注

核桃是怎么种出来的?

📅 07-10 👁️ 3255
核桃是怎么种出来的?
365 体育投注

核桃是怎么种出来的?

📅 07-10 👁️ 3255
云梦助眠引导多久关闭
365 体育投注

云梦助眠引导多久关闭

📅 06-28 👁️ 1149