内核提供的通用I2C设备驱动I2C-dev.c分析:file_ops篇

往期内容

I2C子系统专栏:

  1. I2C(IIC)协议讲解-CSDN博客
  2. SMBus 协议详解-CSDN博客
  3. I2C相关结构体讲解:i2c_adapter、i2c_algorithm、i2c_msg-CSDN博客
  4. 内核提供的通用I2C设备驱动I2c-dev.c分析:注册篇

总线和设备树专栏:

  1. 总线和设备树_憧憬一下的博客-CSDN博客
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客

前言

在上一篇,注册篇中,从讲解普通的字符设备驱动框架后再讲解了关于i2c-dev.c中是如何去注册字符设备驱动的,接下来就讲解关于其file_operations中定义的函数是如何去实现的。

内核中提供的驱动文件: Linux-4.9.88/drivers/i2c/i2c-dev.c📎i2c-dev.c

先来file_operation看看定义了哪些函数:

static const struct file_operations i2cdev_fops = {.owner		= THIS_MODULE,.llseek		= no_llseek,.read		= i2cdev_read,.write		= i2cdev_write,.unlocked_ioctl	= i2cdev_ioctl,.open		= i2cdev_open,.release	= i2cdev_release,
};

1. i2cdev_open

img

static int i2cdev_open(struct inode *inode, struct file *file)
{unsigned int minor = iminor(inode);   // 获取次设备号struct i2c_client *client;struct i2c_adapter *adap;// 根据次设备号获取对应的 I²C 适配器adap = i2c_get_adapter(minor);if (!adap)return -ENODEV;  // 如果适配器不存在,返回 -ENODEV 表示没有设备/* 创建一个匿名的 i2c_client 结构体,该结构体仅在用户空间与 I2C* 设备通信时使用,并不会注册到内核的 I²C 驱动模型中。*/client = kzalloc(sizeof(*client), GFP_KERNEL);if (!client) {i2c_put_adapter(adap);  // 如果内存分配失败,释放适配器并返回 -ENOMEMreturn -ENOMEM;}// 将适配器编号和 "i2c-dev" 作为客户端的名字snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);// 将 I²C 适配器指针存储到客户端结构体中client->adapter = adap;// 将创建的匿名客户端结构体存储在 file->private_data 中,供后续操作使用file->private_data = client;return 0;  // 成功返回 0
}
  • 匿名客户端:此处创建的 i2c_client 结构体是匿名的,它不会注册到 I²C 驱动模型或内核的 I²C 核心代码中。这意味着该客户端仅用于用户空间与 I²C 适配器的通信,不会影响系统中的其他 I²C 驱动和设备。
  • 用例:创建匿名客户端允许用户通过字符设备接口(如 /dev/i2c-X)对 I²C 总线进行操作,用户可以通过 ioctl 等系统调用向特定的从设备发送命令。

i2cdev_open 的核心功能是为打开的 I²C 设备文件创建一个匿名的 I²C 客户端(i2c_client),该客户端只用于当前文件操作的上下文中,允许用户通过字符设备与 I²C 适配器通信。

2. i2cdev_ioctl

i2cdev_ioctl 函数负责处理来自用户空间的 ioctl 系统调用,允许用户通过 I²C 设备文件对 I²C 设备进行各种控制操作。它主要基于用户传入的命令 (cmd) 来执行不同的功能。 先看下代码,这里添加了一些自己理解的注释,具体的参数在下面小点讲解

