【I2C】Linux I2C子系统分析

news2025/1/12 17:05:23

文章目录

  • 一、I2C体系架构
  • 二、主要的结构体
    • 1. i2c_adapter
    • 2. i2c_algorithm
    • 3. i2c_driver
    • 4. i2c_client
      • 4.1 方式一:通过I2C bus number静态方式来创建
      • 4.2 方式二:通过Device Tree来创建
      • 4.3 方式三:直接通过i2c_new_device来创建
      • 4.3 方式四:通过用户空间(user-space)手动来创建
    • 5. i2c_msg
  • 三、参考资料
  • 四、验证测试时使用

一、I2C体系架构

主要由部分组成:

  1. I2C核心层
    提供I2C Adapter、I2C设备驱动的注册和注销方法,I2C Algorithm通信方法,与适配器无关的代码以及探测设备等。
  2. I2C Adapter控制器驱动
    它一般是由芯片原厂来完成编写,根据Device Tree里面的I2C硬件资源来配置新的I2C Adapter,然后将它注册到I2C核心层。
  3. I2C设备驱动
    I2C设备驱动它包含:I2C Driver和I2C Client。i2c_driver会提供该类I2C硬件的驱动方法,I2C Client会提供当前真实I2C物理设备信息,如:I2C设备地址、挂载的哪条I2C总线(I2C Adapter)等。

在这里插入图片描述

二、主要的结构体

1. i2c_adapter

一般i2c adapter的注册都是由芯片原厂来完成,它一般存放在kernel\drivers\i2c\busses\目录下。==i2c_adapter==的核心是i2c_algorithm,主要是它提供了I2C read/write收发数据的时方法。

struct i2c_adapter {
	const struct i2c_algorithm *algo; /* 存放I2C传输数据的方法 */
	...
	int nr; // I2C adapter的编号,默认从0开始。
	char name[48]; // adapter的name,如:i2c@21a4000
	...
};

下面以kernel\drivers\i2c\busses\i2c-imx.c注册一个I2C Adapter的为例,简答分析它如何注册的一个adapter的。
设备树里面的compatible和驱动里面的of_device_id.compatible匹配成功后,就调用驱动里面的probe函数。
probe函数主要完成如下几个步骤:

  1. 给i2c adapter申请空间
  2. 初始化i2c adapter
  3. 将i2c adapter注册到i2c核心层
struct imx_i2c_struct {
	struct i2c_adapter	adapter;
	...
};

static int i2c_imx_probe(struct platform_device *pdev)
{
	...
	/* 1.给i2c adapter申请空间 */
	i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);
	/* 2.初始化i2c adapter */
	strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));
	i2c_imx->adapter.owner		= THIS_MODULE;
	i2c_imx->adapter.algo		= &i2c_imx_algo; // 这里就是imx实现的I2C传输数据方法
	i2c_imx->adapter.dev.parent	= &pdev->dev;
	i2c_imx->adapter.nr		= pdev->id; // 配置adaper的编号
	i2c_imx->adapter.dev.of_node	= pdev->dev.of_node;
	...
	/* 3.将i2c adapter注册到i2c核心层 */
	ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
	...
}

