rk36566 uboot - dm 模型数据结构与常见接口

文章目录

  • 一、数据结构
    • 1、udevice
    • 2、driver
    • 3、uclass
    • 4、uclass\_driver
    • 5、 总结
    • 6、device\_probe
  • 二、常用接口
    • 1、udevice 创建接口
      • 1) device_bind_with_driver_data
      • 2) device_bind
      • 3) device_bind_by_name
    • 2、uclass 操作函数
      • 1) uclass_get
      • 2) uclass_get_name
      • 3) uclass_find_device
      • 4) uclass_find_first_device
      • 5) uclass_find_first_device
      • 6) uclass_find_device_by_name
      • 7) uclass_find_device_by_seq
      • 8) uclass_find_device_by_ofnode
    • 3、device_probe 封装接口
      • 1) uclass_get_device
      • 2) uclass_get_device_by_name
      • 3) uclass_get_device_by_seq
      • 4) uclass_get_device_by_ofnode
      • 6) uclass_first_device
      • 7) uclass_next_device

作者: baron

一、数据结构

1、udevice

  用于描述具体的硬件设备, 在当前的 dm 模型中, 在 uboot 启动的时候扫描 dts 自动创建. 详情参考 dts 加载和 dm 模型的本质. 其中需要关注的数据结构有 priv 常用于设置设备硬件私有数据结构. 通过接口void *dev_get_priv(struct udevice *dev)返回.

struct udevice {  const struct driver *driver;   // 在 device_bind_common 中连接对应的 drvconst char *name;              // 自动创建时, 由匹配到的 drv->name 设置. 也可以通过参数传入设置.void *platdata;                // 在 device_bind_common 由 drv->platdata_auto_alloc_size 指定void *parent_platdata;         // 在 device_bind_common 由 parent->driver->per_child_platdata_auto_alloc_size;void *uclass_platdata;         // 在 device_bind_common 由 uc->uc_drv->per_device_platdata_auto_alloc_size; 指定ofnode node;ulong driver_data;              // 由  device_bind_common 传入的参数设置struct udevice *parent;         // 在 device_bind_common 设置, 连接父设备void *priv;                     // 在 device_probe 中设置,由 dev->drv->priv_auto_alloc_size 决定大小struct uclass *uclass;          // 一般在 device_bind_common 中由 void *uclass_priv;              // 在 device_probe 中设置,由 dev->uclass->uc_drv->per_device_auto_alloc_size // 在 device_probe 中设置, 由 dev->parent->driver->per_child_auto_alloc_size 指定大小. // 如果不存在则由 dev->parent->uclass->uc_drv->per_child_auto_alloc_size 指定大小void *parent_priv;   struct list_head uclass_node;  // 在 device_bind_common 中的 uclass_bind_device 设置, 连接到 uc->dev_headstruct list_head child_head;   // 在子设备创建时设置, 连接子设备的 sibling_nodestruct list_head sibling_node; // 在 device_bind_common 中设置, 连接到 parent->child_headuint32_t flags;          int req_seq;int seq;                       //  // 在 device_probe 中设置, 表示 device 是第几个被注册, 由于他的唯一性, 因此也可以通过 seq 查找设备.
#ifdef CONFIG_DEVRESstruct list_head devres_head;
#endif
};

2、driver

  对应的 udevice 的驱动, 其中 probe 用于驱动的初始化. 已经 probe 就表示该设备已就绪可以使用. ofdata_to_platdata 接口在 probe 之前调用, 用于解析设备树. ops 则用于创建 drv 真的的操作接口.

struct driver {char *name;enum uclass_id id;const struct udevice_id *of_match;int (*bind)(struct udevice *dev);               // 第三个在 device_bind_common 中被调用, 这个接口比较常用int (*probe)(struct udevice *dev);              // 在 device_probe 中第 5 个被调用int (*remove)(struct udevice *dev);      int (*unbind)(struct udevice *dev);int (*ofdata_to_platdata)(struct udevice *dev); // 在 device_probe 中第 4 个被调用int (*child_post_bind)(struct udevice *dev);    // 在子设备的 device_bind_common 中第四个被调用int (*child_pre_probe)(struct udevice *dev);    // 在 device_probe 中第 3 个被调用int (*child_post_remove)(struct udevice *dev);int priv_auto_alloc_size;       // 在 device_probe 中指定  dev->priv 的大小int platdata_auto_alloc_size;   // 在 device_bind_common 中指定 dev->platdata 的大小int per_child_auto_alloc_size;  // 在 device_probe 中指定 child_dev->parent_priv 的大小int per_child_platdata_auto_alloc_size;const void *ops;    /* driver-specific operations */uint32_t flags;
};

