i.MX6网卡驱动程序fec.c的分析(AR8035网卡驱动程序的详细分析)之一

学习交流加

  • 个人qq:
    1126137994
  • 个人微信:
    liu1126137994
  • 学习交流资源分享qq群:
    962535112

因为做的项目需要用到ethercat主站,而用ethercat主站,标准的网卡网络协议栈性能就无法达到要求,需要根据ethercat官方提供的文档修改网卡驱动程序的网络协议栈。那么在做正式的工作之前呢,我们先来分析i.MX6的网卡驱动程序的整体框架,弄懂后,再去修改。
参考资料:
《Linux network driver development Training lab book》,是一本很好的讲述网卡驱动是如何从无到有的编写过程。下载地址:点击下载

参考文章:点击查看参考的文章

我们的网卡驱动程序的位置位于:drviers/net/fec.c,对应还有一个fec.h作头文件。
(分析内核的驱动程序,必须用source insight阅读代码,或者其他类似的代码阅读工具,方便代码的查找与跳跃,不然很难分析。)

首先是驱动程序的入口:

static int __init
fec_enet_module_init(void)
{printk(KERN_INFO "FEC Ethernet Driver\n");return platform_driver_register(&fec_driver);
}static void __exit
fec_enet_cleanup(void)
{platform_driver_unregister(&fec_driver);
}module_exit(fec_enet_cleanup);
module_init(fec_enet_module_init);MODULE_LICENSE("GPL");

接着是上面的设置从uboot获取网卡地址信息的函数:fec_mac_addr_setup

static int fec_mac_addr_setup(char *mac_addr)
{char *ptr, *p = mac_addr;unsigned long tmp;int i = 0, ret = 0;while (p && (*p) && i < 6) {ptr = strchr(p, ':');if (ptr)*ptr++ = '\0';if (strlen(p)) {ret = strict_strtoul(p, 16, &tmp);if (ret < 0 || tmp > 0xff)break;macaddr[i++] = tmp;}p = ptr;}return 0;
}/* 从uboot传给内核的启动参数中捕获fec_mac(即mac地址)参数,并将该参数传递给fec_mac_addr_setup */
__setup("fec_mac=", fec_mac_addr_setup);

接下来是fec_driver结构体:

