「知识分享源码分析」linux内核调度详解

一口Linux 2022-10-26 11:50

点击左上方蓝色“一口Linux”,选择“设为星标

第一时间看干货文章 

【干货】嵌入式驱动工程师学习路线
【干货】一个适合初学者的Linux物联网综合项目
【干货】Linux嵌入式知识点-思维导图



 1

本文档基于linux3.14 ,linux内核调度详解

1、概述

1.1、调度策略

定义位于
linux/include/uapi/linux/sched.h中

#define SCHED_NORMAL		0
#define SCHED_FIFO 1
#define SCHED_RR 2
#define SCHED_BATCH 3
/* SCHED_ISO: reserved but not implemented yet */
#define SCHED_IDLE 5
#define SCHED_DEADLINE 6

SCHED_NORMAL:普通的分时进程,使用的fair_sched_class调度类

SCHED_FIFO:先进先出的实时进程。当调用程序把CPU分配给进程的时候,它把该进程描述符保留在运行队列链表的当前位置。此调度策略的进程一旦使用CPU则一直运行。如果没有其他可运行的更高优先级实时进程,进程就继续使用CPU,想用多久就用多久,即使还有其他具有相同优先级的实时进程处于可运行状态。使用的是rt_sched_class调度类。

SCHED_RR:时间片轮转的实时进程。当调度程序把CPU分配给进程的时候,它把该进程的描述符放在运行队列链表的末尾。这种策略保证对所有具有相同优先级的SCHED_RR实时进程进行公平分配CPU时间,使用的rt_sched_class调度类

SCHED_BATCH:是SCHED_NORMAL的分化版本。采用分时策略,根据动态优先级,分配CPU资源。在有实时进程的时候,实时进程优先调度。但针对吞吐量优化,除了不能抢占外与常规进程一样,允许任务运行更长时间,更好使用高速缓存,适合于成批处理的工作,使用的fair_shed_class调度类

SCHED_IDLE:优先级最低,在系统空闲时运行,使用的是idle_sched_class调度类,给0号进程使用

SCHED_DEADLINE:新支持的实时进程调度策略,针对突发型计算,并且对延迟和完成时间敏感的任务使用,基于EDF(earliest deadline first),使用的是dl_sched_class调度类。

1.2、调度类

定义调度类struct sched class

struct sched_class {
const struct sched_class *next;

void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);
void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags);
void (*yield_task) (struct rq *rq);
bool (*yield_to_task) (struct rq *rq, struct task_struct *p, bool preempt);

void (*check_preempt_curr) (struct rq *rq, struct task_struct *p, int flags);

struct task_struct * (*pick_next_task) (struct rq *rq);
void (*put_prev_task) (struct rq *rq, struct task_struct *p);

#ifdef CONFIG_SMP
int (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags);
void (*migrate_task_rq)(struct task_struct *p, int next_cpu);

void (*pre_schedule) (struct rq *this_rq, struct task_struct *task);
void (*post_schedule) (struct rq *this_rq);
void (*task_waking) (struct task_struct *task);
void (*task_woken) (struct rq *this_rq, struct task_struct *task);

void (*set_cpus_allowed)(struct task_struct *p,
const struct cpumask *newmask);

void (*rq_online)(struct rq *rq);
void (*rq_offline)(struct rq *rq);
#endif

void (*set_curr_task) (struct rq *rq);
void (*task_tick) (struct rq *rq, struct task_struct *p, int queued);
void (*task_fork) (struct task_struct *p);
void (*task_dead) (struct task_struct *p);

void (*switched_from) (struct rq *this_rq, struct task_struct *task);
void (*switched_to) (struct rq *this_rq, struct task_struct *task);
void (*prio_changed) (struct rq *this_rq, struct task_struct *task,
int oldprio);

unsigned int (*get_rr_interval) (struct rq *rq,
struct task_struct *task);

#ifdef CONFIG_FAIR_GROUP_SCHED
void (*task_move_group) (struct task_struct *p, int on_rq);
#endif
};

