Android启动流程

目标:

1.zygote怎么创建进程?

fork进程:fork复制一份进程,对应的返回值有两个,pid=0,子进程;pid!=0父进程

写时拷贝: fork的子进程以后,子进程和父进程公用一份内存空间,只有子进程数据有修改时,才将内存复制一份新的写入。

2.init进程有什么作用?

3.systemserver为什么不在init中启动?

一、安卓启动流程

启动流程图

1.1 Boot Rom

安卓上电以后,引导芯片从ROM加载引导程序BootLoader到RAM,然后开始执行

1.2 BootLoader

BootLoader是什么?

相当于Windows BIOS

引导操作系统启动,启动安卓的第一个进程idle. 

1.3 idle进程

IDLE进程初始化进程管理、内存管理,加载Binder Driver、Display、Camera Driver灯相关工作。

Idle会创建两个进程,一个是kthreadd进程(内核进程)和init进程。

kthreadd进程:内核进程的鼻祖。

1.4 init进程

init进程是用户空间的鼻祖。init会fork创建zygote进程。

1.5 zygote进程

zygote进程是Java进程的鼻祖。所有的Java进程都是由zygote创建的(fork)。启动时zygote会

fork出大儿子 SystemServer进程。

1.6 SystemServer

SystemServer会启动一系列Android系统服务。

1.7 App进程

zygote利用AMS创建出app进程。

二、init进程的启动

2.1 启动入口

在/kernal/common/init/main.c

kernal_init方法

try_to_run_init_process("/bin/init")

启动init进程。

2.2 init进程main方法

关键方法:init会执行多次。

第一阶段:FirstStageMain:

  • 挂载磁盘文
  • 重定向输入输出
  • 初始化内核日志输出
  • 启动selinux_setup:Selinux初始化

安卓采用的Selinux安全策略

第二阶段:SecondStageMain

PropertyInit: 初始化属性域

初始化子进程终止信号:InstallSignalFDHandler(&epoll)

处理僵尸进程

什么是僵尸进程?

匹配命令和函数关系:GetBuiltinFunctionMap

解析Init.rc: LoadBootScripts()

创建解析器createParser,进行对应配置解析。

解析成功后,执行脚本命令。

进入while循环,循环等待启动进程。

am.ExecuteOneCommand

2.3 init主要功能

init主要功能

  1. 挂载文件
  2. 设置Selinux,开启安全策略
  3. 开启属性服务,注册到epoll
  4. 解析init启动脚本init.rc
  5. 循环处理脚本--》启动zygote
  6. 循环等待

三、zygote进程

zygote横跨C/C++和Java。

3.1 zygote启动入口

app_process(app_process32、app_process64)

 

入口方法:app_main.cpp的main方法

安卓运行时环境是zygote启动的。
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));