static struct platform_driver fec_driver = {.driver	= {.name	= DRIVER_NAME,.owner	= THIS_MODULE,
#ifdef CONFIG_PM.pm	= &fec_pm_ops,
#endif},.id_table = fec_devtype,.probe	= fec_probe,.remove	= __devexit_p(fec_drv_remove),
};

这里面都是我们所熟悉的name,owner,probe,idtable等,有一个不熟悉,.pm这个参数,我们看看这是什么?通过source insight查看,发现这个pm所需要的宏,并没有被定义,所以这个参数暂时没有用到,暂时不分析。

那么接下来分析id_table字段,对应的函数为:


static struct platform_device_id fec_devtype[] = {{.name = "enet",.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_BUG_TKT168103,},{.name = "fec",.driver_data = 0,},{.name = "imx28-fec",.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |FEC_QUIRK_BUG_TKT168103,},{ }
};

这里的fec_devtype[]数组主要是用来进行device与driver的匹配。默认的匹配函数内核代码中有(drivers/base):platform.c中:

static int platform_match(struct device *dev, struct device_driver *drv)
{struct platform_device *pdev = to_platform_device(dev);struct platform_driver *pdrv = to_platform_driver(drv);/* Attempt an OF style match first */if (of_driver_match_device(dev, drv))return 1;/* Then try to match against the id table */if (pdrv->id_table)return platform_match_id(pdrv->id_table, pdev) != NULL;/* fall-back to driver name match */return (strcmp(pdev->name, drv->name) == 0);
}

可以看到匹配的三个选择:
1.如果存在设备树机制,则优先匹配设备树,这种情况下,内核会寻找.driver 中的.of_match_table匹配。
2.使用驱动中的.id_table列表匹配,在id_table数组中寻找与device的名字相匹配的成员。
3.最后单纯地比较device的名字和driver的名字是否相同,是则匹配成功返回。

看完driver与device,接下来先看一下板级信息是在哪里添加的吧。
在arch/arm/mach-mx6/board-mx6q_sabresd.c文件中,可以找到函数mx6_sabresd_board_init:这里面是一些设备的初始化,我们找到了函数:imx6_init_fec(fec_data);这个函数是添加以太网device设备的函数。

而mx6_sabresd_board_init这个函数,在MACHINE_START里中被初始化:

/** initialize __mach_desc_MX6Q_SABRESD data structure.*/
MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board")/* Maintainer: Freescale Semiconductor, Inc. */.boot_params = MX6_PHYS_OFFSET + 0x100,.fixup = fixup_mxc_board,.map_io = mx6_map_io,.init_irq = mx6_init_irq,.init_machine = mx6_sabresd_board_init,.timer = &mx6_sabresd_timer,.reserve = mx6q_sabresd_reserve,
MACHINE_END

下面我们主要分析添加以太网设备的函数:imx6_init_fec(fec_data),从而扩散到驱动程序的整个架构。这个函数被定义在:arch/arm/mach-mx6/mx6_fec.c中,如下:

void __init imx6_init_fec(struct fec_platform_data fec_data)
{fec_get_mac_addr(fec_data.mac);if (!is_valid_ether_addr(fec_data.mac))random_ether_addr(fec_data.mac);if (cpu_is_mx6sl())imx6sl_add_fec(&fec_data);elseimx6q_add_fec(&fec_data);
}

这里的参数:struct fec_platform_data fec_data,是设备的私有数据,被定义在:include/linux/fec.h中:

struct fec_platform_data {int (*init) (struct phy_device *);int (*power_hibernate) (struct phy_device *);phy_interface_t phy;unsigned char mac[ETH_ALEN];int gpio_irq;
};

而这个参数fec_data已经在board-mx6q_sabresd.c中静态初始化好的如下:

static struct fec_platform_data fec_data __initdata = {.init = mx6q_sabresd_fec_phy_init,.phy = PHY_INTERFACE_MODE_RGMII,//.gpio_irq = MX6_ENET_IRQ,
};

而函数imx6_init_fec最终是执行函数imx6q_add_fec(&fec_data)进行添加设备(arch/arm/mach-mx6/devices-imx6q.h中):

extern const struct imx_fec_data imx6q_fec_data __initconst;
#define imx6q_add_fec(pdata)	\imx_add_fec(&imx6q_fec_data, pdata)

这里引入了外部的函数:imx6q_fec_data,这个函数定义在arch/arm/plat-mxc/devices/platform-fec.c中,如下:

#ifdef CONFIG_SOC_IMX6Q
const struct imx_fec_data imx6q_fec_data __initconst =imx_fec_data_entry_single(MX6Q, "enet");const struct imx_fec_data imx6sl_fec_data __initconst =imx_fec_data_entry_single(MX6DL, "fec");
#endif

我们看到了“enet”“fec”,这是设备的name,找到这里,就找到了设备的name。
而imx_fec_data_entry_single这个函数也是定义在上相同的文件中,如下:

#define imx_fec_data_entry_single(soc, _devid)				\{								\.iobase = soc ## _FEC_BASE_ADDR,			\.irq = soc ## _INT_FEC,					\.devid = _devid,					\}

这个函数的参数soc,后面的两个 ## 号代表把前后两个字符串连接成为一个变量,## 左右的空格会被忽略。那么上面的函数把传进来的参数展开后就等同于下面的代码:

#define imx_fec_data_entry_single(soc, _devid)				\{								\.iobase = MX6Q_FEC_BASE_ADDR,			\.irq = MX6Q_INT_FEC,					\.devid = _devid,					\}

MX6Q_FEC_BASE_ADDR,MX6Q_INT_FEC这两个值被定义在:arch/arm/plat-mxc/include/mach/mx6.h,通过宏展开可以查到这两个值。
下面继续分析函数:imx_add_fec(&imx6q_fec_data, pdata),它定义在:arch/arm/plat-mxc/devices/platform-fec.c中如下:

struct platform_device *__init imx_add_fec(const struct imx_fec_data *data,const struct fec_platform_data *pdata)
{struct resource res[] = {{.start = data->iobase,.end = data->iobase + SZ_4K - 1,.flags = IORESOURCE_MEM,}, {.start = data->irq,.end = data->irq,.flags = IORESOURCE_IRQ,},};if (!fuse_dev_is_available(MXC_DEV_ENET))return ERR_PTR(-ENODEV);return imx_add_platform_device_dmamask(data->devid, 0,res, ARRAY_SIZE(res),pdata, sizeof(*pdata), DMA_BIT_MASK(32));
}

从上面的函数得知imx_fec_data最终被传送给了resource结构体。然后通过函数:imx_add_platform_device_dmamask将resource和额外的pdata数据添加到device中,最后在driver中再来获取device的这些信息。

以上是device的添加和driver的加载过程,当device与driver匹配之后,就进入驱动的probe入口函数。

static int __devinit
fec_probe(struct platform_device *pdev)
{	。。。。。。(函数太长不全部复制了,下面部分分析复制)
}

probe函数传进来的参数是刚刚分析过的imx_add_fec,也就是将resource和额外的pdata数据传进来。先获得resource资源属性,然后申请内存资源空间。代码如下:

	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!r)return -ENXIO;r = request_mem_region(r->start, resource_size(r), pdev->name);if (!r)return -EBUSY;

