当前位置: 首页 > news >正文

【Flutter入门到进阶】Dart进阶篇---DartVM单线程设计原理

1 虚拟机的指令执行设计

1.1 虚拟机的分类

        基于栈的虚拟机,比如JVM虚拟机

        基于寄存器的虚拟机,比如Dalvik虚拟机

1.2 虚拟机的概念

        首先问一个基本的问题,作为一个虚拟机,它最基本的要实现哪些功能?

他应该能够模拟物理CPU对操作数的移进移出,理想状态下,它应该包含如下概念:

(1)将源码编译成VM指定的字节码。

(2)包含指令和操作数的数据结构(指令用于处理操作数作何种运算)。

(3)一个为所有函数操作的调用栈。

(4)一个“指令指针(Instruction Point —IP)”:用于指向下一条将要执行的指令。

(5)一个虚拟的“CPU”–指令的派发者:

        1)取指:获取下一条指令(通过IP获取)

        2)译码:对指令进行翻译,将要作何种操作。

        3)执行:执行指令。

        以上是CPU的三级流水线操作,实际上五级流水线还包括回写,即把执行后生成的结果回写进存储器。

有两种基本的方法实现虚拟机:

        基于Stack的和基于Register的,比如基于Stack的虚拟机有JVM、.net的CLR、还有最近热门的以太坊EVM,这种基于Stack实现虚拟机是一种广泛的实现方法。而基于Register的虚拟机有Lua VM(是Lau编程语言的虚拟机)和Dalvik VM。

        这两种虚拟机实现的不同主要在于操作数和结果的存储和检索机制不一样。

        注意Dalvik因为是基于寄存器的虚拟机,其编译结果文件的格式和class文件是完全不同的

1.3 核心概念

1.3.1 最终的指令的执行的表现形式是

        ADD P1 P2--- P1 P2为操作数(操作数的本质是地址)

1.3.2 核心需要理解的是,不同芯片,他们的线路设计规格不一致(含单片机)

        1.常规CPU Intel  ARM AMD等:32、64

        2.单片机:8、16

1.4 Stack-Based虚拟机(最大特征就是一堆pop push)

        Stack-Based虚拟机(最大特征就是一堆pop push)

        如下就是一个典型的计算20+7在栈中的计算过程:

        指令执行过程为
           1、POP 20
           2、POP 7
           3、ADD   // 此时单条指令就可以了,不需要再跟 20, 7, result
           4、PUSH result

1.5 Register-Based虚拟机(最大特征就是较多操作数)

        基于寄存器的虚拟机,它们的操作数是存放在CPU的寄存器的。没有入栈和出栈的操作和概念。但是执行的指令就需要包含操作数的地址了,也就是说,指令必须明确的包含操作数的地址,这不像栈可以用栈指针去操作。比如如下的加法操作:

1.5.1 指令执行过程

        ADD R1, R2, R3 ;就一条指令搞定了。

1.6 总结

1.6.1 因为计算机芯片规格不一致

        像常规Intel ARM AMD这些芯片能够支持比较多的操作,因为一般他们的线路设计都是32以上

        像单片机这种,最便宜的单片机可能在线路上的设计是8

1.6.2 因此考虑到设备上的兼容

        JAVA的方案

                栈---兼容最小8的指令执行(能兼容单片机等设备)

        Android的方案

                寄存器---无需兼容,android设备一般依赖于高性能芯片

1.7 思考?

        基于上篇了解指令执行过程中的问题,那么需要考虑线程资源

        什么是线程资源?

                参考:C++ chapter 11  STL之线程管理

                        https://www.processon.com/view/link/62ab388e5653bb5256dae2fd

                PS:最好去把这节课看了

        线程资源本质

                1.指令的最终执行由OS分配

                2.虚拟机需要做的事情是将指令编译完成扔给OS的内核

                3.完成指令提交通过OS的系统接口完成

                4.内核在处理代码时会对代码进行组织,即task_struct对象

                5.在内核角度,一个task_struct其实就是一个进程|线程

                        对于linux而言其实进程与线程没什么太大的区别, 就是信息上有一点差异

                6.在内核角度,提供pthread_create这种形式让我们动态生成一个线程资源,本质就是通过该函数告诉linux,哥们开一个新的task_struct,代码就是{自己指定}这一段

