当前位置: 首页 > news >正文

tun驱动之tun_init

tun驱动的初始化方法是tun_init。

static int __init tun_init(void)
{int ret = 0;pr_info("%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);ret = rtnl_link_register(&tun_link_ops);if (ret) {pr_err("Can't register link_ops\n");goto err_linkops;}ret = misc_register(&tun_miscdev);if (ret) {pr_err("Can't register misc device %d\n", TUN_MINOR);goto err_misc;}ret = register_netdevice_notifier(&tun_notifier_block);if (ret) {pr_err("Can't register netdevice notifier\n");goto err_notifier;}return  0;err_notifier:misc_deregister(&tun_miscdev);
err_misc:rtnl_link_unregister(&tun_link_ops);
err_linkops:return ret;
}

一 rtnl_link_register

rtnl_link_register的原型如下:

参数如下:

int rtnl_link_register(struct rtnl_link_ops *ops);

其中ops对应的是:

static struct rtnl_link_ops tun_link_ops __read_mostly = {

  .kind = DRV_NAME, // 区分不同的驱动

  .priv_size = sizeof(struct tun_struct),

  .setup = tun_setup,

  .validate = tun_validate,

};

rtnl_link_register的主要逻辑是将ops添加到全局列表link_ops中:

list_add_tail(&ops->list, &link_ops);

二 misc_register 

misc_register的参数是:

static struct miscdevice tun_miscdev = {

  .minor = TUN_MINOR,

  .name = "tun",

  .nodename = "net/tun",

  .fops = &tun_fops,

};

字符设备和 块设备利用主设备号和次设备号来区分设备驱动。如一台机器接了一个打印机,其驱动的主设备号是X,次设备号是Y。这台机器再接入同一型号的一台打印机,则驱动主设备号是X,次设备号是Y1。X标识打印机的型号,Y标识此型号的具体一台打印机。

主设备号最大2^12,即4096,而次设备号最大2^20。为了解决主设备号短缺的问题,引入了miscdevice,杂设备。

杂设备驱动,主设备号固定是10;次设备号可以指定,也可以自动获取。

// 自动获取

if (is_dynamic) {
    int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);

    if (i >= DYNAMIC_MINORS) {
        err = -EBUSY;
        goto out;
    }
    misc->minor = DYNAMIC_MINORS - i - 1;
    set_bit(i, misc_minors);
} else {
    struct miscdevice *c;

    // 指定次设备号,进行判重

    list_for_each_entry(c, &misc_list, list) {
        if (c->minor == misc->minor) {
            err = -EBUSY;
            goto out;
        }
    }
}

最后将杂设备添加到全局列表misc_list中:

list_add(&misc->list, &misc_list); 

在用户空间,调用open打开一个杂设备时,就能看到杂设备设计的巧妙之处,后面会再详细介绍。

三 register_netdevice_notifier

register_netdevice_notifier的参数为:

static struct notifier_block tun_notifier_block __read_mostly = {

.notifier_call = tun_device_event,

};

int register_netdevice_notifier(struct notifier_block *nb)
{struct net_device *dev;struct net_device *last;struct net *net;int err;rtnl_lock();err = raw_notifier_chain_register(&netdev_chain, nb);if (err)goto unlock;if (dev_boot_phase)goto unlock;for_each_net(net) { // 遍历net_namespace_listfor_each_netdev(net, dev) { // 遍历net->dev_base_headerr = call_netdevice_notifier(nb, NETDEV_REGISTER, dev);err = notifier_to_errno(err);if (err)goto rollback;if (!(dev->flags & IFF_UP))continue;call_netdevice_notifier(nb, NETDEV_UP, dev);}}unlock:rtnl_unlock();return err;

raw_notifier_chain_register将nb(即tun_notifier_block)添加到netdev_chain中的链表head上。

register_netdevice_notifier中的两层for循环,遍历网络空间中的每个设备,通知其NETDEV_REGISTER和NETDEV_UP。

call_netdevice_notifier(nb, NETDEV_REGISTER, dev);

call_netdevice_notifier(nb, NETDEV_UP, dev);

static int call_netdevice_notifier(struct notifier_block *nb, unsigned long val,struct net_device *dev)
{struct netdev_notifier_info info = {.dev = dev,};// 对于tun驱动,nb为 tun_notifier_blockreturn nb->notifier_call(nb, val, &info);
}

最终调用到:

static int tun_device_event(struct notifier_block *unused,unsigned long event, void *ptr)
{struct net_device *dev = netdev_notifier_info_to_dev(ptr);struct tun_struct *tun = netdev_priv(dev);if (dev->rtnl_link_ops != &tun_link_ops)return NOTIFY_DONE;switch (event) {case NETDEV_CHANGE_TX_QUEUE_LEN:if (tun_queue_resize(tun))return NOTIFY_BAD;break;default:break;}return NOTIFY_DONE;
}

两次通知,tun_device_event仅仅返回NOTIFY_DONE。

http://www.lryc.cn/news/20362.html

相关文章:

  • 模拟退火算法优化bp
  • 【NFC音乐相册】简易制作
  • 每日一题——L1-085 试试手气(15)
  • FreeRTOS信号量
  • Leetcode.2385 感染二叉树需要的总时间
  • [蓝桥杯 2022 国 B] 卡牌(贪心/二分)
  • 1301:大盗阿福
  • Netty——序列化的作用及自定义协议
  • 一起Talk Android吧(第五百零五回:如何调整组件在约束布局中的大小)
  • 【数据库】数据库的完整性
  • 基因净化车间装修设计方案SICOLAB
  • java 内部类的四种“写法”
  • 【python】main方法教程
  • 公司对不同职级能力抽象要求的具体化
  • Java之MinIO存储桶和对象API使用
  • 如何用java实现同时进行多个请求,可以将它们并行执行,从而减少总共的请求时间。
  • 高端装备的AC主轴头结构
  • 【Proteus仿真】【51单片机】粮仓温湿度控制系统设计
  • 【LINUX】环境变量以及main函数的参数
  • 使用Pyparsing为嵌入式开发定义自己的脚本语言
  • C win32基础学习(二)
  • 理论五:控制反转、依赖反转、依赖注入,这三者有何区别和联系?
  • 读书笔记//《数据分析之道》
  • 1个串口用1根线实现多机半双工通信+开机控制电路
  • KUKA机器人外部自动运行模式的相关信号配置
  • 【RabbitMQ笔记02】消息队列RabbitMQ七种模式之最简单的模式
  • Spring MVC 源码- RequestToViewNameTranslator 组件
  • Linux--TCP编程--0216 17
  • 关于设计模式的记录
  • Lambda-常见的函数式接口