Next:指向下一个调度类,用于在函数pick_next_task、check_preempt_curr、set_rq_online、set_rq_offline用于遍历整个调度类根据调度类的优先级选择调度类。优先级为stop_sched_class->dl_sched_class->rt_sched_class->fair_sched_class->idle_sc*hed_class

enqueue_task:将任务加入到调度类中

dequeue_task:将任务从调度类中移除

yield_task/ yield_to_task:主动放弃CPU

check_preempt_curr:检查当前进程是否可被强占

pick_next_task:从调度类中选出下一个要运行的进程

put_prev_task:将进程放回到调度类中

select_task_rq:为进程选择一个合适的cpu的运行队列

migrate_task_rq:迁移到另外的cpu运行队列

pre_schedule:调度以前调用

post_schedule:通知调度器完成切换

task_waking、task_woken:用于进程唤醒

set_cpus_allowed:修改进程cpu亲和力affinity

rq_online:启动运行队列

rq_offline:关闭运行队列

set_curr_task:当进程改变调度类或者进程组时被调用

task_tick:将会引起进程切换,驱动运行running强占。由time_tick调用

task_fork:进程创建时调用,不同调度策略的进程初始化不一样

task_dead:进程结束时调用

switched_from、switched_to:进程改变调度器时使用

prio_changed:改变进程优先级

1.3、调度触发


调度的触发主要有两种方式,一种是本地定时中断触发调用scheduler_tick函数,然后使用当前运行进程的调度类中的task_tick,另外一种则是主动调用schedule,不管是哪一种最终都会调用到__schedule函数,该函数调用pick_netx_task,通过rq->nr_running ==rq->cfs.h_nr_running判断出如果当前运行队列中的进程都在cfs调度器中,则直接调用cfs的调度类(内核代码里面这一判断使用了likely说明大部分情况都是满足该条件的)。如果运行队列不都在cfs中,则通过优先级stop_sched_class->dl_sched_class->rt_sched_class->fair_sched_class->idle_sched_class遍历选出下一个需要运行的进程。然后进程任务切换。

处于TASK_RUNNING状态的进程才会被进程调度器选择,其他状态不会进入调度器。系统发生调度的时机如下:

à调用cond_resched()时

à显式调用schedule()时

à从中断上下文返回时

当内核开启抢占时,会多出几个调度时机如下:

à在系统调用或者中断上下文中调用preemt_enable()时(多次调用系统只会在最后一次调用时会调度)

à在中断上下文中,从中断处理函数返回到可抢占的上下文时

1.4、__schedule的实现

分析_schedule的实现有利于理解调度类的实体如果在