2 回顾:GC的实现方案与思路

2.1 虚拟机内存管理思路

2.1.1 说明

        虚拟机进行内存管理,本质上是自己提前申请一块内存,然后自己来维护这一块内存,具体案例如下

2.1.2 示例

#include <stdio.h> #include <malloc.h> 
#define EDEN_SIZE 1024 * 1024 * 16 int main() {     //堆区初始化大小16M,实际开辟 16 * 3 48M     //其中16M为EDEN 32M为堆区     char* head = malloc(EDEN_SIZE * 3);     printf("申请空间位置%x ~ %x\n",&head,(&head + (EDEN_SIZE * 3)));     //年轻代设计分配     int yong_begin = &head ;     int yong_end = &head + EDEN_SIZE;     int eden_begin = &yong_begin;     int eden_end = &yong_begin + (int)(EDEN_SIZE * 0.8-1);     //survivor区分配     int survivor1_begin =&yong_begin + (int)(EDEN_SIZE * 0.8);     int survivor1_end = &yong_begin + (int)(EDEN_SIZE * 0.9 - 1);     int survivor2_begin =&yong_begin + (int)(EDEN_SIZE * 0.9);     int survivor2_end =&yong_begin + (int)(EDEN_SIZE);     //老年代设计     int old_begin = &head + EDEN_SIZE;     int old_end = old_begin + EDEN_SIZE * 3;     printf("eden空间分配区间%x ~ %x\n",eden_begin,eden_end);     printf("survivor1空间分配区间%x ~ %x\n",survivor1_begin,survivor1_end);     printf("survivor2空间分配区间%x ~ %x\n",survivor2_begin,survivor2_end);     printf("eden空间分配区间%x ~ %x\n",old_begin,old_end);     //java代码:int i = 10;     //指针标记     char* index = &head;     printf("当前地址%x\n",index);     int i = 20;    //length 4 value 20     char* val = 20;     int length = 4;     char* v1Index = index;     *index = val;     index += length;     printf("1当前分配后位置%x\n",index);     char* v2Index = index;     char* val2 = 30;     int length2 = 4;     *index = val2;     index += length2;     printf("2当前分配后位置%x\n",index);     int value = *v1Index;     printf("value:%d\n",value);     int value2 = *v2Index;     printf("value2:%d\n",value2);     return 0; 
}

2.1.3 运行结果

2.2 内存碎片处理方案

        2.2.1 这种自己来写,首先需要考虑的是内存碎片的问题

        2.2.2 传统处理方案一般是依赖于两个阶段

                1.标记要处理的数据对象

                2.清空要处理的数据对象(PS:但是对于内存数据管理来讲不进行情况,只进行覆盖)

       2.2.3  业内处理算法方案

         1、垃圾确认算法--标记阶段算法

                1)引用计数算法 

                2)GCRoot可达性分析算法

        2、清除垃圾算法--清除阶段算法

1)清除算法:直接删

        有碎片问题

2)复制算法:牺牲空间,换碎片

无碎片问题

3)压缩/整理算法:牺牲时间,换碎片

无碎片问题

        3、三种算法的性能指标对比

        效率上来说,复制算法最快,但是内存浪费最多而为了尽量兼顾上面三个指标,标记整理算法相对平滑一些,但是效率上不仅如此任意,他比复制算法多了一个标记阶段,比清除多了一个整理内存阶段

2.3 分代设计的思路

难道没有一种最优算法吗?