int main(int argc, char* const argv[])
174  {
175      if (!LOG_NDEBUG) {
176        String8 argv_String;
177        for (int i = 0; i < argc; ++i) {
178          argv_String.append("\"");
179          argv_String.append(argv[i]);
180          argv_String.append("\" ");
181        }
182        ALOGV("app_process main with argv: %s", argv_String.string());
183      }
184  
185      AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
186      // Process command line arguments
187      // ignore argv[0]
188      argc--;
189      argv++;
190  
191      // Everything up to '--' or first non '-' arg goes to the vm.
192      //
193      // The first argument after the VM args is the "parent dir", which
194      // is currently unused.
195      //
196      // After the parent dir, we expect one or more the following internal
197      // arguments :
198      //
199      // --zygote : Start in zygote mode
200      // --start-system-server : Start the system server.
201      // --application : Start in application (stand alone, non zygote) mode.
202      // --nice-name : The nice name for this process.
203      //
204      // For non zygote starts, these arguments will be followed by
205      // the main class name. All remaining arguments are passed to
206      // the main method of this class.
207      //
208      // For zygote starts, all remaining arguments are passed to the zygote.
209      // main function.
210      //
211      // Note that we must copy argument string values since we will rewrite the
212      // entire argument block when we apply the nice name to argv0.
213      //
214      // As an exception to the above rule, anything in "spaced commands"
215      // goes to the vm even though it has a space in it.
216      const char* spaced_commands[] = { "-cp", "-classpath" };
217      // Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
218      bool known_command = false;
219  
220      int i;
221      for (i = 0; i < argc; i++) {
222          if (known_command == true) {
223            runtime.addOption(strdup(argv[i]));
224            // The static analyzer gets upset that we don't ever free the above
225            // string. Since the allocation is from main, leaking it doesn't seem
226            // problematic. NOLINTNEXTLINE
227            ALOGV("app_process main add known option '%s'", argv[i]);
228            known_command = false;
229            continue;
230          }
231  
232          for (int j = 0;
233               j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
234               ++j) {
235            if (strcmp(argv[i], spaced_commands[j]) == 0) {
236              known_command = true;
237              ALOGV("app_process main found known command '%s'", argv[i]);
238            }
239          }
240  
241          if (argv[i][0] != '-') {
242              break;
243          }
244          if (argv[i][1] == '-' && argv[i][2] == 0) {
245              ++i; // Skip --.
246              break;
247          }
248  
249          runtime.addOption(strdup(argv[i]));
250          // The static analyzer gets upset that we don't ever free the above
251          // string. Since the allocation is from main, leaking it doesn't seem
252          // problematic. NOLINTNEXTLINE
253          ALOGV("app_process main add option '%s'", argv[i]);
254      }
255  
256      // Parse runtime arguments.  Stop at first unrecognized option.
257      bool zygote = false;
258      bool startSystemServer = false;
259      bool application = false;
260      String8 niceName;
261      String8 className;
262  
263      ++i;  // Skip unused "parent dir" argument.
264      while (i < argc) {
265          const char* arg = argv[i++];
266          if (strcmp(arg, "--zygote") == 0) {
267              zygote = true;
268              niceName = ZYGOTE_NICE_NAME;
269          } else if (strcmp(arg, "--start-system-server") == 0) {
270              startSystemServer = true;
271          } else if (strcmp(arg, "--application") == 0) {
272              application = true;
273          } else if (strncmp(arg, "--nice-name=", 12) == 0) {
274              niceName.setTo(arg + 12);
275          } else if (strncmp(arg, "--", 2) != 0) {
276              className.setTo(arg);
277              break;
278          } else {
279              --i;
280              break;
281          }
282      }
283  
284      Vector<String8> args;
285      if (!className.isEmpty()) {
286          // We're not in zygote mode, the only argument we need to pass
287          // to RuntimeInit is the application argument.
288          //
289          // The Remainder of args get passed to startup class main(). Make
290          // copies of them before we overwrite them with the process name.
291          args.add(application ? String8("application") : String8("tool"));
292          runtime.setClassNameAndArgs(className, argc - i, argv + i);
293  
294          if (!LOG_NDEBUG) {
295            String8 restOfArgs;
296            char* const* argv_new = argv + i;
297            int argc_new = argc - i;
298            for (int k = 0; k < argc_new; ++k) {
299              restOfArgs.append("\"");
300              restOfArgs.append(argv_new[k]);
301              restOfArgs.append("\" ");
302            }
303            ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
304          }
305      } else {
306          // We're in zygote mode.
307          maybeCreateDalvikCache();
308  
309          if (startSystemServer) {
310              args.add(String8("start-system-server"));
311          }
312  
313          char prop[PROP_VALUE_MAX];
314          if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
315              LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
316                  ABI_LIST_PROPERTY);
317              return 11;
318          }
319  
320          String8 abiFlag("--abi-list=");
321          abiFlag.append(prop);
322          args.add(abiFlag);
323  
324          // In zygote mode, pass all remaining arguments to the zygote
325          // main() method.
326          for (; i < argc; ++i) {
327              args.add(String8(argv[i]));
328          }
329      }
330  
331      if (!niceName.isEmpty()) {
332          runtime.setArgv0(niceName.string(), true /* setProcName */);
333      }
334  
335      if (zygote) {
336          runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
337      } else if (className) {
338          runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
339      } else {
340          fprintf(stderr, "Error: no class name or --zygote supplied.\n");
341          app_usage();
342          LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
343      }
344  }