drv 需要手动创建, 通过 U_BOOT_DRIVER 创建.

#define U_BOOT_DRIVER(__name)                       \ll_entry_declare(struct driver, __name, driver)#define ll_entry_declare(_type, _name, _list)               \_type _u_boot_list_2_##_list##_2_##_name __aligned(4)       \__attribute__((unused,              \section(".u_boot_list_2_"#_list"_2_"#_name)))

  展开后得到.

struct driver _u_boot_list_2_driver_2___name __aligned(4) __attribute__((unused, section(".u_boot_list_2_driver_2___name")));

  因此该宏将对应的 drv 编译到指定的段 u_boot_list_2_driver_2_中. 可以通过以下接口获取对应的 drv.

struct driver *lists_driver_lookup_name(const char *name)
{struct driver *drv = ll_entry_start(struct driver, driver);const int n_ents = ll_entry_count(struct driver, driver);struct driver *entry;// 遍历所有的 drv 返回对应 name 的 drvfor (entry = drv; entry != drv + n_ents; entry++) {if (!strcmp(name, entry->name))return entry;}/* Not found */return NULL;
}

3、uclass

  在创建 udevice 时自动创建, 管理一类设备, 即同类的 udevice 由 uclass 进行统一管理. 每一个 uclalss 都有一个唯一的 uc_drv->uclass_id 进行描述.

struct uclass {void *priv;                        // 在 device_bind_common 由 uc_drv->priv_auto_alloc_size 指定struct uclass_driver *uc_drv;      // 对应的 uclass drvstruct list_head dev_head;         // 用于连接所属的 udevicestruct list_head sibling_node;     // 连接到 gd->uclass_root
};

4、uclass_driver

  给出该类设备的统一接口. post_probe 接口常用来设置该类设备共有属性, 例如 i2c 的速率. post_bind 接口则常设置为 dm_scan_fdt_dev 用于扫描并创建其下的子设备 udevice. 和 driver 类似通过 UCLASS_DRIVER创建

struct uclass_driver {const char *name;enum uclass_id id;                                 // 所属的 uclassint (*post_bind)(struct udevice *dev);             // 第五个在 device_bind_common 中被调用int (*pre_unbind)(struct udevice *dev);int (*pre_probe)(struct udevice *dev);             // 在 device_probe 中第 1 个被调用int (*post_probe)(struct udevice *dev);            // 在 device_probe 中第 6 个被调用int (*pre_remove)(struct udevice *dev);int (*child_post_bind)(struct udevice *dev);       // 第二个在 device_bind_common 中被调用int (*child_pre_probe)(struct udevice *dev);       // 在 device_probe 中第 2 个被调用 int (*init)(struct uclass *class);                 // 第一个在 device_bind_common 中被调用int (*destroy)(struct uclass *class);int priv_auto_alloc_size;int per_device_auto_alloc_size;                    // 在 device_probe 中为 dev->uclass_priv 分配 dev->uclass->uc_drv->per_device_auto_alloc_sizeint per_device_platdata_auto_alloc_size;           // 在 device_bind_common 中指 child_dev->uclass_platdata 的大小int per_child_auto_alloc_size;                     // 在 device_probe 中如果 dev->parent->driver->per_child_auto_alloc_size 不存在则由它指定 child_dev->uclass_priv 大小.int per_child_platdata_auto_alloc_size;            // 在 device_bind_common 中指定 child_dev->parent_platdata 的大小const void *ops;                                  // uint32_t flags;
};

5、 总结

  udevice, driver, uclass, uclass_driver 他们四为位一体, 在 uboot 中扫描 dts 自动创建. 以 i2c 为例进行说明. 下图展示了 rk3566 i2c 的组织架构.

在这里插入图片描述

  第一个阶段通过扫描 dts 创建了 i2c0 控制器 i2c2: i2c@fe5b0000的 udevice 然后以及挂在该子设备 pmic 和 rk817_fg 对应的 device. 需要注意第一个阶段只会创建 device 并不会对硬件进行初始化(probe).

   第二阶段, 即调用 probe 初始化硬件, 注意和 linux 内核自动 probe 不同. uboot 的设计理念是, 即用即初始化, 不用不初始化. 因此 porbe 是手动调用的. 需要初始化硬件的时候手动调用 probe 函数进行初始化. 在初始化硬件(probe)的时候会检测其父设备的硬件是否已经初始化(probe), 如果父设备没有准备好则先初始化父设备. 如上图所示, 在 probe pmic 的时候会检测 i2c0 是否已经 probe, i2c0 没有 probe 则先调用 i2c0 的 probe 初始化 i2c0, 再调用 i2c class 提供的dm_i2c_read dm_i2c_write等统一接口在 pmic 的 probe 中初始化 pmic. 核心接口为 device_probe.

