全部博文(41)
分类: LINUX
2009-08-24 17:56:15
tcp.c文件的tcp_readable函数
978计划工作组
/*
* Walk down the receive queue counting readable data until we hit the end or we find a gap
* in the received data queue (ie a frame missing that needs sending to us). Not
* sorting using two queues as data arrives makes life so much harder.
*/
static int tcp_readable(struct sock *sk)
{
unsigned long counted;
unsigned long amount;
struct sk_buff *skb;
int sum;
unsigned long flags;
if(sk && sk->debug)
printk("tcp_readable: %p - ",sk);
save_flags(flags);
cli();
if (sk == NULL || (skb = skb_peek(&sk->receive_queue)) == NULL)
{
restore_flags(flags);
if(sk && sk->debug)
printk("empty\n");
return(0);
}
counted = sk->copied_seq; /* Where we are at the moment */
amount = 0;
/*
* Do until a push or until we are out of data.
*/
do
{
if (before(counted, skb->h.th->seq)) /* Found a hole so stops here */
break;
sum = skb->len -(counted - skb->h.th->seq); /* Length - header but start from where we are up to (avoid overlaps) */
if (skb->h.th->syn)
sum++;
if (sum > 0)
{ /* Add it up, move on */
amount += sum;
if (skb->h.th->syn)
amount--;
counted += sum;
}
/*
* Don't count urg data ... but do it in the right place!
* Consider: "old_data (ptr is here) URG PUSH data"
* The old code would stop at the first push because
* it counted the urg (amount==1) and then does amount--
* *after* the loop. This means tcp_readable() always
* returned zero if any URG PUSH was in the queue, even
* though there was normal data available. If we subtract
* the urg data right here, we even get it to work for more
* than one URG PUSH skb without normal data.
* This means that select() finally works now with urg data
* in the queue. Note that rlogin was never affected
* because it doesn't use select(); it uses two processes
* and a blocking read(). And the queue scan in tcp_read()
* was correct. Mike
*/
if (skb->h.th->urg)
amount--; /* don't count urg data */
if (amount && skb->h.th->psh) break;
skb = skb->next;
}
while(skb != (struct sk_buff *)&sk->receive_queue);
restore_flags(flags);
if(sk->debug)
printk("got %lu bytes.\n",amount);
return(amount);
}
查看目前可读数据的长度。
sk->copied_seq:本地已被读取的最后一个数据的序列号加1。
skb->len:接收链表的某节点的数据包长度,不包括tcp头、ip头和MAC头。
counted:用于定位下个可读取的字节的序列号,包括标志字节数。
sk->h.th->seq:tcp头的序列号,即本节点数据的起始序列号。
before:counted小于sk->h.th->seq则返回1,否则返回0;如为1则表示数据流出现了断裂所以执行break退出while循环,直到无断裂后继续读取。
sum:用于计算某个sk_buff节点未读取的且未统计的字节数,包括标志字节数。
skb->h.th->syn:同步数据标志位,占1个序列号。
amount:统计可读取的字节数,不包括标志位。
整体解释:如果sum大于零表示有数据或有标志位被置位(ack标志除外),因amount不包括标志位所以amount加了sum后还要把标志位减去,而counted包括标志位(ack标志除外)所以不用减。
skb->h.th->urg:值为1表示紧急指针字段值有效,否则无效。紧急数据是将接收的数据迅速传替给应用程序的一种手段。
amount--:紧急标志被置1后,我们称数据传送进入紧急模式,实现中将紧急数据处理成一个字节,这种处理是实现相关的,即数据长度为1且占1个序列号,所以counted不能减1,而amount又不统计紧急数据,所以此处要减1,因为本身对于紧急数据的处理方式TCP规范上并无明确说明,各具体实现可以自行对如何处理紧急数据进行各自的解释。
skb->h.th->psh:psh为1时的意义如同urg为1,表示需要将数据立刻交给应用程序,所以此处要break。