【V4L2】 v4l2框架分析之v4l2_subdev

news2025/1/12 0:49:14

文章目录

    • 一、v4l2_subdev简介
    • 二、初始化v4l2_subdev
    • 三、注册/注销subdev
    • 四、异步注册子设备

一、v4l2_subdev简介

相关源码文件:

  • /include/media/v4l2-subdev.h
  • /drivers/media/v4l2-core/v4l2-subdev.c

在linux内核中,许多驱动程序需要与子设备通信,这些子设备用于完成一些子任务,最常见的是:处理音频或视频的播放、编码或解码。例如:对于网络摄像机来说,常用的子设备是:传感器摄像机控制器。这些设备通常是I2C设备(但也不全是)。为了给驱动程序提供与这些子设备一致的接口,故创建了v4l2_subdev结构(定义在v4l2-subdev.h文件中),定义如下:

struct v4l2_subdev {
#if defined(CONFIG_MEDIA_CONTROLLER)
	struct media_entity entity; //指向struct media_entity的指针。
#endif
	struct list_head list;   //子设备链表
	struct module *owner; //子设备模块,其所有者与驱动程序的 struct device所有者相同。
	bool owner_v4l2_dev; //如果sd->owner与v4l2_dev->dev owner的所有者匹配,则为True。由v4l2_device_register_subdev()初始化。
	u32 flags;           //子设备标志。可选值有:V4L2_SUBDEV_FL_IS_I2C  - 如果子设备是i2c设备,设置这个标志;V4L2_SUBDEV_FL_IS_SPI - 如果子设备为spi设备,设置这个标志;V4L2_SUBDEV_FL_HAS_DEVNODE - 如果子设备需要设备节点,则设置这个标志;V4L2_SUBDEV_FL_HAS_EVENTS - 如果子设备生成事件,则设置这个标志。
	struct v4l2_device *v4l2_dev; //指向struct v4l2_device的指针。
	const struct v4l2_subdev_ops *ops; //指向struct v4l2_subdev_ops的指针。v4l2_subdev_ops是子设备的操作结合。
	/* Never call these internal ops from within a driver! */
	const struct v4l2_subdev_internal_ops *internal_ops;  //指向struct v4l2_subdev_internal_ops的指针。在驱动程序中不能调用这些函数指针。
	/* The control handler of this subdev. May be NULL. */
	struct v4l2_ctrl_handler *ctrl_handler; //子设备的control处理程序,可能为NULL。
	/* name must be unique */
	char name[V4L2_SUBDEV_NAME_SIZE]; //子设备的名称,该名称必须唯一。
	/* can be used to group similar subdevs, value is driver-specific */
	u32 grp_id; 用于分组相同的子设备,值是驱动特有的。
	/* pointer to private data */
	void *dev_priv; //指向私有数据的指针
	void *host_priv; //指向连接的子设备所使用的私有数据的指针
	/* subdev device node */
	struct video_device *devnode; //子设备的设备节点。
	/* pointer to the physical device, if any */
	struct device *dev; //指向物理设备的指针(如果有的话)
	/* Links this subdev to a global subdev_list or @notifier->done list. */
	struct list_head async_list; //用于将子设备链接到全局subdev_list或notificer_done列表。
	/* Pointer to respective struct v4l2_async_subdev. */
	struct v4l2_async_subdev *asd; //指向各自struct v4l2_async_subdev的指针。
	/* Pointer to the managing notifier. */
	struct v4l2_async_notifier *notifier; //指向管理通知程序的指针
	/* common part of subdevice platform data */
	struct v4l2_subdev_platform_data *pdata; //子设备平台数据的公共部分。
};

在子设备驱动程序中必须包含一个v4l2_subdev结构。对于简单的子设备驱动程序,可以直接使用struct v4l2_subdev进行描述。但是如果需要存储更多的状态信息,就需要将v4l2_subdev嵌入到更大的结构中,这时候通常会有一个描述底层设备的结构(例如i2c_client),它包含内核设置的设备数据。可以使用v4l2_set_subdevdata()将指向该设备数据的指针存储在v4l2_subdev的私有数据中,通过这种方式,则可以方便从v4l2_subdev访问实际特定总线的底层设备的数据。还需要一种方法从底层结构转移到v4l2_subdev结构。对于公共的i2c_client结构,可以调用i2c_set_clientdata()存储指向v4l2_subdev的指针,对于其他总线,一般也会提供对应的函数完成底层结构到上层结构的关联操作。

