PS:期望值:这里给出重要的函数分支=(我认为比较重要会给出解释)
首先是总体的调用顺序:(从用户通过tcp/协议进入物理层。当然还有udp,icmp等,以后再说。)(用户程序)read()-> sys_read()->sock_read()->inet_read()->tcp_read();先说到这里。
从sock_read()开始
调用接口函数为 static int sock_read(struct inode* inode, struct file *file, char *ubuf, int size)
{
1、sock = socki_loopup(indoe) ; /*这个根据节点找到对应socket结构的指针,如果感兴趣见前面对从内存到文件系统的分析 (文件我已经写好),最重要的一部分*/
其他的是对sock的属性的设置;细节必须自己看
2、verify_arae(VERIFY_WRITE, ubuf, size)/*给内核提供写时赋值的渠道*/
3、sock->ops->read(sock, ubuf, size, (file->f_flags & O_NONBLOCK)) /*这里就是调用inet_read()*/
}
这个结构来自这里: /*sock->ops为struct proto_ops结构,因为是inet域,sock->ops被赋值为inet_proto_ops。如果是unix域,就被赋值为unix_proto_ops类似的。
static struct proto_ops inet_proto_ops ={
AF_INET,
inet_create,
. . .
inet_read,
. . .
}
所以后面调用的是inet_read()函数:
static int inet_read(struct socket *sock, char *ubuf, int size, int noblock) /*noblock,发生阻塞时是否立即返回, *ubuf指向用户内存的地址,int size,读取的大小,sock 对应的接口文件*/
{
struct sock *sk = (struct sock *) sock->data; /*data域一般都是存放里面的小的重要的数据结构类型,比如sokect->data 是sock 类型,而sock->data 就是sk_buff类型,真方便*/
1、inet_autobind(sk) /*这个函数为sk结构分配一个端口号*/
2、 sk->prot->read(sk, (unsigned char *)ubuf, size, noblock, 0);
}
先说inet_autobind:
static inet_auto_bind(struct sock *sk)
{
. . .
1.1、sk->num = get_new_socknum(sk->prot, 0); /*这里真正是分配端口号的函数*/
1.2 put_sock(sk->num, sk); /*这里是将sock结构的sk,送入其对应协议的端口号队列,sk->num是大于1024的*/
. . .
}
/*分配规则:就是在这256个数组队列中,找到数目最少的sock结构进行分配*/
1.1 unsigned short get_new_socknum(struct proto *prot, unsigned short base)
{
. . . . .
{
if (base == 0)
base = PROT_SOCK+1+(start & 1023); /*书中原代有错误,此来自net/ipv4/af_inet.c*/
if (base <= PROT_SOCK)
{
base += PROT_SOCK+(start & 1023); /*
}
/*PROT 为 1024*/
}
for (i = 0; i< SOCK_ARRAY_SIZE; i ++) /*SOCK_ARRAY_SIZE 为256; i最大值为 255 */
{
. . . .
sk = prot->sock_array[(i + base + 1) &(SOCK_ARRAY_SIZE - 1)]; /*proto结构中有个sock_array是存储本协议的sock队列的*/
while(sk != NULL)
{
sk = sk->next;
j ++; /*j是计算在本队列中连续个sock的个数,因为入队列的时候也是连续的,所以这就是本队列中所有的sock个数*/
}
if (j == 0)
{
start = (i + 1 + start) &1023 /*程序开头定义static start = 0;这里出现这个我很无奈,不清楚*/
return (i +base + 1); /*返回一个那个从base开始的第一个端口*/
}
. . . . .
}
}
1.1.1 while (sk_inuse(prot, base+best + 1))/这里调用来确认这个端口没有被使用*/
{
bset = SOCK_ARRAY_SIZE;
}
return (best + base + 1);
/*这里是调用的函数*/
1.1.1 static sk_inuse(struct proto *prot, int num)
{
for(sk = prot->sock_array[num &(SOCK_ARRAY_SIZE - 1)]; sk != NULL; sk = sk->next)
{
if (sk->num = num)
return (1); /*此sock结构已经使用了这个端口号,返回1,这里可以如果是不同协议的话,可以有相同的端口号,但是相同协议下,却不可以。想想也是*/
}
return (0);/*如果确实没有使用,那么就返回0*/
}
1.2
void put_sock(unsigned short num, struct sock *sk)
{
struct sock *sk1;
struct sock *sk2; /*这里定义两个sock*结构为了让sk进入队列任意两个结构之间,后面见到*/
. . . .
num = num &(SOCK_ARRAY_SIZE - 1); /*因为数组中队列最大下标为 255。*/
save_flags(flags); /*保存当前的falgs,为了禁止中断*/
sk->prot->inuse += 1;/*sk->prot使用计数加1*/
= 0xff000000; != 0xffffffff; = ( >> 8) | )
& ->) && (mask & sk->saddr) != (mask & 0xfffffff)
/*这里是算有效位(1)的数目,以8位为一个段,目前我还没有弄明白作者的深意*/
= << 8;
}/*这里判断用于此ip地址的相应mask,为了使用利用前面的1的数量来选择位置*/
sk1 = sk->prot->sock_array[num];/*取当前的第一个sock结构,用其中的saddr来和新的sk->addr进行比较*/
for(sk2 = sk1; sk != NULL; sk2 = sk2->next)
{
if(!(sk2->saddr &mask)) /*sk->saddr 和sk 的mask是对应的*/
if(sk == sk1)
{
sk->next = sk->prot_array_array[num];
sk->prot-sock_array[num] = sk;
sti();
return ;
}
. . . . . . /*后面就是插入两个sock之间的操作*/
}
}
举例:函数中实现的插入顺序有效位(即非零位)从多到少排列的,如
10.16.1.23 排在 10.16.1.0 之前,10.16.1.0 排在 10.16.0.0 之前,依次类推。
2、这里为tcp_read了。
static int tcp_read(struct sock *sk, unsigned char *to, int len, int nonblock, unsigned flags)
{
struct wait_queue wait = {current, NULL};/*这是等待进程初始化为current当前进程*/
其他的一些情况判断:
比如if (sk->state == TCP_LISTEN) /*侦听套结字不负责数据传送,其receive_queue缓存的均是请求连接(数据包(SYN数据包),如果读取的套结字状态是侦听,说明用户调用出现了问题*/
if(flags &MSG_OOB) /*如果请求读紧急数据,就返回读取紧急函数读取值*/
1.2.1 return tcp_read_urg(sk, nonblock, to , len, flags);
/*分配要更新的变量,如果仅仅是PEEK预处理,不对内核变量更新*/
peek_seq = sk->copied_seq;
seq = &sk->copied_seq;
if (flags &MSG_PEEK)
seq = &peek_seq;
/*将前面的wait变量加到sock结构的sleep睡眠队列中,下文代码随时可以进入睡眠等待状态*/
add_wait_queue(sk->sleep, &wait);
while(len > 0) /*len 表示用户需要读取的数据长度,满足就返回*/
{
/*如果有紧急数据,立即跳出循环*/
if (copied && sk->urg_data && sk->urg_seq == *seq) /*
break;
current->state = TASK_INTERRUPTABLE; /*程序为可中断状态,条件不满足时,可以随时进入睡眠* skb = skb_peek(&sk->receive_queue); /*将接受队列中的下一个sk_buff结构取出来,为NULL,是返回NULL*/
do{
if(!skb)
break; /*如果为NULL,中断do .. while循环*/
if (before(*seq, skb->h.th->seq))
break; /*要读取的序列号在当前接受到的序列号前,说明出现了点问题*/
offset = *seq - skb->h.th->seq; /*读取的序列号和当前sk_buff序列号中相对偏移*/
if(skb->h.th->syn)
offset -- ; /*如果sk_buff是个syn数据包,不算入偏移*/
if(offset < skb->len)
goto found_ok_skb; /*如果offset(序列号) 小于skb->len的长度,那么表示skb所表示的数据包中有可用数据*/
if(skb->h.th->fin);
goto found_fin_ok; /*跳转到fin包处理;
if(!flags &MSG_PEEK)) /*PEEK表示预处理,不做处理*/
skb->used = 1; /*skb->used置一,表示数据包已作处理*/
skb = skb->next;
}while(skb != (struct sk_buff *)&sk->receive_queue); /*将sk->receive中的sk_buff都处理完*/
if (copied) /*如果拷贝了数据,就跳出最外层while循环*/
break;
. . . . . .
一些判断
1.2.2 cleanup_rbuf(sk);
1.2.3 release_sock(sk);
sk->sockect->flags |= SO_WAITDATA;
/*这个不算*/ schedule(); /*进程调用,前面本进程已经设置为TASK_INTERRUPTABLE状态了*/
sk->inuse = 1;
if (current->signal &~current->blocked)
{
copied = -ERESTARTSYS;
break;
}
continue;
}
found_ok_skb:
skb->users ++; /*防止内核其他部分将其释放*/
used = skb->len - offset; /*sk->len表示数据包长度,offset表示可读取的数据的起始偏移量,相减得到:used 表示可读取的数据字节数*/
if (len < used) /*上层要读取的小于此处提供的,那么只读取要求的数量*/
used = len; /*被初始化本次需要读取的字节数*/
if(sk->urg_data)/*如果包含紧急数据,此版本紧急数据为 一个字节*/
unsigned long urg_offset = sk->urg_seq - *seq;
if {. . . . .)
else
used = urg_offset;/*如果普通数据中有紧急数据,那么不能越过此进行处理*/
memcpy_tofs(to, ((unsigned char *)skb->h.th) + skb->h.th->doff * 4 +offset, used);/*拷贝到用户空间*/
从tcpheadr + 本身长度(skb->h.th->doff * 4(头部以4字节为一个长度单位)+offset(此为可读取的偏移地址),used表示数据的长度,如果有紧急数据,此长度就是紧急数据长度*/
. . . . .
found_fin_ok:
++ *seq; /*fin数据包只占一个序列号*/
}end of the while
remove_wait_queue(sk->sleep, &wait);
current->state = TASK_RUUNING; /*将本进程从睡眠队列中删除,被重新设置成可运行状态*/
cleanup_rbuf(sk);
release_sock(sk);
return copied; /*copied 记录拷贝了多少字节的值*/
}
1..2.1
static int tcp_read_urg(struct sock *sk, int nonblock, unsigned char *to, int len, unsigned flags)
{
. . . . . ./*紧急数据为一个字节,所以用char c来接受*/
char c = sk->urg_data;
put_fs_byte(c, to); /*将c放入to的用户空间*/
1.2.1.1 release_sock(sk);
return 1;
. . . .. .
}
1.2.2:
最后一个函数:
void release_sock(struct sock*sk) /*将读取的那个sock结构对应在sk->back_log中sk_buff都读到receive_queue中,正好继续来处理*/
{
. . . . . .
while(skb = skb_dequeue(&sk->back_log)) ! = NULL)
sk->blog = 1;
if (sk->prot->rcv)
1.2.2.1 sk->prot->rcv(skb, skb->dev, skb->opt, skb->saddr, skb->len, skb->daddr, 1, (struct
inet_protocol *)sk->pair); /*此函数的作用调用get_sock()函数和tcp_check函数(此函数用户计算校验和)。
. . . . . .
if (sk->dead &&sk->state == TCP_CLOSE)
{
1.2.2.2 reset_timer(sk, TIME_DONE, min(sk->rtt*2, TCP_DONE_TIME);/*如果sk->state为关闭状态,设定时器*/
}
}
1.2.2.1:
int tcp_rcv(strcut sk_buff *skb, struct device *dev, struct options *opt, unsigned long daddr, unsigned short len, unsigned long saddr, int redo, struct inet_protocol *protocol)
{
skb : 为被接受的数据包,dev:为接受此数据包的设备:IP选项:
len为:IP负载长度;
redo: 0:表示一个新的数据包,redo为1,表示缓存的;
if (!redo)
{
if(tcp_check(th, len ,addr, daddr)) /*校验,返回0为成功*/
kfree_skb(skb, FREE_READ); /*释放kmalloc分配的空间*/
if (sk == NULL)
1.2.2.1.1 tcp_reset(daddr, saddr, th, &tcp_prot, opt, dev, skb, skb->ip_hdr->tos, 255);/*发送一个RST复位数据包,使对方断开连接,如果对方还连接,对方再次发出connect;*/
if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf)
{
kfree_skb(skb, FREE_READ);/*如果接受缓冲区剩余空间小,就丢包*/
relese_sock(sk); /*这里将可以传送进receive_queue就送进来*/
return (0); /*返回/*
根据sock的一系列属性,进行对应操作比如:
如果是个
if(sk->state = TCP_LISTEN)
tcp_conn_request(sk, skb, daddr, saddr, opt,dev, tcp_init_seq())/*这个函数完成新通信套结字的创建和初始化工作,返回后请求数据包的发送以及后面的操作由此套结字负责,侦听套结字只负责第一个SYN数据包。*/
/*创建一个新套结字状态设置为TCP_SYN_RECV,并将此sock通过put_sock()送入相关队列,下一次处理时,查找的将是这个新的套结子*/
}
if(sk->state == TCP_SYN_SENT)/*本地(客户端发出SYN信号,用于通信开始)
{
if(th->ack)
{
1.2.2.1.2 if(!tcp_ack(sk, th, addrlen, len)) /*
{
tcp_statistics.TcpAttemptFails ++;
. . . . . .
}
if(th->rst)
return tcp_std_reset(sk, skb);
}
1.2.2.1.2: extern __inline__ int tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr,
int len)
{
if(sk->zapped) /*此字段为1表示该套结字之前收到远端的RST数据包*/
return (1);
ack = ntohl(th->ack_seq); /*期望的下一个序列号*/
. . . . . .
开启定时器,并发送,比较长。*/
}
阅读(2705) | 评论(0) | 转发(0) |