static void __sched __schedule(void)
{
struct task_struct *prev, *next;
unsigned long *switch_count;
struct rq *rq;
int cpu;

need_resched:
/*关闭抢占*/
preempt_disable();
/*获取当前CPU*/
cpu = smp_processor_id();
/*取得当前cpu的运行队列rq*/
rq = cpu_rq(cpu);
/*更新全局状态,标识当前CPU发生上下文切换*/
rcu_note_context_switch(cpu);
prev = rq->curr; /*当前运行的进程存入Prev中*/

schedule_debug(prev);

if (sched_feat(HRTICK)) //取消为当前进程运行的hrtimer
hrtick_clear(rq);

/*
* Make sure that signal_pending_state()->signal_pending() below
* can't be reordered with __set_current_state(TASK_INTERRUPTIBLE)
* done by the caller to avoid the race with signal_wake_up().
*/

smp_mb__before_spinlock(); //内存屏障
raw_spin_lock_irq(&rq->lock); //上锁该队列

switch_count = &prev->nivcsw; //记录当前进程切换次数
if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { //当前进程非运行状态,并且非内核抢占
if (unlikely(signal_pending_state(prev->state, prev))) { //当前进程有信号待处理,设置进程为就绪态
prev->state = TASK_RUNNING; //设置为RUNNING
} else {
deactivate_task(rq, prev, DEQUEUE_SLEEP); //将其从队列中删除
prev->on_rq = 0; //将在运行队列中变量置为0

/*
* If a worker went to sleep, notify and ask workqueue
* whether it wants to wake up a task to maintain
* concurrency.
*/

if (prev->flags & PF_WQ_WORKER) { //判断是否是工作队列进程
struct task_struct *to_wakeup;

to_wakeup = wq_worker_sleeping(prev, cpu); //如果有工作队列进程唤醒则尝试唤醒
if (to_wakeup)
try_to_wake_up_local(to_wakeup);
}
}
switch_count = &prev->nvcsw;
}

pre_schedule(rq, prev); //通知调度器,即将发生进程切换

if (unlikely(!rq->nr_running))
idle_balance(cpu, rq);

put_prev_task(rq, prev); //通知调度器,即将用另一个进程替换当前进程
next = pick_next_task(rq); //挑选一个优先级高的任务,使用调度类的方法
clear_tsk_need_resched(prev); //清空TIF_NEED_RESCHED标志
clear_preempt_need_resched(); //清空PREEMPT_NEED_RESCHED标志
rq->skip_clock_update = 0;

if (likely(prev != next)) { //如果如果选出的进程和当前进程不是同一个进程
rq->nr_switches++; //队列切换次数更新
rq->curr = next; //将当前进程换成刚才挑选的进程
++*switch_count; //进程切换次数更新

context_switch(rq, prev, next); /* unlocks the rq */ /*进程上下文切换 */
/*
* The context switch have flipped the stack from under us
* and restored the local variables which were saved when
* this task called schedule() in the past. prev == current
* is still correct, but it can be moved to another cpu/rq.
*/

/*上下文切换以后当前运行CPU也可能会改变,需要从新获取当前运行进程的运行队列*/
cpu = smp_processor_id();
rq = cpu_rq(cpu);
} else
raw_spin_unlock_irq(&rq->lock); //解锁该队列

post_schedule(rq); //通知调度器,完成了进程切换

sched_preempt_enable_no_resched(); //开启内核抢占
if (need_resched()) //如果该进程被其他进程设置了TIF_NEED_RESCHED,则需要重新调度
goto need_resched;
}

其中有几个重要的与调度器密切相关的函数:

pre_scheduleà prev->sched_class->pre_schedule 在调度以前调用

put_prev_taskàprev->sched_class->put_prev_task 将前一个进程调度以前放回调度器中

pick_next_taskà class->pick_next_task从调度器中选出下一个需要运行的进程

post_scheduleà rq->curr->sched_class->post_scheduleCFS中为NULL

2、 CFS调度

该部分代码位于linux/kernel/sched/fair.c中

定义了const struct
sched_classfair_sched_class,这个是CFS的调度类定义的对象。其中基本包含了CFS调度的所有实现。

CFS实现三个调度策略:

1> SCHED_NORMAL这个调度策略是被常规任务使用

2> SCHED_BATCH 这个策略不像常规的任务那样频繁的抢占,以牺牲交互性为代价下,因而允许任务运行更长的时间以更好的利用缓存,这种策略适合批处理

3> SCHED_IDLE 这是nice值甚至比19还弱,但是为了避免陷入优先级导致问题,这个问题将会死锁这个调度器,因而这不是一个真正空闲定时调度器

CFS调度类:

n enqueue_task(…) 当任务进入runnable状态,这个回调将把这个任务的调度实体(entity)放入红黑树并且增加nr_running变量的值

n dequeue_task(…) 当任务不再是runnable状态,这个回调将会把这个任务的调度实体从红黑树中取出,并且减少nr_running变量的值

n yield_task(…) 除非compat_yield sysctl是打开的,这个回调函数基本上就是一个dequeue后跟一个enqueue,这那种情况下,他将任务的调度实体放入红黑树的最右端

