文章目录

  • 网络设备状态: net_device->state
    • __LINK_STATE_START
    • __LINK_STATE_PRESENT
    • __LINK_STATE_NOCARRIER
    • __LINK_STATE_LINKWATCH_PENDING
    • __LINK_STATE_DORMANT
  • 排队规则状态: Qdisc->state
    • __QDISC_STATE_RUNNING
    • __QDISC_STATE_SCHED
    • __QDISC_STATE_DEACTIVATED
  • 收发队列状态: netdev_queue->state
    • __QUEUE_STATE_XOFF
    • __QUEUE_STATE_FROZEN
  • 设备的挂起和恢复
    • 挂起: netif_device_detach()
    • 恢复: netif_device_attach()
  • 链路状态侦测
    • 报告链路状态变化事件
    • linkwatch事件列表
    • 事件的处理

从前面的数据包收发过程中可以看到,过程中有很多的设备状态检查和设置的操作,这篇笔记来单独分析下涉及的状态以及它们的含义。

收发过程中,实际上涉及如下三个数据结构对象的状态检查和设置。

网络设备状态: net_device->state

dev->state字段描述了设备和设备队列的状态,当前定义有如下值:

/* These flag bits are private to the generic network queueing* layer, they may not be explicitly referenced by any other* code.*/
enum netdev_state_t
{__LINK_STATE_START,__LINK_STATE_PRESENT,__LINK_STATE_NOCARRIER,__LINK_STATE_LINKWATCH_PENDING,__LINK_STATE_DORMANT,
};

__LINK_STATE_START

表示设备是否已经被打开。在dev_open()的时候设置该标记位,在dev_close()清除该标记位。

可以使用netif_running()检测是否设置了该标志位:

/*** netif_running - test if up* @dev: network device** Test if the device has been brought up.*/
static inline int netif_running(const struct net_device *dev)
{return test_bit(__LINK_STATE_START, &dev->state);
}

__LINK_STATE_PRESENT

表示网络设备对象是否存在。网络设备对象注册过程会将该标记置位。此外,在电源管理过程中,设备被挂起时会清除该标记使得设备暂时不可用,在恢复时重新置位使设备恢复,具体看下面的“设备挂起”与“设备恢复”。

可以使用netif_device_present()检测是否设置了该标志位:

/*** netif_device_present - is device available or removed*  @dev: network device** Check if device has not been removed from system.*/
static inline int netif_device_present(struct net_device *dev)
{return test_bit(__LINK_STATE_PRESENT, &dev->state);
}

__LINK_STATE_NOCARRIER

当驱动程序感知到设备的载波变化时,要使用netif_carrier_on/off()通知内核,使得内核可以在这些情况下合理的关闭设备的收发能力,具体见下文。

可以使用netif_carrier_ok()检查是否设置了该标志位:

/*** netif_carrier_ok - test if carrier present* @dev: network device** Check if carrier is present on device*/
static inline int netif_carrier_ok(const struct net_device *dev)
{return !test_bit(__LINK_STATE_NOCARRIER, &dev->state);
}

__LINK_STATE_LINKWATCH_PENDING

如果设备已经产生了LINKWATCH事件并且正在调度过程中,设置该标志位。这是为了防止同一个设备同时被多次调度。

__LINK_STATE_DORMANT

和__LINK_STATE_NOCARRIER类似,也是表示链路层状态,设置了该标记后,设备无法收发数据包。驱动程序可使用netif_dormant_on/off()操作该标志位。

排队规则状态: Qdisc->state

enum qdisc_state_t
{__QDISC_STATE_RUNNING,__QDISC_STATE_SCHED,__QDISC_STATE_DEACTIVATED,
};

__QDISC_STATE_RUNNING

表示qdisc_run()函数是否正在被调用,可以防止一个排队规则同时被多个CPU同时调度。该标记在进入qdisc_run()之前设置,退出时清除。

__QDISC_STATE_SCHED

表示排队规则是否正在被发送软中断调度。

__QDISC_STATE_DEACTIVATED

设置该标记位表示该排队规则被禁用,禁用的排队规则是无法用来发送数据包的。设备被关闭时会关闭发送队列,此时对应的排队规则就会被设置该标记。

收发队列状态: netdev_queue->state

enum netdev_queue_state_t
{__QUEUE_STATE_XOFF,__QUEUE_STATE_FROZEN,
};

