cpufreq子系统

news2024/12/24 21:12:55

cpufreq是linux上负责实现动态调频的关键,这篇笔记总结了linux内核cpufreq子系统的关键实现(Linux 3.18.140)。

概述

借用一张网络上的图片来看cpufreq子系统的整体结构:

  • 用户态接口:cpufreq通过sysfs向用户态暴露接口,这些节点部分是为了展示内核的配置,部分节点是可以配置的,通过这些节点可以控制cpufreq的一些行为。
  • core层:cpufreq子系统的核心层,负责管理子系统中的policy、governor和driver组件,是三者的纽带,通过core层,实现了调频策略和调频机制的分离。
  • policy:调频策略,每个CPU都有一个调频策略,规定了该CPU的最大、最小可运行频率等信息。
  • governor:可以独立于core层实现,通过规定的接口和core层交互。policy必须和某个governor关联,由governor实现具体的调频策略。可以认为policy负责管理调频参数,governor基于调频参数实现调频策略。
  • driver:平台相关的调频驱动,core层使用driver中的接口完成具体的调频操作。
  • notifier:cpufreq基于通知链机制对外发布的通知事件,外部模块可以通过监听这些事件在CPU的调频策略或者频率发生变化时做出一些处理。
  • stats:一些频率调整的统计信息,我们不关注。

这篇笔记我们重点关注core层的实现,其它部分模块仅作简要介绍。

core层关键实现

core层是cpufreq子系统的关键,它主要包含下面内容:

  1. driver的管理。
  2. policy的管理。
  3. governor的管理。
  4. 事件通知。
  5. 用户态接口。
  6. 为了方便driver、governor实现而提供的一些公共的API。

core层初始化

开机过程中,core层最开始的初始化非常简单,仅仅是创建一个kobject对象,该对象在sysfs中对应到目录/sys/devices/system/cpu/cpufreq

struct kobject *cpufreq_global_kobject;

static int __init cpufreq_core_init(void)
{
    if (cpufreq_disabled()) // cpu动态调频功能未使能
        return -ENODEV;

    // 创建kobject代表cpufreq
    cpufreq_global_kobject = kobject_create();
    return 0;
}
core_initcall(cpufreq_core_init);

driver的管理

cpufreq驱动的实现需要实例化一个struct cpufreq_dirver对象,然后通过cpufreq_register_driver()函数向core层注册自己。系统只需要一个cpufreq驱动来实现具体的CPU频率调整功能,所以core层只允许注册一个驱动。

struct cpufreq_driver

下面是cpufreq_driver的一些核心字段:

  • init():该回调必须实现,core层在为CPU设置policy时会用分配的policy为参数调用驱动的该回调,驱动需要在实现中设置policy的部分字段。
  • verify():该回调必须实现,core层会通过该回调让驱动校验policy中的参数设置是否正确。
  • setpolicy()、target()、target_index():这几个回调必须实现一个。当实现setpolicy()时,驱动表示软件只需要设置一个策略参数(如功耗优先or性能优先)即可,硬件来决定具体的CPU工作频率。当实现后两个回调时,表示需要由governor来决定CPU的具体工作频率,然后通过这两个接口将CPU工作频率设定为指定值。
struct cpufreq_driver {
    char name[CPUFREQ_NAME_LEN]; // 驱动名称会在sysfs中体现
    u8 flags; // 表明一些驱动的feature
    void *driver_data; // cpufreq驱动自己的指针,core层不关注

    int    (*init)    (struct cpufreq_policy *policy);
    int    (*verify)    (struct cpufreq_policy *policy);

    int    (*setpolicy)    (struct cpufreq_policy *policy);

    int    (*target)    (struct cpufreq_policy *policy,    /* Deprecated */
         unsigned int target_freq,
 unsigned int relation);
    int    (*target_index)    (struct cpufreq_policy *policy,
unsigned int index);

    // 可选的,但一般都会实现。获取指定CPU的当前工作频率
    unsigned int    (*get)    (unsigned int cpu);