n check_preempt_curr(…) 这个回调函数是检查一个任务进入runnable状态是否应该抢占当前运行的任务

n pick_next_task(…) 这个回调函数选出下一个最合适运行的任务

n set_curr_task(…) 当任务改变他的调度类或者改变他的任务组,将调用该回调函数

n task_tick(…) 这个回调函数大多数是被time tick调用。他可能引起进程切换。这就驱动了运行时抢占

2.1、调度实体

/*
一个调度实体(红黑树的一个节点),其包含一组或一个指定的进程,包含一个自己的运行队列,一个父亲指针,一个指向需要调度的队列
*/

struct sched_entity {
/*权重,在数组prio_to_weight[]包含优先级转权重的数值*/
struct load_weight load; /* for load-balancing */
/*实体在红黑树对应的节点信息*/
struct rb_node run_node;
/*实体所在的进程组*/
struct list_head group_node;
/*实体是否处于红黑树运行队列中*/
unsigned int on_rq;

/*开始运行时间*/
u64 exec_start;
/*总运行时间*/
u64 sum_exec_runtime;
/*
虚拟运行时间,在时间中断或者任务状态发生改变时会更新
其会不停的增长,增长速度与load权重成反比,load越高,增长速度越慢,就越可能处于红黑树最左边被调度
每次时钟中断都会修改其值
具体见calc_delta_fair()函数
*/

u64 vruntime;
/*进程在切换进cpu时的sum_exec_runtime值*/
u64 prev_sum_exec_runtime;
/*此调度实体中进程移到其他cpu组的数量*/
u64 nr_migrations;

#ifdef CONFIG_SCHEDSTATS
/*用于统计一些数据*/
struct sched_statistics statistics;
#endif

#ifdef CONFIG_FAIR_GROUP_SCHED
/*
父亲调度实体指针,如果是进程则指向其运行队列的调度实体,如果是进程组则指向其上一个进程组的调度实体
在set_task_rq函数中设置
*/

struct sched_entity *parent;
/* rq on which this entity is (to be) queued: */
/*实体所处红黑树运行队列*/
struct cfs_rq *cfs_rq;
/* rq "owned" by this entity/group: */
/*实体的红黑树运行队列,如果为NULL表明其是一个进程,若非NULL表明其是调度组*/
struct cfs_rq *my_q;
#endif

#ifdef CONFIG_SMP
/* Per-entity load-tracking */
struct sched_avg avg;
#endif
};

其中几个重要的变量

字段

描述

load

指定了权重, 决定了各个实体占队列总负荷的比重, 计算负荷权重是调度器的一项重任, 因为CFS所需的虚拟时钟的速度最终依赖于负荷, 权重通过优先级转换而成,是vruntime计算的关键

Run_node

调度实体在红黑树对应的结点信息, 使得调度实体可以在红黑树上排序

Sum_exec_runtime

记录程序运行所消耗的CPU时间, 以用于完全公平调度器CFS

On_rq

调度实体是否在就绪队列上接受检查, 表明是否处于CFS红黑树运行队列中,需要明确一个观点就是,CFS运行队列里面包含有一个红黑树,但这个红黑树并不是CFS运行队列的全部,因为红黑树仅仅是用于选择出下一个调度程序的算法。很简单的一个例子,普通程序运行时,其并不在红黑树中,但是还是处于CFS运行队列中,其on_rq为真。只有准备退出、即将睡眠等待和转为实时进程的进程其CFS运行队列的on_rq为假

vruntime

虚拟运行时间,调度的关键,其计算公式:一次调度间隔的虚拟运行时间 = 实际运行时间 * (NICE_0_LOAD / 权重)。可以看出跟实际运行时间和权重有关,红黑树就是以此作为排序的标准,优先级越高的进程在运行时其vruntime增长的越慢,其可运行时间相对就长,而且也越有可能处于红黑树的最左结点,调度器每次都选择最左边的结点为下一个调度进程。注意其值为单调递增,在每个调度器的时钟中断时当前进程的虚拟运行时间都会累加。单纯的说就是进程们都在比谁的vruntime最小,最小的将被调度