2.3.1 分代收集算法

        为了满足垃圾回收的效率最优性,所以分代收集算法应运而生

        分代收集算法基于一个事实:不同的对象生命周期是不一样的,因此,不同生命周期的对象可以采取不同的手机方式,以便于提高回收效率。一般是把堆分为新生代和老年代,这样就可以根据各个年代的特点使用不同回收算法,相对提高效率

        在系统运行过程汇总,会产生大量对象,其中有些对象是业务信息相关,如HTTP请求的Session、线程、Socket连接等对象,这类对象跟业务挂钩,因此生命周期长,还有一部分是运行过程汇总生成的临时变量,这些对象生命周期短,比如:String,这些对象甚至只使用一次即可回收

2.3.2 分代算法衍生后

1)eden作为生产地,运用清除算法

2)但是可能存在需要存活的对象数据,所以预留一块数据进行保存存活数据(old)

3)为了保证不产生碎片,且这块数据的体量实际不大,所以这里采用复制算法
 

4)指针碰撞!

5)处理方案:

​        维护一个空闲列表,解决碎片化

​            算法:清除、复制、整理算法

​            如何自己来设计这个方案

​                    70-99% - 一次性  年轻人

​                    1-30% 长久       老年人

​                    建议新老年代占比:90% 10%

​            年青代

​                eden(生产)-survivor(缓冲)

​            老年代

​                eden-清除算法  all clear

​                survivor - 复制  缓冲

​                old - 清理

2.4 内存回收方案

2.4.1 分区

2.4.2 增量

3 Dart 内存结构设计

3.1 堆栈区设计思路

3.1.1 JAVA

3.1.2 Dart

3.2.3 说明

1.JAVA采取的方案

        栈区独立内存开销,维持指令运行

        堆区数据共享

        优点

                无额外数据开销,线程间共享数据直接找共享堆区

        缺点

                因为共享,会出现临界区问题,需要引入锁机制处理

2.Dart采取的方案

        GC隔离

        如上图展示,在内存管理设计角度,将栈,堆,等数据进行完全隔离设计,包括GC线程也进行隔离

2.2 线程间数据传递问题

2.2.1 JAVA

        直接共享堆,但是需要设计锁业务

2.2.2 DART

        独立堆栈,但是需要设计隔离区的通信机制

4 事件轮询机制

4.1 事件轮训机制建立的目的

        单线程内部建立异步体系

        这种方案一般适用于有事件驱动下的业务体系(一般指UI体系)

        服务体系一般是多线程

        这里设计的目的请注意:

        1.dart出现的目的是为了去对标JS做所谓的跨平台

        2.后续切入到移动端其实他的本质也不变,因为是为了做UI的跨平台

        3.既然是UI的开发那么整个业务运载的模型肯定是事件驱动模型

        4.在android当中,是运用handler来支撑事件驱动模型

4.2 程序运行设计模型

4.2.1 单线程模型

        单线程同步模型中,任务依次执行,如果某个任务因为I/O阻塞了,其他任务也只能等待,直到前面的任务执行完成后才能执行。这样3个没有依赖关系的任务,需要互相等待顺序执行,大大降低了执行效率。

说明

        这种是一根线程的状态,很多单片机是这种,有耗时操作后续就等着

4.2.2 多线程模型

        多线程模型中,任务分别在独立的线程中执行,不需要互相等待。在多处理器系统上可以并行处理,在单处理器系统上可以交替执行。这种方式更有效率,但是开发者需要保护共享资源,防止其被多个线程同时访问。多线程程序更加难以推断,因为这类程序不得不通过线程同步机制如锁、可重入函数、线程局部存储或者其他机制来处理线程安全问题,如果实现不当就会导致出现微妙且令人痛不欲生的bug。

        典型的多线程状态,如果存在分支功能,让另外一个线程去进行维护

4.2.3 事件驱动模型

        事件驱动模型中,3个任务交替执行,但仍然在一个单独控制的线程中。当处理一个I/O或其他昂贵的操作时,注册一个回调到事件队列中,当I/O操作完成后继续执行。回调描述了如何处理某个事件。事件队列轮询事件,事件发生后将事件分派给事件处理器进行处理。这种方式让程序尽可能执行而不需要额外的线程。使用事件驱动模型开发者不需要担心线程安全的问题。

        单线程下的无等待形式运行

        1.传统单线程如果需要等待事件的响应去处理某个业务,那么只能等待一个事件,其他需要等这一个事件处理完成后才响应