然后申请net_device结构体,初始化网络设备,传进来driver的私有数据的尺寸,代码如下:

/* Init network device */ndev = alloc_etherdev(sizeof(struct fec_enet_private));if (!ndev) {ret = -ENOMEM;goto failed_alloc_etherdev;}

然后告诉内核我们的设备属于网络设备,由于net_device不属于char和block设备,因此不能用常规的方法来设计驱动。

SET_NETDEV_DEV(ndev, &pdev->dev);

定义如下:

/* Set the sysfs physical device reference for the network logical device* if set prior to registration will cause a symlink during initialization.*/
#define SET_NETDEV_DEV(net, pdev)	((net)->dev.parent = (pdev))

分配完net_device结构体之后,接下来,需要用下面的函数获取分配所得的私有数据的指针:

fep = netdev_priv(ndev);

然后用ioremap函数将网卡的寄存器的物理地址映射到内核空间,并且给获取得到的私有数据指针赋值。代码如下:

	fep->hwp = ioremap(r->start, resource_size(r));fep->pdev = pdev;if (!fep->hwp) {ret = -ENOMEM;goto failed_ioremap;}

然后下面的代码是用来设置平台driver的数据,以及接收之前在板级文件中设置的platform_data,

	platform_set_drvdata(pdev, ndev);//pdev->dev->p->driver_data = ndevpdata = pdev->dev.platform_data;//接收之前在板级文件下静态添加device的platform_data

接下来是对接收到的数据进行判断解析。这里的pdata->phy用来判断MAC物理层和以太网物理层的的接口的,从之前的fec_data里获取的数据,得知我们的iMX6q用的接口是:PHY_INTERFACE_MODE_RGMII。

	if (pdata)fep->phy_interface = pdata->phy;

接下来是关于中断的相关判断代码如下:

	if (pdata->gpio_irq > 0) {gpio_request(pdata->gpio_irq, "gpio_enet_irq");gpio_direction_input(pdata->gpio_irq);irq = gpio_to_irq(pdata->gpio_irq);ret = request_irq(irq, fec_enet_interrupt,IRQF_TRIGGER_RISING,pdev->name, ndev);if (ret)goto failed_irq;} else {/* This device has up to three irqs on some platforms */for (i = 0; i < 3; i++) {irq = platform_get_irq(pdev, i);if (i && irq < 0)break;ret = request_irq(irq, fec_enet_interrupt,IRQF_DISABLED, pdev->name, ndev);if (ret) {while (--i >= 0) {irq = platform_get_irq(pdev, i);free_irq(irq, ndev);}goto failed_irq;}}}

由之前在平台层静态设置的fec_data结构中:

static struct fec_platform_data fec_data __initdata = {.init = mx6q_sabresd_fec_phy_init,.phy = PHY_INTERFACE_MODE_RGMII,//.gpio_irq = MX6_ENET_IRQ,
};