启动参数解析:

启动zygote进程

3.2 AndroidRuntime.start

核心进程保护机制:

zygote或者init进程、systemserver进程杀死的话,会重启安卓系统。

zygote的native启动处理

3.2.1 zygote启动虚拟机

进程和虚拟机什么关系?哪个大?

zygote进程--》startVm启动虚拟机

虚拟机运行在进程中,虚拟机代码实现了这个功能:内存管理。

一个进程会创建一个虚拟机。

3.2.2 注册JNI

Java代码不能直接访问C++代码,需要通过JNI桥梁进行访问。

注册JNI就是建立桥梁,将java的本地方法和native的方法关联起来。

3.2.3 调用ZygoteInit.main方法

AndroidRuntime调用ZygoteInit.main方法。ZygoteInit.java是Java代码。

JNI实现了C++和Java相互调用的桥梁。 

3.3 ZygoteInit.main 

ZygoteInit处理zygote的java启动流程。进程不分native进程和Java进程。

ZygoteInit.main主要处理流程:

1)preload预处理(加快进程启动)

2)启动SystemServer进程

3)启动死循环,接收AMS的app启动请求

3.4 zygote启动总结

main方法中创建ZygoteServer。

3.3.1 ZygoteServer

Zygote的socket Server端。

创建一个Socket服务器。

此处为什么不使用Binder?

fork当前进程拷贝,如果创建进程添加了锁,拷贝的时候锁会丢失,导致进程出现死锁。

四、SystemServer进程

4.1 zygote如何启动SystemServer

systemserver是一个独立的进程,管理服务的,启动90多个系统服务。主要功能是为APP提供各种系统服务。AMS、PKMS都是systemserver中的关键服务。

zygoteInit-->zygote.forkSystemServer-->nativeForkSystemServer

根据3.2.2 注册JNI方法,

找到nativeForkSystemServer 对应的C++实现。

/frameworks/base/core/jni/

static const JNINativeMethod gMethods[] = {
2445          {"nativeForkAndSpecialize",
2446           "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
2447           "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I",
2448           (void*)com_android_internal_os_Zygote_nativeForkAndSpecialize},
2449          {"nativeForkSystemServer", "(II[II[[IJJ)I",
2450           (void*)com_android_internal_os_Zygote_nativeForkSystemServer},
2451          {"nativeAllowFileAcrossFork", "(Ljava/lang/String;)V",
2452           (void*)com_android_internal_os_Zygote_nativeAllowFileAcrossFork},
2453          {"nativePreApplicationInit", "()V",
2454           (void*)com_android_internal_os_Zygote_nativePreApplicationInit},
2455          {"nativeInstallSeccompUidGidFilter", "(II)V",
2456           (void*)com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter},
2457          {"nativeForkUsap", "(II[IZ)I", (void*)com_android_internal_os_Zygote_nativeForkUsap},
2458          {"nativeSpecializeAppProcess",
2459           "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/"
2460           "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V",
2461           (void*)com_android_internal_os_Zygote_nativeSpecializeAppProcess},
2462          {"nativeInitNativeState", "(Z)V",
2463           (void*)com_android_internal_os_Zygote_nativeInitNativeState},
2464          {"nativeGetUsapPipeFDs", "()[I",
2465           (void*)com_android_internal_os_Zygote_nativeGetUsapPipeFDs},
2466          {"nativeRemoveUsapTableEntry", "(I)Z",
2467           (void*)com_android_internal_os_Zygote_nativeRemoveUsapTableEntry},
2468          {"nativeGetUsapPoolEventFD", "()I",
2469           (void*)com_android_internal_os_Zygote_nativeGetUsapPoolEventFD},
2470          {"nativeGetUsapPoolCount", "()I",
2471           (void*)com_android_internal_os_Zygote_nativeGetUsapPoolCount},
2472          {"nativeEmptyUsapPool", "()V", (void*)com_android_internal_os_Zygote_nativeEmptyUsapPool},
2473          {"nativeBlockSigTerm", "()V", (void*)com_android_internal_os_Zygote_nativeBlockSigTerm},
2474          {"nativeUnblockSigTerm", "()V", (void*)com_android_internal_os_Zygote_nativeUnblockSigTerm},
2475          {"nativeBoostUsapPriority", "()V",
2476           (void*)com_android_internal_os_Zygote_nativeBoostUsapPriority},
2477          {"nativeParseSigChld", "([BI[I)I",
2478           (void*)com_android_internal_os_Zygote_nativeParseSigChld},
2479          {"nativeSupportsTaggedPointers", "()Z",
2480           (void*)com_android_internal_os_Zygote_nativeSupportsTaggedPointers},
2481  };

