应用层发送一个数据包的时候,是如何到达网卡的(下)
发布日期:2021-05-09 16:03:19 浏览次数:19 分类:精选文章

本文共 6194 字,大约阅读时间需要 20 分钟。

从前面的一篇文章可以知道,应用层发送一个数据包的时候首先经过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 (miquels@drinkel.ow.org)			 */			// 对所有包都感兴趣的、不是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 intel_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层,数据包也不一定是直接发送出去的,他可能会先缓存在发送队列里,按序发送。如果发送失败,则放回发送队列,等待重发。

上一篇:UDP协议源码解析之发送
下一篇:网卡收到一个数据包的时候,是如何传给应用层的

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2025年04月16日 23时56分57秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章