打印

狗拿耗子LWIP

[复制链接]
4673|49
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
zheng522|  楼主 | 2015-5-27 18:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
狗拿耗子LWIP

狗拿耗子LWIP.pdf

958.19 KB

沙发
zheng522|  楼主 | 2015-5-27 18:28 | 只看该作者
1、 lwip 的背景
lwip 是 Swedish Institute of Computer Science 开发的用于嵌入式系统的 TCP\IP 协议栈,从
网上的评论看似乎用的人不少。它的实现是应该参考了 BSD 的实现,在介绍 TCP 的时候,
大家就会发现,其拥塞控制的实现算法机会与 BSD 的一模一样。 lwip 的整个代码写的比
YASS2 差一截,难以入手介绍,我打算按照 TCP 的 server 与 client 的角度分别走一遍代码。
lwip 的内核运行在同一个任务中, lwip 提供的系统调用通过 mailbox 与内核进行通信,然
后用户阻塞在一个专门的 mailbox 上,内核完成用户的请求后 post 该 mailbox,用户得以继
续执行。有的协议栈的实现却是,每层跑在一个任务中,这样层与层间的相互调用将会引起
上下文的切换。更重要的是 lwip 可以运行在裸机的环境中,即不需要操作系统的支持。这
对一些低成本的设备还是很具有吸引力的。
lwip 的官方网站为 http://savannah.nongnu.org/projects/lwip/,目前最新的版本是 1.3.0,而
本文参考的是 1.2.0。

使用特权

评论回复
板凳
zheng522|  楼主 | 2015-5-27 18:29 | 只看该作者
2、 netconn_new 系统调用
2.1 相关的数据结构
enum netconn_type {
NETCONN_TCP,
NETCONN_UDP,
NETCONN_UDPLITE,
NETCONN_UDPNOCHKSUM,
NETCONN_RAW
};

使用特权

评论回复
地板
zheng522|  楼主 | 2015-5-27 18:29 | 只看该作者
struct netconn {
enum netconn_type type;
enum netconn_state state;
union {
struct tcp_pcb *tcp;
struct udp_pcb *udp;
struct raw_pcb *raw;
} pcb;
err_t err;
sys_mbox_t mbox;
sys_mbox_t recvmbox;
sys_mbox_t acceptmbox;
sys_sem_t sem;
int socket;
u16_t recv_avail;
void (* callback)(struct netconn *, enum netconn_evt, u16_t len);
};

使用特权

评论回复
5
zheng522|  楼主 | 2015-5-27 18:29 | 只看该作者
struct netconn 用一个 union 将 udp、 tcp、 raw 的 pcb 包含起来,实现由 netconn 到不同协
议的分发,这是 c 语言编程的一个常用技巧。
z sys_mbox_t mbox,用户阻塞在该 mailbox 上,内核处理完用户的请求后, post 该 mailbox,用户继续
执行。
z sys_mbox_t recvmbox,如其名,用户用该 mailbox 接收来自内核的数据。
z sys_mbox_t acceptmbox,用户调用 accept 阻塞在该 mailbox 上,内核接收到来自网络的连接请求并完
成三次握手后, post 该 mailbox。
z sys_sem_t sem,系统调用 netconn_write 发现内核没有足够空间时 wait 该 semaphore,内核在适当的时
候会 post 该 semaphore,则操作系统唤醒运行在用户任务的系统调用,再次尝试发送数据。

使用特权

评论回复
6
zheng522|  楼主 | 2015-5-27 18:30 | 只看该作者
2.2 流程
struct netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u16_t proto,
  void (*callback)(struct netconn *, enum netconn_evt, u16_t len))
{
struct netconn *conn;
struct api_msg *msg;
conn = memp_malloc(MEMP_NETCONN);
......
conn->err = ERR_OK;
conn->type = t;
conn->pcb.tcp = NULL; /* 表示没有关联pcb */
if ((conn->mbox = sys_mbox_new()) == SYS_MBOX_NULL) {
memp_free(MEMP_NETCONN, conn);
return NULL;
}

使用特权

评论回复
7
zheng522|  楼主 | 2015-5-27 18:30 | 只看该作者
conn->recvmbox = SYS_MBOX_NULL;
conn->acceptmbox = SYS_MBOX_NULL;
conn->sem = sys_sem_new(0);
......
conn->state = NETCONN_NONE;
conn->socket = 0;
conn->callback = callback;
conn->recv_avail = 0;
if((msg = memp_malloc(MEMP_API_MSG)) == NULL) {
memp_free(MEMP_NETCONN, conn);
return NULL;
}

使用特权

评论回复
8
zheng522|  楼主 | 2015-5-27 18:30 | 只看该作者
msg->type = API_MSG_NEWCONN;
msg->msg.msg.bc.port = proto; /* misusing the port field */
msg->msg.conn = conn;

使用特权

评论回复
9
zheng522|  楼主 | 2015-5-27 18:31 | 只看该作者
api_msg_post(msg); /* 请求内核完成操作,内核需要根据链接类型分配并初始化 pcb */
sys_mbox_fetch(conn->mbox, NULL); /* 等待内核完成操作 */
memp_free(MEMP_API_MSG, msg);
......
return conn;
}

使用特权