__QUEUE_STATE_XOFF

标记队列是否被关闭。关闭后的队列是无法用来收发数据的。驱动程序可以通过操作该标记位影响设备接口层的发送逻辑,进而可以临时性的开启和关闭指定收发队列。

__QUEUE_STATE_FROZEN

标记队列是否被冻结,当一个队列被冻结时,该队列无法收发数据。

设备的挂起和恢复

当电源管理模块通知设备系统即将进入休眠态时,驱动程序必须要执行休眠准备工作,在该过程中,驱动需要调用netif_device_detach()告诉框架设备即将休眠。类似的,设备退出休眠态时,驱动程序必须要执行恢复工作,在该过程中,驱动需要调用netif_device_attach()告诉框架设备即将恢复。

挂起: netif_device_detach()

在挂起时,需要干两件事:

  1. 清除__LINK_STATE_PRESENT标志位,使得设备暂时不可用;
  2. 如果设备已经打开(调用过dev_open(),可以正常收发数据了),还需要关闭设备的发送队列。
/*** netif_device_detach - mark device as removed* @dev: network device** Mark device as removed from system and therefore no longer available.*/
void netif_device_detach(struct net_device *dev)
{// cond1:之前设备的PRESET标记存在,即没有休眠// cond2:设备的START标记存在,即设备是打开的if (test_and_clear_bit(__LINK_STATE_PRESENT, &dev->state) &&netif_running(dev)) {// 关闭设备的发送队列netif_stop_queue(dev);}
}

恢复: netif_device_attach()

恢复设备时,干两件事:

  1. 重新社长是__LINK_STATE_PRESENT标志位;
  2. 如果设备已经打开,那么开启发送队列并且重新调度设备使其可以发送
/*** netif_device_attach - mark device as attached* @dev: network device** Mark device as attached from system and restart if needed.*/
void netif_device_attach(struct net_device *dev)
{// cond1:之前设备的PRESET标记不在,即休眠了// cond2:设备的START标记存在,即设备是打开的if (!test_and_set_bit(__LINK_STATE_PRESENT, &dev->state) &&netif_running(dev)) {// 开启发送队列;将设别加入发送轮询队列;激活发送软中断netif_wake_queue(dev);// 启动WatchDog,其实是延迟回调驱动提供的tx_timeout()接口,// 不知道这种设计有什么目的__netdev_watchdog_up(dev);}
}

链路状态侦测

驱动程序可以感知到网络设备硬件是否处于可用状态的变化,比如网线是否掉了等事件,这些事件可以简单的划分为可用和不可用两种状态。当这两个事件发生时,驱动程序应该将这种状态传递给设备接口层,这是通过netif_carrier_on()和netif_carrier_off()来实现的。

/*** netif_carrier_on - set carrier* @dev: network device** Device has detected that carrier.*/
void netif_carrier_on(struct net_device *dev)
{// 清除设备的__LINK_STATE_NOCARRIER标志if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) {// 产生一个链路状态变化事件linkwatch_fire_event(dev);// 如果设备已经打开,启动发送异常检测WatchDog定时器if (netif_running(dev))__netdev_watchdog_up(dev);}
}/***   netif_carrier_off - clear carrier*  @dev: network device** Device has detected loss of carrier.*/
void netif_carrier_off(struct net_device *dev)
{// 设置__LINK_STATE_NOCARRIER标志位if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state)) {if (dev->reg_state == NETREG_UNINITIALIZED)return;// 产生一个链路状态变化事件linkwatch_fire_event(dev);}
}

报告链路状态变化事件

如上,链路状态变化的事件最先由驱动程序感知到,驱动通过netif_carrier_on/off()、netif_dormant_on/off()通知设备接口层,设备接口层会调用linkwatch_fire_event()触发链路状态变化事件的产生。