这一行://.gpio_irq = MX6_ENET_IRQ,已经被屏蔽掉了,得知上面的代码是执行else分支。
(现在我们来分析一下,如果上面那行代码没有被屏蔽掉,那么我们该如何判断出上面的代码到底要执行哪一个分支呢?
分析:我们这里的MX6_ENET_IRQ的值是6,我们可以知道在添加私有数据之前,有如下代码:

	if (enet_to_gpio_6)// Make sure the IOMUX_OBSRV_MUX1 is set to ENET_IRQ. mxc_iomux_set_specialbits_register(IOMUX_OBSRV_MUX1_OFFSET,OBSRV_MUX1_ENET_IRQ,OBSRV_MUX1_MASK);elsefec_data.gpio_irq = -1;imx6_init_fec(fec_data); 

它用来判断是否对fec_data的gpio_irq赋值为-1,而我们的enet_to_gpio_6是一个被定义在arch/arm/mach-mx6/cpu.c中的全局变量,默认值为0,同样在该文件可以找到下面的代码:

static int __init set_enet_irq_to_gpio(char *p)
{enet_to_gpio_6 = true;return 0;
}early_param("enet_gpio_6", set_enet_irq_to_gpio);

early_param用来解析uboot传递给内核的参数,只有当uboot参数中带有enet_gpio_6这个参数时,参会执行set_enet_irq_to_gpio,所以上面的enet_to_gpio_6得值就为默认值0,所以会执行上面的这一行语句:fec_data.gpio_irq = -1;那么pdata->gpio_irq<0,所以刚才的代码就不会执行:if (pdata->gpio_irq > 0)语句,而会执行else分支分支。)

即上面的代码是从platform_device 的resource获取中断号的。由上面的分析,我们知道imx_fec_data_entry_single这个宏,展开后是如下代码:

	#define imx_fec_data_entry_single(soc, _devid)				\{								\.iobase = MX6Q_FEC_BASE_ADDR,			\.irq = MX6Q_INT_FEC,					\.devid = _devid,					\}

得知我们的中断号是:MX6Q_INT_FEC,它的值为150.对应的中断注册函数为:fec_enet_interrupt()。

中断的注册分析完了后,接下来是时钟了,linux内核有一组专门的clock api用来处理时钟,简单点来说就只要知道首先clk_get()然后再clk_enable()就可以了,driver remove的时候反之即clk_disable()再clk_put()即可。
代码如下:

	fep->clk = clk_get(&pdev->dev, "fec_clk");if (IS_ERR(fep->clk)) {ret = PTR_ERR(fep->clk);goto failed_clk;}fep->mdc_clk = clk_get(&pdev->dev, "fec_mdc_clk");if (IS_ERR(fep->mdc_clk)) {ret = PTR_ERR(fep->mdc_clk);goto failed_clk;}clk_enable(fep->clk);

这里的fec_clk是Ethernet控制器的clock,fec_mdc_clk是MAC层与物理层接口MDIO的Clock。

接下来是MAC层的初始化函数,代码如下:

	ret = fec_enet_init(ndev); (比较重要,后面具体函数再分析)if (ret)goto failed_init;

再接着就是MAC层与物理层接口的初始化函数:

	ret = fec_enet_mii_init(pdev); (比较重要,后面具体函数再分析)if (ret)goto failed_mii_init;

再接着就是对IEEE 1588 时钟同步协议的初始化:

	if (fec_ptp_malloc_priv(&(fep->ptp_priv))) {if (fep->ptp_priv) {fep->ptp_priv->hwp = fep->hwp;ret = fec_ptp_init(fep->ptp_priv, pdev->id);if (ret)printk(KERN_WARNING "IEEE1588: ptp-timer is unavailable\n");elsefep->ptimer_present = 1;} elseprintk(KERN_ERR "IEEE1588: failed to malloc memory\n");}

然后把内核网络层的传输队列关闭(即禁止发送),关闭时钟,等一些列辅助操作:

	/* Carrier starts down, phylib will bring it up */netif_carrier_off(ndev);clk_disable(fep->clk);INIT_DELAYED_WORK(&fep->fixup_trigger_tx, fixup_trigger_tx_func);