4.3 事件轮训机制详解

4.3.1 Dart实现过程

图例

说明

        一个Dart应用有一个消息循环和两个消息队列-- event队列和microtask队列。

        event队列包含所有外来的事件:I/O,mouse events,drawing events,timers,isolate之间的message等。

        microtask 队列在Dart中是必要的,因为有时候事件处理想要在稍后完成一些任务但又希望是在执行下一个事件消息之前。

        event队列包含Dart和来自系统其它位置的事件。但microtask队列只包含来自当前isolate的内部代码。

        正如下面的流程图,当main方法退出后,event循环就开始它的工作。首先它会以FIFO的顺序执行micro task,当所有micro task执行完后它会从event 队列中取事件并执行。如此反复,直到两个队列都为空。

        当一个Dart应用开始的标志是它的main isolate执行了main方法。当main方法退出后,main isolate的线程就会去逐一处理消息队列中的消息。

        正如下面的流程图,当main方法退出后,event循环就开始它的工作。首先它会以FIFO的顺序执行micro task,当所有micro task执行完后它会从event 队列中取事件并执行。如此反复,直到两个队列都为空。

        当一个Dart应用开始的标志是它的main isolate执行了main方法。当main方法退出后,main isolate的线程就会去逐一处理消息队列中的消息

4.3.2 JAVA模拟示例

Task

package com.kerwin.event; 
public abstract class Task {     public abstract void run(); 
}

ScheduleMicrotask

package com.kerwin.event; 
public class ScheduleMicrotask extends Task {     @Override     public void run() {} 
}

Timer

package com.kerwin.event; 
public class Timer extends Task {     @Override     public void run() {} 
}

Main

package com.kerwin.event; 
import java.util.Queue; 
import java.util.concurrent.LinkedBlockingQueue; public class Main {     //微任务队列-优先级高private static Queue<ScheduleMicrotask> scheduleMicrotasks 
= new LinkedBlockingQueue<>();         //事件队列-优先级低     private static Queue<Timer> timers = new LinkedBlockingQueue<>();     public static void processAsync(){while(!scheduleMicrotasks.isEmpty() || !timers.isEmpty()){Task task = null;             if((task = scheduleMicrotasks.poll()) != null){}else if((task = timers.poll()) != null){}             task.run();         }     }   public static void main(String[] args){         System.out.println("main start!");         timers.offer(new Timer(){            @Override             public void run() {                 System.out.println("timer - event - A");                 scheduleMicrotasks.offer(new ScheduleMicrotask(){                     @Override                     public void run() {                         System.out.println("ScheduleMicrotask - A - in Timer A");      }                 });                 scheduleMicrotasks.offer(new ScheduleMicrotask(){                     @Override                     public void run() {                         System.out.println("ScheduleMicrotask - B - in Timer A");}                 });             }         });       scheduleMicrotasks.offer(new ScheduleMicrotask(){             @Override             public void run() {                 System.out.println("ScheduleMicrotask - C - in MAIN ");                 timers.offer(new Timer(){                     @Override                     public void run() {                         System.out.println("timer - event - B - in ScheduleMicrotask - C ");                     }                 });             }         });         System.out.println("main end!");         processAsync();     } 
}

4.4 示例

import 'dart:async';void main(){print("main begin");Timer.run(() {print("timer - event - A");scheduleMicrotask(() {print("ScheduleMicrotask - A - in Timer A");});scheduleMicrotask(() {print("ScheduleMicrotask - B - in Timer A");});});scheduleMicrotask(() {print("ScheduleMicrotask - C - in MAIN ");Timer.run(() {print("timer - event - B - in ScheduleMicrotask - C ");});});print("main end");
}

 

 

 

