您的位置:首页 > 产品设计 > UI/UE

uip协议栈分析

2013-09-28 11:50 232 查看
转自http://blog.csdn.net/angle0615303/article/details/7716151

Uip源代码可以从http://www.sics.se/~adam/uip/index.php/Main_Page上下载得到。

Uip的源码目录结构:

D:.

├─apps

│ ├─dhcpc

│ ├─hello-world

│ ├─resolv

│ ├─smtp

│ ├─telnetd

│ ├─webclient

│ └─webserver

│ └─httpd-fs

├─doc

│ └─html

├─lib

├─sourceinsight

├─uip

└─unix

App目录是一些应用层的协议和实例,Doc目录是文档,lib目录下有内存块管理函数,uip下uip协议栈的源代码,unix下是unix环境里的uip应用例子。

研究unix下的代码可以知道uip是如何使用的,关键是理解uip协议栈的主控制循环。

int main(void)

{

int i;

uip_ipaddr_t ipaddr;

struct timer periodic_timer, arp_timer;

//设置TCP超时处理时间和ARP老化时间

timer_set(&periodic_timer, CLOCK_SECOND / 2);

timer_set(&arp_timer, CLOCK_SECOND * 10);

//驱动初始化

tapdev_init();

//协议栈初始化

uip_init();

//设置IP地址、网关等参数

uip_ipaddr(ipaddr, 192,168,0,2);

uip_sethostaddr(ipaddr);

uip_ipaddr(ipaddr, 192,168,0,1);

uip_setdraddr(ipaddr);

uip_ipaddr(ipaddr, 255,255,255,0);

uip_setnetmask(ipaddr);

//应用层初始化

httpd_init();

//主循环

while(1)

{

//从网卡读取数据

uip_len = tapdev_read();

//如果数据存在则按协议处理

if(uip_len > 0)

{

//如果收到的是IP数据,调用uip_input()处理

if(BUF->type == htons(UIP_ETHTYPE_IP))

{

uip_arp_ipin();

uip_input();

/* 处理完成后如果UIP_BUF里有数据,即

uip_len>0,则调用tapdev_send发送出去*/

if(uip_len > 0) {

uip_arp_out();

tapdev_send();

}

}

//如果收到的是ARP数据,调用uip_arp_arpin处理

else if(BUF->type == htons(UIP_ETHTYPE_ARP))

{

uip_arp_arpin();

/* 查看是否有要发送的数据并发送*/

if(uip_len > 0)

{

tapdev_send();

}

}

}

//查看0.5s是否到了,调用uip_periodic处理TCP超时程序

else if(timer_expired(&periodic_timer))

{

timer_reset(&periodic_timer);

for(i = 0; i < UIP_CONNS; i++)

{

uip_periodic(i);

if(uip_len > 0)

{

uip_arp_out();

tapdev_send();

}

}

#if UIP_UDP

//处理udp超时程序

for(i = 0; i < UIP_UDP_CONNS; i++)

{

uip_udp_periodic(i);

if(uip_len > 0)

{

uip_arp_out();

tapdev_send();

}

}

#endif /* UIP_UDP */

/* 10s到了就处理ARP*/

if(timer_expired(&arp_timer))

{

timer_reset(&arp_timer);

uip_arp_timer();

}

}

}

return 0;

}

uip的内存管理方法:

内存管理的实现在memb.c/memb.h里。

这两个文件负责uip的内存块的管理,内存块是由MEMB()宏声明。内存从声明的内存块里用memb_alloc()分配,用memb_free()释放。因为命名空间的冲突,每个C模块只能有一个MEMB()宏声明。

先看memb.h文件:

#define MEMB_CONCAT2(s1, s2) s1##s2

#define MEMB_CONCAT(s1, s2) MEMB_CONCAT2(s1, s2)

这两个宏很容易看出来是连接两个字符串的。

#define MEMB(name, structure, num) /

static char MEMB_CONCAT(name,_memb_count)[num]; /

static structure MEMB_CONCAT(name,_memb_mem)[num]; /

static struct memb_blocks name = {sizeof(structure), num, /

MEMB_CONCAT(name,_memb_count), /

(void*)MEMB_CONCAT(name,_memb_mem)}

这个宏用来声明一个数组,这个数组是由多个特定大小的内存块组成。第一个参数用来作为内存块的名字标示,第二个参数是内存块中的子块的数据结构,第三个参数是内存块中子块的数目。

static char MEMB_CONCAT(name,_memb_count)[num];这句明显可以看出来是声明一个数组,表示子块的引用计数,展开后就是static char name_memb_count[num]。

static structure MEMB_CONCAT(name,_memb_mem)[num];这句就是静态数组用来分配实际内存的,展开后就是static structure name_memb_mem[num]。包含num个子块的内存,内存的数据类型是structure。

static struct memb_blocks name = {sizeof(structure), num, /

MEMB_CONCAT(name,_memb_count), /

(void *)MEMB_CONCAT(name,_memb_mem)}

这句声明一个指定名字的内存块,并为内存块结构体赋值,内存块结构体memb_blocks:

struct memb_blocks {

unsigned short size; //子块的大小

unsigned short num; //子块的数目

char *count; //每个子块引用计数,表示正在使用还是未被使用

void *mem; //内存块首地址

};

再看memb.c:

void memb_init(struct memb_blocks *m)

{

memset(m->count, 0, m->num);

memset(m->mem, 0, m->size * m->num);

}

这个函数把前面用MEMB宏声明的内存块初始化,即把memb_blocks结构体里的count和mem项清零。参数m就是用MEMB声明的name。

void * memb_alloc(struct memb_blocks *m)

{

int i;

for(i = 0; i < m->num; ++i) {

if(m->count[i] == 0) {

/* 如果子块未被使用, 增加应用计数表示现在被使用,并且返回指向这个内存块的指针*/

++(m->count[i]);

return (void *)((char *)m->mem + (i * m->size));

}

}

/* 未发现空闲块, 返回NULL表示分配内存失败*/

return NULL;

}

char memb_free(struct memb_blocks *m, void *ptr)

{

int i;

char *ptr2;

/* 遍历内存块列表试图找到ptr指针指向的内存子块*/

ptr2 = (char *)m->mem;

for(i = 0; i < m->num; ++i) {

if(ptr2 == (char *)ptr) {

/* 找到ptr指向的内存子块,减少引用计数并返回新的计数值 */

if(m->count[i] > 0) {

/*确保没有减少未使用的内存子块引用计数 */

--(m->count[i]);

}

return m->count[i];

}

ptr2 += m->size;

}

return -1;

}

App目录下的telnetd.c正好用到了memb.c里的函数:

struct telnetd_line {

char line[TELNETD_CONF_LINELEN];

};

MEMB(linemem, struct telnetd_line, TELNETD_CONF_NUMLINES);

memb_init(&linemem); memb_alloc(&linemem); memb_free(&linemem, line);

使用起来非常方便容易,缺点是只能分配固定大小的子块。但至少不会产生内存碎片了,而且由于是静态声明的,会自动进行对齐。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: