13-netty基礎-手寫rpc-消費方生成代理-05

?netty系列文章:

01-netty基礎-socket
02-netty基礎-java四種IO模型
03-netty基礎-多路復用select、poll、epoll
04-netty基礎-Reactor三種模型
05-netty基礎-ByteBuf數據結構
06-netty基礎-編碼解碼
07-netty基礎-自定義編解碼器
08-netty基礎-自定義序列化和反序列化
09-netty基礎-手寫rpc-原理-01
10-netty基礎-手寫rpc-定義協議頭-02
11-netty基礎-手寫rpc-支持多序列化協議-03
12-netty基礎-手寫rpc-編解碼-04
13-netty基礎-手寫rpc-消費方生成代理-05
14-netty基礎-手寫rpc-提供方(服務端)-06

1 功能邏輯

在客戶端啟動的時候要為添加了BonnieRemoteReference注解的屬性生成一個代理類;代理類的主要功能:在spring容器加載完BeanDefinition之后,在Bean初始化之前,觸發生成代理類。
邏輯:

  1. 獲取到所有的BeanDefinition
  2. 拿到BeanDefinition對應的class
  3. 遍歷class下的所有被BonnieRemoteReference修飾的屬性(成員變量)
  4. 為被BonnieRemoteReference修飾的屬性,使用BeanDefinitionBuilder構建BeanDefinition,設置interfaceClass、serviceAddress、servicePort屬性,并放入到spring容器中,對象的類型為SpringRpcReferenceBean;
  5. SpringRpcReferenceBean實現FactoryBean接口,然后在getObject中返回代理對象。
  6. 編寫NettyClient代碼

補充:

Spring 的?FactoryBean?是一個工廠 bean 接口,用于自定義 bean 的創建邏輯。它的核心作用是:

  • 當容器獲取該 bean 時(如?getBean("xxx")),實際返回的是?getObject()?方法創建的對象,而非?SpringRpcReferenceBean?自身實例。
  • 常用于創建復雜對象(如遠程服務代理、數據庫連接池等)

2 重點代碼介紹

2.1 觸發生成代理類入口代碼

在spring容器加載BeanDefinition之后,在Bean初始化之前執行,實現接口BeanFactoryPostProcessor接口中postProcessBeanFactory方法即可
?

獲取所有的beanDefinitionNames
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();

獲取beanClassName對應的類信息
Class<?> clazz = ClassUtils.resolveClassName(beanClassName, this.classLoader);

獲取clazz上的所有屬性(成員變量)
ReflectionUtils.doWithFields(clazz, this::parseRpcReference);

當前這個field是否被BonnieRemoteReference注解修飾
BonnieRemoteReference remoteReference = AnnotationUtils.getAnnotation(field, BonnieRemoteReference.class);

生成SpringRpcReferenceBean的BeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SpringRpcReferenceBean.class)
放入屬性,遠程調用中需要的內容,比如是那個類,以及地址端口信息
builder.addPropertyValue("interfaceClass", field.getType());
builder.addPropertyValue("serviceAddress", rpcClientProperties.getServiceAddress());
builder.addPropertyValue("servicePort", rpcClientProperties.getServicePort());
BeanDefinition beanDefinition = builder.getBeanDefinition();
rpcRefBeanDefinitionMap.put(field.getName(), beanDefinition);

放入到spring容器中
registry.registerBeanDefinition(entry.getKey(), entry.getValue());