再进一步分析i2c_add_numbered_adapter,它是如何注册一个adapter的?
它们的调用关系大致如下:
在这里插入图片描述

  • idr_alloc函数:
    新的adapter将根据总线number添加到I2C Core的全局变量i2c_adapter_idr中,为以后get对应的adapter做准备。
    在这里插入图片描述
    后面的所有的adapter的注册都会挂载在i2c_adapter_idr上,最终的结果大致如下:
    在这里插入图片描述

  • device_register函数:
    这里主要是在I2C总线下,注册一个i2c adapter类型的设备。部分代码如下:
    在这里插入图片描述
    device_register函数注册成功后,会在/sys/bus/i2c/devices目录下创建对应的文件夹。如下:
    在这里插入图片描述
    device_register函数非常的关键,在adapter被添加时会导致i2c-dev.c中的i2cdev_notifier_call被调用。

    device_register
    -->device_add
       -->blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
      				     BUS_NOTIFY_ADD_DEVICE, dev);
    

    因为在i2c_dev_init有注册一个notifier

    static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
      		 void *data)
    {
      	struct device *dev = data;
      
      	switch (action) {
      	case BUS_NOTIFY_ADD_DEVICE:
      		return i2cdev_attach_adapter(dev, NULL);
      	case BUS_NOTIFY_DEL_DEVICE:
      		return i2cdev_detach_adapter(dev, NULL);
      	}
      
      	return 0;
    }
      
    static struct notifier_block i2cdev_notifier = {
      	.notifier_call = i2cdev_notifier_call,
    };
      
    static int __init i2c_dev_init(void)
    {
    	...
    	res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
    	...
    }
    

    接收到通知后,i2cdev_attach_adapter就会被调用,这里创建/dev/i2c-*字符设备。代码如下:
    在这里插入图片描述
    所以,当成功添加一个i2c adapter后,都会在/dev/目录下创建i2c-*设备。结果如下:
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/27e0f756d3534474b493f9f149f468e5.png

  • of_i2c_register_devices函数:
    在这里插入图片描述

    • of_i2c_get_board_info:它会解析dts里面I2C子节点数据。
      &i2c1 {
      clock-frequency = <100000>;
      pinctrl-names = "default";
      pinctrl-0 = <&pinctrl_i2c1>;
      status = "okay";
      
      mag3110@e {
      	compatible = "fsl,mag3110";
      	reg = <0x0e>;
      	position = <2>;
      };
      
    • i2c_new_device :它创建一个i2c_client设备,实际对应外围I2C设备。
      /* 下面是i2c_new_device的调用关系 */
      i2c_new_device
      -->i2c_new_client_device
         -->i2c_dev_set_name
         -->device_register
      
      static void i2c_dev_set_name(struct i2c_adapter *adap,
      		     struct i2c_client *client,
      		     struct i2c_board_info const *info)
      {
      	...
      	dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
      		     i2c_encode_flags_to_addr(client));
      }
      
      i2c_dev_set_name设置I2C设备的名字,例如:0-000e,0:代表i2c adapter0 (i2c1),000e:代表I2C设备从机地址。device_register函数会创建该设备,然后我们就可以在/sys/bus/i2c/devices目录创建对应的目录。
      在这里插入图片描述
  • i2c_scan_static_board_info函数:
    __i2c_board_list保存了通过i2c_register_board_info函数静态注册设备信息,然后根据board info信息通过i2c_new_device来创建一个i2c_client设备。
    在这里插入图片描述
    文件路径:kernel\drivers\i2c\i2c-boardinfo.c

    int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
    {
    	...
    	list_add_tail(&devinfo->list, &__i2c_board_list);
    	...
    }
    

2. i2c_algorithm

i2c_algorithm是根据Soc的I2C硬件寄存器来实现read、write通信的方法,适配器需要通过i2c_algorithm提供的通信函数来产生对应的访问时序。所以i2c_adapter中包含i2c_algorithm的指针。

i2c_algorithm使用master_xfer()来产生I2C时序,以i2c_msg为单位,i2c_msg代表一次传输的数据。

//I2C传输方法
struct i2c_algorithm {
    //i2c传输函数指针
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
			   int num);
    //smbus传输函数指针  
    int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr,
			  unsigned short flags, char read_write,
			  u8 command, int size, union i2c_smbus_data *data);
	...
};

???问题:收发数据的函数master_xfer是如何被调用的???
我们可以通过i2c_master_sendi2c_master_recv函数来收发I2C设备数据,从而分析它的调用关系如下:
在这里插入图片描述

3. i2c_driver

i2c_dirver就是i2c标准总线设备驱动模型中的驱动部分,它主要是现实该I2C设备具体初始化、操作函数接口等的实现,等待app程序的对该I2C设备实现业务上面的逻辑。i2c_dirver这部分的就是需要我们自己的来实现的。