6、device_probe

  1. 检查标志位 DM_FLAG_ACTIVATED 判断是否已经完成 probe, 如果已经 probe 则直接返回.
  2. 为 dev 分配一些列空间如下.
dev->priv ==> drv->priv_auto_alloc_size
dev->uclass_priv ==> dev->uclass->uc_drv->per_device_auto_alloc_size
dev->parent_priv ==> dev->parent->driver->per_child_auto_alloc_size==> dev->parent->uclass->uc_drv->per_child_auto_alloc_size // 如果前面的不存在则使用这个
  1. 调用父设备的 probe 函数, 遍历 uclass 中的子设备在没有用到的 seq 中返回一个空闲的 seq 设置 dev->seq 这个 seq 可以表示 dev 的初始化顺序, 第一个被初始化的 dev 的 seq 为 0, 第二个为 1 以此类推. 因此可以通过 seq 判断 dev 的初始化顺序.

  2. 进行一系列回调

回调 dev->uclass->uc_drv->pre_probe(dev); -->
回调 dev->parent->uclass->uc_drv->child_pre_probe(dev); -->
回调 dev->parent->driver->child_pre_probe(dev); -->
回调 dev->drv->ofdata_to_platdata(dev); -->         // 常用接口用于解析 dts.
回调 dev->drv->probe(dev); -->                      // 常用接口用于初始化硬件的 probe 接口.
回调 dev->uclass->uc_drv->post_probe(dev); -->      // 在这里设置共有的硬件特性
  1. 调用 pinctrl 设置 default 的 pin 脚状态.
int device_probe(struct udevice *dev)
{const struct driver *drv;int size = 0;int ret;int seq;if (!dev)return -EINVAL;// 检查是否已经完成 probeif (dev->flags & DM_FLAG_ACTIVATED)return 0;// 获取 drvdrv = dev->driver;assert(drv);// 为 dev->priv 分配空间 drv->priv_auto_alloc_sizeif (drv->priv_auto_alloc_size && !dev->priv) {dev->priv = alloc_priv(drv->priv_auto_alloc_size, drv->flags);if (!dev->priv) {ret = -ENOMEM;goto fail;}}// 为 dev->uclass_priv 分配空间 dev->uclass->uc_drv->per_device_auto_alloc_sizesize = dev->uclass->uc_drv->per_device_auto_alloc_size;if (size && !dev->uclass_priv) {dev->uclass_priv = calloc(1, size);if (!dev->uclass_priv) {ret = -ENOMEM;goto fail;}}//  为 dev->parent_priv 分配空间 dev->parent->driver->per_child_auto_alloc_size// 如果没有 per_child_auto_alloc_size 则根据 dev->parent->uclass->uc_drv->per_child_auto_alloc_size 分配空间if (dev->parent) {size = dev->parent->driver->per_child_auto_alloc_size;if (!size) {size = dev->parent->uclass->uc_drv->per_child_auto_alloc_size;}if (size && !dev->parent_priv) {dev->parent_priv = alloc_priv(size, drv->flags);if (!dev->parent_priv) {ret = -ENOMEM;goto fail;}}// 调用父设备的 probe 函数ret = device_probe(dev->parent);if (ret)goto fail;if (dev->flags & DM_FLAG_ACTIVATED)return 0;}// 遍历 uclass 中的子设备在没有用到的 seq 中返回一个空闲的 seq// 这个 seq 可以表示 dev 的初始化顺序, 第一个被初始化的 dev 的 seq 为 0, 第二个为 1 以此类推// 可以通过 seq 判断 dev 的初始化顺序.seq = uclass_resolve_seq(dev);if (seq < 0) {ret = seq;goto fail;}dev->seq = seq;// 设置标志位dev->flags |= DM_FLAG_ACTIVATED;// 回调 pinctrl 设置 pin 脚状态if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL)pinctrl_select_state(dev, "default");// 回调 dev->uclass->uc_drv->pre_probe(dev);// 回调 dev->parent->uclass->uc_drv->child_pre_probe(dev);ret = uclass_pre_probe_device(dev);if (ret)goto fail;// 回调  dev->parent->driver->child_pre_probe(dev);if (dev->parent && dev->parent->driver->child_pre_probe) {ret = dev->parent->driver->child_pre_probe(dev);if (ret)goto fail;}// 回调 dev->drv->ofdata_to_platdata(dev);if (drv->ofdata_to_platdata && dev_has_of_node(dev)) {ret = drv->ofdata_to_platdata(dev);if (ret)goto fail;}// 回调 dev->drv->probe(dev);if (drv->probe) {ret = drv->probe(dev);if (ret) {dev->flags &= ~DM_FLAG_ACTIVATED;goto fail;}}// 回调 dev->uclass->uc_drv->post_probe(dev);ret = uclass_post_probe_device(dev);if (ret)goto fail_uclass;// 回调 pinctrl 再次设置 pin 脚状态if (dev->parent && device_get_uclass_id(dev) == UCLASS_PINCTRL)pinctrl_select_state(dev, "default");return 0;
fail_uclass:if (device_remove(dev, DM_REMOVE_NORMAL)) {dm_warn("%s: Device '%s' failed to remove on error path\n",__func__, dev->name);}
fail:dev->flags &= ~DM_FLAG_ACTIVATED;dev->seq = -1;device_free(dev);return ret;
}