找到了对应的native方法 

 进一步看native方法

static jint com_android_internal_os_Zygote_nativeForkSystemServer(
2075          JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
2076          jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
2077          jlong effective_capabilities) {
2078    std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()),
2079                     fds_to_ignore(fds_to_close);
2080  
2081    fds_to_close.push_back(gUsapPoolSocketFD);
2082  
2083    if (gUsapPoolEventFD != -1) {
2084      fds_to_close.push_back(gUsapPoolEventFD);
2085      fds_to_ignore.push_back(gUsapPoolEventFD);
2086    }
2087  
2088    if (gSystemServerSocketFd != -1) {
2089        fds_to_close.push_back(gSystemServerSocketFd);
2090        fds_to_ignore.push_back(gSystemServerSocketFd);
2091    }
2092  
2093    pid_t pid = ForkCommon(env, true,
2094                           fds_to_close,
2095                           fds_to_ignore,
2096                           true);
2097    if (pid == 0) {
2098        // System server prcoess does not need data isolation so no need to
2099        // know pkg_data_info_list.
2100        SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
2101                         permitted_capabilities, effective_capabilities,
2102                         MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
2103                         false, nullptr, nullptr, /* is_top_app= */ false,
2104                         /* pkg_data_info_list */ nullptr,
2105                         /* whitelisted_data_info_list */ nullptr, false, false);
2106    } else if (pid > 0) {
2107        // The zygote process checks whether the child process has died or not.
2108        ALOGI("System server process %d has been created", pid);
2109        gSystemServerPid = pid;
2110        // There is a slight window that the system server process has crashed
2111        // but it went unnoticed because we haven't published its pid yet. So
2112        // we recheck here just to make sure that all is well.
2113        int status;
2114        if (waitpid(pid, &status, WNOHANG) == pid) {
2115            ALOGE("System server process %d has died. Restarting Zygote!", pid);
2116            RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
2117        }
2118  
2119        if (UsePerAppMemcg()) {
2120            // Assign system_server to the correct memory cgroup.
2121            // Not all devices mount memcg so check if it is mounted first
2122            // to avoid unnecessarily printing errors and denials in the logs.
2123            if (!SetTaskProfiles(pid, std::vector<std::string>{"SystemMemoryProcess"})) {
2124                ALOGE("couldn't add process %d into system memcg group", pid);
2125            }
2126        }
2127    }
2128    return pid;
2129  }

fork进程使用的是ForkCommon,

pid_t pid = ForkCommon(env, true,
2094                           fds_to_close,
2095                           fds_to_ignore,
2096                           true);

fork方法,fork出systemserver进程。

4.2 fork进程

fork会得到两个进程,一个父进程,一个子进程。因此有两个返回值,pid_t

怎么判断fork后的进程是父进程还是子进程?

根据返回值判断,pid==0,则是子进程。

ZygoteInit.main中

4.3 SystemServer启动服务

创建上下文 createSystemContext

SystemServer进程需要一直活着,存在Loop.loop()进入死循环

启动服务主要有三个主要函数:

startBootStrapServices

startCoreServices

startOtherServices

都是通过systemManager.startService启动的。

4.4 SystemServer

SystemServer: 是一个进程,管理服务的进程

SystemServerManager: 管理系统服务的类。这些服务都继承SystemServie