// I2C设备驱动
struct i2c_driver {
	...
	/* Standard driver model interfaces */
	int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
	int (*remove)(struct i2c_client *client);
	struct device_driver driver; // 驱动的name和匹配device tree
	const struct i2c_device_id *id_table; //该设备所支持的设备ID表
};

下面是举例的一个模板代码:

static const struct of_device_id of_match_ids_xxxx[] = {
	{ .compatible = "xxxx,xxxx",		.data = NULL },
	{ /* END OF LIST */ },
};

static const struct i2c_device_id xxxx_ids[] = {
	{ "xxxx",	(kernel_ulong_t)NULL },
	{ /* END OF LIST */ }
};

static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int ap3216c_remove(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static struct i2c_driver i2c_xxxx_driver = {
	.driver = {
		.name = "xxxx",
		.of_match_table = of_match_ids_xxxx,
	},
	.probe = xxxx_probe,
	.remove = xxxx_remove,
	.id_table = xxxx_ids,
};


static int __init i2c_driver_xxxx_init(void)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return i2c_add_driver(&i2c_xxxx_driver);
}

static void __exit i2c_driver_xxxx_exit(void)
{
	i2c_del_driver(&i2c_xxxx_driver);
}

module_init(i2c_driver_xxxx_init);
module_exit(i2c_driver_xxxx_exit);
MODULE_LICENSE("GPL");

4. i2c_client

i2c_clent对应真实的物理设备,每个i2c设备都需要一个i2c_client来描述。i2c_driveri2c_client是一对多的关系,一个i2c_driver上可以支持多个同类型的i2c_client

struct i2c_client {
	unsigned short flags;		/* 标记,标记相关属性		*/
	unsigned short addr;		/* 芯片地址,7bit地址	*/
	char name[I2C_NAME_SIZE];   /* i2c设备的名称 */
	struct i2c_adapter *adapter;	/* 依附的i2c_adapter	*/
	struct device dev;		/* 该设备相关属性配置	*/
	int irq;			/* 设备使用的中断号		*/
	...
};

创建i2c_clent种方式,具体如下:

4.1 方式一:通过I2C bus number静态方式来创建

i2c_client可以通过i2c_register_board_info函数来创建,并且会注册到I2C总线。重要提示:这个方式最大的缺陷是就是一定要编译进内核,并且该驱动一定要在i2c adapter驱动之前就要加载,否则会导致该设备未注册从而导致对应的驱动不会被加载。 解决方法:可以使用arch_initcall将该模块提前被加载。

static int __init i2c_client_ap3216c_init(void)
{
	static struct i2c_board_info board_info[] = {
	  {I2C_BOARD_INFO("ap3216c", 0x1e),},
	};
	
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* register I2C device, 0:代表i2c-0总线 */
    i2c_register_board_info(0, board_info, ARRAY_SIZE(board_info));
	
	return 0;
}

arch_initcall(i2c_client_ap3216c_init);

i2c_register_board_info其实就是添加board info到__i2c_board_list全局链接,然后在添加i2c adapter时,遍历该链接将挂载当前adapter的设备进行创建。(备注:上面在介绍adapter的时候已经分析过这块,如需要可以向上回看。)

4.2 方式二:通过Device Tree来创建

这种方式是目前使用最多的方式,将需要的I2C设备信息添加到对应的I2C总线下。一般只需要包括2个单元:

  1. compatible:该name将被用来匹配i2c driver。
  2. reg :挂载的I2C Slave设备地址,这里是7Bit地址,不包括读写位。

(备注:上面在介绍adapter的时候已经分析过这块,如需要可以向上回看。)

&i2c1 {
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c1>;
	status = "okay";
	
	mag3110@e {
		compatible = "fsl,mag3110";
		reg = <0x0e>;
		position = <2>;
	};
};

4.3 方式三:直接通过i2c_new_device来创建

其实其它方式都会最终会调用i2c_new_device来创建I2C Client设备,我们可以通过它来直接创建。使用该函数需要知道i2c_adapter,我们可以通过i2c_get_adapter函数来获取。具体参考代码如下:

static struct i2c_client *ap3216c_client;