二、常用接口

1、udevice 创建接口

1) device_bind_with_driver_data

设用设备节点 node 创建并返回一个 udevice

// parent: 父设备
// driver: 创建设备的 drv
// name: 设备名称, 一般设置的和 drv name 相同
// driver_data: drv 的私有数据
// node: 该设备对应的 dts 节点, 可以为 null
// devp: 返回创建的 udevice
int device_bind_with_driver_data(struct udevice *parent,const struct driver *drv, const char *name, ulong driver_data, ofnode node, struct udevice **devp)

2) device_bind

使用设备树偏移地址 of_offset 创建 udevice

// parent: 父设备
// driver: 创建设备的 drv
// name: 设备名称, 一般设置的和 drv name 相同
// platdata: 设置 dev->platedata
// of_offset: 设备节点的偏移, 可以为 -1
// devp: 返回创建的 udevice
int device_bind(struct udevice *parent, const struct driver *drv,const char *name, void *platdata, int of_offset, struct udevice **devp);

3) device_bind_by_name

使用 driver_info 创建 udevice, 不使用设备树.

// parent: 父设备
// pre_reloc_only: 是否已经重定位
// driver_info: udevice 设备描述结构
// devp: 返回创建的 udevice
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, const struct driver_info *info, struct udevice **devp);

2、uclass 操作函数

1) uclass_get

通过 uclass_id 返回对应的 uclass, 没有则创建一个 uclass 返回.

int uclass_get(enum uclass_id id, struct uclass **ucp);

2) uclass_get_name

通过 uclass_id 返回 uclass 的 name

const char *uclass_get_name(enum uclass_id id);

3) uclass_find_device

返回对应 uclass_id 的 uclass 对应的设备链表上的第 index 个 udevice

int uclass_find_device(enum uclass_id id, int index, struct udevice **devp);

4) uclass_find_first_device

返回对应 uclass_id 的 uclass 对应的设备链表上的第一个 udevice

int uclass_find_first_device(enum uclass_id id, struct udevice **devp);

5) uclass_find_first_device

返回所属 uclass 链表的下一个 udevice

int uclass_find_next_device(struct udevice **devp);

6) uclass_find_device_by_name

返回对应 uclass_id 的 uclass 的设备链表上对应 name 的 udevice

int uclass_find_device_by_name(enum uclass_id id, const char *name, struct udevice **devp);

7) uclass_find_device_by_seq

uclass_id 的 uclass 的设备链表上通过 seq 查找 udevice

int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq, bool find_req_seq, struct udevice **devp)

8) uclass_find_device_by_ofnode

uclass_id 的 uclass 的设备链表查找对应 node 的 device

int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node, struct udevice **devp

3、device_probe 封装接口

1) uclass_get_device

返回 uclass_id 的 uclass 的设备链表上第 index 个 udevice 并进行 device_probe

int uclass_get_device(enum uclass_id id, int index, struct udevice **devp);

2) uclass_get_device_by_name

返回 uclass_id 的 uclass 的设备链表上对应 name 的 udevice 并进行 device_probe