    struct freq_attr    **attr; // 驱动可以定义一些自己需要在sysfs中体现的参数
...    
};

注册cpufreq驱动

core层为驱动提供了cpufreq_register_driver()函数来注册cpufreq驱动,cpufreq_unregister_driver()是去注册接口,不再展开。注册过程包含三个关键逻辑:

  1. 检查drver实例中的参数设置是否正确。
  2. 保存driver实例到全局变量cpufreq_driver中,相当于完成注册。
  3. 触发为系统中的CPU设置policy的流程,该流程见下文分析。
static struct subsys_interface cpufreq_interface = {
    .name = "cpufreq",
    .subsys = &cpu_subsys, // cpu子系统
    .add_dev = cpufreq_add_dev, // 向cpufreq子系统添加和移除cpu的回调
    .remove_dev    = cpufreq_remove_dev,
};

int cpufreq_register_driver(struct cpufreq_driver *driver_data)
{
    unsigned long flags;

    // 检查驱动的回调实现是否正确
    if (!driver_data || !driver_data->verify || !driver_data->init ||
        !(driver_data->setpolicy || driver_data->target_index ||
 driver_data->target) ||
        (driver_data->setpolicy && (driver_data->target_index ||
 driver_data->target)) ||
        (!!driver_data->get_intermediate != !!driver_data->target_intermediate))
        return -EINVAL;

    if (driver_data->setpolicy)
        driver_data->flags |= CPUFREQ_CONST_LOOPS;

     // cpufreq驱动只能注册一个,保存到全局变量cpufreq_driver中
    write_lock_irqsave(&cpufreq_driver_lock, flags);
    if (cpufreq_driver) {
        write_unlock_irqrestore(&cpufreq_driver_lock, flags);
        return -EEXIST;
    }
    cpufreq_driver = driver_data;
    write_unlock_irqrestore(&cpufreq_driver_lock, flags);

    // 这一步会为系统的所有CPU设置policy
    ret = subsys_interface_register(&cpufreq_interface);

    // 监听CPU热插拔事件
    register_hotcpu_notifier(&cpufreq_cpu_notifier);
}

policy的管理

调频策略用struct cpufreq_policy来描述。系统中每个CPU都必须有一个调频策略,由于可能存在多个CPU共用一个调频策略的情况(如手机上常见的一个cluster中的CPU频率必须保持一致),所以系统中实际的cpufreq_policy对象数量可能少于CPU个数。core层用Per-CPU变量为每个CPU保存对应的cpufreq_policy对象指针。

static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
// 系统中所有的cpufreq_policy对象组织到全局链表中
static LIST_HEAD(cpufreq_policy_list);

struct cpufreq_policy

cpufreq_policy代表一个CPU调频策略,其关键字段如下:

struct cpufreq_policy {
    // 共享该policy的CPU掩码,cpus为online状态的CPU集合,related_cpus为所有共享该plocy的CPU集合
    cpumask_var_t cpus;
    cpumask_var_t related_cpus;

    unsigned int cpu; // 每个policy都有一个管理cpu
    unsigned int last_cpu; // 由于CPU可以热插拔,保存前一个管理该policy的CPU
 
    // CPU支持的最大、最小等频率信息,由驱动设置
    struct cpufreq_cpuinfo cpuinfo;/* see above */

    unsigned int min;    /* in kHz */
    unsigned int max;    /* in kHz */
    unsigned int cur;    /* in kHz, only needed if cpufreq
 governors are used */

    unsigned int policy; // 硬件自己控制具体的CPU频率时才使用该字段
    struct cpufreq_governor    *governor; // 软件控制频率时,具体的governor实现
    void *governor_data;
    bool governor_enabled; /* governor start/stop flag */

    // CPU的工作频率并非是可以调节为任意值的,驱动提供了可调节的频率挡位
    struct cpufreq_frequency_table    *freq_table;

    struct list_head policy_list; // 将该policy组织到全局链表中
};