每个v4l2_subdev都包含子设备驱动程序可以实现的函数指针struct v4l2_subdev_ops,因为子设备可以做很多事情,所以函数指针根据类别进行排序,每个类别都有自己的ops结构。顶层ops结构包含指向各个类别ops结构的指针,如果子设备驱动程序不支持该类别中的内容,则struct v4l2_subdev_ops可以为NULL。如下代码所示:

struct v4l2_subdev_core_ops {
    int (*log_status)(struct v4l2_subdev *sd);
    int (*s_io_pin_config)(struct v4l2_subdev *sd, size_t n, struct v4l2_subdev_io_pin_config *pincfg);
    int (*init)(struct v4l2_subdev *sd, u32 val);
    int (*load_fw)(struct v4l2_subdev *sd);
    int (*reset)(struct v4l2_subdev *sd, u32 val);
    int (*s_gpio)(struct v4l2_subdev *sd, u32 val);
    long (*command)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
    long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
#ifdef CONFIG_COMPAT;
    long (*compat_ioctl32)(struct v4l2_subdev *sd, unsigned int cmd, unsigned long arg);
#endif;
#ifdef CONFIG_VIDEO_ADV_DEBUG;
    int (*g_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);
    int (*s_register)(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg);
#endif;
    int (*s_power)(struct v4l2_subdev *sd, int on);
    int (*interrupt_service_routine)(struct v4l2_subdev *sd, u32 status, bool *handled);
    int (*subscribe_event)(struct v4l2_subd、
    
    ev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub);
    int (*unsubscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub);
};

struct v4l2_subdev_tuner_ops {
    int (*standby)(struct v4l2_subdev *sd);
    int (*s_radio)(struct v4l2_subdev *sd);
    int (*s_frequency)(struct v4l2_subdev *sd, const struct v4l2_frequency *freq);
    int (*g_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq);
    int (*enum_freq_bands)(struct v4l2_subdev *sd, struct v4l2_frequency_band *band);
    int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);
    int (*s_tuner)(struct v4l2_subdev *sd, const struct v4l2_tuner *vt);
    int (*g_modulator)(struct v4l2_subdev *sd, struct v4l2_modulator *vm);
    int (*s_modulator)(struct v4l2_subdev *sd, const struct v4l2_modulator *vm);
    int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type);
    int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config);
};

struct v4l2_subdev_audio_ops {
    int (*s_clock_freq)(struct v4l2_subdev *sd, u32 freq);
    int (*s_i2s_clock_freq)(struct v4l2_subdev *sd, u32 freq);
    int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);
    int (*s_stream)(struct v4l2_subdev *sd, int enable);
};

struct v4l2_subdev_video_ops {
    int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);
    int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags);
    int (*g_std)(struct v4l2_subdev *sd, v4l2_std_id *norm);
    int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm);
    int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std);
    int (*g_std_output)(struct v4l2_subdev *sd, v4l2_std_id *std);
    int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std);
    int (*g_tvnorms)(struct v4l2_subdev *sd, v4l2_std_id *std);
    int (*g_tvnorms_output)(struct v4l2_subdev *sd, v4l2_std_id *std);
    int (*g_input_status)(struct v4l2_subdev *sd, u32 *status);
    int (*s_stream)(struct v4l2_subdev *sd, int enable);
    int (*g_pixelaspect)(struct v4l2_subdev *sd, struct v4l2_fract *aspect);
    int (*g_frame_interval)(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *interval);
    int (*s_frame_interval)(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *interval);
    int (*s_dv_timings)(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings);
    int (*g_dv_timings)(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings);
    int (*query_dv_timings)(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings);
    int (*s_rx_buffer)(struct v4l2_subdev *sd, void *buf, unsigned int *size);
    int (*pre_streamon)(struct v4l2_subdev *sd, u32 flags);
    int (*post_streamoff)(struct v4l2_subdev *sd);
};

struct v4l2_subdev_pad_ops {
    int (*init_cfg)(struct v4l2_subdev *sd, struct v4l2_subdev_state *state);
    int (*enum_mbus_code)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code);
    int (*enum_frame_size)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_frame_size_enum *fse);
    int (*enum_frame_interval)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_frame_interval_enum *fie);
    int (*get_fmt)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_format *format);
    int (*set_fmt)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_format *format);
    int (*get_selection)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel);
    int (*set_selection)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel);
    int (*get_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
    int (*set_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
    int (*dv_timings_cap)(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap);
    int (*enum_dv_timings)(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings);
#ifdef CONFIG_MEDIA_CONTROLLER;
    int (*link_validate)(struct v4l2_subdev *sd, struct media_link *link,struct v4l2_subdev_format *source_fmt, struct v4l2_subdev_format *sink_fmt);
#endif ;
    int (*get_frame_desc)(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *fd);
    int (*set_frame_desc)(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *fd);
    int (*get_mbus_config)(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_config *config);
    int (*set_routing)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state,enum v4l2_subdev_format_whence which, struct v4l2_subdev_krouting *route);
    int (*enable_streams)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, u32 pad, u64 streams_mask);
    int (*disable_streams)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, u32 pad, u64 streams_mask);
};

