Linux0.11 进程切换(十)

news2025/1/21 4:29:01

在这里插入图片描述

系列文章目录


Linux 0.11启动过程分析(一)
Linux 0.11 fork 函数(二)
Linux0.11 缺页处理(三)
Linux0.11 根文件系统挂载(四)
Linux0.11 文件打开open函数(五)
Linux0.11 execve函数(六)
Linux0.11 80X86知识(七)
Linux0.11 内核体系结构(八)
Linux0.11 系统调用进程创建与执行(九)
Linux0.11 进程切换(十)


文章目录

  • 系列文章目录
  • 一、简介
  • 二、代码
  • 三、进程切换
  • 1、switch_to(1) 执行前
    • 1.1 **task0** 用户态
      • 寄存器的信息
      • task_struct 信息
      • ldt
      • tss
    • 1.2 **task1**
      • task_struct
      • ldt
      • tss
  • 2、switch_to(1) 执行后
    • 2.1 task0 信息变化
      • tss
    • 2.2 task1 信息变化
      • 寄存器的信息(用户态)
      • 寄存器的信息(内核态)


一、简介

    sched.c 是内核中有关任务(进程)调度管理的程序,其中包括有关调度的基本函数(sleep_on()、wakeup()、schedule()等)以及一些简单的系统调用函数(比如 getpid())。系统时钟中断处理过程中调用的定时函数 do_timer()也被放置在本程序中。另外,为了便于软盘驱动器定时处理的编程,Linus 也将有关软盘定时操作作的几个函数放到了这里。

   这几个基本函数的代码虽然不长,但有些抽象,比较难以理解。好在市面上有许多教科书对此解释得都很清楚,因此可以参考其他书籍对这些函数的讨论。这些也就是教科书上重点讲述的对象,否则理论书籍也就没有什么好讲的了。这里仅对调度函数 schedule()作一些详细说明。

   schedule()函数负责选择系统中下一个要运行的进程。它首先对所有任务(进程)进行检测,唤醒任何一个已经得到信号的任务。具体方法是针对任务数组中的每个任务,检查其报警定时值 alarm。如果任务的 alarm 时间已经过期(alarm < jiffies),则在它的信号位图中设置 SIGALRM 信号,然后清 alarm 值。jiffies 是系统从开机开始算起的滴答数(10ms/滴答)。在 sched.h 中定义。如果进程的信号位图中除去被阻塞的信号外还有其他信号,并且任务处于可中断睡眠状态(TASK_INTERRUPTIBLE),则置任务为就绪状态(TASK_RUNNING)。

   随后是调度函数的核心处理部分。这部分代码根据进程的时间片和优先权调度机制,来选择随后要执行的任务。它首先循环检查任务数组中的所有任务,根据每个就绪态任务剩余执行时间的值 counter,选取该值最大的一个任务,并利用 switch_to()函数切换到该任务。若所有就绪态任务的该值都等于零,表示此刻所有任务的时间片都已经运行完,于是就根据任务的优先权值 priority,重置每个任务的运行时间片值 counter,再重新执行循环检查所有任务的执行时间片值。

   另两个值得一提的函数是自动进入睡眠函数 sleep_on()和唤醒函数 wake_up(),这两个函数虽然很短,却要比 schedule()函数难理解。这里用图示的方法加以解释。简单地说,sleep_on()函数的主要功能是当一个进程(或任务)所请求的资源正忙或不在内存中时暂时切换出去,放在等待队列中等待一段时间。当切换回来后再继续运行。放入等待队列的方式是利用了函数中的 tmp 指针作为各个正在等待任务的联系。

   函数中共牵涉到对三个任务指针操作:*ptmpcurrent,*p 是等待队列头指针,如文件系统内存 i 节点的 i_wait 指针、内存缓冲操作中的 buffer_wait 指针等;tmp 是在函数堆栈上建立的临时指针,存储在当前任务内核态堆栈上;current 是当前任务指针。对于这些指针在内存中的变化情况我们可以用图 8-6 的示意图说明。图中的长条表示内存字节序列。