static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{struct i2c_client *client = file->private_data; // 获取当前文件对应的 I²C 客户端unsigned long funcs;// 输出调试信息,显示 ioctl 调用的命令和参数dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n", cmd, arg);switch (cmd) {case I2C_SLAVE:case I2C_SLAVE_FORCE:if ((arg > 0x3ff) || (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))return -EINVAL; // 检查设备地址的有效性if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))return -EBUSY;  // 检查地址是否忙client->addr = arg; // 设置客户端的 I²C 地址return 0;case I2C_TENBIT:if (arg)client->flags |= I2C_M_TEN; // 启用 10 位地址模式elseclient->flags &= ~I2C_M_TEN; // 禁用 10 位地址模式return 0;case I2C_PEC:if (arg)client->flags |= I2C_CLIENT_PEC; // 启用 PEC 校验elseclient->flags &= ~I2C_CLIENT_PEC; // 禁用 PEC 校验return 0;case I2C_FUNCS:funcs = i2c_get_functionality(client->adapter); // 获取适配器的功能集return put_user(funcs, (unsigned long __user *)arg); // 将功能集返回给用户空间case I2C_RDWR:return i2cdev_ioctl_rdwr(client, arg); // 执行多字节读写操作case I2C_SMBUS:return i2cdev_ioctl_smbus(client, arg); // 处理 SMBus 操作case I2C_RETRIES:client->adapter->retries = arg; // 设置重试次数break;case I2C_TIMEOUT:client->adapter->timeout = msecs_to_jiffies(arg * 10); // 设置超时时间,单位为 10 毫秒break;default:return -ENOTTY; // 对于未识别的命令,返回 `-ENOTTY` 表示不支持此 ioctl}return 0;
}

2.1 i2cdev_ioctl: I2C_SLAVE/I2C_SLAVE_FORCE

img

**I2C_SLAVE** **I2C_SLAVE_FORCE** 命令

  • 这两个命令用于设置 I²C 设备的从设备地址(arg 参数)。
  • I2C_SLAVE:在设置地址之前检查是否有其他设备占用该地址,如果占用则返回 -EBUSY
  • I2C_SLAVE_FORCE:强制设置地址,不做占用检查。
  • 如果地址超出 7 位(标准地址模式)或者 10 位(十位地址模式),返回 -EINVAL 表示参数无效。

2.2 i2cdev_ioctl: I2C_RDWR

发起I2C传输

img

2C_RDWR 命令

  • 这个命令用于执行读写操作,调用 i2cdev_ioctl_rdwr() 实现。用户app调用ioctl时使用这个参数,可以触发读写操作。

来看一下i2cdev_ioctl_rdwr函数, 处理用户空间发起的 I2C_RDWR 命令的函数,它执行多条 I²C 消息的读写操作。这是通过 ioctl 调用实现的,用于对 I²C 总线进行低层次的数据操作。函数主要完成从用户空间获取请求,执行 I²C 传输,并将结果返回给用户空间。

static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,unsigned long arg)
{struct i2c_rdwr_ioctl_data rdwr_arg;struct i2c_msg *rdwr_pa;  //传输的基本单位,msgu8 __user **data_ptrs;int i, res;// 从用户空间复制数据到 rdwr_argif (copy_from_user(&rdwr_arg,(struct i2c_rdwr_ioctl_data __user *)arg,sizeof(rdwr_arg)))return -EFAULT;// 检查消息数量是否超过限制if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS)return -EINVAL;// 从用户空间复制消息结构数组到内核空间rdwr_pa = memdup_user(rdwr_arg.msgs,rdwr_arg.nmsgs * sizeof(struct i2c_msg));if (IS_ERR(rdwr_pa))return PTR_ERR(rdwr_pa);// 为数据指针数组分配内存data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);if (data_ptrs == NULL) {kfree(rdwr_pa);return -ENOMEM;}res = 0;// 遍历每条 I²C 消息for (i = 0; i < rdwr_arg.nmsgs; i++) {// 检查消息长度是否合法if (rdwr_pa[i].len > 8192) {res = -EINVAL;break;}// 保存用户空间指针,并从用户空间复制消息的缓冲区data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);if (IS_ERR(rdwr_pa[i].buf)) {res = PTR_ERR(rdwr_pa[i].buf);break;}// 如果消息长度是由从设备决定的,需要处理接收缓冲区if (rdwr_pa[i].flags & I2C_M_RECV_LEN) {if (!(rdwr_pa[i].flags & I2C_M_RD) ||rdwr_pa[i].buf[0] < 1 ||rdwr_pa[i].len < rdwr_pa[i].buf[0] +I2C_SMBUS_BLOCK_MAX) {res = -EINVAL;break;}rdwr_pa[i].len = rdwr_pa[i].buf[0]; // 设置接收长度}}// 如果发生错误,释放分配的内存if (res < 0) {int j;for (j = 0; j < i; ++j)kfree(rdwr_pa[j].buf);kfree(data_ptrs);kfree(rdwr_pa);return res;}// 调用内核的 i2c_transfer 函数进行 I²C 传输res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);// 传输完成后,将数据从内核空间复制回用户空间while (i-- > 0) {if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,rdwr_pa[i].len))res = -EFAULT;}kfree(rdwr_pa[i].buf); // 释放消息缓冲区}kfree(data_ptrs); // 释放数据指针数组kfree(rdwr_pa);   // 释放消息结构数组return res;
}

