应用层发送一个数据包的时候,是如何到达网卡的(下)
程序员文章站
2022-04-24 14:26:00
...
从前面的一篇文章应用层发送一个数据包的时候,是如何到达网卡的(上)可以知道,应用层发送一个数据包的时候首先经过tcp_write和ip_queue_xmit函数,然后调用mac层的dev_queue_xmit函数。该函数代码如下,主要功能是完成arp解析(如果还没解析的话)、把数据包复制一份和对所有数据包都感兴趣的协议、把数据包插入发送队列,然后发送发送队列中的数据包。如果发送失败则加到发送队列里等待重发。
// dev为路由项对应的设备,在数据包路由的时候赋值
void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
{
unsigned long flags;
int nitcount;
struct packet_type *ptype;
int where = 0; /* used to say if the packet should go */
/* at the front or the back of the */
/* queue - front is a retransmit try */
if (dev == NULL)
{
printk("dev.c: dev_queue_xmit: dev = NULL\n");
return;
}
if(pri>=0 && !skb_device_locked(skb))
skb_device_lock(skb); /* Shove a lock on the frame */
#ifdef CONFIG_SLAVE_BALANCING
save_flags(flags);
cli();
if(dev->slave!=NULL && dev->slave->pkt_queue < dev->pkt_queue &&
(dev->slave->flags & IFF_UP))
dev=dev->slave;
restore_flags(flags);
#endif
#ifdef CONFIG_SKB_CHECK
IS_SKB(skb);
#endif
skb->dev = dev;
/*
* This just eliminates some race conditions, but not all...
*/
if (skb->next != NULL)
{
/*
* Make sure we haven't missed an interrupt.
*/
printk("dev_queue_xmit: worked around a missed interrupt\n");
start_bh_atomic();
dev->hard_start_xmit(NULL, dev);
end_bh_atomic();
return;
}
/*
* Negative priority is used to flag a frame that is being pulled from the
* queue front as a retransmit attempt. It therefore goes back on the queue
* start on a failure.
*/
// 类似坐标轴,-1和1是对称的,如果传的是负数,则先取他的对边,因为数组是从0开始,需要减一,比如传2即数组下标是1
if (pri < 0)
{
pri = -pri-1;
// 是直接发送还是先缓存到发送队列,1说明直接发送,0说明当前的skb先入队尾,先发送队列的队头节点
where = 1;
}
if (pri >= DEV_NUMBUFFS)
{
printk("bad priority in dev_queue_xmit.\n");
pri = 1;
}
/*
* If the address has not been resolved. Call the device header rebuilder.
* This can cover all protocols and technically not just ARP either.
*/
/*
还没有完成arp解析,重新构建mac头,如果当前arp解析还没成功则直接返回,
等待解析完成重新执行该函数(arp_rcv->arp_send_q).
*/
if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) {
return;
}
save_flags(flags);
cli();
/*
1 where一般是0,即pri是正整数,这时候skb会先插入队尾,先发送队头的节点,
并且把数据包复制一份给对数据包感兴趣的协议,然后发送。
2 where等于1,即pri是负数代表这个skb是发送失败后重发的,这时候这个数据包时直接发送出去的,不再走1的那些流程
*/
if (!where) {
#ifdef CONFIG_SLAVE_BALANCING
skb->in_dev_queue=1;
#endif
// 插入队尾,取出队头节点发送
skb_queue_tail(dev->buffs + pri,skb);
skb_device_unlock(skb); /* Buffer is on the device queue and can be freed safely */
skb = skb_dequeue(dev->buffs + pri);
skb_device_lock(skb); /* New buffer needs locking down */
#ifdef CONFIG_SLAVE_BALANCING
skb->in_dev_queue=0;
#endif
}
restore_flags(flags);
/* copy outgoing packets to any sniffer packet handlers */
if(!where)
{
// 把所有发出去的数据包传一份给其他协议
for (nitcount= dev_nit, ptype = ptype_base; nitcount > 0 && ptype != NULL; ptype = ptype->next)
{
/* Never send packets back to the socket
* they originated from - MvS ([email protected])
*/
// 对所有包都感兴趣的、不是packet协议产生的packet_type节点
if (ptype->type == htons(ETH_P_ALL) &&
(ptype->dev == dev || !ptype->dev) &&
((struct sock *)ptype->data != skb->sk))
{
struct sk_buff *skb2;
if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
break;
/*
* The protocol knows this has (for other paths) been taken off
* and adds it back.
*/
skb2->len-=skb->dev->hard_header_len;
ptype->func(skb2, skb->dev, ptype);
nitcount--;
}
}
}
start_bh_atomic();
// 调用驱动层发送
if (dev->hard_start_xmit(skb, dev) == 0) {
end_bh_atomic();
/*
* Packet is now solely the responsibility of the driver
*/
return;
}
end_bh_atomic();
/*
* Transmission failed, put skb back into a list. Once on the list it's safe and
* no longer device locked (it can be freed safely from the device queue)
*/
cli();
#ifdef CONFIG_SLAVE_BALANCING
skb->in_dev_queue=1;
dev->pkt_queue++;
#endif
skb_device_unlock(skb);
// 发送失败则把数据包重新加入队列
skb_queue_head(dev->buffs + pri,skb);
restore_flags(flags);
}
由以上代码可知mac层最后调用驱动层的函数去发送数据包,这里以3c501网卡为例。下面是发送函数的代码。
static int
el_start_xmit(struct sk_buff *skb, struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
unsigned long flags;
if (dev->tbusy) {
if (jiffies - dev->trans_start < 20) {
if (el_debug > 2)
printk(" transmitter busy, deferred.\n");
return 1;
}
if (el_debug)
printk ("%s: transmit timed out, txsr %#2x axsr=%02x rxsr=%02x.\n",
dev->name, inb(TX_STATUS), inb(AX_STATUS), inb(RX_STATUS));
lp->stats.tx_errors++;
outb(TX_NORM, TX_CMD);
outb(RX_NORM, RX_CMD);
outb(AX_OFF, AX_CMD); /* Just trigger a false interrupt. */
outb(AX_RX, AX_CMD); /* Aux control, irq and receive enabled */
dev->tbusy = 0;
dev->trans_start = jiffies;
}
if (skb == NULL) {
dev_tint(dev);
return 0;
}
save_flags(flags);
/* Avoid incoming interrupts between us flipping tbusy and flipping
mode as the driver assumes tbusy is a faithful indicator of card
state */
cli();
/* Avoid timer-based retransmission conflicts. */
if (set_bit(0, (void*)&dev->tbusy) != 0)
{
restore_flags(flags);
printk("%s: Transmitter access conflict.\n", dev->name);
}
else {
int gp_start = 0x800 - (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
unsigned char *buf = skb->data;
lp->tx_pkt_start = gp_start;
lp->collisions = 0;
/*
* Command mode with status cleared should [in theory]
* mean no more interrupts can be pending on the card.
*/
outb(AX_SYS, AX_CMD);
inb(RX_STATUS);
inb(TX_STATUS);
/*
* Turn interrupts back on while we spend a pleasant afternoon
* loading bytes into the board
*/
restore_flags(flags);
outw(0x00, RX_BUF_CLR); /* Set rx packet area to 0. */
outw(gp_start, GP_LOW); /* aim - packet will be loaded into buffer start */
// 传输数据到网卡
outsb(DATAPORT,buf,skb->len); /* load buffer (usual thing each byte increments the pointer) */
outw(gp_start, GP_LOW); /* the board reuses the same register */
outb(AX_XMIT, AX_CMD); /* fire ... Trigger xmit. */
dev->trans_start = jiffies;
}
if (el_debug > 2)
printk(" queued xmit.\n");
dev_kfree_skb (skb, FREE_WRITE);
return 0;
}
通过对整个过程的分析我们知道,一个数据包从应用层到网卡的过程中,数据包在tcp层处理完后下发到ip层,ip层会缓存一份数据到缓存队列,以备重传,但其实这里tcp层的工作。再到mac层,数据包也不一定是直接发送出去的,他可能会先缓存在发送队列里,按序发送。如果发送失败,则放回发送队列,等待重发。
上一篇: Python中函数的可变参数
下一篇: phpmyadmin怎样检查数据库表?