最后就是我们再熟悉不过的,将前面分配的net_device结构体注册进内核:

	ret = register_netdev(ndev);if (ret)goto failed_register;

接下来我将详细讲述MAC层,物理层接口以及1588协议支持的代码,从而用ethercat的协议进行网卡协议的修改。放到下一篇文章分析(点击链接查看下一篇文章)

想一起探讨以及获得各种学习资源加我(有我博客中写的代码的原稿):
qq:1126137994
微信:liu1126137994
可以共同交流关于嵌入式,操作系统,C++语言,C语言,数据结构等技术问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/423318.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TOAD常用快捷键

现在在企业中&#xff0c;操作oracle数据库的客户端&#xff0c;除了PL/SQL外&#xff0c;使用的较多的就是TOAD了&#xff01; 为此&#xff0c;我在网上搜索了下&#xff0c;整理了些简单TOAD的使用技巧&#xff0c;现分享给大家。 常用快捷键&#xff1a; F8 调出以前执行的…

i.MX6网卡驱动程序fec.c的分析(AR8035网卡驱动程序的详细分析)之二

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 今天接着分析上次没有分析完的i.MX6网卡驱动程序。上一篇分析了iMX6网卡驱动程序的driver与device的加载过程&#xff08;点击可以查看上一篇文章&…

文档视图

IntelliFMEA是从IntelliQMS项目的子项目&#xff0c;可单独发布。在IntelliQMS中的APQP插件设计了一个更为完整的项目管理。在IntelliFMEA中的项目管理只是对FMEA有关的文档进行管理的一种方式。IntelliFMEA文档视图的工作方式是:1. 浏览和级联显示IntelliFMEA当前项目的文档清…

阅读ethercat官方文档关于ethercat网卡驱动程序的一些内容

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 改造iMX6&#xff08;fec&#xff09;网卡驱动程序前期工作之&#xff1a;阅读ethercat-1.5.2.pdf文档的第四章内容。 ethercat-1.5.2.pdf文档链接&a…

事务和锁

事务和锁 事务的定义 事务&#xff08;Transaction&#xff09;&#xff0c;一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。 事务…

数据库表的字段中含空格怎么办?

数据库建表&#xff1a; create table t1203 ("ha ha" varchar(100));查询语句&#xff1a;select "ha ha" from t1203;插入数据&#xff1a;insert into t1203("ha ha") values(hello world);其他类似.... 转载于:https://www.cnblogs.com/s…

jz2440开发板移植U-boot之修改代码支持DM9000网卡

今天我们来移植U-boot到jz2440开发板&#xff0c;修改代码支持DM9000网卡。查看之前写的移植记录请点击链接&#xff1a;点击查看之前的移植记录 现在大多数开发板都支持DM9000网卡。我们的U-boot源码里面也是有DM9000网卡的驱动程序的。文件为Dm9000x.c&#xff08;drivers\n…

利用Lombok编写优雅的spring依赖注入代码,去掉繁人的@Autowired

1.引入Lombok 视频教程 https://www.projectlombok.org 2.Lombok jar 下载地址 https://plugins.jetbrains.com/plugin/6317-lombok-plugin 3.大家平时使用spring依赖注入&#xff0c;都是怎么写的&#xff1f; Service public class OrderService { Autowired private Us…

99%与100%

一个表格可以正常的显示&#xff0c;也就是有所有的边框&#xff0c;可是打印的时候却没有右边框。相对来说这个表格比较复杂&#xff0c;首先他有headgroup footgroup也就是在打印的时候分页打印并显示多行表头用的&#xff0c;所以在css上下了一些功夫。可是上边说的问题怎么…

移植U-BOOT之裁剪和修改默认参数(易用性)启动内核,以及对uboot进行分区

今天我们来裁剪U-BOOT&#xff0c;使其更加易用&#xff0c;修改默认参数&#xff0c;以及制作最终修改好得补丁文件方便以后的快速移植。 那么如果想看之前的关于网卡以及flash等的移植&#xff0c;请点击链接查看&#xff1a;点击链接查看 在裁剪修改之前呢&#xff0c;我们…