SystemManager:也是一个进程,管理系统服务的binder

ActivityManagerTaskService继承IActivityManagerTask.Stub

有需要继承SystemServie?怎么呢?采用Lyficycle类型,继承SysytemService,

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用ShinyCell展示你的单细胞数据

在我参与发表我的第一篇植物单细胞文章中&#xff0c;我用Shiny开发了一个简单的单细胞可视化网站&#xff0c;目前已经运行了5年了&#xff0c;有上万的访问&#xff0c;唯一的不足就是太简陋。我一直想着能不能找个一个更好的工具进行展示&#xff0c;最近发现了一个工具&…

每日5题Day24 - LeetCode 116 - 120

每一步向前都是向自己的梦想更近一步&#xff0c;坚持不懈&#xff0c;勇往直前&#xff01; 第一题&#xff1a;116. 填充每个节点的下一个右侧节点指针 - 力扣&#xff08;LeetCode&#xff09; /* // Definition for a Node. class Node {public int val;public Node left;…

【数据质量人人有责】数据质量是什么?

引言&#xff1a;数据是当代企业的核心资源之一&#xff0c;对于支持业务决策、优化运营和增强竞争力至关重要。然而&#xff0c;仅仅拥有数据并不意味着能够充分发挥其潜在价值。只有先确保数据质量&#xff0c;才能保证后续数据分析和挖掘的正确的结果对管理和业务有价值&…

LabVIEW 与组态软件在自动化系统中的应用比较与选择

LabVIEW 确实在非标单机设备、测试和测量系统中有着广泛的应用&#xff0c;特别是在科研、教育、实验室和小型自动化设备中表现突出。然而&#xff0c;LabVIEW 也具备一定的扩展能力&#xff0c;可以用于更复杂和大型的自动化系统。以下是对 LabVIEW 与组态软件在不同应用场景中…

Mysql中使用where 1=1有什么问题吗

昨天偶然看见一篇文章&#xff0c;提到说如果在mysql查询语句中&#xff0c;使用where 11会有性能问题&#xff1f;&#xff1f; 这着实把我吸引了&#xff0c;因为我项目中就有不少同事&#xff0c;包括我自己也有这样写的。为了不给其他人挖坑&#xff0c;赶紧学习一下&…

关于JavaScript技术的基础内容汇总

目录 JavaScript 基础知识1. JavaScript 基本语法2. 变量和常量3. 数据类型4. 运算符5. 控制结构6. 函数7. 对象8. 数组9. 事件处理10. DOM 操作 JavaScript 基础知识 学习 JavaScript&#xff08;简称 JS&#xff09;是前端开发的重要组成部分&#xff0c;它是一种动态的、弱…

【总线】AMBA总线架构的发展历程

目录 引言 发展历程 第一代AMBA&#xff08;AMBA 1&#xff09; 第二代AMBA&#xff08;AMBA 2&#xff09; 第三代AMBA&#xff08;AMBA 3&#xff09; 第四代AMBA&#xff08;AMBA 4&#xff09; 第五代AMBA&#xff08;AMBA 5&#xff09; AMBA协议简介 ASB&#x…

【Linux】常见指令的使用

文章目录 which指令stat 指令wc指令echo指令tree 指令whoami指令clear指令alias指令ls指令pwd指令cd 指令touch指令mkdir指令&#xff08;重要&#xff09;rmdir指令 && rm 指令&#xff08;重要&#xff09;man指令&#xff08;重要&#xff09;cp指令&#xff08;重要…

Redis原理篇——分布式锁

Redis原理篇——分布式锁 分布式锁是什么&#xff1f;分布式锁有哪些特性&#xff1f;分布式锁常用实现方式Redis 实现分布式锁一、简单的 Redis 锁二、带过期时间的 Redis 锁三、加上 Owner 的 Redis 锁四、Lua 脚本确保原子性 分布式锁是什么&#xff1f; 分布式锁是在分布式…

数据中台:生产制造产业链的“智慧大脑”!