在这里插入图片描述

    当刚进入该函数时,队列头指针 * p 指向已经在等待队列中等待的任务结构(进程描述符)。 当然,在系统刚开始执行时,等待队列上无等待任务。因此上图中原等待任务在刚开始时是不存在的,此时 *p 指向 NULL。通过指针操作,在调用调度程序之前,队列头指针指向了当前任务结构,而函数中的临时指针 tmp 指向了原等待任务。在执行调度程序并在本任务被唤醒重新返回执行之前,当前任务指针被指向新的当前任务,并且 CPU 切换到该新的任务中执行。这样本次 sleep_on()函数的执行使得 tmp 指针指向队列中队列头指针指向的原等待任务,而队列头指针则指向此次新加入的等待任务,即调用本函数的任务。从而通过堆栈上该临时指针 tmp 的链接作用,在几个进程为等待同一资源而多次调用该函数时,内核程序就隐式地构筑出一个等待队列。从图 8-7 中我们可以更容易地理解 sleep_on()函数的等待队列形成过程。图中示出了当向队列头部插入第三个任务时的情况。
在这里插入图片描述

    在插入等待队列后 sleep_on()函数就会调用 schedule()函数去执行别的进程。当进程被唤醒而重新执行时就会执行后续的语句,把比它早进入等待队列的一个进程唤醒。注意,这里所谓的唤醒并不是指进程处于执行状态,而是处于可以被调度执行的就绪状态。

    唤醒操作函数 wake_up(把正在等待可用资源的指定任务置为就绪状态。该函数是一个通用唤醒函数。在有些情况下,例如读取磁盘上的数据块,由于等待队列中的任何一个任务都可能被先唤醒,因此还需要把被唤醒任务结构的指针置空。这样,在其后进入睡眠的进程被唤醒而又重新执行 sleep_on()时,就无需唤醒该进程了。

    还有一个函数 interruptible_sleep_on(),它的结构与 sleep_on()的基本类似,只是在进行调度之前是把当前任务置成了可中断等待状态,并在本任务被唤醒后还需要判断队列上是否有后来的等待任务,若有,则调度它们先运行。在内核 0.12 开始,这两个函数被合二为一,仅用任务的状态作为参数来区分这两种情况。
    在阅读本文件的代码时,最好同时参考包含文件 include/linux/sched.h 文件中的注释,以便更清晰地了解内核的调度机理。

二、代码

// kernel/sched.c

// 把当前任务置为不可中断的等待状态,并让睡眠队列头指针指向当前任务。
// 只有明确地唤醒时才会返回。该函数提供了进程与中断处理程序之间的同步机制。
// 函数参数 p 是等待任务队列头指针。指针是含有一个变量地址的变量。这里参数 p 使用了指针的
// 指针形式'**p',这是因为 C 函数参数只能传值,没有直接的方式让被调用函数改变调用该函数
// 程序中变量的值。但是指针' *p' 指向的目标(这里是任务结构)会改变,因此为了能修改调用该
// 函数程序中原来就是指针变量的值,就需要传递指针' *p' 的指针,即'**p'。参见图 8-6 中 p'指针。
// 的使用情况。
void sleep_on(struct task_struct **p)
{
	struct task_struct *tmp;
// 若指针无效,则退出。(指针所指的对象可以是 NULL,但指针本身不应该为 0)。另外,如果
// 当前任务是任务 0,则死机。因为任务 0 的运行不依赖自己的状态,所以内核代码把任务 0 置
// 为睡眠状态毫无意义。
	if (!p)
		return;
	if (current == &(init_task.task))
		panic("task[0] trying to sleep");
// 让 tmp 指向已经在等待队列上的任务(如果有的话),例如 inode->i_wait。并且将睡眠队列头
// 的等待指针指向当前任务。这样就把当前任务插入到了 *p 的等待队列中。然后将当前任务置。
// 为不可中断的等待状态,并执行重新调度。
	tmp = *p;
	*p = current;
	current->state = TASK_UNINTERRUPTIBLE;
	schedule();
// 只有当这个等待任务被唤醒时,调度程序才又返回到这里,表示本进程已被明确地唤醒(就
// 绪态)。既然大家都在等待同样的资源,那么在资源可用时,就有必要唤醒所有等待该资源
// 的进程。该函数嵌套调用,也会嵌套唤醒所有等待该资源的进程。这里嵌套调用是指当一个
// 进程调用了 sleep_on( 后就会在该函数中被切换掉,控制权被转移到其他进程中。此时若有
// 进程也需要使用同一资源,那么也会使用同一个等待队列头指针作为参数调用 sleep_on() 函
// 数,并且也会"陷入"该函数而不会返回。只有当内核某处代码以队列头指针作为参数 wake_up
// 了该队列,那么当系统切换去执行头指针所指的进程 A 时,该进程才会继续执行 163 行,把。
// 队列后一个进程 B 置位就绪状态(唤醒)。 而当轮到 B 进程执行时,它也才可能继续执行
// 163 行。若它后面还有等待的进程 C,那么它也会把 C 唤醒等。这里在 163 行前还应该添加。
// 一条语句:*p = tmp; 见 183 行上的解释。
	if (tmp)		// 若在其前还存在等待的任务,则也将其置为就绪状态(唤醒)。
		tmp->state=0;
}

三、进程切换

    调度程序头文件,定义了任务结构 task_struct、初始任务 0 的数据,还有一些有关描述符参数设置和获取以及任务上下文切换 switch_to()的嵌入式汇编函数宏。下面详细描述一下任务切换宏的执行过程。
    任务切换宏 switch_to(n)(从 171 行开始)首先申明了一个结构 ‘struct {long a,b;} __tmp’,用于在任务内核态堆栈上保留出 8 字节的空间来存放将切换到新任务的任务状态段 TSS 的选择符。然后测试我们是否是在执行切换到当前任务的操作,如果是则什么也不需要做,直接退出。否则就把新任务 TSS 的选择符保存到临时结构 __tmp 中的偏移位置 4 处,此时 __tmp 中的数据设置为:
__tmp+0:未定义(long)
__tmp+4:新任务 TSS 的选择符(word)
__tmp+6:未定义(word)

   接下来把 %ecx 寄存器中的新任务指针与全局变量 current 中的当前任务指针相交换,让 current 含有我们将要切换到的新任务的指针值,而 ecx 中则保存着当前任务(本任务)的指针值。接着执行间接长跳转到 __tmp 的指令 Ijmp。长跳转到新任务 TSS 选择符的指令将忽略 __tmp 中未定义值的部分,CPU 将自动跳转到 TSS 段指定新任务中去执行,而本任务也就到此暂停执行。这也是我们无需设置结构变量 __tmp 中其他未定义部分的原因。参见第 5 章中图 2-22:任务切换操作示意图。
   当一段时间之后,某个任务的 ljmp 指令又会跳转到本任务 TSS 段选择符,从而造成 CPU 切换回本任务,并从 ljmp 的下一条指令开始执行。此时 ecx 中含有本任务即当前任务的指针,因此我们可以使用该指针来检查它是否是最后(最近)一个使用过数学协处理器的任务。若本任务没有使用过协处理器则立刻退出,否则执行 clts 指令以复位控制寄存器 CR0 中的任务已切换标志 TS。每当任务切换时 CPU 都会设置该标志位,并且在执行协处理器指令之前测试该标志位。Linux 系统中的这种处理 TS 标志的方法可以让内核避免对协处理状态不必要的保存、恢复操作过程,从而提高了协处理器的执行性能。

// include/linux/sched.h

/*
* 在 GDT 表中寻找第 1 个 TSS 的入口。0-没有用 nul, 1-代码段 cs, 2-数据段 ds, 3-系统调用 syscall
* 4-任务状态段 TSS0, 5-局部表 LTD0, 6-任务状态段 TSS1, 等。
*/
// 从该英文注释可以猜想到,Linus 当时曾想把系统调用的代码专门放在 GDT 表中第 4 个独立的段中。
// 但后来并没有那样做,于是就一直把 GDT 表中第 4 个描述符项(上面 syscall 项)闲置在一旁。
// 下面定义宏:全局表中第 1 个任务状态段(TSS)描述符的选择符索引号。
#define FIRST_TSS_ENTRY 4
// 全局表中第 1 个局部描述符表(LDT)描述符的选择符索引号。
#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
// 宏定义,计算在全局表中第 n 个任务的 TSS 段描述符的选择符值(偏移量)。
// 因每个描述符占 8 字节,因此 FIRST_TSS_ENTRY<<3 表示该描述符在 GDT 表中的起始偏移位置。
// 因为每个任务使用 1 个 TSS 和 1 个 LDT 描述符,共占用 16 字节,因此需要 n<<4 来表示对应
// TSS 起始位置。该宏得到的值正好也是该 TSS 的选择符值。
#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
/// 宏定义,计算在全局表中第 n 个任务的 LDT 段描述符的选择符值(偏移量)。
#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
// 宏定义,把第 n 个任务的 TSS 段选择符加载到任务寄存器 TR中。
#define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n)))
// 宏定义,把第 n 个任务的 LDT 段选择符加载到局部描述符表寄存器 LDTR 中。
#define lldt(n) __asm__("lldt %%ax"::"a" (_LDT(n)))
// 取当前运行任务的任务号(是任务数组中的索引值,与进程号 pid 不同)。
// 返回:n - 当前任务号。用于(kerne1/traps.c, 79)。
#define str(n) \
__asm__("str %%ax\n\t" \
	"subl %2,%%eax\n\t" \
	"shrl $4,%%eax" \
	:"=a" (n) \
	:"a" (0),"i" (FIRST_TSS_ENTRY<<3))
	
/*
* switch_to(n)将切换当前任务到任务 nr,即 n。首先检测任务 n 不是当前任务,
* 如果是则什么也不做退出。如果我们切换到的任务最近(上次运行)使用过数学。
* 协处理器的话,则还需复位控制寄存器 cr0 中的 TS 标志。
*/
// 跳转到一个任务的 TSS 段选择符组成的地址处会造成 CPU 进行任务切换操作。
// 输入:%0 - 指向 _tmp;			
//		%1 - 指向__tmp.b 处,用于存放新 TSS 的选择符;
//		dx - 新任务 n 的 TSS 段选择符;
//		ecx - 新任务 n 的任务结构指针 task[n]。
//
// 其中临时数据结构 _tmp 用于组建 177 行远跳转(far jump)指令的操作数。该操作数由 4 字节偏移
// 地址和 2 字节的段选择符组成。因此__tmp 中 a 的值是 32 位偏移值,而 b 的低 2 字节是新 TSS 段的
// 选择符(高 2 字节不用)。跳转到 TSS 段选择符会造成任务切换到该 TSS 对应的进程。对于造成任务。
// 切换的长跳转,a 值无用。177 行上的内存间接跳转指令使用 6 字节操作数作为跳转目的地的长指针,
// 其格式为:jmp 16 位段选择符:32 位偏移值。但在内存中操作数的表示顺序与这里正好相反。
// 任务切换回来之后,在判断原任务上次执行是否使用过协处理器时,是通过将原任务指针与保存在
// last_task_used_math 变量中的上次使用过协处理器任务指针进行比较而作出的,参见文件。
// kernel/sched.c 中有关 math_state_restore()函数的说明。
#define switch_to(n) {\
struct {long a,b;} __tmp; \
__asm__("cmpl %%ecx,current\n\t" \ 	// 任务 n 是当前任务吗?(current ==task[n]?)
	"je 1f\n\t" \					// 是,则什么都不做,退出。
	"movw %%dx,%1\n\t" \			// 将新任务 TSS 的 16 位选择符存入__tmp.b 中。
	"xchgl %%ecx,current\n\t" \		// current = task[n]; ecx = 被切换出的任务。
	"ljmp *%0\n\t" \				// 执行长跳转至*&___tmp,造成任务切换。
									// 在任务切换回来后才会继续执行下面的语句。
	"cmpl %%ecx,last_task_used_math\n\t" \ 		// 原任务上次使用过协处理器吗?
	"jne 1f\n\t" \					// 没有则跳转,退出。
	"clts\n" \						// 原任务上次使用过协处理器,则清 cr0 中的任务切换
	"1:" \							// 标志 TS。
	::"m" (*&__tmp.a),"m" (*&__tmp.b), \
	"d" (_TSS(n)),"c" ((long) task[n])); \	// edx 存放了新任务的 TSS, ecx 存放了 task[n]
}

   从上面代码可知:当前任务对 GDT 中的 TSS 描述符执行 LJMP 指令可导致任务切换;在任务切换期间,当前运行任务的执行环境(称为任务的状态或上下文)会被保存到它的 TSS 中并且暂停该任务的执行。此后新调度任务的上下文会被加载进处理器中,并且从加载的 EIP 指向的指令处开始执行新任务。具体可参考: 九、任务管理, 4、任务切换


   以下是在 task0 的用户态执行 switch_to(1) 切换到 task1 的情况。