2.3 i2cdev_ioctl: I2C_SMBUS

发起SMBus传输

img

当用户app调用了ioctl时传的参数是I2C_SMBUS,设备驱动程序中就会调用i2cdev_ioctl_smbus来发起smbus传输,i2cdev_ioctl_smbus在i2c-dev.c中定义如下,这里添加了一些自己理解的注释:

static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,unsigned long arg)
{struct i2c_smbus_ioctl_data data_arg;union i2c_smbus_data temp = {};int datasize, res;// 从用户空间获取 SMBus 请求数据if (copy_from_user(&data_arg,(struct i2c_smbus_ioctl_data __user *) arg,sizeof(struct i2c_smbus_ioctl_data)))return -EFAULT;// 检查数据大小是否在有效范围内if ((data_arg.size != I2C_SMBUS_BYTE) &&(data_arg.size != I2C_SMBUS_QUICK) &&(data_arg.size != I2C_SMBUS_BYTE_DATA) &&(data_arg.size != I2C_SMBUS_WORD_DATA) &&(data_arg.size != I2C_SMBUS_PROC_CALL) &&(data_arg.size != I2C_SMBUS_BLOCK_DATA) &&(data_arg.size != I2C_SMBUS_I2C_BLOCK_BROKEN) &&(data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) &&(data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) {dev_dbg(&client->adapter->dev,"size out of range (%x) in ioctl I2C_SMBUS.\n",data_arg.size);return -EINVAL;}// 检查读/写标志是否合法if ((data_arg.read_write != I2C_SMBUS_READ) &&(data_arg.read_write != I2C_SMBUS_WRITE)) {dev_dbg(&client->adapter->dev,"read_write out of range (%x) in ioctl I2C_SMBUS.\n",data_arg.read_write);return -EINVAL;}// 如果是 I2C_SMBUS_QUICK 或 I2C_SMBUS_BYTE 的写操作,不使用数据指针if ((data_arg.size == I2C_SMBUS_QUICK) ||((data_arg.size == I2C_SMBUS_BYTE) &&(data_arg.read_write == I2C_SMBUS_WRITE)))return i2c_smbus_xfer(client->adapter, client->addr,client->flags, data_arg.read_write,data_arg.command, data_arg.size, NULL);// 检查数据指针是否为空if (data_arg.data == NULL) {dev_dbg(&client->adapter->dev,"data is NULL pointer in ioctl I2C_SMBUS.\n");return -EINVAL;}// 根据 SMBus 命令类型确定数据大小if ((data_arg.size == I2C_SMBUS_BYTE_DATA) ||(data_arg.size == I2C_SMBUS_BYTE))datasize = sizeof(data_arg.data->byte);else if ((data_arg.size == I2C_SMBUS_WORD_DATA) ||(data_arg.size == I2C_SMBUS_PROC_CALL))datasize = sizeof(data_arg.data->word);else // 块数据或块处理调用datasize = sizeof(data_arg.data->block);// 如果是写操作或处理调用,复制用户空间的数据到内核空间if ((data_arg.size == I2C_SMBUS_PROC_CALL) ||(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||(data_arg.size == I2C_SMBUS_I2C_BLOCK_DATA) ||(data_arg.read_write == I2C_SMBUS_WRITE)) {if (copy_from_user(&temp, data_arg.data, datasize))return -EFAULT;}// 处理旧版 I2C 块命令,保持二进制兼容性if (data_arg.size == I2C_SMBUS_I2C_BLOCK_BROKEN) {data_arg.size = I2C_SMBUS_I2C_BLOCK_DATA;if (data_arg.read_write == I2C_SMBUS_READ)temp.block[0] = I2C_SMBUS_BLOCK_MAX;}// 调用内核的 i2c_smbus_xfer 执行 SMBus 传输res = i2c_smbus_xfer(client->adapter, client->addr, client->flags,data_arg.read_write, data_arg.command, data_arg.size, &temp);// 如果是读取操作,传输完成后,将数据复制回用户空间if (!res && ((data_arg.size == I2C_SMBUS_PROC_CALL) ||(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||(data_arg.read_write == I2C_SMBUS_READ))) {if (copy_to_user(data_arg.data, &temp, datasize))return -EFAULT;}return res;
}