在当今激烈竞争的生产制造领域&#xff0c;数据中台正扮演着至关重要的角色&#xff0c;它就像是产业链的“智慧大脑”&#xff0c;引领着产业的发展方向&#xff01;数据中台在生产制造产业链、生态链中起到以下关键作用&#xff1a; 1. 数据整合与共享&#xff1a;将产业链各…

R可视化:R语言基础图形合集

R语言基础图形合集 欢迎大家关注全网生信学习者系列&#xff1a; WX公zhong号&#xff1a;生信学习者Xiao hong书&#xff1a;生信学习者知hu&#xff1a;生信学习者CDSN&#xff1a;生信学习者2 基础图形可视化 数据分析的图形可视化是了解数据分布、波动和相关性等属性必…

物业客服“逆袭”记:从被质疑到被点赞,只因用了这款小程序

作为物业服务企业来说&#xff0c;物业客服人员是物业公司的核心部门。客服人员不仅仅要进行各部门之间的工作协调沟通&#xff0c;而且也是物业与业主沟通的主要桥梁。但是&#xff0c;往往客服人员经常被传统的报修方式所困扰&#xff0c;导致业主对物业客服人员存在质疑与谩…

Linux:多线程的操作

多线程操作 进程与线程线程的创建 create_pthread创建线程池给线程传入对象的指针 线程等待 pthread_join退出线程 pthread_exit线程等待参数 retval 与 线程退出参数 retval 线程中断 pthread_cancel获取线程编号 pthread_self线程分离 pthread_detach 进程与线程 进程是资源…

OpenCV读取和显示和保存图像

# 导入 OpenCV import cv2 as cv # 读取图像 image cv.imread(F:\\mytupian\\xihuduanqiao.jpg) # 创建窗口 #显示图像后&#xff0c;允许用户随意调整窗口大小 cv.namedWindow(image, cv.WINDOW_NORMAL) # 显示图像 cv.imshow(image, image)# 将图像保存到文件 success cv…

Linux之网络编程

Linux之网络编程 TCP协议 TCP(Transmission ControlProtocol) : 传输控制协议&#xff0c;是一个 面向连接的、可靠的、基于字节流的传输层的协议。TCP 协议建立的是一种点到点的&#xff0c;一对一的可靠连接协议 特点&#xff1a; 数据无丢失数据无失序数据无错误数据无重…

Zynq7000 系列FPGA模块化仪器

• 基于 XilinxXC7Z020 / 010 / 007S • 灵活的模块组合 • 易于嵌入的紧凑型外观结构 • 高性能的 ARM Cortex 处理器 • 成熟的 FPGA 可编程逻辑 &#xff0c;基于 IP 核的软件库 FPGA 控制器 Zynq7000 系列模块是基于 Xilinx XC7Z020/010/007S 全可编程片上系统 (SoC) 的…

Opengauss开源4年了,都谁在向其贡献代码?

2020 年 6 月 30 日&#xff0c;华为将Opengauss正式开源&#xff0c;截止目前已经过去4年时间&#xff0c;社区力量对这款数据库产品都起到了哪些作用&#xff0c;谁的代码贡献更大一些&#xff1f; 根据社区官网信息统计&#xff0c;截止目前&#xff08;2024年6月12日&…

【Java基础】OkHttp 超时设置详解

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

光纤跳线(又称光纤连接器)的种类

光纤跳线&#xff08;又称光纤连接器&#xff09;&#xff0c;也就是接入光模块的光纤接头&#xff0c;也有好多种&#xff0c;且相互之间不可以互用。SFP模块接LC光纤连接器&#xff0c;而GBIC接的是SC光纤连接器。下面对网络工程中几种常用的光纤连接器进行详细的说明&#x…

数字化制造案例分享以及数字化制造能力评估(34页PPT)

资料介绍&#xff1a; 通过全面的数字化企业平台和智能制造技术的应用&#xff0c;制造型企业不仅提升了自身的竞争力&#xff0c;也为整个制造业的数字化转型提供了借鉴。同时&#xff0c;数字化制造能力的评估是企业实现数字化转型的关键环节&#xff0c;需要从技术变革、组…