linux网络设备驱动(三)

网络发送调用栈:


__dev_queue_xmit -> __dev_xmit_skb -> __qdisc_run -> qdisc_restart -> sch_direct_xmit -> dev_hard_start_xmit

__dev_queue_xmit -> __dev_xmit_skb -> sch_direct_xmit -> dev_hard_start_xmit

__dev_queue_xmit  -> dev_hard_start_xmit

dev_hard_start_xmit -> xmit_one -> netdev_start_xmit  ->

ops->ndo_start_xmit(驱动层的发送函数  net_device_ops.ndo_start_xmit)

__dev_xmit_skb函数:

static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
				 struct net_device *dev,
				 struct netdev_queue *txq)
{
	spinlock_t *root_lock = qdisc_lock(q);
	bool contended;
	int rc;

	qdisc_pkt_len_init(skb);
	qdisc_calculate_pkt_len(skb, q);
	/*
	 * Heuristic to force contended enqueues to serialize on a
	 * separate lock before trying to get qdisc main lock.
	 * This permits __QDISC___STATE_RUNNING owner to get the lock more
	 * often and dequeue packets faster.
	 */
     /*判断qdisc是否处于运行状态 */
	contended = qdisc_is_running(q);
	if (unlikely(contended))
		spin_lock(&q->busylock);

	spin_lock(root_lock);
	if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) {
		kfree_skb(skb);
		rc = NET_XMIT_DROP;
	} else if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) &&
		   qdisc_run_begin(q)) {
		/*
		 * This is a work-conserving queue; there are no old skbs
		 * waiting to be sent out; and the qdisc is not running -
		 * xmit the skb directly.
		 */

        /*
        实际上这里是直接发送报文,无需使用流量控制,但需要满足三个条件
        1. pfifo设置TCQ_F_CAN_BYPASS标志,
        2. qdisc_qlen为0,也就是没有多余到数据包待发送,
        3. qdisc_run_bein(q)=1,也就是说txq队列上没有运行流量控制
        */
        
        /*更新统计信息 */
		qdisc_bstats_update(q, skb);

         /*直接调用sch_direct_xmit发送数据,如果返回1,表示需要启用流量控制 */
		if (sch_direct_xmit(skb, q, dev, txq, root_lock, true)) {
			if (unlikely(contended)) {
				spin_unlock(&q->busylock);
				contended = false;
			}
			__qdisc_run(q);/*启动流量控制 */
		} else
			qdisc_run_end(q);/*标记停止流量控制 */

		rc = NET_XMIT_SUCCESS;
	} else {
		rc = q->enqueue(skb, q) & NET_XMIT_MASK;
		if (qdisc_run_begin(q)) {
			if (unlikely(contended)) {
				spin_unlock(&q->busylock);
				contended = false;
			}
			__qdisc_run(q);
		}
	}
	spin_unlock(root_lock);
	if (unlikely(contended))
		spin_unlock(&q->busylock);
	return rc;
}
///include/net/sch_generic.h

static inline int qdisc_qlen(const struct Qdisc *q)
{
	return q->q.qlen;
}

static inline bool qdisc_run_begin(struct Qdisc *qdisc)
{
	if (qdisc_is_running(qdisc))
		return false;
	qdisc->__state |= __QDISC___STATE_RUNNING;
	return true;
}

static inline bool qdisc_is_running(const struct Qdisc *qdisc)
{
	return (qdisc->__state & __QDISC___STATE_RUNNING) ? true : false;
}

/*
 * Transmit one skb, and handle the return status as required. Holding the
 * __QDISC_STATE_RUNNING bit guarantees that only one CPU can execute this
 * function.
 *
 * Returns to the caller:
 *                                0  - queue is empty or throttled.
 *                                >0 - queue is not empty.
 */
int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
                    struct net_device *dev, struct netdev_queue *txq,
                    spinlock_t *root_lock)
{
        int ret = NETDEV_TX_BUSY;

        spin_unlock(root_lock);
        if (!netif_xmit_frozen_or_stopped(txq))
            ret = dev_hard_start_xmit(skb, dev, txq);
        spin_lock(root_lock);

        if (dev_xmit_complete(ret)) {                    // 1. 驱动发送成功
            ret = qdisc_qlen(q);                         //    将 qdisc 队列的剩余长度作为返回值
        } else if (ret == NETDEV_TX_LOCKED) {            // 2. 驱动获取发送锁失败
            ret = handle_dev_cpu_collision(skb, txq, q);
        } else {                                         // 3. 驱动发送“正忙”,当前无法发送
            ret = dev_requeue_skb(skb, q);               //    将数据重新入队,等下次发送。
        }

        if (ret && netif_xmit_frozen_or_stopped(txq))
            ret = 0;

        return ret;

static inline
struct netdev_queue *netdev_get_tx_queue(const struct net_device *dev,
					 unsigned int index)
{
	return &dev->_tx[index];
}

static inline struct netdev_queue *skb_get_tx_queue(const struct net_device *dev,
						    const struct sk_buff *skb)
{
	return netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
}
static inline u16 skb_get_queue_mapping(const struct sk_buff *skb)
{
	return skb->queue_mapping;
}

ref:

Linux内核网络数据发送(五)——排队规则_JinrongLiang的博客-CSDN博客_linux 任务排队

Linux 网络子系统之网络协议接口层(二) - 陈富林 - 博客园

【干货】25 张图一万字,拆解 Linux 网络包发送过程(下) - 知乎

linux-network详解4数据包发送 - Action_er - 博客园

Linux网络之设备接口层:发送数据包流程dev_queue_xmit - 明明是悟空 - 博客园

Linux内核网络(一)——初探内核网络

[转][译]Linux 网络栈监控和调优:发送数据(2017)

25 张图,一万字,拆解 Linux 网络包发送过程 - 腾讯云开发者社区-腾讯云

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 JavaScript 编写的记忆游戏(附源代码)   项目:JavaScript 记忆游戏(附源代码) 记忆检查游戏是一个使用 HTML5、CSS 和 JavaScript 开发的简单项目。这个游戏是关于测试你的短期 记忆技能。玩这个游戏 时,一系列图像会出现在一个盒子形状的区域中 。玩家必须找到两个相同的图像并单击它们以使它们消失。 如何运行游戏? 记忆游戏项目仅包含 HTML、CSS 和 JavaScript。谈到此游戏的功能,用户必须单击两个相同的图像才能使它们消失。 点击卡片或按下键盘键,通过 2 乘 2 旋转来重建鸟儿对,并发现隐藏在下面的图像! 如果翻开的牌面相同(一对),您就赢了,并且该对牌将从游戏中消失! 否则,卡片会自动翻面朝下,您需要重新尝试! 该游戏包含大量的 javascript 以确保游戏正常运行。 如何运行该项目? 要运行此游戏,您不需要任何类型的本地服务器,但需要浏览器。我们建议您使用现代浏览器,如 Google Chrome 和 Mozilla Firefox, 以获得更好、更优化的游戏体验。要玩游戏,首先,通过单击 memorygame-index.html 文件在浏览器中打开游戏。 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值