i2cdev_ioctl_smbus 函数用于处理用户空间发起的 SMBus(系统管理总线)相关的 ioctl 调用。SMBus 是基于 I²C 总线协议的一种更高层的通信协议,常用于传感器和设备管理器之间的通信。这个函数的主要作用是根据传入的 SMBus 命令,对设备执行相应的读写操作。

2.3 read和write

static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,loff_t *offset)
{char *tmp;int ret;struct i2c_client *client = file->private_data;// 限制读取数据的大小,防止超过8192字节if (count > 8192)count = 8192;// 分配内核缓冲区用于存储从设备读取的数据tmp = kmalloc(count, GFP_KERNEL);if (tmp == NULL)return -ENOMEM;  // 内存分配失败,返回错误码pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",iminor(file_inode(file)), count);// 从I2C设备读取数据ret = i2c_master_recv(client, tmp, count);   ------1if (ret >= 0)// 将读取的数据复制到用户空间,若失败则返回 -EFAULTret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;// 释放内核缓冲区kfree(tmp);return ret;  // 返回读取的字节数或者错误码
}static ssize_t i2cdev_write(struct file *file, const char __user *buf,size_t count, loff_t *offset)
{int ret;char *tmp;struct i2c_client *client = file->private_data;// 限制写入数据的大小,防止超过8192字节if (count > 8192)count = 8192;// 将用户空间的数据复制到内核空间tmp = memdup_user(buf, count);if (IS_ERR(tmp))return PTR_ERR(tmp);  // 内存分配或数据复制失败pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",iminor(file_inode(file)), count);// 向I2C设备写入数据ret = i2c_master_send(client, tmp, count);   ------2// 释放内核缓冲区kfree(tmp);return ret;  // 返回写入的字节数或错误码
}

诶???怎么没有看到i2c_transfer函数呢,其实就在i2c_master_recv进而i2c_master_send内,这两个是i2c-core.c核心层提供给设备驱动的接口:

int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
{struct i2c_adapter *adap = client->adapter;struct i2c_msg msg;int ret;msg.addr = client->addr;msg.flags = client->flags & I2C_M_TEN;msg.flags |= I2C_M_RD;msg.len = count;msg.buf = buf;ret = i2c_transfer(adap, &msg, 1);/** If everything went ok (i.e. 1 msg received), return #bytes received,* else error code.*/return (ret == 1) ? count : ret;
}int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
{int ret;struct i2c_adapter *adap = client->adapter;struct i2c_msg msg;msg.addr = client->addr;msg.flags = client->flags & I2C_M_TEN;msg.len = count;msg.buf = (char *)buf;ret = i2c_transfer(adap, &msg, 1);/** If everything went ok (i.e. 1 msg transmitted), return #bytes* transmitted, else error code.*/return (ret == 1) ? count : ret;
}

就不用解释吧,其实也很好懂了,设置i2c_msg msg,标志其从设备地址,控制器,读写标志位flags等,然后利用i2c_transfer发起传输。

3. 总结img

可以看出,发起i2c传输的函数最基本的就是要牢记:i2c_transferi2c_smbus_xfer,至于发起的是读还是写,就由msg.flags决定,关于msg结构体,在之前的文章中也有讲过,见上文 往期内容 。

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

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