int uclass_get_device_by_name(enum uclass_id id, const char *name, struct udevice **devp);

3) uclass_get_device_by_seq

返回 uclass_id 的 uclass 的设备链表上对应 seq 的 udevice 并进行 device_probe

int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp);

4) uclass_get_device_by_ofnode

返回 uclass_id 的 uclass 的设备链表上对应设备节点 node 的 udevice 并进行 device_probe

int uclass_get_device_by_ofnode(enum uclass_id id, ofnode node, struct udevice **devp);

6) uclass_first_device

返回 uclass_id 的 uclass 的设备链表上第一个 udevice 并进行 device_probe

int uclass_first_device(enum uclass_id id, struct udevice **devp);

7) uclass_next_device

返回所属 uclass 链表上的下一个 udevice 并进行 device_probe

int uclass_next_device(struct udevice **devp);

简单总结: 如果只需要返回某个 udevice 则使用带 find 的接口, 如果需要返回并且 probe 则使用带 get 的接口.

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

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

相关文章

java中的日期类

1.1 第一代日期类 第一代日期时间API主要有java.util.Date和日期时间格式化有关的java.text.DateFormat及其子类。 1.1.1 Date类 JDK1.0就在java.util包下面提供了Date类用于表示特定的瞬间&#xff0c;可以精确到毫秒。   通过API或源码&#xff0c;可以看出Date类的大部…

vscode 导入前端项目

vscode 导入前端项目 导入安装依赖 运行 参考vscode 下载 导入 安装依赖 运行 在前端项目的终端中输入npm run serve

C#,数值计算,数据测试用的对称正定矩阵(Symmetric Positive Definite Matrix)的随机生成算法与源代码

C.Hermite 1、对称矩阵 对称矩阵(Symmetric Matrices)是指以主对角线为对称轴,各元素对应相等的矩阵。在线性代数中,对称矩阵是一个方形矩阵,其转置矩阵和自身相等。1855年,埃米特(C.Hermite,1822-1901年)证明了别的数学家发现的一些矩阵类的特征根的特殊性质,如称为埃…

ASPICE-SYSSWE

文章主要内容&#xff1a; Automotive SPICE 过程参考模型 SYS.1 需求挖掘 过程ID SYS.1 过程名称 需求挖掘 过程目的 需求挖掘过程的目的是:在产品和/或服务的整个生命周期内收集、处理和跟踪不断变化的利益相关方的需要和需求&#xff0c;从而建立一个需求基线&#x…

交换机/路由器的存储介质-思科