Cfs_rq

此调度实体所处于的CFS运行队列

My_q

如果此调度实体代表的是一个进程组,那么此调度实体就包含有一个自己的CFS运行队列,其CFS运行队列中存放的是此进程组中的进程,这些进程就不会在其他CFS运行队列的红黑树中被包含(包括顶层红黑树也不会包含他们,他们只属于这个进程组的红黑树)

Sum_exec_runtime

  •  跟踪运行时间是由update_curr不断累积完成的. 内核中许多地方都会调用该函数, 例如, 新进程加入就绪队列时, 或者周期性调度器中. 每次调用时, 会计算当前时间和exec_start之间的差值, exec_start则更新到当前时间. 差值则被加到sum_exec_runtime.

  •  在进程执行期间虚拟时钟上流逝的时间数量由vruntime统计

  •  在进程被撤销时, 其当前sum_exec_runtime值保存到prev_sum_exec_runtime, 此后, 进程抢占的时候需要用到该数据, 但是注意, 在prev_sum_exec_runtime中保存了sum_exec_runtime的值, 而sum_exec_runtime并不会被重置, 而是持续单调增长

每一个进程的task_struct中都嵌入了sched_entry对象,所以进程是可调度的实体,但是可调度的实体不一定是进程,也可能是进程组。

2.2、CFS调度

Tcik 中断,主要会更新调度信息,然后调整当前进程在红黑树中的位置。调整完成以后如果当前进程不再是最左边的叶子,就标记为Need_resched标志,中断返回时就会调用scheduler()完成切换、否则当前进程继续占用CPU。从这里可以看出CFS抛弃了传统时间片概念。Tick中断只需要更新红黑树。

红黑树键值即为vruntime,该值通过调用update_curr函数进行更新。这个值为64位的变量,会一直递增,__enqueue_entity中会将vruntime作为键值将要入队的实体插入到红黑树中。__pick_first_entity会将红黑树中最左侧即vruntime最小的实体取出。

end


一口Linux 


关注,回复【1024】海量Linux资料赠送

精彩文章合集


文章推荐

【专辑】ARM
【专辑】粉丝问答
专辑linux入门
专辑计算机网络
专辑Linux驱动
【干货】嵌入式驱动工程师学习路线
【干货】Linux嵌入式所有知识点-思维导图