static int __init i2c_client_ap3216c_init(void)
{
    struct i2c_adapter *adapter;

	static struct i2c_board_info board_info = {
	  I2C_BOARD_INFO("ap3216c", 0x1e),
	};
	
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* register I2C device, 0:代表i2c-0总线 */
	adapter = i2c_get_adapter(0);
	ap3216c_client = i2c_new_device(adapter, &board_info);
	i2c_put_adapter(adapter);
	
	return 0;
}

static void __exit i2c_client_ap3216c_exit(void)
{
    i2c_unregister_device(ap3216c_client);
}

module_init(i2c_client_ap3216c_init);
module_exit(i2c_client_ap3216c_exit);
MODULE_LICENSE("GPL");

4.3 方式四:通过用户空间(user-space)手动来创建

调试时、或者不方便通过代码明确地生成i2c_client时,可以通过用户空间来生成。

#创建一个i2c_client, .name = "ap3216c", .addr=0x1e, .adapter是i2c-0
echo ap3216c 0x1e > /sys/bus/i2c/devices/i2c-0/new_device
  
#删除一个i2c_client
echo 0x1e > /sys/bus/i2c/devices/i2c-0/delete_device

这里所使用的new_devicedelete_device是在添加i2c adapter时创建的,i2c_adapter_type包含了该设备的相关属性。
在这里插入图片描述
下面代码可以看到i2c_adapter_type就包含了new_devicedelete_device相关属性,如果使用文件系统操作new_device节点,那么就会调用i2c_sysfs_new_device->i2c_new_client_device函数来创建i2c_client设备。

static ssize_t
i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
		     const char *buf, size_t count)
{
	struct i2c_client *client;
	...
	client = i2c_new_client_device(adap, &info);
	...
}
static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);

static struct attribute *i2c_adapter_attrs[] = {
	&dev_attr_name.attr,
	&dev_attr_new_device.attr,
	&dev_attr_delete_device.attr,
	NULL
};
ATTRIBUTE_GROUPS(i2c_adapter);

struct device_type i2c_adapter_type = {
	.groups		= i2c_adapter_groups,
	.release	= i2c_adapter_dev_release,
};
EXPORT_SYMBOL_GPL(i2c_adapter_type);

下面是i2c adapter创建成功后,进入文件系统后我们可以看到的相关属性的节点。
在这里插入图片描述

5. i2c_msg

在Linux驱动里面使用i2c收发函数最终都会调用到i2c_transfer,它传输数据的单元基本都是i2c_msg

//I2C传输数据结构体,代表一个消息数据
struct i2c_msg {
	__u16 addr;	 //设备地址
	__u16 flags;  //标志
	__u16 len;	//消息长度
	__u8 *buf;	//消息数据
};

下面是是直接使用i2c_msg 来收发数据的伪代码如下:

struct i2c_client *xxx_client= NULL;

static int i2c_read_bytes(unsigned char *reg, char *data, int data_len)
{
    struct i2c_msg msgs[] = {
        {
            .addr    = xxx_client->addr,
            .flags    = 0,
            .len    = 1,
            .buf    = reg,
        },{
            .addr    = dev_addr,
            .flags    = I2C_M_RD,
            .len    = data_len,
            .buf    = data,
        }
    };

    if (i2c_transfer(xxx_client->adapter, msgs, 2) < 0)
        return -1;
    else
        return 0;

}

static int i2c_write__bytes(unsigned char *buf, int len)
{
    struct i2c_msg msg[] = {
        {
            .addr    = xxx_client->addr,
            .flags    = 0,
            .len    = len,
            .buf    = buf,
        }
    };

    if (i2c_transfer(xxx_client->adapter, msg, 1) < 0)
        return -1;
    else
        return 0;
}

static int xxx_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	xxx_client = client;
	
	return 0;
}

三、参考资料

1:Linux驱动之I2C驱动架构
https://blog.51cto.com/u_11866419/4935228
2:Linux i2c 学习 -2 i2c adapter 注册
https://blog.csdn.net/Royal2012/article/details/116081632

四、验证测试时使用