// 何为紧急事件
static int linkwatch_urgent_event(struct net_device *dev)
{// 设备打开 && 链路状态由off变为on && 排队规则有变更return netif_running(dev) && netif_carrier_ok(dev) &&dev->qdisc != dev->qdisc_sleeping;
}void linkwatch_fire_event(struct net_device *dev)
{// 判断此次是否为紧急事件int urgent = linkwatch_urgent_event(dev);// 设置LINKWATCH_PENDING标记,表示该设备的LINKWATCH事件正在被调度中,同一时间段内同一个设备只能有// 一个LINKWATCH事件被调度,因为调度的多了也是浪费资源,完全没有必要if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) {// 设备没有LINKWATCH事件被调度,所以增加引用计数并将该设备加入到系统的LINKWATCH事件链表中dev_hold(dev);linkwatch_add_event(dev);} else if (!urgent)// 非紧急事件,并且该设备的LINKWATCH事件已经在调度中了,不再重复定都return;// 紧急事件、或者发生了首次调度linkwatch_schedule_work(urgent);
}

linkwatch事件列表

系统中会有多个网络设备,这些设备都有可能会有LINKWATCH事件产生,这些来自不同设备的LINKWATCH需要组织起来。内核是使用一个简单的链表组织的。

static struct net_device *lweventlist;
static DEFINE_SPINLOCK(lweventlist_lock);// 将网络设备对象加入linkwatch表中
static void linkwatch_add_event(struct net_device *dev)
{unsigned long flags;spin_lock_irqsave(&lweventlist_lock, flags);dev->link_watch_next = lweventlist;lweventlist = dev;spin_unlock_irqrestore(&lweventlist_lock, flags);
}

事件的处理

通过linkwatch_schedule_work()调度事件,这里涉及到延迟任务的执行原理,不是我们关注的重点,直接看处理事件的处理。

static unsigned long linkwatch_nextevent;static void __linkwatch_run_queue(int urgent_only)
{struct net_device *next;/** Limit the number of linkwatch events to one* per second so that a runaway driver does not* cause a storm of messages on the netlink* socket.  This limit does not apply to up events* while the device qdisc is down.*/// 对于非紧急事件,下次延迟任务的执行最少在1s以后if (!urgent_only)linkwatch_nextevent = jiffies + HZ;/* Limit wrap-around effect on delay. */else if (time_after(linkwatch_nextevent, jiffies + HZ))linkwatch_nextevent = jiffies;// 正在处理,清除LW_URGENT标志clear_bit(LW_URGENT, &linkwatch_flags);// 处理LINKWATCH事件队列spin_lock_irq(&lweventlist_lock);next = lweventlist;lweventlist = NULL;spin_unlock_irq(&lweventlist_lock);while (next) {struct net_device *dev = next;next = dev->link_watch_next;// 只处理紧急事件,但当前事件不是紧急事件,那么等待正常调度到后执行if (urgent_only && !linkwatch_urgent_event(dev)) {linkwatch_add_event(dev);continue;}/** Make sure the above read is complete since it can be* rewritten as soon as we clear the bit below.*/smp_mb__before_clear_bit();/* We are about to handle this device,* so new events can be accepted*/// 清除PENDING标记clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);rfc2863_policy(dev);if (dev->flags & IFF_UP) {// 根据状态使能或者禁用网络设备if (netif_carrier_ok(dev))dev_activate(dev);elsedev_deactivate(dev);// 设备状态发生了变化,通过RT_NETLINK通知用户空间netdev_state_change(dev);}dev_put(dev);}// 如果LINKWATCH队列非空,触发下一次延迟任务的执行(上述处理过程中,外部可能又设置了该队列)if (lweventlist)linkwatch_schedule_work(0);
}