// 所有的policy对象保存到全局链表中
DEFINE_MUTEX(cpufreq_governor_lock);
static LIST_HEAD(cpufreq_policy_list);

设置CPU的policy

在驱动注册的最后,会调用subsys_interface_register()函数遍历系统中所有CPU,将每个CPU以设备的方式通过cpufreq_add_dev()函数添加到core层,这时会为CPU设置调频策略。

static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
{
    return __cpufreq_add_dev(dev, sif);
}

static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
{
    unsigned int j, cpu = dev->id;
    int ret = -ENOMEM;
    struct cpufreq_policy *policy;
    unsigned long flags;
    bool recover_policy = cpufreq_suspended;

    if (cpu_is_offline(cpu)) // 只为online的CPU分配调频策略
        return 0;

#ifdef CONFIG_SMP
    // 可能存在多个CPU共用一个策略的情况,所以如果该CPU已经设置了策略,则处理结束
    policy = cpufreq_cpu_get(cpu);
    if (unlikely(policy)) {
        cpufreq_cpu_put(policy);
        return 0;
    }
#endif

    // 分配一个policy对象
    policy = recover_policy ? cpufreq_policy_restore(cpu) : NULL;
    if (!policy) {
        recover_policy = false;
        policy = cpufreq_policy_alloc();
    }

    // 设置该CPU为policy的管理CPU
    if (recover_policy && cpu != policy->cpu)
        WARN_ON(update_policy_cpu(policy, cpu, dev));
    else
        policy->cpu = cpu;
    cpumask_copy(policy->cpus, cpumask_of(cpu)); // 当前cpu属于该policy管理

    init_completion(&policy->kobj_unregister);
    INIT_WORK(&policy->update, handle_update);

    // 调用驱动的init回调。驱动会设置policy中的参数
    ret = cpufreq_driver->init(policy);

    // 驱动在init()回调中必须正确的设置哪些CPU会共享该policy,这样core层才能正确设置policy中的cpus字段
    cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus);
    cpumask_and(policy->cpus, policy->cpus, cpu_online_mask);

    if (!recover_policy) {
        policy->user_policy.min = policy->min;
        policy->user_policy.max = policy->max;
    }

    // 为共用同一个policy的其它CPU设置Per-CPU指针,指向分配的policy对象
    write_lock_irqsave(&cpufreq_driver_lock, flags);
    for_each_cpu(j, policy->cpus)
        per_cpu(cpufreq_cpu_data, j) = policy;
    write_unlock_irqrestore(&cpufreq_driver_lock, flags);

    // 获取CPU的当前工作频率
    if (cpufreq_driver->get && !cpufreq_driver->setpolicy) {
        policy->cur = cpufreq_driver->get(policy->cpu);
    }

    // 对于那种开机时不以可调节频率表中频率运行的情况,这里将CPU的频率调整为一个已知的频率
    if ((cpufreq_driver->flags & CPUFREQ_NEED_INITIAL_FREQ_CHECK) && has_target()) {
        /* Are we running at unknown frequency ? */
        ret = cpufreq_frequency_table_get_index(policy, policy->cur);
        if (ret == -EINVAL) {
            ret = __cpufreq_driver_target(policy, policy->cur - 1, CPUFREQ_RELATION_L);
        }
    }

    // 发送CPUFREQ_START通知
    blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_START, policy);

    if (!recover_policy) {
        // 为policy在sysfs中创建属性文件,即/sys/devices/system/cpu/cpufreq/policyX/XXX文件
        ret = cpufreq_add_dev_interface(policy, dev);
        // 发送CPUFREQ_CREATE_POLICY通知
        blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
            CPUFREQ_CREATE_POLICY, policy);
    }

    // 将policy对象加入到全局链表中
    write_lock_irqsave(&cpufreq_driver_lock, flags);
    list_add(&policy->policy_list, &cpufreq_policy_list);
    write_unlock_irqrestore(&cpufreq_driver_lock, flags);

    // 为policy关联governor
    cpufreq_init_policy(policy);

    if (!recover_policy) {
        policy->user_policy.policy = policy->policy;
        policy->user_policy.governor = policy->governor;
    }
}
  1. 只有online状态的cpu才会设置调频策略。
  2. cpufreq_cpu_get()函数根据Per-CPU变量cpufreq_cpu_data的设置,检查该CPU是否和其它CPU复用了一个policy对象,如果是则不需要进行后续的处理。
  3. 分配一个policy对象,将当前cpu设置为该policy的管理CPU。然后调用驱动的init()回调,驱动必须在该回调中对policy中的cpu、频率的信息进行正确的设置。
  4. 继续用驱动设置好的参数设置policy中的其它字段。特别重要的一步是将该共享的policy对象设置到其它CPU的cpufreq_cpu_data中。
  5. 对外发送通知,并在sysfs中为该policy创建属性节点。
  6. 将policy加入全局的cpufreq_policy_list链表中。
  7. 为该policy设置governor,该流程见下文介绍。