struct v4l2_subdev_ops {
  const struct v4l2_subdev_core_ops  *core;
  const struct v4l2_subdev_tuner_ops *tuner;
  const struct v4l2_subdev_audio_ops *audio;
  const struct v4l2_subdev_video_ops *video;
  const struct v4l2_subdev_pad_ops *video;
};

在上述五种类别的ops中,v4l2_subdev_core_ops对所有的子设备都是通用的,而其他四种ops是否需要实现取决于子设备,例如:视频设备不太可能支持音频设备。这种设置限制了函数指针的数量,也便于添加新的操作和类别。

二、初始化v4l2_subdev

在实际驱动程序中,使用v4l2_subdev_init (sd, &ops)初始化v4l2_subdev结构,v4l2_subdev_init()实现如下:

如果子设备驱动程序需要与媒体框架集成,即entity有pads字段,还必须调用media_entity_pads_init()来初始化嵌入在v4l2_subdev结构(entity字段)中的media_entity结构:

struct media_pad *pads = &my_sd->pads;
int err;

err = media_entity_pads_init(&sd->entity, npads, pads);

当子设备被销毁时需要使用:

media_entity_cleanup(&sd->entity);

来清除媒体实例。

三、注册/注销subdev

目前有两种向V4L2核心注册子设备的方法。第一种(传统的)是让bridge driver注册子设备。当bridge driver拥有连接到它的子设备的完整信息并且确切知道什么时候注册它们时,就可以完成这一点。这通常是内部子设备的情况,如soc或复杂PCI(e)板中的视频数据处理单元、USB摄像头中的摄像机传感器或连接到soc的摄像机传感器。通常在它们的platform数据中,它们会将有关它们的信息传递给bridge driver

然而,也存在子设备必须异步注册到桥接设备的情况。这种配置的一个例子是基于设备树的系统,其中关于子设备的信息可独立于桥接设备。例如,当子设备在DT中定义为I2C设备节点时。

使用不同的注册方法只会影响.probe过程,在两种情况下运行时,桥-子设备之间的交互是相同的。

在同步情况下,设备(网桥)驱动程序需要向v4l2_device注册v4l2_subdev

v4l2_device_register_subdev (v4l2_dev, sd)

如果子dev模块在注册之前消失,则此操作可能失败,在成功调用v4l2_device_register_subdev()函数后,subdev->dev字段指向v4l2_device。如果v4l2_device父设备有一个非NULL的mdev字段,那么子设备实体将自动注册到媒体设备。

如果需要注销一个子设备,则使用下列函数:

v4l2_device_unregister_subdev (sd).

在调用该函数后,将卸载子设备模块,并且将sd->dev设置为NULL

在异步情况下进行子设备.probe时,可以不依赖于bridge driver是否可用。但是在子设备驱动程序中必须验证是否满足probe的所有条件:
可能包括检查主时钟是否可用,如果任何一个条件不满足,驱动程序可以返回-EPROBE_DEFER以请求尝试重新probe;如果所有的条件满足,接着应该使用v4l2_async_register_subdev()注册子设备,以这种方式注册的子设备存储在一个全局的子设备列表(subdev_list)中,准备由bridge driver拾取:

bridge driver中必须使用v4l2_async_nf_register()注册一个通知器对象。当不需要通知器时,驱动程序还必须调用v4l2_async_nf_unregister()注销通知器。两个函数中都接收两个参数:一个指向struct v4l2_device的指针和一个指向struct v4l2_async_notifier的指针。

在注册通知器之前,bridge driver必须做两件事:

(1)必须使用v4l2_async_nf_init()初始化通知器。

(2)根据设备的类型和驱动程序的需求,bridge driver可以形成桥接设备运行所需的子设备描述符列表。例如:v4l2_async_nf_add_fwnode_remote()v4l2_async_nf_add_i2c()用于网桥和ISP驱动程序,用于向通知器注册它们的异步子设备。

如果需要注销异步子设备时,需使用v4l2_async_unregister_subdev()取消注册的子设备。

四、异步注册子设备

