我们上面讲了套接字缓冲区基本的操作方法,利用它们就可以完成数据包的发送和接收工作。为了保证网络传输的高效和稳定,我们需要对整个过程进行流程控制,因此,我们又引进了两个支持例程。它们是利用信号的交互来完成任务的。
sock_queue_rcv_skb()函数用来对数据的接收进行控制,通常调用它的的形式为:
sk=my_find_socket(whatever);
if(sock_queue_rcv_skb(sk,skb)==-1)
{
myproto_stats.dropped++;
kfree_skb(skb,FREE_READ);
return;
}
它利用套接字的读队列的计数器,从而避免了大量的数据包堆积在套接字层。一旦到达这个极限,其余的数据包就会被丢弃。这样做是为了保障高层的应用协议有足够快的读取速度,比如TCP协议,包含对该流程的控制,当接收端不能再接收数据时,TCP 协议就告诉发送端的机器停止传输。
在数据传输方面, sock_alloc_send_skb()
可以对发送队列进行控制, 我们不能把所有的缓冲区都填充数据,使得发送队列总有空余, 避免了数据堵塞。这个函数在具体应用时有很多微妙之处,所以推荐编写网络协议的作者尽可能使用它。
许多发送例程利用这个函数几乎可以做所有的工作:
skb=sock_alloc_send_skb(sk,....)
if(skb==NULL)
return -err;
skb->sk=sk;
skb_reserve(skb, headroom);
skb_put(skb,len);
memcpy(skb->data, data, len);
protocol_do_something(skb);
上面大部分代码我们前面已经见过。其中最重要的一句是 skb->sk=sk。
sock_alloc_send_skb()
负责把缓冲区送到套接字层。通过设置
skb->sk, 我们告诉内核无论哪个例程对缓冲区进行 kfree_skb()处理,都必须保证缓冲区已经成功地送到套接字层。 因此一旦网络设备驱动程序发送一个缓冲区, 并将之释放,我们就认为数据已经发送成功,这样我们就可以继续发送数据了。在源代码中我们看到
kfree_skb 操作一执行就会触发sock_alloc_send_skb()。