governor的管理

policy的调频策略实际由governor实现的,系统可以有多个governor,这些governor注册到core层后,用户态可以通过sysfs节点来为CPU指定具体使用哪个governor。系统用链表保存所有支持的governor。

static LIST_HEAD(cpufreq_governor_list);

core层提供了governor的注册函数cpufreq_register_governor(),实现很简单,不再展开。

struct governor

每个governor都需要实例化一个该对象,然后将其注册到core层。

struct cpufreq_policy {
...
    struct cpufreq_governor    *governor;
    void *governor_data;
}

struct cpufreq_governor {
    char name[CPUFREQ_NAME_LEN]; // 每个governor都有一个唯一的名字
    int    initialized;

    // 处理governor的启动和停止事件
    int    (*governor)    (struct cpufreq_policy *policy,
 unsigned int event);

    ssize_t    (*show_setspeed)    (struct cpufreq_policy *policy,
 char *buf);
    int    (*store_setspeed)    (struct cpufreq_policy *policy,
 unsigned int freq);

    // governor实现对驱动的约束,要求驱动切换频率的最大时延必须小于该值
    unsigned int max_transition_latency;

    struct list_head governor_list; // 将governor组织到全局链表中

};

为policy设置governor

policy的调频策略需要由governor来实现,如前面“设置CPU的policy”中分析,CPU设置了policy后,需要为policy关联一个governor。这是通过cpufreq_init_policy()函数完成的。用户态可以通过sysfs修改policy的governor,此时也会触发类似的流程。

// 记录每个CPU的governor,如果不配置则为其选择默认的governor
static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor);

static void cpufreq_init_policy(struct cpufreq_policy *policy)
{
    struct cpufreq_governor *gov = NULL;
    struct cpufreq_policy new_policy; // 借助一个临时变量完成设置过程
    int ret = 0;

    memcpy(&new_policy, policy, sizeof(*policy));

    // 根据名字从全局链表中选择governor,开机时可能尚未配置,选择一个默认的配置,默认配置来自系统config
    gov = __find_governor(per_cpu(cpufreq_cpu_governor, policy->cpu));
    if (gov)
        pr_debug("Restoring governor %s for cpu %d\n", policy->governor->name, policy->cpu);
    else
        gov = CPUFREQ_DEFAULT_GOVERNOR;
    new_policy.governor = gov;

    if (cpufreq_driver->setpolicy)
        cpufreq_parse_governor(gov->name, &new_policy.policy, NULL);

    // 用新的配置更新当前policy
    ret = cpufreq_set_policy(policy, &new_policy);
}

// policy : current policy.
 new_policy: policy to be set.
static int cpufreq_set_policy(struct cpufreq_policy *policy,
 struct cpufreq_policy *new_policy)
{
    struct cpufreq_governor *old_gov;
    int ret;

    memcpy(&new_policy->cpuinfo, &policy->cpuinfo, sizeof(policy->cpuinfo));

    // 让驱动校验policy的参数设置是否正确
    ret = cpufreq_driver->verify(new_policy);

