加载中…
个人资料
老衲五木
老衲五木
  • 博客等级:
  • 博客积分:0
  • 博客访问:352,641
  • 关注人气:330
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

API消息机制《LwIP协议栈源码详解—TCP/IP协议的实现》

(2012-09-17 19:36:17)
标签:

lwip

tcp

api

it

分类: 嵌入式网络那些事

现在有必要来看看前面一直提到的内核协议栈进程是什么样子的。这个函数叫tcpip_thread,其源码如下,其中去掉了不相关的编译选项和非重点讨论部分。

static void tcpip_thread(void *arg)

{

       struct tcpip_msg *msg;

       sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);//创建IP分片重装超时事件

       sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);// 创建ARP超时事件

       while (1) {                          // 进程循环

           sys_mbox_fetch(mbox, (void *)&msg);    // 阻塞在邮箱上接收要处理的消息

           switch (msg->type) {  // 判断消息类型

             case TCPIP_MSG_API:  // 若是API消息,调用消息内部的function函数

                   msg->msg.apimsg->function(&(msg->msg.apimsg->msg));

                   break;

             case TCPIP_MSG_INPKT:// 若是接收到IP层递交的数据包

                 if (msg->msg.inp.netif->flags & NETIF_FLAG_ETHARP) // 支持ARP

                     ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);//先进行ARP处

//理,再判断是否递交IP层处理

else  //否则直接递交给IP层

ip_input(msg->msg.inp.p, msg->msg.inp.netif);

                 memp_free(MEMP_TCPIP_MSG_INPKT, msg);

                 break;

。。。。。。

            default:  break;

         }// switch

     } // while

}//

邮箱mbox是协议栈初始化时建立的用于tcpip_thread接收消息的邮箱,该函数能够识别的消息类型是tcpip_msg结构的,所以不管是API部分还是IP数据包输入部分,都必须将自己的信息封装成tcpip_msg结构。

struct tcpip_msg {

  enum tcpip_msg_type type;  // 枚举结构,消息类型

  sys_sem_t *sem;    // 信号量指针,该字段似乎没怎么被用到

  union {   // 共用体,不同消息类型使用不同的结构

    struct api_msg *apimsg;  // API消息指针

    struct netifapi_msg *netifapimsg;  // 不讨论

    struct {            // 接收到IP层数据包相关指针

      struct pbuf *p;

      struct netif *netif;

      } inp;

    struct {    // 不讨论

      void (*f)(void *ctx);

      void *ctx;

     } cb;

    struct {    // 不讨论

      u32_t msecs;

      sys_timeout_handler h;

      void *arg;

    } tmo;

  } msg;   // 枚举类型的名字

};

这个结构和上节讨论的api_msg_msg有着很相似,api_msg_msg里面也包含了一个叫做的msg的结构体。枚举型tcpip_msg_type的内部成员就是在函数tcpip_thread中看到的TCPIP_MSG_API和TCPIP_MSG_INPKT等,这里只讨论这两种类型。

API函数内部调用来向内核进程发送消息的函数叫tcpip_apimsg,该函数填充一个TCPIP_MSG_API类型的tcpip_msg结构,并把该结构投递到协议栈阻塞的邮箱mbox:

err_t tcpip_apimsg(struct api_msg *apimsg)

{

  struct tcpip_msg msg;     // 定义一个消息变量

  if (mbox != SYS_MBOX_NULL) {

    msg.type = TCPIP_MSG_API;   // 消息类型

    msg.msg.apimsg = apimsg;  // 使用tcpip_msg中的msg.apimsg字段记录相关信息

    sys_mbox_post(mbox, &msg); // 投递消息

    sys_arch_sem_wait(apimsg->msg.conn->op_completed, 0); //阻塞,等待内核处理完毕

    return ERR_OK;

  }

  return ERR_VAL;

}

底层向内核进程递交接收到的IP数据包是通过调用网络接口结构netif中指针input指向的函数来实现的(参见前面netif描述的部分)。通常这个函数是tcpip_input。

err_t tcpip_input(struct pbuf *p, struct netif *inp)

{

  struct tcpip_msg *msg; // 定义了一个消息指针

  if (mbox != SYS_MBOX_NULL) {

    msg = memp_malloc(MEMP_TCPIP_MSG_INPKT);  // 为新的消息申请空间

    if (msg == NULL) {

      return ERR_MEM;

    }

    msg->type = TCPIP_MSG_INPKT; // 消息类型

    msg->msg.inp.p = p;  // 使用tcpip_msg中的msg.inp字段记录相关信息

    msg->msg.inp.netif = inp;

    if (sys_mbox_trypost(mbox, msg) != ERR_OK) { // 投递一次消息

      memp_free(MEMP_TCPIP_MSG_INPKT, msg);//投递不成功则删除消息

      return ERR_MEM;

    }

    return ERR_OK;

  }

  return ERR_VAL;

}

哎,这个过程是十分的复杂纠结啊,下面这个图可能更容易理解。这里函数tcpip_thread还是只处理TCPIP_MSG_API和TCPIP_MSG_INPKT这两种类型的消息。


API消息机制《LwIP协议栈源码详解—TCP/IP协议的实现》

从上面的图中可以清晰的看出两部分API函数之间的交互过程,以及应用程序和内核函数之间的交互过程。API函数netconn_xxx在文件api_lib.c中,而API实现的另一部分函数do_xxx在api_msg.c中。

    接下来的一节我们将精力集中在api_lib.c中的各个函数,而不去过多关心api_msg.c中的各个do_xxx是怎样与内核函数交互完成相关工作的。netconn_xxx函数在LwIP说明文档16小节也有相关的描述,这里打算再重复的描述下各个函数的功能以及它们的实现过程。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有