相关文章

从零开始实现大语言模型(十二):文本生成策略

1. 前言 大语言模型GPTModel通过多轮推理生成连续自然语言文本&#xff0c;每轮推理仅生成一个token。对输入文本做tokenization&#xff0c;将输入文本转换成包含num_tokens个token ID的列表&#xff0c;并输入大语言模型GPTModel&#xff0c;可以得到num_tokens个维度为voca…

第T4周:TensorFlow实现猴痘识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标&#xff1a; 实现猴痘病例图片的准确实别 具体实现&#xff1a; &#xff08;一&#xff09;环境&#xff1a; 语言环境&#xff1a;Python 3.10 编 译…

mysql innodb 引擎如何直接复制数据库文件?

mysql innodb 引擎如何直接复制数据库文件&#xff1f;介绍如下&#xff1a; 1、首先找到数据库文件所在位置 一般可以看my.conf/my.ini配置的文件的“datadir” 看示例&#xff1a; “MAMP”在Macos下的数据库文件位置&#xff1a; /Library/Application Support/appsolu…

DAY35||452.用最少数量的箭引爆气球 |435.无重叠区间 |763.划分字母区间

重叠区间专场。 452.用最少数量的箭引爆气球 题目:452. 用最少数量的箭引爆气球 - 力扣&#xff08;LeetCode&#xff09; 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0c;其中points[i] [xstart, xend] 表示水平直径在 xst…

恢复已删除文件的 10 种安卓数据恢复工具

由于我们现在在智能手机上存储了大量重要文件&#xff0c;因此了解数据恢复工具变得很重要。您永远不会知道什么时候需要使用 安卓 数据恢复工具。 由于不乏 Windows 数据恢复工具&#xff0c;因此从崩溃的计算机中恢复文件很容易。但是&#xff0c;当涉及到从 安卓恢复数据时…

论文笔记:Ontology-enhanced Prompt-tuning for Few-shot Learning

论文来源&#xff1a;WWW 2022 论文地址&#xff1a;https://arxiv.org/pdf/2201.11332.pdfhttps://arxiv.org/pdf/2201.11332.pdf 论文代码&#xff1a;暂未公开 笔记仅供参考&#xff0c;撰写不易&#xff0c;请勿恶意转载抄袭&#xff01; Abstract 小样本学习旨在基于…

深入浅出:MySQL索引优化指南

一、引言 在数据库应用开发中&#xff0c;性能优化是一个永恒的话题。而索引作为提高数据库查询效率的关键手段&#xff0c;其设计和使用直接影响着系统的性能表现。本文将深入探讨MySQL索引的原理、类型以及优化策略&#xff0c;帮助读者更好地理解和应用索引&#xff0c;从而…

FreeRTOS:事件标志组

目录 一、简介 二、 事件控制块 三、相关API 四、 应用场景 一、简介 在FreeRTOS中&#xff0c;使用信号量可以实现同步&#xff0c;但是使用信号量来同步的话任务只能与单个的任务进行同步。有时候某个任务可能会需要与多个任务进行同步&#xff0c;此时信号量就无能为力。…

mysql 09 独立表空间结构

表空间中的页实在是太多了&#xff0c;为了更好的管理这些页面&#xff0c;设计 InnoDB 的大叔们提出了 区 &#xff08;英文名&#xff1a; extent &#xff09;的概念。对于16KB的页来说&#xff0c;连续的64个页就是一个 区 &#xff0c;也就是说一个区默认占用1MB空间大小。…

QT--单选按钮(QRadioButton)和复选按钮(QCheckBox)

在Qt中&#xff0c;单选按钮&#xff08;QRadioButton&#xff09;和复选按钮&#xff08;QCheckBox&#xff09;是两种常用的用户界面控件&#xff0c;它们的主要区别在于选择行为和用途&#xff1a; QRadioButton&#xff08;单选按钮&#xff09; 选择行为&#xff1a;单选…

jvm虚拟机调优实战