测试验证四种创建i2c client的方法代码:
在这里插入图片描述
参考代码下载地址:
正在上传…

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

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

相关文章

openEuler22.03制作openstack平台使用的镜像

系列文章目录 第一章 openEuler22.03制作openstack平台使用的镜像 文章目录 系列文章目录前言一、virt-manager上的准备工作1、网卡类型切换为virtio2、IDE驱动设置成Virtio3、Display设置成vnc3、虚拟机系统分区 二、安装普通工具包三、安装云化工具包1、安装工具包2、修改配…

数字化转型,企业为什么要转型?如何转型?

数字化转型是利用数字化技术&#xff08;例如云计算、大数据、人工智能、物联网、区块链等&#xff09;和能力来驱动组织商业模式创新和商业生态系统重构的途径和方法即是数字化转型。其目的是实现企业业务的转型、创新、增长。 核心强调了两点&#xff0c;其一是数字化技术的应…

每日一练 | 华为认证真题练习Day51

1、如下图所示&#xff0c;IPSec传输模式中AH的头部应该插入到以下哪个位置&#xff1f; A. 1 B. 2 C. 3 D. 4 2、以下哪种远程登录方式最安全&#xff1f; A. Telnet B. Stelnet v100 C. Stelnet v2 D. Stelnet v1 3、以下业务模块的ACL默认动作为permit的是&#xff1…

玩转 ChatGPT,看这条就够了,Prompt 最全中文合集

Prompt 最全中文合集 玩转 ChatGPT&#xff0c;看这条就够了&#xff01; &#x1f680; 简化流程&#xff1a;ChatGPT Shortcut 提供了快捷指令表&#xff0c;可以快速筛选和搜索适用于不同场景的提示词&#xff0c;帮助用户简化使用流程。 &#x1f4bb; 提高生产力&#…

CSDN打出各种数学符号和数学公式

目录 1、基本四则运算2、指数对数3、根号、省略号、向量4、大&#xff08;小&#xff09;于等于号5、特殊符号、希腊字母符号6、累加累乘7、矩阵8、更改公式中的颜色 我们在用CSDN打出各种数学符号和数学公式时&#xff0c;需要学习一些关于LaTex的语法&#xff0c;在此做一个记…

java数组学习

一、数组的概述 1.数组的理解:数组(Array),是多个相同类型数据按一定顺序排列的集合&#xff0c; 并使用一个名字命名&#xff0c;并通过编号的方式对这些数据进行统一管理。 2.数组相关的概念: >数组名 >元素 >角标、下标、索引 >数组的长度&#xff1a;元素…

联通云数据库CUDB:基于openGauss打造新一代自主创新云原生数据库

总体概述 联通云彰显央企担当&#xff0c;围绕国家对信息技术基础软件的政策要求&#xff0c;开展数据库自主研发。在openGauss开源社区版软件基础上&#xff0c;聚焦政企市场&#xff0c;坚持内核创新&#xff0c;完善工具生态&#xff0c;基于海量云存储能力、存算分离架构…

React中的懒加载以及在Ice中实践

您好&#xff0c;如果喜欢我的文章&#xff0c;可以关注我的公众号「量子前端」&#xff0c;将不定期关注推送前端好文~ 前言 对于页面性能优化&#xff0c;组件懒加载是个比较不错的方案&#xff0c;并且在整个项目打包后&#xff0c;如果未做代码分割&#xff0c;构建出的文…

代理ip的优势、用途及注意事项

随着互联网的高速发展&#xff0c;代理ip的名气和地位也随着水涨船高。那么是什么让它们被我们所知悉的呢&#xff1f;下面我们就代理ip的优势、用途和注意事项来分析一下它为什么能迎合着互联网的发展而壮大自己的。 一、优势 每一个脱颖而出的产品必然有它的优势&#xff0c;…

Axure教程—菜单(中继器)

本文将教大家如何用AXURE中的中继器制作菜单&#xff08;自动折叠其他菜单&#xff09; 一、效果介绍 如图&#xff1a; 预览地址&#xff1a;https://iuek50.axshare.com 下载地址&#xff1a;https://download.csdn.net/download/weixin_43516258/87854640?spm1001.2014.30…