移植U-BOOT之支持烧写YAFFS文件系统以及制作U-BOOT补丁

今天&#xff0c;我们来移植U-BOOT让其支持YAFFS文件系统映像的烧写&#xff0c;以及最后的终极目标&#xff0c;制作Uboot补丁&#xff0c;因为我们信心苦苦移植好了Uboot&#xff0c;如果换一个地方的或者换一台电脑之类的&#xff0c;我们也不想再浪费时间从头开始移植&…

PHP字符串函数大全

PHP字符串函数大全 AddSlashes: 字符串加入斜线。 bin2hex: 二进位转成十六进位。 Chop: 去除连续空白。 Chr: 返回序数值的字符。 chunk_split: 将字符串分成小段。 convert_cyr_string: 转换古斯拉夫字符串成其它字符串。 crypt: 将字符串用 DES 编码加密。 echo: 输出字符串…

SpringBoot 使用教程

SpringBoot系列一&#xff1a;SpringBoot入门Spring Boot基础教程 ( 五 ) &#xff1a;构建 RESTful API 与单元测试Spring Boot基础教程 ( 四 ) &#xff1a;Spring Boot 属性配置文件详解Spring Boot基础教程 ( 三 ) &#xff1a;使用 Cloud Studio 在线编写、调试和管理 Spr…

【数据结构学习之完全从零实现所有数据结构的代码编写之一】泛型编程简介

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 今天开始系统性学习数据结构内容&#xff0c;之前也看过大话数据结构这本书&#xff0c;对大多数概念以及数据结构都有一定的了解&#xff0c;但是就是…

javascript tabIndex属性

tabIndex 的用处很简单&#xff0c;就是利用tab键遍历页面的表单元素和链接&#xff0c;按照tabindex的大小决定顺序。虽然微不足道&#xff0c;但细节处见真功夫&#xff0c;这是任何一个WEB应用应当具备的亲用力&#xff0c;保证用户在没有鼠标的情况下&#xff08;如WAP&…

zookeeper 安装和使用

1.Windows安装和使用zookeeper 之前整理过一篇文章《zookeeper 分布式锁服务》&#xff0c;本文介绍的 Zookeeper 是以 3.4.5 这个稳定版本为基础&#xff0c;最新的版本可以通过官网 http://hadoop.apache.org/zookeeper/来获取&#xff0c;Zookeeper 的安装非常简单&#xf…

【移植Linux 3.4.2内核第一步】之简单修改

前一阵子已经将U-boot移植好了&#xff0c;从今天开始&#xff0c;我们开始移植linux内核。移植的内核为3.4.2&#xff0c;移植的开发板为&#xff1a;jz2440开发板。 想看之前移植U-boot的记录&#xff0c;可以查看我的博客专栏&#xff0c;点击链接&#xff1a;点击查看U-bo…

HBase 2.0版本正式发布

1.HBase 2.0版本正式发布 关于HBase版本定义 HBase从1.0.0版本开始&#xff0c;在版本定义上正式遵循了Semantic Versioning规范&#xff1a; 一个版本号&#xff0c;由三部分组成&#xff1a; MAJOR.MINOR.PATCH&#xff0c;关于这三部分数字的变更&#xff0c;定义如下&am…

前端学习(77):css中常见margin塌陷问题之解决办法

塌陷问题 当两个盒子在垂直方向上设置margin值时&#xff0c;会出现一个有趣的塌陷现象。 ①垂直并列 首先设置两个DIV,并为其制定宽高 1 1 /*HTML部分*/2 <body>3 <div class"box1">box1</div>4 <div class"box2">box2…

HBase2.0 vs HBase1.x 延时比较

hbase2.0已经正式发布&#xff0c;对比之前1.x版本&#xff0c;2.0在读写链路上做了完善的优化&#xff0c;offheap、netty rpc等&#xff0c;这里做个小测试实验对比1.x和2.0在读写上的延时情况。本测试基于特定测试环境与软件版本得到的结果&#xff0c;仅供参考。 测试介绍 …