相关文章:

【Flutter入门到进阶】Dart进阶篇---DartVM单线程设计原理

1 虚拟机的指令执行设计 1.1 虚拟机的分类 基于栈的虚拟机&#xff0c;比如JVM虚拟机 基于寄存器的虚拟机&#xff0c;比如Dalvik虚拟机 1.2 虚拟机的概念 首先问一个基本的问题&#xff0c;作为一个虚拟机&#xff0c;它最基本的要实现哪些功能&#xff1f; 他应该能够模拟…...

Dem和NvM(NVRAM Manager)的交集

NVRAM&#xff08;NvM&#xff09;提供了在NVRAM中存储数据Block的机制。 NVRAM Block&#xff08;最大大小取决于配置&#xff09;被分配给Dem&#xff0c;并由Dem实现事件状态信息和相关数据的永久存储&#xff08;例如通电复位&#xff09;。 ECU 状态管理器&#xff08;Ec…...

AI神经网络CNN/RNN/DNN/SNN的区别对比

@版权声明: 本文由 ChatGpt 创作; BiliBili: https://www.bilibili.com/video/BV17D4y1P7pM/?share_source=copy_web&vd_source=6d217e0ff6387a749dc570aba51d36fd 引言 随着人工智能技术的发展,神经网络作为人工智能的核心技术之一,被广泛应用于图像识别、语音识别、…...

【JavaWeb】一文学会JPA

✅✅作者主页&#xff1a;&#x1f517;孙不坚1208的博客 &#x1f525;&#x1f525;精选专栏&#xff1a;&#x1f517;JavaWeb从入门到精通&#xff08;持续更新中&#xff09; &#x1f4cb;&#x1f4cb; 本文摘要&#xff1a;本篇文章主要介绍JPA的概念、注解实现ORM规范…...

【安卓逆向】APK修改与反编译回编译

【安卓逆向】反编译修改APK回编译使用工具流程步骤Apktool相关安装与使用常用命令备查APK签名命令备查实战练习反编译查看修改的地方使用Apktool反编译得到产物文件夹并进行修改回编APK实用场景在日常开发我们可能需要替换某些资源或者修改某些代码&#xff0c;但是我们没有源码…...

【计组笔记04】计算机组成原理之多模块存储器、Cache高速缓存存储器、Cache地址映射

这篇文章,主要介绍计算机组成原理之多模块存储器、Cache高速缓存存储器、Cache地址映射。 目录 一、双口RAM和多模块存储器 1.1、存取周期 1.2、双口RAM 1.3、多模块存储器...

英语基础-状语的应用

1. 非谓语动词作状语 1. 试着翻译下列句子 当他是一个小孩子的时候&#xff0c;他很喜欢玩电脑游戏。 When he was a child, he liked playing computer games. 如果他通过考试&#xff0c;他妈妈就会给他买一台新电脑。 If he passes the examination, his mother will b…...

发表论文需要注意的两点(建议收藏)

在学习人工智能的过程中&#xff0c;论文有着重要的作用&#xff0c;无论是深入学术科研&#xff0c;还是毕业找工作&#xff0c;都离不开发表论文这一步骤&#xff0c;所以今天就和大家分享一些关于论文发表的经验&#xff0c;希望对大家有所帮助。 为什么要早点发表论文&…...

ISTQB-TM-大纲

1. 测试过程 1.1 简介 在 ISTQB 软件测试基础级认证大纲中已描述了基本的测试过程包括以下活动&#xff1a; 计划和控制分析和设计实施和执行评估出口准则和报告测试结束活动 基础级大纲认同这些活动虽然有逻辑顺序&#xff0c;但过程中的某些活动可能重叠&#xff0c;或并行…...

Java SPI 机制详解

在面向对象的设计原则中&#xff0c;一般推荐模块之间基于接口编程&#xff0c;通常情况下调用方模块是不会感知到被调用方模块的内部具体实现。一旦代码里面涉及具体实现类&#xff0c;就违反了开闭原则。如果需要替换一种实现&#xff0c;就需要修改代码。 为了实现在模块装…...