知识图谱简介

什么是知识图谱&#xff1f; 参考&#xff1a;知识图谱1、知识图谱2 本质上&#xff0c;知识图谱主要目标是用来描述真实世界中存在的各种实体和概念&#xff0c;以及他们之间的关系&#xff0c;因此可以认为是一种语义网络。 主要作用&#xff1a;通过数据&#xff0c;建立图…

智能自动化助力业务升级:探究低代码开发和业务流程自动化

当我们开始探索业务流程自动化&#xff08;BPA&#xff09;时&#xff0c;就证明我们已经真正进入到企业数字化转型的核心领域了——企业越来越关注如何通过创新技术来提高效率、降低成本并实现业务流程的自动化。在这个背景下&#xff0c;低代码开发平台和业务流程自动化成为了…

vue 滚动加载

在 Vue中&#xff0c;如果一个组件是一个 button&#xff0c;那就可以直接调用 input &#xff08;&#xff09;方法&#xff0c;将组件的 button放入到v-ui中。 然而在v-ui中&#xff0c;一个组件可能不止一个 button&#xff0c;而这些 button还需要从浏览器加载到 DOM树中。…

一个投喂ChatGPT大内容的小技巧

大家好&#xff0c;我是五竹。心血来潮整理了一份手册&#xff1a;《ChatGPT学习指南》并且将为小白们持续更新和GPT相关的资源和教程&#xff0c;专注于打造一部最好的GPT入门指南&#xff0c;欢迎大家转发、收藏、点赞支持&#xff01;谨防失联&#xff01; 至今还有很多人都…

渗透测试适合小白学习吗会让人感觉到无聊吗?

渗透测试是一项复杂的技能&#xff0c;需要具备扎实的计算机知识&#xff0c;对网络和系统安全有深入的理解和认识。对于初学者来说&#xff0c;建议先学习计算机网络、操作系统、编程语言等相关基础知识&#xff0c;了解渗透测试的概念、流程和常用工具。同时&#xff0c;需要…

HMR API及其原理

很久之前&#xff0c;遇到一个面试题&#xff1a;【在代码变更之后&#xff0c;如何实时看到更新后的页面效果呢&#xff1f;】 在传统的方案中&#xff0c;我们可以通过 live reload 也就是自动刷新页面的方式来解决的&#xff0c;不过随着前端工程的日益庞大&#xff0c;开发…

CSS3+HTML5

1、HTML5 2、HTML5标签 链接标签 <a href"https://www.baidu.com">打开百度&#xff0c;你就知道&#xff01;</a> 链接的属性与作用 属性1&#xff1a;href 作用&#xff1a;用于指定链接目标的url地址&#xff0c;当为标签应用href属性时…

(2022,DynamicD)使用动态鉴别器改进 GAN

Improving GANs with A Dynamic Discriminator 公众号&#xff1a;EDPJ 目录 0. 摘要 1. 简介 2. 相关工作 3. 方法 3.1 基础 3.2 动态鉴别器 3.3 针对不同数据体系的两种方案 4. 实验 4.1 设置 4.2 实证研究 4.3 与现有方法的比较 4.4 DynamicD 的泛化性和兼…

如何从底层优化memmove

如何从底层优化memmove 先实现memmove: 如果没有重叠&#xff0c;可以按任何方向拷贝如果有重叠&#xff0c;先拷贝重叠位置&#xff0c;再考虑是前重叠还是后重叠 优化策略&#xff1a; 拷贝数据的时候应该根据寄存器的大小来设计拷贝单位&#xff0c;注意数据区域可能不是…

怎么快速搭建Vue+Vite项目?

快速搭建VueVite项目的步骤如下&#xff1a; 1.随便创建一个文件夹&#xff0c;用来放项目。我这里创建的文件夹名称是my-vite。 2.在当前目录的路径显示框输入cmd,快速打开cmd命令窗口 3.输入以下命令查询电脑是否已经安装了node.js&#xff0c;此时需注意node版本需要&g…