评论回复
10
zheng522|  楼主 | 2015-5-27 18:31 | 只看该作者
3、内核函数 do_newconn
......
msg->conn->err = ERR_OK;
/* Allocate a PCB for this connection */
switch(msg->conn->type) {
......
case NETCONN_TCP:
msg->conn->pcb.tcp = tcp_new();
if(msg->conn->pcb.tcp == NULL) {
msg->conn->err = ERR_MEM;
break;
}
setup_tcp(msg->conn);
break;
}

使用特权

评论回复
11
zheng522|  楼主 | 2015-5-27 18:31 | 只看该作者
sys_mbox_post(msg->conn->mbox, NULL); /* 为 TCP connection 分配并初始化 pcb, post 系统调用准备
的 mailbox,系统调用得以继续执行 */
}

使用特权

评论回复
12
zheng522|  楼主 | 2015-5-27 18:31 | 只看该作者
3.1 内核函数 tcp_alloc
struct tcp_pcb *
tcp_alloc(u8_t prio)
{
struct tcp_pcb *pcb;
u32_t iss;
pcb = memp_malloc(MEMP_TCP_PCB);
if (pcb == NULL) {
/* Try killing oldest connection in TIME-WAIT. */
LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n"));
tcp_kill_timewait();
pcb = memp_malloc(MEMP_TCP_PCB);
if (pcb == NULL) {
tcp_kill_prio(prio); /* 释放优先级低的 pcb,这招够狠的 */
pcb = memp_malloc(MEMP_TCP_PCB);

使用特权

评论回复
13
zheng522|  楼主 | 2015-5-27 18:32 | 只看该作者
if (pcb != NULL) {
memset(pcb, 0, sizeof(struct tcp_pcb));
pcb->prio = TCP_PRIO_NORMAL;
pcb->snd_buf = TCP_SND_BUF; /* self available buffer space for sending (in bytes). */
pcb->snd_queuelen = 0; /* self available buffer space for sending (in tcp_segs). */
pcb->rcv_wnd = TCP_WND; /* self receiver window (in bytes)*/
pcb->tos = 0;
pcb->ttl = TCP_TTL;
pcb->mss = TCP_MSS;
pcb->rto = 3000 / TCP_SLOW_INTERVAL;
pcb->sa = 0;
pcb->sv = 3000 / TCP_SLOW_INTERVAL;
pcb->rtime = 0;
pcb->cwnd = 1;
iss = tcp_next_iss();
pcb->snd_wl2 = iss; /* acknowledgement numbers of last window update from peer. */
pcb->snd_nxt = iss; /* next seqno to be sent by peer*/
pcb->snd_max = iss; /* Highest seqno sent by peer. */
pcb->lastack = iss; /* Highest acknowledged seqno from peer. */
pcb->snd_lbb = iss;
pcb->tmr = tcp_ticks;
pcb->polltmr = 0;

使用特权

评论回复
14
zheng522|  楼主 | 2015-5-27 18:32 | 只看该作者
#if LWIP_CALLBACK_API
pcb->recv = tcp_recv_null;
#endif /* LWIP_CALLBACK_API */
/* Init KEEPALIVE timer */
pcb->keepalive = TCP_KEEPDEFAULT;
pcb->keep_cnt = 0;
}
return pcb;
}
从 pcb 分配函数分配一个 pcb,并初始化发送、接收、定时器参数。

使用特权

评论回复
15
zheng522|  楼主 | 2015-5-27 18:33 | 只看该作者
3.2 内核函数 setup_tcp
static void setup_tcp(struct netconn *conn)
{
struct tcp_pcb *pcb;
pcb = conn->pcb.tcp;
tcp_arg(pcb, conn);

使用特权

评论回复
16
zheng522|  楼主 | 2015-5-27 18:33 | 只看该作者
tcp_recv(pcb, recv_tcp);
tcp_sent(pcb, sent_tcp);
tcp_poll(pcb, poll_tcp, 4);
tcp_err(pcb, err_tcp);
}
在 pcb 上绑定各个事件的处理函数,相应功能在随后介绍。

使用特权

评论回复
17
zheng522|  楼主 | 2015-5-27 18:33 | 只看该作者
4、系统调用 netconn_bind
err_t netconn_bind(struct netconn *conn, struct ip_addr *addr,
u16_t port)
{
struct api_msg *msg;
if (conn == NULL) {
return ERR_VAL;
}

使用特权

评论回复
18
zheng522|  楼主 | 2015-5-27 18:34 | 只看该作者
if (conn->type != NETCONN_TCP &&
conn->recvmbox == SYS_MBOX_NULL) {
if ((conn->recvmbox = sys_mbox_new()) == SYS_MBOX_NULL) {
return ERR_MEM;
}
}
if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) {
return (conn->err = ERR_MEM);
}

使用特权

评论回复
19
zheng522|  楼主 | 2015-5-27 18:34 | 只看该作者
msg->type = API_MSG_BIND;
msg->msg.conn = conn;
msg->msg.msg.bc.ipaddr = addr;
msg->msg.msg.bc.port = port;
api_msg_post(msg);
sys_mbox_fetch(conn->mbox, NULL);
memp_free(MEMP_API_MSG, msg);
return conn->err;
}
为 tcp connnection 分配 receive mailbox,然后发消息 API_MSG_BIND 给内核。

使用特权

评论回复
20
zheng522|  楼主 | 2015-5-27 18:34 | 只看该作者
5、内核函数 do_bind
static void do_bind(struct api_msg_msg *msg)
{
......
switch (msg->conn->type) {
......

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

73

主题

821

帖子

3

粉丝