1、switch_to(1) 执行前

1.1 task0 用户态

寄存器的信息

在这里插入图片描述

task_struct 信息

在这里插入图片描述

ldt

在这里插入图片描述

tss

在这里插入图片描述

1.2 task1

task_struct

在这里插入图片描述

ldt

在这里插入图片描述

tss

在这里插入图片描述

2、switch_to(1) 执行后

2.1 task0 信息变化

从前面我们知道,任务切换时,CPU 会自动保存原有的信息到 task0tss 段上。因此其 tss 段发生了变化。备注:ldt 保持不变。

tss

在这里插入图片描述

保存的字段可参考: 3.1 任务状态段, 4、任务切换

结合 task0 用户态中的寄存器的信息,可见任务切换时,这些信息确实保存在 tss 字段中。

2.2 task1 信息变化

任务切换时,CPU 会自动保存原有的信息到 tasktss 段上。同时会加载当前 tss 段信息到相应的寄存器中(恢复的是用户态的信息)。

寄存器的信息(用户态)

在这里插入图片描述

寄存器的信息(内核态)

在这里插入图片描述

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

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

相关文章

AI 写文献回顾,好使吗?

&#xff08;注&#xff1a;本文为小报童精选文章&#xff0c;已订阅小报童或加入知识星球「玉树芝兰」用户请勿重复付费&#xff09;作为一个大学老师&#xff0c;我平时少不了指导学生写论文。选题阶段很重要的工作&#xff0c;就是文献回顾。文献回顾的目的&#xff0c;是要…