数据收发过程中的网络设备状态相关推荐

  1. 45 | 答疑(五):网络收发过程中,缓冲区位置在哪里?

    问题 1:网络收发过程中缓冲区的位置 第一点,是网络收发过程中,收发队列和缓冲区位置的疑问. 在 关于 Linux 网络,你必须要知道这些 中,我曾介绍过 Linux 网络的收发流程.这个流程涉及到了 ...

  2. TCP 数据收发过程抓包分析

    本文简单对 TCP 协议的三次握手.数据传输.四次挥手过程进行抓包分析. 一. 抓包准备 首先本地通过套接字实现一个 TCP 通信,然后通过 Wireshark 抓包,套接字通信代码如下: Serve ...

  3. DTCC:数据库安全重点在数据拷贝过程中

    本文讲的是DTCC:数据库安全重点在数据拷贝过程中,2017年5月11日-13日,2017中国数据库技术大会于北京国际会议中心盛大开幕.作为国内最受关注的数据库技术大会,本届大会以"数据驱动 ...

  4. 数据可视化过程中常见的错误类型

    数据可视化是一个沟通复杂信息的强大武器.通过可视化信息,我们的大脑能够更好地抓取和保存有效信息,增加信息的印象.但如果数据可视化做的较弱,反而会带来负面效果.错误的表达会损害数据的传播,完全曲解他们. ...

  5. 英文投稿过程中的十种状态

    以下是英文投稿过程中的十种状态. 1. Submitted to Journal 当上传结束后,显示的状态是Submitted to Journal,这bai个状态是自然形成的无需处理. 2. Wit ...

  6. coco数据集进行裁剪、数据增强过程中的ground_truth bbox的设定以及变化

    目录 前言: 1. 写入json文件时的 ground truth bbox 2. 导入图片 和 获取对应的 ground truth bbox 3. 进行数据增强后, 决定了 最终的 ground ...

  7. 数据治理过程中会遇到哪些数据问题?

    目前,企业数据的管理和使用仍存在许多问题,导致数据不能很好地利用.企业数据治理过程中主要有以下五类问题: 1.睡眠数据 睡眠数据是指被收集和处理但不用于任何目的的数据.有数据而不使用,甚至业务部门和领 ...

  8. 计算机系统中数据加工过程中,数据库系统复习题-第1章绪论

    使用数据库系统有什么好处?(填空题) 使用数据库系统的好处是由数据库管理系统的特点或优点决定的. 使用数据库系统的好处很多,例如可以大大提高应用开发的效率,方便用户的使用,减轻数据库系统管理人员维护的 ...

  9. 大数据运营过程中存在哪些问题?

    大数据在概念领域已经受到了很多企业和个人的认可,但是在是实际的使用过程中,越来越多的人希望通过大数据的应用来指导企业的运营,但是企业资源是不是可以被充分的使用,大数据的应用真的可以和概念一样达到相应的 ...

  10. POI导入数据的过程中,遇到读取以科学计数法显示的数据

    在Poi读取Excel数据的过程当中,经常会因为用户操作的问题或是Excel的自动转换将单元格数据转换为科学计数法形式表现,那么这时在后台读取数据并进行校验数据格式的过程当中有可能就会出现错误,或者是 ...

最新文章

  1. C++编程思想重点笔记(下)
  2. 记号一次更换IBM X3650M4主板后RAID无法启动的解决
  3. elasticsearch-1
  4. 特斯拉上海超级工厂正尽全力生产和交付 外媒称已进入Plaid模式
  5. SharePoint 2013 的HTML5特性之响应式布局
  6. 微信小程序推广多多进宝商品,微信小程序跳转拼多多小程序领券页面,微信小程序跳转多多进宝推广链接
  7. 后缀表达式转前缀表达式
  8. linux qq传文件怎么安装,在Ubuntu Linux下怎样安装QQ
  9. iOS动画和特效(二)UIKit力学行为
  10. SNDA校园招聘,参加哈尔滨和西安的面试
  11. MySQL复制 slave_exec_mode 参数IDEMPOTENT 说明
  12. Notepad++实用插件整理(Json Viewer、Compare、Explorer、AnalysePlugin)
  13. STM32F103C8T6引脚笔记
  14. C语言——深度剖析数据在内存中的存储
  15. 大学我这样过,成了别人眼中的大神
  16. Ubuntu apt卸载
  17. java web电子商务网站_电子商务 javaweb b2b b2c o2o 平台
  18. 管理网络与业务网络分离+虚拟网络部署
  19. MATLAB利用均值滤波的方法去除图像的噪声,将滤除噪声前后的图像输出。
  20. TurnipBit:和孩子一起动手DIY“滚动”的生日礼物

热门文章

  1. WPS公式编辑器编辑公式遇到的问题
  2. 工商管理企业经营战略知识归纳
  3. vue怎么改logo_vue项目添加网页logo
  4. php生成财务科目编码,会计科目代码(2019会计科目代码对照表)
  5. java链接打印机打印文件
  6. clickHouse 使用常见问题踩坑
  7. 数据抽取:增量与全量的区别
  8. 2021年危险化学品经营单位主要负责人试题及解析及危险化学品经营单位主要负责人理论考试
  9. 应对CentOS 停服,麒麟信安迁移方案已就绪
  10. error: passing ‘const xxx’ as ‘this’ argument discards qualifiers [-fpermissive]的解决方案