在早期的内核版本中,V4L2子设备的注册是通过静态定义和手动调用注册函数的方式完成的。然而,这种方法限制了子设备的动态添加和移除能力。所以为了支持更灵活的子设备注册和管理,以及异步通知机制,Linux内核引入了struct v4l2_async_notifier结构体,该结构体和相关的异步通知机制允许驱动程序在运行时动态添加或移除子设备,并通知V4L2核心进行注册或注销。

相关源码文件:

  • /include/media/v4l2-async.h

  • /include/media/v4l2-core/v4l2-async.c

struct v4l2_async_notifier结构定义如下:

struct v4l2_async_notifier {
	const struct v4l2_async_notifier_operations *ops;//异步通知操作的函数指针,用于处理注册和注销子设备的回调函数。
	struct v4l2_device *v4l2_dev;
	struct v4l2_subdev *sd;
	struct v4l2_async_notifier *parent;// 指向父级异步通知器的指针,用于在嵌套异步通知的情况下建立父子关系。
	struct list_head asd_list;
	struct list_head waiting;
	struct list_head done;
	struct list_head list;
};

异步通知机制允许驱动程序在子设备可用时通知V4L2核心进行注册,而不需要在驱动程序初始化阶段进行静态注册。这种机制非常适用于需要动态添加或移除子设备的情况,例如热插拔设备或根据特定条件动态注册设备的应用场景。

异步通知的使用流程如下:

  • 🔺(1)初始化异步通知器

通过调用void v4l2_async_nf_init(struct v4l2_async_notifier *notifier);初始化异步通知器。

  • 🔺(2)添加子设备

使用v4l2_async_nf_add_fwnode()将子设备添加到异步通知器的子设备列表中。

  • 🔺(3)设置异步通知回调

通过设置struct v4l2_async_notifier_operations结构体中的回调函数指针,定义注册和注销子设备时的操作。在注册或注销子设备的过程中,异步通知器的回调函数会被调用,从而通知V4L2核心进行相应的操作。这些回调函数的实现由驱动程序开发人员完成,并根据需要执行必要的操作,如分配资源、初始化子设备、连接管道等。

  • 🔺(4)注册异步通知器

调用int v4l2_async_nf_register(struct v4l2_device *v4l2_dev, struct v4l2_async_notifier *notifier);将异步通知器注册到V4L2设备。

  • 🔺(5)注销异步通知器

通过调用void v4l2_async_nf_unregister(struct v4l2_async_notifier *notifier);注销异步通知器。

通过使用struct v4l2_async_notifier结构体和相应的函数,在驱动程序中可以实现子设备的异步注册和注销。这为动态管理和控制子设备提供了灵活性和可扩展性。

参考链接:
https://docs.kernel.org/driver-api/media/v4l2-subdev.html

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

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

相关文章

【嵌入式linux】spi驱动加载后probe函数未执行的问题

【嵌入式linux】spi驱动加载后probe函数未执行的问题 问题描述解决办法 问题描述 嵌入式linux平台下的spi分为设备、总线和驱动,一般半导体原厂已经实现好了spi设备和总线的相关代码,开发者只需根据实际使用情况修改设备树以及编写驱动部分的代码即可。…

Android进阶 四大组件的工作过程(四):ContentProvider的工作过程

Android进阶 四大组件的工作工程(四):ContentProvider的工作过程 导语 本篇是介绍四大组件的最后一篇文章,前三篇文章里我们已经介绍了Activity,Service以及Broadcast的工作流程,那么这篇文章我们就来介绍…

【数据结构与算法分析】一文搞定插入排序、交换排序、简单选择排序、合并排序的代码实现并给出详细讲解

文章目录 排序相关的基本概念排序算法及其实现插入排序直接插入排序折半插入排序希尔排序 交换排序冒泡排序快速排序 合并排序归并排序简单选择排序 算法比较 排序相关的基本概念 排序:将数组中所有元素按照某一顺序(从小到大或从大到小)重新排列的过程。排序算法的…

DJ2-5 内容分发网络 CDN

目录 单一的大规模数据中心 内容分发网络 CDN 单一的大规模数据中心 存在三个问题: ① 如果客户远离数据中心,服务器到客户的分组将跨越许多通信链路并很可能通过许多 ISP,给用户带来恼人的时延。 ② 流行的视频很可能经过相同的通信链路…

[C++11] 智能指针

长路漫漫,唯剑作伴。 目录 长路漫漫,唯剑作伴。 为什么需要智能指针 RAII 使用RAII思想管理内存 重载 * 和-> 总结一下智能指针的原理: C的智能指针和拷贝问题 auto_ptr (C98) ​编辑 auto_ptr的实现原理…

EmGUCV中类函数 FastFeatureDetector使用详解