jsp企业职工考勤管理系统myeclipse定制开发sqlserver数据库网页模式java编程jdbc

一、源码特点 jsp企业职工考勤管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 jsp企业职工考勤管理系统myeclipse定制开发sqls二、功能介绍 此次系统主要在JS…

Java集合框架概述

java中的集合分为单列集合(Collection)和双列集合(Map)&#xff0c;都在java.util包下&#xff0c;所有的集合框架都分为三大块内容&#xff1a;对外的接口、接口的实现、集合中的算法&#xff0c; 接口&#xff1a;代表集合的抽象数据类型&#xff0c;Collection(List、Set)、…

使用向量机(SVM)算法的推荐系统部署实现

包括3个模块&#xff1a;数据预处理、模型训练及保存、模型测试&#xff0c;下面分别给出各模块的功能介绍及相关代码。 数据集下载链接为https://www.aitechclub.com/data-detail? data_id29&#xff0c;停用词典下载链接为http://www.datasoldier.net/archives/636。 1.数…

Python进阶内容--迭代器和生成器

什么是迭代器 在 Python 中&#xff0c;迭代器&#xff08;Iterator&#xff09;是一个访问集合元素的对象&#xff0c;它能够实现遍历集合的所有元素&#xff0c;而无需了解集合底层结构和细节。Python 中所有可迭代的对象&#xff08;如 列表、元组、字符串、字典、集合等&a…

NumPy 基础知识 :1~5

原文&#xff1a;Numpy Essentials 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 一、NumPy 简介 “我宁愿使用通用语言进行数学运算&#xff0c;也不愿尝试使用数学语言进行通用编程。” – John D Cook 在过去的十年中&#xff0c;Python 已成为科学计算中最受欢迎…

(C语言版)力扣(LeetCode)面试题 17.04. 消失的数字5种解法

消失的数字题目介绍第一种解法&#xff1a;按位异或第二种解法&#xff1a;公式运算第三种解法&#xff1a;临时数组第四种解法&#xff1a;相加再相减第五种解法&#xff1a;快排加二分查找结语题目介绍 该题目取自力扣&#xff08;LeetCode&#xff09;面试题 17.04. 消失的…

Direct3D 12——灯光——光照模型的概述

将之前所述的所有光照内容都结合起来&#xff0c;即表面反射的光量相当于环境反射光、漫反射光以及 镜面反射光的光量总和。 1.环境光Ca&#xff1a;模拟经表面反射的间接光量。 2.漫反射光Cd&#xff1a;对进入介质内部&#xff0c;又经过表面下吸收而最终散射岀表面的光进行…

盐边县高堰沟灌区综合信息化管理系统及平台建设-案例分享

项目背景 盐边县南部降雨逐年减少&#xff0c;可用水源有限&#xff0c;部分村庄的水源取自仅20亩的山坪塘&#xff0c;常常因降雨量小而蓄不满水&#xff0c;出现“人争水、地缺水”的现象&#xff0c;且支渠及以下渠系基本为土渠&#xff0c;渗漏损失严重。为解决不同程度的工…

解决魔兽世界wow中鼠标莫名其妙消失或自动跳回屏幕中间等类似问题整理