package com.bonnie.protocol.spring.reference;import com.bonnie.protocol.annotation.BonnieRemoteReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;import java.lang.reflect.Field;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;@Slf4j
public class SpringRpcReferencePostProcessor implements ApplicationContextAware, BeanClassLoaderAware, BeanFactoryPostProcessor {private ApplicationContext applicationContext;private ClassLoader classLoader;//保存發布的引用bean的信息private final Map<String, BeanDefinition> rpcRefBeanDefinitionMap = new ConcurrentHashMap<>();private RpcClientProperties rpcClientProperties;public SpringRpcReferencePostProcessor(RpcClientProperties rpcClientProperties) {this.rpcClientProperties = rpcClientProperties;}/*** 實現postProcessBeanFactory方法,spring容器加載了bean的定義文件之后, 在bean實例化之前執行* 1、將類型的存在的BonnieRemoteReference注解的屬性,構造BeanDefinition放在容器中,beanName是類的全限定名, BeanDefinition(類的全限定名,客戶端IP,客戶端端口號)* @param beanFactory* @throws BeansException*/@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {// 獲取到所有的beanDefinitionString[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();// 遍歷for (String beanDefinitionName : beanDefinitionNames) {BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);String beanClassName = beanDefinition.getBeanClassName();if (Objects.nonNull(beanClassName)) {// 獲取到這個類的所有fieldClass<?> clazz = ClassUtils.resolveClassName(beanClassName, this.classLoader);// 該方法遍歷class對象中的所有的field屬性,并且作為參數傳入到parseRpcReference方法中ReflectionUtils.doWithFields(clazz, this::parseRpcReference);}}// 將生成的BeanDefinition放入到容器中BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;Set<Map.Entry<String, BeanDefinition>> entries = this.rpcRefBeanDefinitionMap.entrySet();for (Map.Entry<String, BeanDefinition> entry : entries) {if (applicationContext.containsBean(entry.getKey())) {log.warn("SpringContext already register bean {}", entry.getKey());} else {registry.registerBeanDefinition(entry.getKey(), entry.getValue());log.info("registered RpcReferenceBean {} success", entry.getKey());}}}private void parseRpcReference(Field field) {// 當前這個field是否被BonnieRemoteReference注解修飾BonnieRemoteReference remoteReference = AnnotationUtils.getAnnotation(field, BonnieRemoteReference.class);// BonnieRemoteReference注解修飾if (Objects.nonNull(remoteReference)) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SpringRpcReferenceBean.class);builder.addPropertyValue("interfaceClass", field.getType());builder.addPropertyValue("serviceAddress", rpcClientProperties.getServiceAddress());builder.addPropertyValue("servicePort", rpcClientProperties.getServicePort());BeanDefinition beanDefinition = builder.getBeanDefinition();rpcRefBeanDefinitionMap.put(field.getName(), beanDefinition);}}@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {this.classLoader = classLoader;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}}

2.2?生成代理類代碼

上面會被BonnieRemoteReference修飾的屬性(Field)為生成SpringRpcReferenceBean對象,并添加相關的屬性。

實現FactoryBean接口,當spring獲取SpringRpcReferenceBean對象的時候,調用的就是里面的getObject對象,在getObject里面生成一個代理類,即可代理被BonnieRemoteReference修飾的類。

package com.bonnie.protocol.spring.reference;import lombok.Setter;
import org.springframework.beans.factory.FactoryBean;import java.lang.reflect.Proxy;/*** 創建SpringRpcReferenceBean的代理對象*/
@Setter
public class SpringRpcReferenceBean implements FactoryBean<Object> {private String serviceAddress;private Integer servicePort;private Class<?> interfaceClass;/*** 返回由工廠創建的目標Bean實例* @return* @throws Exception*/@Overridepublic Object getObject() throws Exception {System.out.println("代理類 serviceAddress "+serviceAddress);System.out.println("代理類 servicePort "+servicePort);System.out.println("代理類 interfaceClass "+interfaceClass);// 為BonnieRemoteReference生成一個代理類return Proxy.newProxyInstance(interfaceClass.getClassLoader(),new Class<?>[]{interfaceClass},new RpcInvokerProxy(serviceAddress, servicePort));}/*** 返回目標Bean的類型* @return*/@Overridepublic Class<?> getObjectType() {return this.interfaceClass;}}

2.3 代理類handler

這塊主要是在發生rpc調用的時候,組裝請求信息,并通過nettyClient向服務端發起連接并且發送請求。

package com.bonnie.protocol.spring.reference;import com.alibaba.fastjson.JSONObject;
import com.bonnie.protocol.core.Header;
import com.bonnie.protocol.core.RpcProtocol;
import com.bonnie.protocol.core.RpcRequest;
import com.bonnie.protocol.core.RpcResponse;
import com.bonnie.protocol.enums.ReqTypeEnum;
import com.bonnie.protocol.enums.RpcConstant;
import com.bonnie.protocol.enums.SerialTypeEnum;
import com.bonnie.protocol.netty.NettyClient;
import io.netty.channel.DefaultEventLoop;
import io.netty.util.concurrent.DefaultPromise;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class RpcInvokerProxy implements InvocationHandler {private String host;private Integer port;public RpcInvokerProxy(String host, Integer port) {this.host = host;this.port = port;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {/*** 構建發送的請求報文,首先去創建RequestHold類,在這個類定義一個原子自增的RequestId,* 在一個就是每次請求都會有結果,那么請求id和結果的關系要有一個映射關系*/RpcProtocol<RpcRequest> reqProtocol = new RpcProtocol<>();long requestId = RequestHolder.REQUEST_ID.incrementAndGet();System.out.println("生成的requestId:" + requestId);Header header = new Header();header.setMagic(RpcConstant.MAGIC);header.setSerialType(SerialTypeEnum.JAVA_SERIAL.getCode());header.setReqType(ReqTypeEnum.REQUEST.getCode());header.setRequestId(requestId);header.setLength(0);RpcRequest rpcRequest = new RpcRequest();rpcRequest.setClassName(method.getDeclaringClass().getName());rpcRequest.setMethodName(method.getName());rpcRequest.setParams(args);rpcRequest.setParameterTypes(method.getParameterTypes());reqProtocol.setHeader(header);reqProtocol.setContent(rpcRequest);// 發起遠程調用NettyClient nettyClient = new NettyClient(host, port);System.out.println("代理發送到服務端請求內容:" + JSONObject.toJSONString(reqProtocol));// new DefaultEventLoop(),是用來去執行監聽器的RpcFuture<RpcResponse> future = new RpcFuture<>(new DefaultPromise<RpcResponse>(new DefaultEventLoop()));// 在發起請求之前,添加映射關系到map中RequestHolder.REQUEST_MAP.put(header.getRequestId(), future);// 客戶端發送數據nettyClient.sendRequest(reqProtocol);// 通過promise,異步等待服務端發送數據來,不然就會一直在此等待// get方法得到的是RpcResponse類,然后調用getData方法獲取到數據return future.getPromise().get().getData();}
}

2.4 netty客戶端代碼

這塊主要包含創建客戶端、向服務端發起連接、發送請求,也會設置前文中自定義編解碼、序列化的操作

package com.bonnie.protocol.netty;import com.bonnie.protocol.code.BonnieDecoder;
import com.bonnie.protocol.code.BonnieEncoder;
import com.bonnie.protocol.core.RpcProtocol;
import com.bonnie.protocol.core.RpcRequest;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;@Slf4j
public class NettyClient {private final Bootstrap bootstrap;private final NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();private String serviceAddress;private Integer servicePort;public NettyClient(String serviceAddress, Integer servicePort) {log.info("開始初始化NettyClient======");bootstrap = new Bootstrap();bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {log.info("開始初始化RpcClientInitializer======");ch.pipeline().addLast(new LoggingHandler()).addLast(new BonnieEncoder()).addLast(new BonnieDecoder()).addLast(new RpcClientHandler());}});this.serviceAddress = serviceAddress;this.servicePort = servicePort;}/*** 發送數據* @param protocol* @throws Exception*/public void sendRequest(RpcProtocol<RpcRequest> protocol) {try {System.out.println(this.serviceAddress+ "===="+this.servicePort);final ChannelFuture channelFuture  = bootstrap.connect(this.serviceAddress, this.servicePort).sync();// 注冊一個監聽器,如果出問題就關閉groupchannelFuture.addListener(listener -> {if (channelFuture.isSuccess()) {log.info("connect rpc server {} success.",this.serviceAddress);} else {log.error("connect rpc server {} failed. ",this.servicePort);channelFuture.cause().printStackTrace();eventLoopGroup.shutdownGracefully();}});log.info("begin transfer data");// 向服務端發送數據channelFuture.channel().writeAndFlush(protocol);} catch (InterruptedException e) {e.printStackTrace();}}}

2.5 netty客戶端接收服務端響應數據

package com.bonnie.protocol.netty;import com.alibaba.fastjson.JSONObject;
import com.bonnie.protocol.core.RpcProtocol;
import com.bonnie.protocol.core.RpcResponse;
import com.bonnie.protocol.spring.reference.RequestHolder;
import com.bonnie.protocol.spring.reference.RpcFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;@Slf4j
public class RpcClientHandler extends SimpleChannelInboundHandler<RpcProtocol<RpcResponse>> {/*** 接收服務端響應數據* @param ctx* @param msg* @throws Exception*/@Overrideprotected void channelRead0(ChannelHandlerContext ctx, RpcProtocol<RpcResponse> msg) throws Exception {long requestId = msg.getHeader().getRequestId();log.info("接收服務端響應的結果====== requestId {} {}", requestId, JSONObject.toJSONString(msg));// 刪除映射關系RpcFuture<RpcResponse> future = RequestHolder.REQUEST_MAP.remove(requestId);// 我們之前說異步等待服務端發送數據過來,那么只要服務端發送數據過來,就會調用管道RpcClentHandler的read方法// 那么當初future.getPromise().get()如果不再阻塞獲取數據呢?就是通過給Promise中的Success設置值,同時會喚醒阻塞的線程// 一當喚醒線程, future.getPromise().get()就會不再阻塞,就獲取到服務端返回的數據future.getPromise().setSuccess(msg.getContent());}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/918425.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/918425.shtml
英文地址,請注明出處:http://en.pswp.cn/news/918425.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ThreadLocal有哪些內存泄露問題,如何避免?

每個Thread都有一個ThreadLocal.ThreadLocalMap的map&#xff0c;該map的key為ThreadLocal實例&#xff0c;它為一個弱引 用&#xff0c;我們知道弱引用有利于GC回收。當ThreadLocal的key null時&#xff0c;GC就會回收這部分空間&#xff0c;但是value卻不一 定能夠被回收&am…

從0到1學LangChain之Agent代理:解鎖大模型應用新姿勢

從0到1學LangChain之Agent代理&#xff1a;解鎖大模型應用新姿勢 本文較長&#xff0c;建議點贊收藏&#xff0c;以免遺失。更多AI大模型開發 學習視頻/籽料/面試題 都在這>>Github<< 什么是 LangChain Agent 代理 如果把大模型比作一個超級大腦&#xff0c;那么…

Spring Boot 2.6.0+ 循環依賴問題及解決方案

Spring Boot 2.6.0 循環依賴問題及解決方案 目錄 背景解決方案 1. 配置文件開啟循環依賴&#xff08;侵入性最低&#xff0c;臨時方案&#xff09;2. Lazy 延遲注入&#xff08;侵入性低&#xff0c;推薦優先嘗試&#xff09;3. 手動從容器獲取&#xff08;ApplicationContex…

本地代碼上傳Github步驟

1.注冊Github賬號 2.下載git客戶端 下載、安裝步驟可以參考網站&#xff1a;(6 封私信 / 10 條消息) 手把手教你用git上傳項目到GitHub&#xff08;圖文并茂&#xff0c;這一篇就夠了&#xff09;&#xff0c;相信你一定能成功&#xff01;&#xff01; - 知乎 3.在Github上…

5G NR 非地面網絡 (NTN) 5G、太空和統一網絡

非地面網絡 5G 和太空&#xff1a;對 NTN 測試與測量的影響NTN 基站測試與測量NTN 用戶設備的測試設備R&SSMW200A 矢量信號發生器R&SSMBV100B 矢量信號發生器總結5G 和太空&#xff1a;對 NTN 測試與測量的影響 5G 非地面網絡 (NTN) 是無線通信向全球性星基和機載通信…

少兒編程比賽(如藍橋杯、創意編程大賽等)的題目類型、知識點及難度總結

以下是針對主流少兒編程比賽&#xff08;如藍橋杯、創意編程大賽等&#xff09;的題目類型、知識點及難度總結&#xff0c;結合了Scratch和C等語言的真題分析&#xff0c;幫助備賽或教學參考&#xff1a; 一、基礎操作與交互題&#xff08;適合6~10歲&#xff09; 考察圖形化編…

SIFThinker: Spatially-Aware Image Focus for Visual Reasoning

SIFThinker: Spatially-Aware Image Focus for Visual Reasoning Authors: Zhangquan Chen, Ruihui Zhao, Chuwei Luo, Mingze Sun, Xinlei Yu, Yangyang Kang, Ruqi Huang 相關工作總結 視覺思維鏈推理 最近的研究表明&#xff0c;通過上下文學習逐步推理可以顯著提升大型…

學習嵌入式第二十五天

IO 1.概念 IO指input/outputLinux中一切皆文件IO的操作對象是文件 2.文件一段數據的集合文件通常存放在外存中&#xff0c;掉電后數據不丟失分類b(block&#xff0c;塊設備文件) 按塊掃描信息的文件。通常存儲類型的設備為塊設備文件。文件IOc(character&#xff0c;字符設備文…

本地部署接入 whisper + ollama qwen3:14b 總結字幕

1. 實現功能 M4-1 接入 whisper ollama qwen3:14b 總結字幕 自動下載視頻元數據如果有字幕&#xff0c;只下載字幕使用 ollama 的 qwen3:14b 對字幕內容進行總結 2.運行效果 &#x1f50d; 正在提取視頻元數據… &#x1f4dd; 正在下載所有可用字幕… [youtube] Extracting U…

【13-向量化-高效計算】

研究者能夠擴展神經網絡并構建非常大型網絡的原因之一&#xff0c;就是神經網絡可以被向量化&#xff0c;vectorized&#xff1b;可以非常高效地用矩陣地乘法實現。 事實上&#xff0c;并行計算硬件&#xff0c;例如GPU&#xff0c;一些CPU的功能&#xff0c;非常擅長進行非常大…

論文中PDF的公式如何提取-公式提取

Mathcheap - An AI-powered, free alternative to Mathpix Snip. 從PDF中截圖公式&#xff0c;之后 ctrl V 轉換成功 &#xff0c;提取成功 復制到word中&#xff0c;是這樣的 這顯然不是我們需要的。 可以使用Axmath 復制進去Axmath 就能正常顯示公式。 之后再插入word…

用 Flink SQL 和 Paimon 打造實時數倉:深度解析與實踐指南

1. 實時數倉的魅力&#xff1a;從離線到分鐘級的飛躍實時數倉&#xff0c;聽起來是不是有點高大上&#xff1f;其實它沒那么神秘&#xff0c;但確實能讓你的數據處理能力像坐上火箭一樣飆升&#xff01;傳統的離線數倉&#xff0c;像 Hadoop 生態的 Hive&#xff0c;動輒小時級…

【已解決】報錯:WARNING: pip is configured with locations that require TLS/SSL

一、問題背景二、問題分析1. SSL模塊缺失的本質2. Anaconda環境特點三、問題表現四、解決方案詳解1. 完整配置環境變量2. 添加環境變量的步驟3. 測試驗證五、實戰示例六、附加建議七、總結八、參考鏈接一、問題背景 在Windows 10系統中使用Python的包管理工具pip時&#xff0c…

Java項目基本流程(三)

一、頁面初始化階段&#xff08;加載即執行&#xff09;加載欄目列表&#xff08;同步請求&#xff09;發送同步 AJAX 請求到SearchChannel接口&#xff0c;獲取所有欄目數據。清空下拉框&#xff08;.channelid&#xff09;后&#xff0c;先添加 “全部” 選項&#xff0c;再循…

鷓鴣云光伏仿真:項目前期決策的“數據明燈”

曾有一處光伏項目&#xff0c;在精心籌備數月后終于建成&#xff0c;卻在運行初期即因未充分評估山體遮擋影響&#xff0c;導致實際發電量較預期大幅降低近一成。前期決策中的微小疏漏&#xff0c;往往成為項目經濟性與可行性的致命傷。而鷓鴣云光伏仿真軟件正是一盞照亮前路的…

開發指南129-基礎類-BaseController

所有接口都需要繼承BaseControllerBaseController里有很多有用的方法&#xff0c;現舉例最重要的幾個&#xff1a;1、getURI返回接口地址&#xff0c;就是PostMapping或GetMapping中定義的接口地址。常用于返回值中&#xff0c;例如接口的異常處理&#xff1a;try {// 處理邏輯…

C++高頻知識點(十八)

文章目錄86. C多線程中&#xff0c;鎖的實現方式有哪些&#xff1f;1. 互斥鎖&#xff08;Mutex&#xff09;2. 遞歸互斥鎖&#xff08;Recursive Mutex&#xff09;3. 讀寫鎖&#xff08;Shared Mutex&#xff09;4. 自旋鎖&#xff08;Spinlock&#xff09;5. 條件變量&#…

【C語言強化訓練16天】--從基礎到進階的蛻變之旅:Day1

&#x1f525;個人主頁&#xff1a;草莓熊Lotso &#x1f3ac;作者簡介&#xff1a;C研發方向學習者 &#x1f4d6;個人專欄&#xff1a; 《C語言》 《數據結構與算法》《C語言刷題集》《Leetcode刷題指南》 ??人生格言&#xff1a;生活是默默的堅持&#xff0c;毅力是永久的…

【軟考中級網絡工程師】知識點之 TCP 協議深度剖析

目錄一、TCP 協議簡介二、TCP 協議的特點2.1 面向連接2.2 可靠性高2.3 擁塞控制2.4 全雙工通信2.5 高效性2.6 支持多種應用協議2.7 可靠的錯誤恢復三、TCP 協議的工作機制3.1 三次握手建立連接3.2 數據傳輸3.3 四次揮手關閉連接四、TCP 協議的數據包格式五、TCP 協議在實際應用…

操作系統1.5:操作系統引導

目錄 總覽 什么是操作系統引導&#xff1f; 磁盤里邊有哪些相關數據? 操作系統引導(開機過程&#xff09; 總覽 什么是操作系統引導&#xff1f; 操作系統引導(boot)——開機的時候&#xff0c;怎么讓操作系統運行起來? 磁盤里邊有哪些相關數據? 一個剛買來的磁盤(硬…