一口Linux 写点代码,写点人生!
评论
  • 工业物联网时代,作为一种普遍应用在汽车电子、工业控制与医疗器械等领域中的串行总线通信技术——CAN(Controller Area Network)总线基于消息广播模式,通过双绞线传输差分信号,是一种多主控(Multi-Master)的总线系统,具备极强的抗干扰能力、极低的传输延迟和高速数据传输性能。一种典型的CAN总线网络示意图在CAN总线通信过程中,CAN收发器作为物理层上的接口芯片,位于CAN控制器(MCU)和CAN总线之间,主要负责将来自CAN控制器(MCU)的数字信号与总线上的差分信号
    华普微HOPERF 2025-07-04 14:44 1961浏览
  • 曾经靠“砍一刀”撕裂传统电商格局的拼多多,如今疲态尽显。数据显示,拼多多今年第一季度实现营收957亿元,同比增长10%,市场预估1016亿元,相比预期低了近60亿元;经营利润为161亿元,相比去年同期下降38%;归属于普通股股东的净利润为147亿元,同比下降47%。与此同时,拼多多市值也坐上了“过山车”。去年市值一度突破 2180 亿美元,力压国内电商巨头阿里,今年(7月1日收盘)市值仅余 1497.59 亿美元,已不足阿里(市值2718.63亿美元)一半
    用户1742991715177 2025-07-05 14:24 423浏览
  • 在物联网无线通信领域,随着行业应用场景的不断拓宽,同一频段下的设备通信需求正呈指数级增长,然而这一增长趋势却与频谱资源的有限性形成了鲜明对立,信道拥挤、信号串扰与非线性失真等不良现象所造成的“通信压力”正在持续放大。从智能家居的设备互联到工业物联网的实时控制,从智慧楼宇的广域组网到智慧城市的海量数据传输,有限的频谱资源不仅需要满足不断增长的设备通信需求,还需要适配不同场景对速率、时延与可靠性等差异化要求。在此背景下,如何在有限的频谱资源中实现更为稳定的无线通信质量,已成为物联网行业发展路径中的核
    华普微HOPERF 2025-07-07 16:13 605浏览
  • 在万物互联浪潮席卷全球的时代背景下,数字气压传感器作为物联网环境感知层的核心硬件之一,正凭借精准、实时的数字化气压测量能力,快速消融着现实世界与数字世界之间的沟通壁垒,其不仅能为物联网终端提供与环境进行对话的“媒介语言”,还能与其他环境感知技术形成协同效应,是物联网社会实现高效沟通的基石之一。数字气压传感器主要通过检测压敏元件在不同环境气压下的电信号变化,实现对气压的测量,并能直接输出数字信号以满足物联网终端对气压数据的传输、储存与记录等需求。现阶段,由于数字气压传感器具备着低功耗、高精度、快速
    华普微HOPERF 2025-07-01 09:20 1637浏览
  • 什么是LoRaWAN? LoRaWAN技术及应用LoRaWAN(Long Range Wide Area Network)是一种低功耗、长距离、广域网络通信协议,特别设计用于连接物联网(IoT)设备。LoRaWAN采用无线通信技术,能够覆盖数十公里的范围,提供长时间的电池寿命,适用于智能城市、农业、工业自动化、环境监测,与健康医疗等领域应用。来源: LoRa Alliance一探究竟:LoRaWAN物联网应用优势营运商采用 LoRaWAN 具有多方面的优势,除了长距离覆盖范围及低功耗的特点外,还
    百佳泰测试实验室 2025-07-10 14:51 325浏览
  •   几个月前,一个老旧的大风扇的散风圈(俺不知其专业名称)的开关按钮不起作用,就是锁不住了,散风圈也就不转了。今天,有空,就拿到工作台,开始拆解分析故障原因,能修好更好。  看看,用的时间够长了吧!皮肤都变颜色了。看标签,合格品2005年的。  底部四个螺丝固定,很容易拆开了。  看到掉下一个标签圆纸片,拿起来看看,是那个横向摇头的电机的。  找到那个按钮开关位置  应该是开关内部的有缺陷了。把它拆下来,一看就是正规合格品。  拿出我日积月累的分类藏宝盒,呵呵,找到一款螺丝孔位正好合适的。   
    自做自受 2025-07-10 11:16 349浏览
  • 现代人对于影音效果的追求持续增加,在多数影音产品中,HDMI是最为重要的接口,而HDMI 2.1中有一新增功能eARC可以透过HDMI线材来传输数字Audio,除了支持Dolby TrueHD、Atoms等声音格式外,也可以支持8声道喇叭,满足消费者对于声音的追求。新时代的高阶电视都具备支援eARC的功能,然而却有些号称支持的电视产品,因为在设计上的疏忽,造成eARC无法输出8声道Audio,以致eARC的功能大打折扣,对于花大钱欲享受高规格电视的消费者来说自然难以接受,对于该电视品牌也会产生负
    百佳泰测试实验室 2025-07-04 14:42 2023浏览
  • 提要:采用LOXIM的微孔雾化专用芯片LX8201,能突破压电陶瓷驱动电压超标(24伏)的技术难题,满足全球市场对喷雾玩具的电压安规认证要求。玩具行业尤其是喷雾玩具行业内人士都知道,喷雾玩具的压电陶瓷驱动电压超标(常需60-100V)与强制安规标准(中国,日本,欧美,都一样)对玩具电压的限制(≤24V)存在根本性冲突,如果采用“多层压电堆叠(MPA)技术“(比如日本TDK公司),成本将增加至现有微孔雾化片的10倍以上,这个矛盾一直没有得到好的解决。喷雾玩具在国内热销(淘宝/抖音),能卖的原因,无
    Loximonline 2025-07-08 10:55 278浏览
  • 在数字化、自动化高速发展的今天,光电耦合器正以一种低调却不可或缺的方式,悄然改变着我们的生活。它不仅是电子电路中的“安全卫士”,更是连接信号世界的“桥梁”,凭借出色的电气隔离能力,为各类设备提供稳定可靠的信号传输保障。电气隔离——让系统更安全在工业控制系统中,安全始终是重中之重。光电耦合器通过光信号进行电气隔离,能够有效防止高压电流侵入低压控制电路。例如,在智能电网系统中,它广泛应用于电表与通信模块之间,确保数据传输的安全性,防止电网高压对低压设备造成冲击。在电动汽车的电池管理系统(BMS)中,
    腾恩科技-彭工 2025-07-05 13:56 453浏览
  • 本文主要针对分立方案高速输出电路,由于MOS管、稳压管、PCB布局布线都存在一些寄生电容,这些寄生电容都会影响高速输出电路的占空比,所以本文对这些寄生电容的影响进行简单说明。测试工况:电压:24V,负载类型:阻性负载2K,输出频率:200Khz,要求占空比45%~55%;电路拓扑如下图所示:图1寄生电容分布情况:如下图所示,MOS管寄生电容为Cgd、Cgs、Cds,稳压管寄生电容为Cd;图2而MOS管手册常见的参数如下图所示为Ciss、Coss、Crss(为啥是这些参数,而不改成Cgs、Cgd、
    用户1751282873645 2025-07-08 23:58 231浏览
  • 在全球广泛倡导绿色低碳与可持续发展的时代浪潮中,新能源汽车作为实现节能减排的重要载体之一,正受到各国政府与企业的加速培育。在此背景下,为提升新能源汽车的市场渗透率,我国已率先进入充电基础设施建设的加速期,从私人专用充电桩到社区公用充电桩,从高速路网补能节点到城市公用充电桩,汽车补能网络正在急速膨胀中。图源:摄图网(已授权)据中国充电联盟(EVCIPA)最新统计数据显示,截止2025年5月份,我国充电基础设施累计数量为1440万台,同比上升45.1%。其中,在2025年1月~5月期间,我国充电基础
    华普微HOPERF 2025-07-09 16:13 293浏览
  •   去年底,整理旧物,扔的扔了,留的留了,这不,十四个几十年前留下来的工业级小型排风扇,下图左上角处,又拿出来,下决心把它们再利用发挥余热。  呵呵,这回不是拆而是装了。怎么装呢?组装、固定、机架、接线,简单,也不简单,原则是一切都用手头现有废旧材料,争取做到一个不买!DIY,废物利用,如今时髦的话,以旧换新!摆上台面,找来木条,策划怎么做?  比一比,看一看,觉得合适,按尺寸锯开木条。  咋走线?想到了,在有限空间内弯转,从一个螺丝孔穿出来,整体拼凑整齐。   咋固定风扇呢?找来木片条,锯断,
    自做自受 2025-07-06 21:37 574浏览
  • 据知名市场研究机构Counterpoint Research发布的数据概览,2025年第二季度,中国智能手机销量预计将迎来小幅回暖,增长率约为1%。在这场销量微增的背后,华为与苹果两大品牌成为了推动市场前行的核心力量。其中华为手机的表现最为亮眼,数据显示,华为在中国市场的智能手机销量份额实现了12%的同比增长,这一成绩不仅使其成为了当季增长最快的品牌,更助力华为重新夺回销量榜首的位置。相比之下,vivo的表现就有些尴尬了。虽然还是位列第二,但vivo在第二季度的智能手机销量份额同比下降了9%,下
    用户1742991715177 2025-07-09 08:19 243浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