魔兽世界已经离开中国大陆~~&#xff0c;最近在玩xxx&#xff0c;咳咳&#xff0c;突然发现一个远古问题&#xff0c;一直以为是自己的鼠标问题&#xff0c;但这么多年换了这么多台电脑&#xff0c;这个问题也一直存在&#xff0c;今天突发奇想查了一下&#xff0c;结果竟然有答…

能聊天、会学习,远不是GPT的终局

自然语言处理&#xff08;NLP&#xff09;技术的发展和运用&#xff0c;使得计算机性能增长速度一举跃过摩尔定律瓶颈&#xff0c;将AI拱入属于它的高光时代。而象征技术融合的ChatGPT一夜爆红&#xff0c;仿佛给整个商业社会带来了一次“技术革命”。 微软、谷歌、百度、华为…

C++ 23 实用工具(二)绑定工具

C 23 实用工具&#xff08;二&#xff09;绑定工具 Adaptors for Functions std::bind、std::bind_front、std::bind_back和std::function这四个函数非常适合一起使用。 其中&#xff0c;std::bind、std::bind_front和std::bind_back可以让您即时创建新的函数对象&#xff0c…

一文! 解决镜像法,电轴法在电磁场中的应用

目录 镜像法原理 例题 模型一&#xff1a;无限大导体平面 一些理解 模型二&#xff1a;球面镜像 情况一&#xff1a;球壳接地 同样的几点思考&#xff1a; 情况二&#xff1a;球壳不接地 球壳不接地&#xff0c;但是点电荷放置在内部 镜像法在双层介质中的作用 电轴法…

C++017-C++指针及其应用

文章目录C017-C指针及其应用C指针及其应用CSP-J目标1. 指针1.指针变量的定义、赋值2.指针的引用与运算2. 基于指针的数组访问3. 指针与字符串4. 结构体与指针在练习&#xff1a;总结C017-C指针及其应用 在线练习&#xff1a; http://noi.openjudge.cn/ https://www.luogu.com.c…

设计师都在用的6个免费设计素材网站~

本期给大家推荐几个设计师都在用的素材网站&#xff0c;免费下载&#xff0c;赶紧收藏起来吧~ 1、菜鸟图库 https://www.sucai999.com/?vNTYwNDUx 菜鸟图库是我推荐过很多次的网站&#xff0c;主要是站内素材多&#xff0c;像平面、UI、电商等设计素材都能找到&#xff0c;还…

零售数据可视化|人、货、场、供、财报表分享

有没有零售数据可视化的例子&#xff0c;让大家看看BI零售数据可视化的效果&#xff1f;有&#xff0c;奥威BI零售标准方案提供了数十张BI数据可视化报表&#xff0c;覆盖人、货、场、供、财等核心业务&#xff0c;既可以让大家一次性体验零售数据可视化报表效果&#xff0c;也…

vLive带你走进虚拟直播世界

虚拟直播是什么&#xff1f; 虚拟直播是基于5G实时渲染技术&#xff0c;在绿幕环境下拍摄画面&#xff0c;通过实时抠像、渲染与合成&#xff0c;再推流到直播平台的一种直播技术。尽管这种技术早已被影视工业所采用&#xff0c;但在全民化进程中却是困难重重&#xff0c;面临…

GEE:Gmeans

G-means是一种聚类算法,它是基于K-means算法的改进版本。K-means算法的一个主要缺点是需要事先指定聚类的数量,而G-means算法则可以自动确定聚类的数量。 G-means算法使用了类似于K-means的迭代过程,但在每次迭代时,它会检查每个聚类是否可以继续细分为两个子聚类。这个检…

怎么免费制作logo?logo免费设计在线生成,从此设计不求人

你有没有因为Logo制作而烦恼过&#xff1f;对于很多人来说&#xff0c;logo制作是一项比较大的工程&#xff0c;需要专门的设计师才能完成。但是请人设计费用高还很费时间&#xff0c;还需多次沟通修改。其实&#xff0c;我们可以自己免费制作logo&#xff0c;下面&#xff0c;…

pytorch2.0 起步

参考&#xff1a;https://pytorch.org/get-started/pytorch-2.0/#ask-the-engineers-20-live-qa-series 总览 特性 fastermore pythonicas dynamic as ever torch.compile&#xff0c;部分零件由C迁移到Python,加强torch.compile的新技术有TorchDynamo, AOTAutograd, PrimT…