腾讯前端经典react面试题(附答案)

React 性能优化在哪个生命周期&#xff1f;它优化的原理是什么&#xff1f; react的父级组件的render函数重新渲染会引起子组件的render方法的重新渲染。但是&#xff0c;有的时候子组件的接受父组件的数据没有变动。子组件render的执行会影响性能&#xff0c;这时就可以使用s…...

Go语言基础(十五):垃圾回收机制(三色标记)

文章目录一、标记清除&#xff08;三色标记&#xff09;大致原理1、标记细节2、root对象二、垃圾回收触发机制垃圾回收&#xff08;Garbage Collection&#xff09;&#xff0c;是一种自动管理内存的机制。传统编程语言&#xff08;如C/C&#xff09;需要开发者对无用内存资源进…...

一文了解build.gradle配置

Gradle 参考官方文档&#xff1a;https://developer.android.com/studio/build?hlzh-cn#groovy settings.gradle 存放于项目根目录下&#xff0c;此设置文件会定义项目级代码库设置&#xff0c;并告知 Gradle 在构建应用时应将哪些模块包含在内 接下来将以一个简单的 settin…...

【Redis 高级】- 持久化 - RDB

【Redis 高级】- 持久化 - RDB &#x1f451;什么是持久化呢&#xff1f; 那当然是够持久呀&#xff0c;这个持久如果在你不主动去删除的情况下&#xff0c;它就一直存在的。 &#x1f3b7;那么这有什么用呢&#xff1f; 举个栗子&#xff1a;我们在用 PowerPoint 在写价值 …...

SpringSecurity的安全认证的详解说明(附完整代码)

SpringSecurity登录认证和请求过滤器以及安全配置详解说明 环境 系统环境&#xff1a;win10 Maven环境&#xff1a;apache-maven-3.8.6 JDK版本&#xff1a;1.8 SpringBoot版本&#xff1a;2.7.8 根据用户名密码登录 根据用户名和密码登录&#xff0c;登录成功后返回Token数据…...

详解制造业业务数据模型

业务数据在企业数字化转型或单体应用的开发中都是至关重要的。站在跨业务跨部门的企业数字化转型角度&#xff0c;离不开业务架构的设计&#xff0c;详细的业务领域和业务数据模型是后续应用架构和数据架构的必要输入。站在单部门单场景的信息化角度&#xff0c;应用程序的需求…...

BigDecimal使用注意避坑

目录一. BigDecimal的初始化精度丢失问题二. BigDecimal在进行除法运算时需设置精度,否则对于除不尽的情况会抛出异常三. 不要使用BigDecimal的equals方法比较大小, 否则可能会因为精度问题导致比较结果和预期的不一致在java.math包中提供了对大数字的操作类&#xff0c;用于进…...

windows环境下,vue启动项目后打开chrome浏览器

前言&#xff1a;关于vue启动后打开chrome浏览器&#xff0c;我查了很多资料&#xff0c;方案如下&#xff1a; 1、增加环境变量BROWSER为chrome&#xff08;试了没效果&#xff09; 2、设置系统的默认浏览器为chrome&#xff08;应该可以&#xff0c;但没试&#xff1b;因为…...

SpringBoot2.X整合ClickHouse项目实战-从零搭建整合(三)

一、ClickHouseSpringBoot2.XMybatisPlus整合搭建 二、需求描述和数据库准备 三、ClickHouse统计SQL编写实战和函数使用 四、ClickHouseSpringBoot2.X案例-基础模块搭建 controller/request层 mapper层 model层 service层 五、ClickHouseSpringBoot2.X案例-数据统计接口 …...

学海记录项目测试报告

⭐️前言⭐️ 本篇文章是博主基于学海记录的个人项目所做的测试报告&#xff0c;用于总结运用自动化测试技术&#xff0c;应用于自己的项目。 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主将持续更新学习记录…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...