FastFeatureDetector Class 释义:FAST(加速检测特)关键点检测器,源自 E. Rosten ("Machine learning for high-speed corner detection, 2006). 继承关系:Emgu.CV.Features2D.FastFeatureDetector 派生&#xff…

记录好项目D5

记录好项目 你好呀,这里是我专门记录一下从某些地方收集起来的项目,对项目修改,进行添砖加瓦,变成自己的闪亮项目。修修补补也可以成为毕设哦 本次的项目是 商品信息管理系统 技术栈:SpringBoot Mybatis Thymelea…

MATLAB|主动噪声和振动控制算法——对较大的次级路径变化具有鲁棒性

\ 💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭…

细谈容器化技术实现原理--以Docker为例

目录 一、Docker解决了什么 二、容器的发展过程 三、容器基础 3.1. 容器实现的原理: ⚠️原理详解: 3.1.1. Namespace 3.1.2. Cgroups 3.1.3. chroot 四、Volume 4.1. Docker是如何做到把一个宿主机上的目录或者文件,挂载到容器里面…

4. 数组更新检测

4.1 v-for更新监测 当v-for遍历的目标结构改变, Vue触发v-for的更新 情况1: 数组翻转 情况2: 数组截取 情况3: 更新值 口诀: 数组变更方法, 就会导致v-for更新, 页面更新 数组非变更方法, 返回新数组, 就不会导致v-for更新, 可采用覆盖数组或this.$set() 这些方法会触发数组改…

基于深度学习的高精度鸽子检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要:基于深度学习的高精度鸽子检测识别系统可用于日常生活中或野外来检测与定位鸽子目标,利用深度学习算法可实现图片、视频、摄像头等方式的鸽子目标检测识别,另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型…

XSS注入——反射性XSS

xss注入的攻击步骤: 1.查找可能存在的注入点(搜索框,留言板,注册) 2.观察输出显示位置: html: 尖括号外部, 尖括号内部: 引号内部》闭合&#xff0…

Django | 基于pycharm的django配置52张全流程截图,红星给你一步一步的男妈妈式教学

演示版本:【windows系统】python3.10pycharm2023.1.2django4.2.2 (本教程全程在虚拟机中演示,读者无需配置虚拟机,直接按教程安装即可) 目录 1.搞到必要的安装包 2.事先准备 3.安装chrome浏览器(也可以…

国产MCU-CW32F030开发学习--按键检测

国产MCU-CW32F030开发学习–按键检测 bsp_key 按键驱动程序用于扫描独立按键,具有软件滤波机制,采用 FIFO 机制保存键值。可以检测 如下事件: 按键按下。 按键弹起。 长按键。 长按时自动连发。 我们将按键驱动分为两个部分来介绍&#xff…

C语言学习笔记:顺序结构

✨博文作者:烟雨孤舟 💖 喜欢的可以 点赞 收藏 关注哦~~ ✍️ 作者简介: 一个热爱大数据的学习者 ✍️ 笔记简介:作为大数据爱好者,以下是个人总结的学习笔记,如有错误,请多多指教! 目录 程序与…

《面试1v1》Spring基础

🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结&#xf…

浅谈微前端

本文呢是我梳理的一个扫盲文,由于最近团队准备使用微前端对项目进行改造,所以我呢就先浅了解一下: 微前端到底是什么? 为什么要使用微前端? 都有哪些微前端方案? 微前端有什么不好的地方吗? 通过…

48 最佳实践-性能最佳实践-Guest-Idle-Haltpoll

文章目录 48 最佳实践-性能最佳实践-Guest-Idle-Haltpoll48.1 概述48.2 操作指导 48 最佳实践-性能最佳实践-Guest-Idle-Haltpoll 48.1 概述 为了保证公平性及降低功耗,当虚拟机vCPU空闲时,虚拟机将执行WFx/HLT指令退出到宿主机中,并触发上…

计算机视觉 - 基于黄金模板比较技术的缺陷检测

一、黄金模板比较概述 基于黄金模板比对的检测是一种常见的视觉应用。当进行缺陷检查而其他缺陷检测方法是不可行的时候,使用金模板比较。另外当物体的表面或物体的形状非常复杂时,此技术特别有用。 虽然说黄金模板比较的技术的思路很简单,但是真正落地实施确不是一件十分容…

广告数仓:数仓搭建(二)

系列文章目录 广告数仓:采集通道创建 广告数仓:数仓搭建 广告数仓:数仓搭建(二) 文章目录 系列文章目录前言DWD层创建1.建表广告事件事实表 2.数据装载初步解析日志解析IP和UA标注无效流量编写脚本 总结 前言 这次我们完成数仓剩下的内容 D…