交换机/路由器的存储介质-思科 本文主要介绍网络设备的存储介质组成。 RAM(random-accessmemory&#xff0c;随机访问存储器) RAM中内容断电丢失&#xff0c;主要用于运行操作系统、运行配置文件、IP 路由表:、ARP 缓存、数据包缓存区。 ROM(read-only memory&#xff0c;只…

uniapp遇到的问题

【uniapp】小程序中input输入框的placeholder-class不生效解决办法 解决&#xff1a;写在scope外面 uniapp设置底部导航 引用&#xff1a;https://www.jianshu.com/p/738dd51a0162 【微信小程序】moveable-view / moveable-area的使用 https://blog.csdn.net/qq_36901092/…

持续创新引领计算机行业在数字经济时代的航向

受2024年政府工作报告的启发&#xff0c;计算机行业正站在新的发展十字路口。政府报告不仅为计算机行业的未来描绘了清晰的轮廓&#xff0c;更为行业的实践提供了扎实的政策支撑和发展空间。本文将深入分析计算机行业在数字化经济大潮中的新机遇与挑战&#xff0c;并对企业和从…

服务器数据恢复—raid5热备盘上线同步数据失败的如何恢复数据

服务器数据恢复环境&故障&分析&#xff1a; 一台存储上有一组由多块硬盘组建的raid5阵列&#xff0c;该raid5阵列中的一块硬盘掉线&#xff0c;热备盘自动上线同步数据的过程中&#xff0c;raid阵列中又有一块硬盘掉线&#xff0c;热备盘的数据同步被中断&#xff0c;r…

【刷题训练】LeetCode:557. 反转字符串中的单词 III

557. 反转字符串中的单词 III 题目要求 示例 1&#xff1a; 输入&#xff1a;s “Let’s take LeetCode contest” 输出&#xff1a;“s’teL ekat edoCteeL tsetnoc” 示例 2: 输入&#xff1a; s “Mr Ding” 输出&#xff1a;“rM gniD” 思路&#xff1a; 第一步&am…

Android studio 性能调试

一、概述 Android studio 的Profiler可用来分析cpu和memory问题&#xff0c;下来进行说明介绍。 二、Android studio CPU调试 从开发模拟器或设备中启动应用程序&#xff1b; 在 Android Studio 中&#xff0c;通过选择View > Tool Windows > Profiler启动分析器。 应…

Mac-自动操作 实现双击即可执行shell脚本

背景 在Mac上运行shell脚本&#xff0c;总是需要开启终端窗口执行&#xff0c;比较麻烦 方案 使用Mac上自带的“自动操作”程序&#xff0c;将shell脚本打包成可运行程序(.app后缀)&#xff0c;实现双击打开即可执行shell脚本 实现细节 找到Mac上 应用程序中的 自动操作&am…

Selenium 学习(0.20)——软件测试之单元测试

我又&#xff08;浪完&#xff09;回来了…… 很久没有学习了&#xff0c;今天忙完终于想起来学习了。没有学习的这段时间&#xff0c;主要是请了两个事假&#xff08;5工作日和10工作日&#xff09;放了个年假&#xff08;13天&#xff09;&#xff0c;然后就到现在了。 看了下…

【大模型系列】图片生成(DDPM/VAE/StableDiffusion/ControlNet/LoRA)

文章目录 1 DDPM(UC Berkeley, 2020)1.1 如何使用DDPM生成图片1.2 如何训练网络1.3 模型原理 2 VAE:Auto-Encoding Variational Bayes(2022&#xff0c;Kingma)2.1 如何利用VAE进行图像增广2.2 如何训练VAE网络2.3 VAE原理2.3.1 Auto-Encoder2.3.2 VAE编码器2.3.3 VAE解码器 3 …

【UE5】持枪状态站立移动的动画混合空间

项目资源文末百度网盘自取 创建角色在持枪状态站立移动的动画混合空间 在BlendSpace文件夹中单击右键选择动画(Animation)中的混合空间(Blend Space) 选择SK_Female_Skeleton 命名为BS_RifleStand 打开 水平轴表示角色的方向&#xff0c;命名为Direction&#xff0c;方…

CASIA-HWDB手写体数据集gnt生成为png格式

👑一、数据集获取 1.1 官方链接获取gnt文件 http://www.nlpr.ia.ac.cn/databases/download/feature_data/HWDB1.1trn_gnt.ziphttp://www.nlpr.ia.ac.cn/databases/download/feature_data/HWDB1.1tst_gnt.zip 1.2 百度网盘获取gnt文件 链接:https://pan.baidu.com/s/1pKa…

Redis 的并发竞争问题是什么?如何解决这个问题?了解 Redis 事务的 CAS 方案吗?

目录 一、面试官心理分析 二、面试题剖析 一、面试官心理分析 这个也是线上非常常见的一个问题&#xff0c;就是多客户端同时并发写一个key&#xff0c;可能本来应该先到的数据后到了&#xff0c;导致数据版本错了;或者是多客户端同时获取一个 key&#xff0c;修改值之后再写回…

KKVIEW: 远程控制软件哪个好用

远程控制软件哪个好用 随着科技的发展和工作方式的改变&#xff0c;远程控制软件越来越受到人们的关注和需求。无论是在家中远程办公&#xff0c;还是技术支持人员为远程用户提供帮助&#xff0c;选择一款高效稳定的远程控制软件至关重要。在众多选择中&#xff0c;有几款远程…

51-30 World Model | 自动驾驶的世界模型:综述

24年3月&#xff0c;澳门大学和夏威夷大学联合发布的工作&#xff0c;World Models for Autonomous Driving: An Initial Survey。花时间反复看了几遍&#xff0c;刚开始觉得世界模型没用&#xff0c;空洞无序&#xff0c;根本不可能部署到实车上&#xff0c;后面逐渐相信&…

idea 导入项目

idea 导入项目并运行 导入设置设置 jdk查看maven 设置 导入 在项目首页 或者 file 选择 open, 然后选择项目根路径 设置 设置 jdk 查看maven 设置

基于java实用的音乐软件微信小程序的设计与实现【附项目源码】分享

基于实用的音乐软件微信小程序的设计与实现: 源码地址&#xff1a;https://download.csdn.net/download/weixin_43894652/88842586 一、引言 随着移动互联网的普及和微信小程序的兴起&#xff0c;音乐类小程序成为了用户随时随地享受音乐的重要工具。本需求文档旨在详细阐述一…