使用命令 jps查看进程使用jstat gc -1 5000查看内存占用和回收情况 正式测试 是否跑job区别。大量的job,部分用户点击的热数据 &#xff0c;不同时刻在跑 600-700对比 200 多了400-500m,代码原数据&#xff08;不占用堆区&#xff09;占了300m,所以 堆空间老年代&#xff08;90…

VsCode环境配置C++环境

目录 第一步下载应用 第二步应用文字汉化 第三步安装编译器MinGW 第四步 环境变量的配置 第五步 打开VsCode 第六步 配置环境设施 几个其他的好用的插件 会了吧 MarsCode: AI Coding Assistant 第一步下载应用 VSCode下载官方指定网址&#xff1a; Visual Studio Cod…

使用豆包MarsCode 来处理 Excel 的数据吧!

作者可乐三分糖 背景 Excel 是大部分没有信息化的公司通用的数据处理手段。但并不是所有的人对 excel 都是非常熟悉的。这些同学主要会遇到三类问题&#xff1a; Excel 的一些操作问题&#xff0c;如公式怎么写跨表处理太复杂&#xff0c;即使是写公式也很繁琐。一些数据批处…

关于k8s集群高可用性的探究

1. k8s的高可用的核心是什么&#xff1f; 说到核心、本质 意味着要从物理层来考虑技术 k8s是一个容器编排管理工具&#xff0c;k8s受欢迎的时机 是docker容器受欢迎时&#xff0c;因为太多的docker容器&#xff0c;管理起来是一个大工程 那么刚好k8s是google自己用了十来年…

如何设计开发RTSP直播播放器?

技术背景 我们在对接RTSP直播播放器相关技术诉求的时候&#xff0c;好多开发者&#xff0c;除了选用成熟的RTSP播放器外&#xff0c;还想知其然知其所以然&#xff0c;对RTSP播放器的整体开发有个基础的了解&#xff0c;方便方案之作和技术延伸。本文抛砖引玉&#xff0c;做个…

【环境搭建】更换电脑后的开发环境怎么重建

目录 &#x1f378;前言 &#x1f37b;一、系统配置检查 &#x1f37a;二、开发环境搭建 &#x1f379;三、章末 &#x1f378;前言 小伙伴们大家好&#xff0c;这次文章跟技术没有关联&#xff0c;因为最近刚更换了装备&#xff0c;开发环境啥的残缺不全&#xff0c;也不能…

Java基础:面向对象编程5

1 Java内部类 1.1 概念 在 Java 中&#xff0c;内部类是指定义在另一个类内部或方法内部的类。内部类可以分为以下几种类型&#xff1a; 成员内部类局部内部类匿名内部类静态内部类 1.2 成员内部类 定义&#xff1a;成员内部类是最常见的内部类&#xff0c;它定义在外部类…

深度解析 Redis 存储结构及其高效性背后的机制

目录 1. Redis 存储结构存储结构存储转换 2. 字典实现数据结构冲突处理负载因子 3. 扩容扩容步骤影响与优化 4. 缩容缩容步骤优化策略 5. 渐进式 Rehash**渐进式 Rehash 的工作原理**Rehash 规则优势 6. SCAN 命令SCAN 的实现原理遍历顺序避免重复和遗漏使用场景 7. 过期&#…

电子商务网站维护技巧:保持WordPress、主题和插件的更新

在这个快节奏的数字时代&#xff0c;维护一个电子商务网站的首要任务之一是保持WordPress、主题和插件的最新状态。过时的软件不仅可能导致功能故障&#xff0c;还可能带来安全风险。本文将深入探讨如何有效地更新和维护您的WordPress网站&#xff0c;以确保其安全性和性能。 …

工业物联网关-ModbusTCP

Modbus-TCP模式把网关视作Modbus从端设备&#xff0c;主端设备可以通过Modbus-TCP协议访问网关上所有终端设备。用户可以自定义多条通道&#xff0c;每条通道可以配置为TCP Server或者TCP Slave。注意&#xff0c;该模式需要指定采集通道&#xff0c;采集通道可以是串口和网口通…