    // 发送CPUFREQ_ADJUST通知
    blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
 CPUFREQ_ADJUST, new_policy);
    blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
 CPUFREQ_INCOMPATIBLE, new_policy);

    // 上述的通知过程中可以调整policy,重新校验policy参数设置
    ret = cpufreq_driver->verify(new_policy);

    /* notification of the new policy */
    blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
 CPUFREQ_NOTIFY, new_policy);

    policy->min = new_policy->min;
    policy->max = new_policy->max;

    if (cpufreq_driver->setpolicy) { // 驱动只需要设置策略的情形
        policy->policy = new_policy->policy;

        return cpufreq_driver->setpolicy(new_policy);
    }

    if (new_policy->governor == policy->governor)
        goto out;
    // 更新governor,对旧的governor执行STOP和EXIT,对新的governor执行INIT和START
    old_gov = policy->governor;

    if (old_gov) {
        __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
        up_write(&policy->rwsem);
        __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT);
        down_write(&policy->rwsem);
    }

    policy->governor = new_policy->governor;
    if (!__cpufreq_governor(policy, CPUFREQ_GOV_POLICY_INIT)) {
        if (!__cpufreq_governor(policy, CPUFREQ_GOV_START))
            goto out;
    }
}

事件通知

事件通知虽然也是在core层实现的,但是它是相对独立的内容,这里单独对其进行分析。

cpufreq子系统利用Linux标准的通知链机制,实现了两种通知:policy通知Transition通知。外部模块可以监听这两种通知事件,在事件发生时执行一些需要的逻辑。

// 通知监听接口
int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list);
int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list);

// list参数代表了要监听的通知类型
#define CPUFREQ_TRANSITION_NOTIFIER    (0)
#define CPUFREQ_POLICY_NOTIFIER        (1)

static BLOCKING_NOTIFIER_HEAD(cpufreq_policy_notifier_list);
static struct srcu_notifier_head cpufreq_transition_notifier_list;

policy通知

cpufreq子系统在CPU的policy发生变化时,会依次发送三个policy通知事件:

  1. 首先发出CPUFREQ_ADJUST事件,给通知接收者一个机会来修改policy中的参数,比如温控模块可能会修改其最大频率。
  2. 然后发出CPUFREQ_INCOMPATIBLE事件,给通知接收者一个机会来检查policy的参数是否和硬件不兼容,如果不兼容可以修改。这两个事件时挨着依次发出的,除了事件含义不同外,效果完全相同,所以事件接收者完全可以在一个事件中将所有该处理的事情执行完毕。
  3. 最后发出CPUFREQ_NOTIFY事件告诉通知接收者,将以该policy作为最终配置。
/* Policy Notifiers  */
#define CPUFREQ_ADJUST            (0)
#define CPUFREQ_INCOMPATIBLE        (1)
#define CPUFREQ_NOTIFY            (2)

// 另外几个事件用来通知policy对象的生命周期变化
#define CPUFREQ_START            (3)
#define CPUFREQ_UPDATE_POLICY_CPU    (4)
#define CPUFREQ_CREATE_POLICY        (5)
#define CPUFREQ_REMOVE_POLICY        (6)

Transition通知

cpufreq子系统在CPU的频率发生变化前后,会依次发送两个Transition通知事件,携带的参数表明了修改的CPU及其变化前后的频率。

/* Transition notifiers */
#define CPUFREQ_PRECHANGE        (0)
#define CPUFREQ_POSTCHANGE        (1)

struct cpufreq_freqs {
    unsigned int cpu;    /* cpu nr */
    unsigned int old;
    unsigned int new;
    u8 flags;        /* flags of cpufreq_driver, see below. */
};

用户态接口

cpufreq子系统在sysfs中有如下接口,通过这些既可以展示一些配置信息,也给用户态提供了一些可调整的参数。

cpufreq子系统的总目录是/sys/devices/system/cpu/cpufreq,下面是系统中所有的policy实例,每个policy目录名中的数字是该policy的管理CPU ID。其中schedutil是一种基于调度器的governor,该目录保存了该governor在sysfs中的内容。

每个policyX目录下是该policy暴露的节点,这些节点大部分是只读的,其含义如下:

  • affected_cpusreleated_cpu分别对应内核中policy->cpus和policy->releated_cpus,是该policy管理的CPU掩码。
  • cpuinfo_xxx_freq字段表示该CPU可以支持CPU频率,单位为kHZ。
  • cpuinfo_transition_latency字段表示该CPU频率切换需要的时延,单位为ns。
  • scaling_available_frequencies表示该组CPU可以调整的频率挡位;scaling_available_governors表示该组CPU可以选择的governor;scaling_driver表示当前的驱动名称;scaling_governor表示当前的governor名称。
  • scaling_xxx_freq表示当前该CPU上配置的可调节频率信息。
  • 用户态可以通过向scaling_setspeed节点写入频率来调整CPU频率,当然内核可能会不支持该操作。

每个CPU都必须有一种policy,所以在cpu的目录下会有一个软链接指向对应的policy目录:

参考资料

  1. cpufreq schedutil原理剖析-CSDN博客;
  2. https://www.cnblogs.com/LoyenWang/p/11385811.html;
  3. 内核Doc:Documentation/cpu-freq/core.txt;

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

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

相关文章

2022年多元统计分析期中试卷

多元正态均值检验 一、去年卖出的一岁牛犊的平均身高为 51 英寸,平均背脂厚度是 0.3 英寸,平均肩高是 56 英寸。已知今年卖出的 76 头一岁牛犊的 3 项平均指标为(50, 0.2, 54)‘,样本协差阵及其逆矩阵为 S [ 3.00 − 0.053 2.97 − 0.053 0…

【Bootstrap5学习 day12】

Bootstrap5 导航 Bootstrap5提供了一种简单快捷的方法来创建基本导航,它提供了非常灵活和优雅的选项卡和Pills等组件。Bootstrap5的所有导航组件,包括选项卡和Pillss,都通过基本的.nav类共享相同的基本标记和样式。 创建基本导航 要创建简单…

eureka注册列表 某服务出现多个服务实例

最近文件导出功能偶发成功,大部分情况都失败,开始以为接口被拦截,gateway服务没有接口调用日志,发现测试环境可以,正式环境功能无法正常使用。 偶然看到注册中心如下 发现file服务有3个实例,调用接口将错误…

Java十种经典排序算法详解与应用

数组的排序 前言 排序概念 排序是将一组数据,依据指定的顺序进行排列的过程。 排序是算法中的一部分,也叫排序算法。算法处理数据,而数据的处理最好是要找到他们的规律,这个规律中有很大一部分就是要进行排序,所以需…

Excel中快速隐藏中间四位手机号或者身份证号等

注意:以下方式必须再新增一列,配合旧的一列用来对比操作,即根据旧的一列的数据源,通过新的一列的操作逻辑来生成新的隐藏数据 1、快捷方式是使用CtrlE 新建一列:手动输入第一个手机号隐藏后的号码,即在N2单…

VS+QT五子棋游戏开发

1、首先安装好VS软件和QT库,将其配置好,具体不在此展开说明。 2、文件结构如下图: 3、绘制棋盘代码,如下: void Qwzq::paintEvent(QPaintEvent* event) {QPainter painter(this);painter.setRenderHint(QPainter::An…

Unity之键盘鼠标的监控

小编最近在玩大表哥2,通过 W、A、S、D 来移动亚瑟,鼠标左键来不吃牛肉 我们都知道玩家通过按键鼠标来控制游戏人物做出相应的行为动作,那在Unity引擎里是怎么知道玩家是如何操作的呢?本篇来介绍Unity是怎样监控键盘和鼠标的。 首先…

智创有术开发公司,星潮宇宙开发。

2024年,星潮宇宙全网首发,一个全新的赛道将被开启,这将对游戏产业产生深远的影响。本文将深入探讨这场首发的对接团队以及他们的突破性举措,以展示其对游戏界的重要意义。 星潮宇宙全网首发概述 星潮宇宙的全网首发意味着将有一…

Cloud模型matlab

学习资料python 多维正态云python 预备知识: 如何获取具有特定均值和方差的正态分布随机数。首先,初始化随机数生成器,以使本示例中的结果具备可重复性。 rng(0,twister);基于均值为 500 且标准差为 5 的正态分布创建包含 1000 个随机值的向…

损失函数篇 | YOLOv8 引入 Shape-IoU 考虑边框形状与尺度的度量

作者导读:Shape-IoU:考虑边框形状与尺度的度量 论文地址:https://arxiv.org/abs/2312.17663 作者视频解读:https://www.bilibili.com 开源代码地址:https://github.com/malagoutou/Shape-IoU/blob/main/shapeiou.py…

Linux第16步_安装NFS服务

NFS(Network File System)是一种在网络上实现的分布式文件系统,它允许不同的操作系统和设备之间共享文件和资源。 在创建的linux目录下,再创建一个“nfs“文件夹,用来供nfs服务器使用,便于”我们的开发板“…

Android Studio 模拟器卡死的解决方法!

目录 前言 一、常规方法 二、简便解决方法 前言 在开发过程中,使用Android Studio模拟器是一种常见的方式来测试和调试应用程序。然而,有时候你可能会遇到模拟器卡死的情况,这给开发工作带来了一定的困扰。模拟器卡死可能会让你无法正常进…

设计模式——迭代器模式(Iterator Pattern)

概述 迭代器模式(Iterator Pattern):提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标(Cursor)。迭代器模式是一种对象行为型模式。 在软件开发中,我们经常需要使用聚合对象来存储一系列数据。聚合对象拥有…

密码学入门 古老的围栏密码技术

1、简述 由于隐私和安全的重要性不断增加,已经开发了多种加密方法和技术来保护我们的敏感数据。随着时间的推移而演变,从经典密码学发展到现代密码学。 在本文中,我们将了解一种被称为围栏密码技术的技术,涵盖其加密和解密过程及其…

Unity组件开发--升降梯

我开发的升降梯由三个部分组成,反正适用于我的需求了,其他人想复用到自己的项目的话,不一定。写的也不是很好,感觉搞的有点复杂啦。完全可以在优化一下,项目赶工期,就先这样吧。能用就行,其他的…

MidJourney笔记(10)-faq-fast-help-imagine-info-public-stealth

/faq 在官方 Midjourney Discord 服务器中使用可快速生成流行提示工艺频道常见问题解答的链接。 不过这个命令,我也是没有找到入口,之前还能在MidJourney的频道里使用,然后最近发现没有权限,有点奇怪。不知道系统又做了什么升级。 /fast 切换到快速模式。

Vue3-34-路由-路由配置参数 props

说明 路由的路径中可以携带参数, 形式如 :/a/:pname ,这个:表示这是个参数,pname :表示 参数名称。 在组件中,可以通过 当前路由对象的 params 属性来获取到这个参数, 当前路由对象 可以通过 us…

Flutter 混合开发 - aar打包

背景 项目接入 Flutter 后有两种方式,一种是 module 引入开发,一种是 aar 依赖开发。当前项目中在 Debug 阶段为了方便调试采用 module 开发,在发版时(即 Release 阶段)采用 aar 依赖引入。为了配合这种模式就需要在 …

SpringBoot中动态注册接口

1. 说明 接口注册,使用RequestMappingHandlerMapping来实现mybatis中动态执行sql使用github上的SqlMapper工具类实现 2. 核心代码片段 以下代码为spring动态注册接口代码示例 Autowired private RequestMappingHandlerMapping requestMappingHandlerMapping;publ…

手机上连网络转接app,电脑连接手机,共用网络转接app的办法

方法一,(不推荐) 因为太简单了所以写一下 电脑安装MuMu模拟器,之后安装网络转接app,这个模拟器设置了从电脑上安装app和,安卓与电脑同步文件夹功能,实现文件共享。所以直接用就可以了。 方法二…