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

Android---系统启动流程

目录

Android 系统启动流程

init 进程分析

init.rc 解析

Zygote 概叙

Zygote 触发过程

Zygote 启动过程

什么时Runtime?

System Server 启动流程

Fork 函数

总结

面试题

Android 是 google 公司开发的一款基于 Linux 的开源操作系统。

Android 系统启动流程

android 系统启动的大概流程如下图所示:

\bullet 第一步:启动电源以及系统启动

当电源按下,引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序到 RAM,然后执行。

\bullet 第二步:引导程序

 引导程序是在 Android 操作系统开始运行前的一个小程序。引导程序是运行的第一个程序,因此它是针对特定的主板与芯片的。设备制造商要么使用很受欢迎的引导程序比如 reboot, uboot, qibootloader 或者开发自己的引导程序,它不是 Android 操作系统的一部分。引导程序是 OEM 厂商或者运营商加锁和限制的地方。

引导程序分两个阶段执行:

第一阶段:检测外部的RAM以及加载第二阶段有用的程序。

第二阶段:引导程序设置网络、内存等等。这些对于运行内核是必须的,为了达到特殊的目标,引导程序可以根据配置参数或者输入数据设置内核。

Android 引导程序可以在 \bootable\bootloader\regacy\usbloader 找到。传统的加载器包含两个文件,需要在这里说明:

init.s 初始化堆栈,清零 BBS 段,调用 main.c 的 main()  函数;

main.c 初始化硬件(闹钟、主板、键盘、控制台) ,创建 linux 标签

 \bullet 第三步:内核(Kernel)

Android 内核与桌面 linux 内核启动方式差不多。内核启动时,设置缓存、保护存储器、计划列表、加载驱动。当内核完成系统设置,它首先在系统文件种寻找 "init" 文件,然后启动 root 进程或者系统的第一个进程。

 \bullet 第四步:init 进程

init 进程是 Linux 系统中用户空间的第一个进程,进程号固定为1。Kernel 启动后,在用户空间启动 init 进程,并调用 init 中的 main() 方法执行 init 进程的职责。

 \bullet 第五步:启动 Lancher App

init 进程分析

其中 init 进程是 Android 系统中及其重要的第一个进程,init 进程做了如下三件事:

  1. 创建和挂载启动所需要的文件目录

  2. 初始化和启动属性服务

  3. 解析 init.rc 配置文件并启动 zygote 进程

init.rc 解析

init.rc 是一个非常重要的配置文件,它是由 Android 初始化语言(Android Init Language) 编写的脚本,它主要包含五种类型语句:Action(Action 中包含了一系列的 Command)、Commands(init 语言中的命令)、Services(由 init 进程启动的服务)、Options(对服务进行配置的选项) 和 Import (导入其它配置文件)。init.rc 的配置代码如下所示:

# \system\core\rootdir\init.rc
on init # L41
sysclktz 0
# Mix device-specific information into the entropy pool
copy /proc/cmdline /dev/urandom
copy /default.prop /dev/urandom
...
on <trigger> [&& <trigger>]* //设置触发器
<command>
<command> //动作触发之后要执行的命令
service <name> <pathname> [ <argument> ]* //<service的名字><执行程序路径><传递参
数>
<option> //Options是Services的参数配置. 它们影响Service如何运行及运行时机
group <groupname> [ <groupname>\* ] //在启动Service前将group改为第一个
groupname,第一个groupname是必须有的,
//默认值为root(或许默认值是无),第二个groupname可以不设置,用于追加组(通过
setgroups)
priority <priority> //设置进程优先级. 在-20~19之间,默认值是0,能过
setpriority实现
socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]//创建
一个unix域的socket,名字叫/dev/socket/name , 并将fd返回给Service. type 只能是
"dgram", "stream" or "seqpacket".
...

 Action

Action: 通过触发器 trigger,即以 on 开头的语句来决定执行相应的 service 的时机:

  \bullet on early-init: 在初始化早期阶段触发;

  \bullet on init: 在初始化阶段触发;

  \bullet on late-init: 在初始化晚期阶段触发;

  \bullet on boot/charger: 当系统启动/充电时触发;

  \bullet on property: 当属性值满足条件时触发。

 Service

 服务 Service,以 service 开头,由 init 进程启动,一般运行在 init 的一个子进程,所以启动 service 前需要判断对应的可执行文件是否存在。init 生成的子进程,定义在 rc 文件,其中每一个 service 在启动时会通过 fork 方式生成子进程

例如:service servicemanager /system/bin/servicemanager 代表的是服务名为 servicemanager,服务执行的路径为 /system/bin/servicemanager。

Command

 下面列举常用的命令:

  \bullet class_start<service_class_name>: 启动属于同一个 class 的所有服务;

  \bullet start<service_name>: 启动指定的服务,若已启动则跳过;

  \bullet stop<service_name>: 停止正在运行的服务;

  \bullet setprop: 设置属性值;

  \bullet mkdir: 创建指定目录;

  \bullet symlink<sym_link>: 创建连接到的<sym_link>符号链接;

  \bullet write: 向文件 path 中写入字符串;

  \bullet exec: fork并执行,会阻塞 init 进程直到程序完毕;

  \bullet exprot: 设置环境变量;

  \bullet loglevel: 设置 log 级别。

Options

Options 是 Service 的可选项,与 Service 配合使用

  \bullet disabled: 不随 class 自动启动,只根据 service 名才启动;

  \bullet oneshot: service 退出后不再重启;

  \bullet user/group: 设置执行服务的用户/用户组,默认都是 root;

  \bullet class: 设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为 default;

  \bullet onrestart: 当服务重启时执行相应命令;

  \bullet socket: 创建名为 /dev/socket/<name> 的socket

  \bullet critical: 在规定时间内该 service 不断重启,则系统会重启并进入恢复模式。

default: 意味着 disabled = false, oneshot = false, critical = false。

Zygote 概叙

Zygote 中文翻译为“受精卵”,它主要用于孵化子进程。在 Android 系统中有以下两种程序:Java 应用程序,主要基于 ART(Android Runtime) 虚拟机,所有的应用程序 apk 都属于这类 native 程序,也就是利用 C/C++ 语言开发的程序,如 bootanimation。所有的 Java 应用程序进程及系统服务(SystemServer)进程都由 Zygote 进程通过 Linux 的 fork() 函数卵化出来的,也就是为什么把它称为 Zygote 的原因,因为他就像一个受精卵,卵化出无数子进程,而 native 程序则由 Init 程序创建启动。Zygote 进程最初的名字不是“zygote”而是“app_process”,这个名字是在 Android.mk 文件中定义的。

Zygote 是 Android 中的第一个 ART 虚拟机,他通过 socket 的方式与其它进程进行通信。这里的“其他进程”其实主要是系统进程--SystemServer。

Zygote 是一个 C/S 模型,Zygote 进程作为服务端,它主要负责创建 Java 虚拟机,加载系统资源,启动 SystemServer 进程,以及在后续运行过程中启动普通的应用程序,其他进程作为客户端向它发出“孵化”请求,而 Zygote 接收到这个请求后就“孵化”出一个新的进程。比如,当点击 Launcher 里的应用程序图标去启动一个新的应用程序时,这个请求会到达框架层的核心服务 ActivityManagerService 中,当 AMS 收到这个请求后,它通过调用 Process 类发出一个“孵化”子进程的 Socket 请求,而 Zygote 监听到这个请求后就立刻 fork 一个新的进程出来。

Zygote 触发过程

1. init.zygoteXX.rc

import /init.${ro.zygote}.rc

${ro.zygote} 会被替换成 ro.zygote 的属性值,这个是由不同的硬件厂商自己制定的,由四个值:

    \bullet zygote32: zygote进程对应的执行程序是 app_process(纯32bit 模式)

    \bullet zygote64: zygote进程对应的执行程序是 app_process64(纯32bit 模式)

    \bullet zygote32_64: 启动两个 zygote 进程(名为 zygote 和 zygote_secondary),对应的执行程序分别是 app_process32(主模式)、approcess63

     \bullet zygote64_32: 启动两个 zygote 进程(名为 zygote 和 zygote_secondary),对应的执行程序分别是 app_process64(主模式)、app_precess32

2. start zygote

位置:system\core\rootdir\init.rc,zygote-start 是在 on late-init 中触发的。

3. app_processXX

位置\frameworks\base\cmds\app_process\

Zygote 启动过程

位置\frameworks\base\cmds\app_process\app_main.cpp
在 app_main.cpp 的 main 函数中,主要做的事情就是参数解析。这个函数有两种启动模式:
        1. zygote 模式,也就是初始化 zygote 进程,传递的参数有 --start-system-server --socket - name = zygote,前者表示启动 SystemServer,后者指定 socket 的名称。
        2. application模式,也就是启动普通应用程序,传递的参数有 class 名字以及 class 带的参数
两者最终都是调用 AppRuntime 对象的 start 函数,加载 ZygoteInit 或 RuntimeInit 两个 Java 类,并将之前整理的参数传入进去。

 app_process 里面定义了三种应用程序类型:

   1. Zygote: com.android.internal.os.ZygoteInit

   2. SystemServer 不单独启动,而是由 Zygote 启动

   3. 其他指定类名的 Java 程序

什么时Runtime?

Runtime 是支撑程序运行的基础库,它是与语言绑定在一起的。比如:

  \bullet C Runtim: 就是 C standard lib,也就是我们常说的 libc。(有意思的是,Wiki 会自动将" C Runtime" 重定向到"C Standard Library")。

  \bullet Java Runtime: 同样,Wiki 将其重定向到" Java Virtual Machine",这里当然包括 Java 的支撑类库(.jar)。

  \bullet AndroidRuntime: 显而易见,就是为 Android 应用运行所需要的运行时环境。这个环境包括以下内容:

        1. Dalvik VM: Android 的 Java VM, 解释运行 Dex 格式 Java 程序。每个进程运行一个虚拟机(什么叫运行虚拟机?说白了,就是一些 C 代码,不停的去解释 Dex 格式的二进制码(Bytecode),把它们转成机器码(Machine code),然后执行,当然,限制大多数的 Java 虚拟机都支持 JIT,也就是说,bytecode 可能在运行前就以及被转换成机器码,从而大大提高了性能。过去一个普遍的认识是 Java 程序比 C/C++ 等静态编译的语言慢,但随着 JIT 的介入和发展,这个有已经完全是过去式了,JIT 的动态性运行允许虚拟机根据运行时环境,优化机器码的生成,在某些情况下,Java 甚至可以比 C/C++ 跑得更快,同时又兼具平台无关性的特性。

        2. Android 的 Java 类库,大部分来自于 Apache Hamony,开源的 Java API 实现,如 java.lang, java.util, java.net。但去除了 AWT,Swing 等部件。

        3. JNI: C 和 Java 互掉的接口。

        4. Libc: Android 也有很多 C 代码,自然少不了 libc,注意的是,Android 的 libc 叫 bionic C

// \frameworks\base\core\jni\androidRuntime.cpp start() L1091
void AndroidRuntime::start(const char* className, const Vector<String8>&
options, bool zygote)
{
...
JNIEnv* env;
//JNI_CreateJavaVM L1015
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
/*
* Register android functions.
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
...
}

  \bullet Java 虚拟机的启动大致做了以下一些事情:

        1. 从 property 读取一系列启动参数。

        2. 创建和初始化结构体全局对象(每个进程)GDVM,及对应于 JavaVM 和 JNIEnv 的内部结构体 JavaVMExt, JNIEnvExt.

        3. 初始化 java 虚拟机,并创建虚拟机线程。

        4. 注册系统的 JNI,Java 程序通过这些 JNI 接口来访问底层的资源。

loadJniLibrary("javacore");
loadJniLibrary("nativehelper");

        5. 为 Zygote 的启动做最后的准备,包括设置 SID/UID,以及 mount 文件系统。

        6. 返回 JavaVM 给 Native 代码,这样它就可以向上访问 Java 的接口。

System Server 启动流程

System Server 是 Zygote fork 的第一个 java 进程,这个进程非常重要,因为他们有很多的系统线程,提供所有核心的系统服务。

看到大名鼎鼎的 WindowManager,ActivityManager 了吗?对了,它们都是运行在 system_server 的进程里。还有很多 "Binder-x" 的线程,它们是各个 Service 为了响应应用程序远程调用请求而创建的。除此之外,还有很多内部线程,比如”UI thread", "InputReader", "InputDispatch" 等等,现在我们只关心 System Server 是如何创建起来的。

SystemServer 的 main() 函数。

public static void main(String[] args) {
new SystemServer().run();
}

接下来我们分成4部分详细分析 SystemServer.run 方法的初始化流程:

\bullet 初始化必要的 SystemServer 环境参数,比如系统时间、默认时区,语言、load 一些 Library 等等。

\bullet 初始化 Looper,我们在主线程中使用到的 looper 就是在 SystemServer 中进行初始化的

\bullet 初始化 Context,只有初始化一个 Context 才能进行启动 Service 等操作,这里看一下源码:

private void createSystemContext() {
ActivityThread activityThread = ActivityThread.systemMain();
mSystemContext = activityThread.getSystemContext();
mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);
final Context systemUiContext = activityThread.getSystemUiContext();
systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
}
看到没有ActivityThread就是这个时候生成的。继续看ActivityThread中如何生成Context:
public ContextImpl getSystemContext() {synchronized (this) {if (mSystemContext == null) {mSystemContext = ContextImpl.createSystemContext(this);}return mSystemContext;}
}

ContextImpl 是 Context 类的具体实现,里面封装完成了几种常用的 createContext 的方法:

static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
//省略代码
return context;
}
static ContextImpl createSystemUiContext(ContextImpl systemContext) {
final LoadedApk packageInfo = systemContext.mPackageInfo;
//省略代码
return context;
}
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk
packageInfo) {
if (packageInfo == null) throw new
IllegalArgumentException("packageInfo");
//省略代码
return context;
}
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo, IBinder
activityToken, int displayId,
Configuration overrideConfiguration) {
//省略代码
return context;
}

初始化 SystemServiceManager,用来管理启动 Service,SystemServiceManager 中封装了启动 Service 的 startService 方法启动系统必要的 Service,启动 service 的流程又分成三步:

// Start services.
try {
traceBeginAndSlog("StartServices");
startBootstrapServices();
startCoreServices();
startOtherServices();
SystemServerInitThreadPool.shutdown();
} catch (Throwable ex) {
//
} finally {
traceEnd();
}

启动 BootstrapServices,就是系统必须需要的服务,这些服务直接耦合性很高,所以就干脆放在一个方法里面一起启动,比如 PowerManagerService、RecoverySystemService、DisplayMangerService、ActivityManagerService 等等启动以基本的核心 Service,很简单,只有三个 BatteryService、UsageStatsService、WebViewUpdateService 启动其它需要用到的 Service 比如 NetworkScoreService、AlarmManagerService

Fork 函数

pid_t fork(void)
1. 参数:不需要参数
2. 需要的头文件 <sys/types.h> <unistd.h>
3. 返回值分两种情况:
        返回0表示成功创建子进程,并且接下来进入子进程执行流程
        返回PID>0),成功创建子进程,并且继续执行父进程流程代码
        返回非正数(<0),创建子进程失败,失败原因主要有:
        进程数超过系统所能创建的上限,errno会被设置为EAGAIN系统内存不足,errno会被设置为
        ENOMEM

 

使用 fork() 函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空
间:包括进程上下文(进程执行活动全过程的静态描述)、进程堆栈、打开的文件描述符、信号控
制设定、进程优先级、进程组号等。子进程所独有的只有它的进程号,计时器等(只有小量信
息)。因此,使用 fork() 函数的代价是很大的

         

总结

1. init 根据 init.rc 运行 app_process,并携带“-zygote" 和"-startSystemServer"参数。

2. AndroidRuntime.cpp::start() 里将启动 JavaVM,并且注册所有 framework 相关的系统 JNI 接口

3. 第一次进入 Java 世界,运行 ZygoteInit.java::main() 函数初始化 Zygote,并创建 Socket 的 Server 端。

4. 然后 fork 一个新的进程并在新进程里初始化 SystemServer.Fork 之前,Zygote 是 preload 常用的 Java 类库,以及系统的 resource,同时 GC 清理内存空间,为子进程省去重复的工作。

5. SystemServer 将所有的系统 Service 初始化,包括 ActivityManager 和 WindowManager,他们是应用程序运行起来的前提。

6. 依次同时,Zygote 监听服务端 Socket,等待新的应用启动请求。

7. ActivityManager ready 之后寻找系统的 “Startup" Application,将请求发给 Zygote。

8. Zygote 收到请求后,fork 出一个新的进程。

9. Zygote 监听并处理 SystemServer 的 SIGCHID 信号,一旦 SystemServer 崩溃,立即将自己 kill 掉。init 会重启 Zygote。

面试题

1. 你了解 Android 系统启动流程吗?
答:当按电源键触发开机,首先会从 ROM 中预定义的地方加载引导程序 BootLoader RAM 中,并执 行 BootLoader 程序启动 Linux Kernel, 然后启动用户级别的第一个进程: init 进程。init 进程会解析 init.rc 脚本做一些初始化工作,包括挂载文件系统、创建工作目录以及启动系统服务进程等,其中系统 服务进程包括 Zygoteservice managermedia 等。在 Zygote 中会进一步去启动 system_server 进 程,然后在 system_server 进程中会启动 AMSWMSPMS 等服务,等这些服务启动之后,AMS 中就 会打开 Launcher 应用的 home Activity,最终就看到了手机的 "桌面"
2. system_server 为什么要在 Zygote 中启动,而不是由 init 直接启动呢?
答:Zygote 作为一个孵化器,可以提前加载一些资源,这样 fork() 时基于 Copy-On-Write 机制创建的其 他进程就能直接使用这些资源,而不用重新加载。比如 system_server 就可以直接使用 Zygote 中的 JNI 函数、共享库、常用的类、以及主题资源。
3. 为什么要专门使用 Zygote 进程去孵化应用进程,而不是让 system_server 去孵化呢?
答:首先 system_server 相比 Zygote 多运行了 AMSWMS 等服务,这些对一个应用程序来说是不需要 的。另外进程的 fork() 对多线程不友好,仅会将发起调用的线程拷贝到子进程,这可能会导致死锁,而 system_server 中肯定是有很多线程的。
4. 能说说具体是怎么导致死锁的吗?
答:在 POSIX 标准中,fork 的行为是这样的:复制整个用户空间的数据(通常使用 copy-on-write 的策略, 所以可以实现的速度很快)以及所有系统对象,然后仅复制当前线程到子进程。这里:所有父进程中别 的线程,到了子进程中都是突然蒸发掉的,对于锁来说,从 OS 看,每个锁有一个所有者,即最后一次 lock 它的线程。假设这么一个环境,在 fork 之前,有一个子线程 lock 了某个锁,获得了对锁的所有权。fork 以后,在子进程中,所有的额外线程都 人间蒸发了。而锁却被正常复制了,在子进程看来,这个锁没有主人,所以没有任何人可以对它解锁。 当子进程想 lock 这个锁时,不再有任何手段可以解开了。程序发生死锁
5. Zygote 为什么不采用 Binder 机制进行 IPC 通信?
答:Binder 机制中存在 Binder 线程池,是多线程的,如果 Zygote 采用 Binder 的话就存在上面说的 fork() 与 多线程的问题了。其实严格来说,Binder 机制不一定要多线程,所谓的 Binder 线程只不过是 在循环读取 Binder 驱动的消息而已,只注册一个 Binder 线程也是可以工作的,比如 service manager 就是这样的。实际上 Zygote 尽管没有采取 Binder 机制,它也不是单线程的,但它在 fork() 前主动停止 了其他线程,fork() 后重新启动了。

相关文章:

Android---系统启动流程

目录 Android 系统启动流程 init 进程分析 init.rc 解析 Zygote 概叙 Zygote 触发过程 Zygote 启动过程 什么时Runtime&#xff1f; System Server 启动流程 Fork 函数 总结 面试题 Android 是 google 公司开发的一款基于 Linux 的开源操作系统。 Android 系统启动…...

【网络】http协议

&#x1f941;作者&#xff1a; 华丞臧. &#x1f4d5;​​​​专栏&#xff1a;【网络】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449; LeetCode刷题网站 文章…...

Thread::interrupted() 什么意思? 如何中断线程?

1、答&#xff1a; Thread::interrupted() 是一个静态方法&#xff0c;用于判断当前线程是否被中断&#xff0c;并清除中断标志位。 具体来说&#xff0c;当一个线程被中断后&#xff0c;它的中断状态将被设置为 true。如果在接下来的某个时间点内调用了该线程的 interrupted…...

Oracle OCP 19c 考试(1Z0-083)中关于Oracle不完全恢复的考点(文末附录像)

欢迎试看博主的专著《MySQL 8.0运维与优化》 下面是Oracle 19c OCP考试&#xff08;1Z0-083&#xff09;中关于Oracle不完全恢复的题目: A database is configured in ARCHIVELOG mode A full RMAN backup exists but no control file backup to trace has been taken A media…...

一起来学习配置Combo接口吧!

Combo接口是一个光电复用的逻辑接口&#xff0c;一个Combo接口对应设备面板上一个GE电接口和一个GE光接口。电接口与其对应的光接口是光电复用关系&#xff0c;两者不能同时工作&#xff08;当激活其中一个接口时&#xff0c;另一个接口就自动处于禁用状态&#xff09;&#xf…...

C++模拟实现红黑树

目录 介绍----什么是红黑树 甲鱼的臀部----规定 分析思考 绘图解析代码实现 节点部分 插入部分分步解析 ●父亲在祖父的左&#xff0c;叔叔在祖父的右&#xff1a; ●父亲在祖父的右&#xff0c;叔叔在祖父的左&#xff1a; 测试部分 整体代码 介绍----什么是红黑树 红…...

HTTPS协议之SSL/TLS详解(下)

目录 前言&#xff1a; SSL/TLS详解 HTTP协议传输安全性分析 对称加密 非对称加密 证书 小结&#xff1a; 前言&#xff1a; 在网络世界中&#xff0c;存在着运营商劫持和一些黑客的攻击。如果明文传输数据是很危险的操作&#xff0c;因为我们不清楚中间传输过程中就被哪…...

OLE对象是什么?为什么要在CAD图形中插入OLE对象?

OLE对象是什么&#xff1f;OLE对象的意思是指对象连接与嵌入。那为什么要在CAD图形中插入OLE对象&#xff1f;一般情况下&#xff0c;在CAD图形中插入OLE对象&#xff0c;是为了将不同应用程序的数据合并到一个文档中。本节内容小编就来给大家分享一下在CAD图形中插入OLE对象的…...

【微信小程序】-- 自定义组件 -- 数据、方法和属性(三十三)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…...

【Spring 深入学习】AOP的前世今生之代理模式

AOP的前世今生之代理模式1. 概述 什么是代理模式呢&#xff1f;&#xff1f;&#xff1f; 在不修改原有代码 或是 无法修改原有代码的情况下&#xff0c;增强对象功能&#xff0c;替代原来的对象去完成功能&#xff0c;从而达成了拓展的目的。 先给大家看下 JavaScript中实现方…...

操作系统复试

2017软学 给出操作系统的定义&#xff0c;分别从资源管理&#xff0c;任务调度&#xff0c;用户接口等三个方面论述操作系统的职能 操作系统是位于硬件层之上、所有其他系统软件层之下的一个系统软件&#xff0c;使得管理系统中的各种软件和硬件资源得以充分利用&#xff0c;方…...

藏经阁(五)温湿度传感器 SHT3x-DIS 手册 解析

文章目录芯片特性芯片内部框图芯片引脚定义芯片温湿度范围芯片寄存器以及时序讲解信号转换公式芯片特性 湿度和温度传感器完全校准&#xff0c;线性化温度补偿数字输出供电电压范围宽&#xff0c;从2.4 V到5.5 VI2C接口通讯速度可达1MHz和两个用户可选地址典型精度 2% RH和 0.…...

PCB焊盘设计基本原则

SMT的组装质量与PCB焊盘设计有直接的关系&#xff0c;焊盘的大小比例十分重要。如果PCB焊盘设计正确&#xff0c;贴装时少量的歪斜可以再次回流焊纠正(称为自定位或自校正效应)&#xff0c;相反&#xff0c;如果PCB焊盘设计不正确&#xff0c;即使贴装位置十分准确&#xff0c;…...

mysql锁分类大全

前言 为什么会出现锁 MySQL中的锁是为了保证并发操作的正确性和一致性而存在的。 当多个用户同时对同一份数据进行操作时&#xff0c;如果不加控制地进行读写操作&#xff0c;就可能导致数据不一致的问题。例如&#xff0c;当多个用户同时对同一行数据进行写操作时&#xff…...

推荐几款主流好用的远程终端连接管理软件

一、介绍 远程终端连接管理软件是管理服务器、虚拟机等远程计算机系统不可或缺的工具之一&#xff0c;它可以通过网络连接到另一台计算机&#xff0c;以执行命令、编辑文件或进行其他管理任务&#xff0c;下面我将为大家介绍几款主流好用的远程终端连接管理软件&#xff0c;并…...

描述性统计

参考文献 威廉 M 门登霍尔 《统计学》 文章目录定性数据的描述方法条形图饼图帕累托图定量数据点图茎叶图频数分布直方图MINITAB 工具在威廉《统计学》一书将统计学分为描述统计学和推断统计学&#xff0c;他们的定义分别如下&#xff1a;描述统计学&#xff1a;致力于数据集的…...

第十四届蓝桥杯三月真题刷题训练——第 7 天

目录 第 1 题&#xff1a;三角回文数 问题描述 答案提交 运行限制 代码&#xff1a; 第 2 题&#xff1a;数数 问题描述 答案提交 运行限制 代码&#xff1a; 第 3 题&#xff1a;倍数问题_同余定理_分情况讨论 题目描述 输入描述 输出描述 输入输出样例 运行限…...

剑指 Offer 57. 和为s的两个数字

一、题目 输入一个递增排序的数组和一个数字s&#xff0c;在数组中查找两个数&#xff0c;使得它们的和正好是s。如果有多对数字的和等于s&#xff0c;则输出任意一对即可。 示例 1&#xff1a; 输入&#xff1a;nums [2,7,11,15], target 9 输出&#xff1a;[2,7] 或者 [7…...

PDF转word在线转换方法!操作简单又高效

相信很多已经工作的人都知道&#xff0c;PDF文件格式的优点在于兼容性强、安全性高&#xff0c;而且查看和传输给他人都很方便。但是&#xff0c;这种格式的文件也有不太方便的地方&#xff0c;那就是不能对文件内容进行编辑和修改。对于许多人来说&#xff0c;如果想要编辑修改…...

Jquery项目中使用vue.js

大家在工作的情况中&#xff0c;可能会遇到之前的老项目采用jq书写&#xff0c;或者修改或者新增功能在jq中&#xff0c;原始jq的项目,代码可维护性很差,一个页面几千行jq,可维护性很差,工作量巨大&#xff0c;所以这个时候大家可以引入vue.js。 第一步&#xff1a;引入vue.js…...

蓝桥杯 删除字符

题目描述 给定一个单词&#xff0c;请问在单词中删除 t 个字母后&#xff0c;能得到的字典序最小的单词是什么&#xff1f; 输入描述 输入的第一行包含一个单词&#xff0c;由大写英文字母组成。 第二行包含一个正整数 t。 其中&#xff0c;单词长度不超过 100&#xff0c…...

析构函数 对象数组 对象指针

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章 &#x1f525;座右铭&#xff1a;“不要等到什么都没有了&#xff0c;才下定决心去做” &#x1…...

Vue对Axios网络请求进行封装

一、为什么要对网络请求进行封装&#xff1f; 因为网络请求的使用率实在是太高了&#xff0c;我们有的时候为了程序的一个可维护性&#xff0c;会把同样的东西放在一起&#xff0c;后期找起来会很方便&#xff0c;这就是封装的主要意义。 二、如何进行封装&#xff1f; 1、将…...

Android framework HAL(HIDL)

简述 当你在Android系统中使用不同的硬件设备&#xff08;例如摄像头、传感器、音频设备等&#xff09;时&#xff0c;你需要与硬件抽象层&#xff08;HAL&#xff09;进行通信。 HAL是一个中间层&#xff0c;它充当了硬件和应用程序之间的桥梁。但是&#xff0c;由于硬件设备…...

QML 模型(ListModel)

LIstModel&#xff08;列表模型&#xff09; ListModel 是ListElement定义的简单容器&#xff0c;每个定义都包含数据角色。内容可以在 QML 中动态定义或显式定义。 属性&#xff1a; count模型中数据条目的数量dynamic动态角色&#xff0c;默认情况下&#xff0c;角色的类型…...

你还在调戏AI,有的公司已经用ChatGPT开展业务了

近日&#xff0c;OpenAI 正式宣布开放 ChatGPT 和 Whisper 两个模型的 API&#xff0c;API 版本的ChatGPT 不仅功能更多、性能更强&#xff0c;而且还更便宜一一相当于目前 GPT-3 模型价格打一折!划重点OpenAl正式开放 ChatGPT 和 Whisper 模型的 API&#xff0c;目前 SnapChat…...

DatenLord前沿技术分享 No.20

达坦科技专注于打造新一代开源跨云存储平台DatenLord&#xff0c;致力于解决多云架构、多数据中心场景下异构存储、数据统一管理需求等问题&#xff0c;以满足不同行业客户对海量数据跨云、跨数据中心高性能访问的需求。喷泉码具有极高的纠错能力&#xff0c;且具有低延迟、地复…...

基于vivado(语言Verilog)的FPGA学习(1)——了解viviado面板和编译过程

基于vivado&#xff08;语言Verilog&#xff09;的FPGA学习&#xff08;1&#xff09;——了解程序面板和编译过程 每日废话&#xff1a;最近找实习略微一些焦虑&#xff0c;不想找软件开发&#xff0c;虽然有些C和python基础&#xff08;之前上课学的&#xff09;&#xff0c;…...

PACS(CT、CR、DR、MR、DSA、RF医院影像管理系统源码)

PACS具体功能介绍&#xff1a; 病人、采集、观片、三维、报告、照相、退出、文件、图像采集、观片操作、三维、测量标注、诊断报告、照相打印、统计报表、系统管理、帮助、病人浏览器、选择数据源、打开图像、病人登记、工作列表、采集、打开画廊。 DICOM查询/获取&#xff1a…...

Centos7 安装Mysql8.0

1、到指定目录下下载安装包[rootVM-0-14-centos ~]# cd /usr/local/src2、下载mysql8[rootVM-0-14-centos src]# wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.20-linux-glibc2.12-x86_64.tar.xz3、解压mysql8, 通过xz命令解压出tar包&#xff0c; 然后通过t…...

2023年全国最新道路运输从业人员精选真题及答案18

百分百题库提供道路运输安全员考试试题、道路运输从业人员考试预测题、道路安全员考试真题、道路运输从业人员证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 181.某客运企业拥有55辆营运客车&#xff0c;下列关于该企业设置…...

web worker的基本使用案例

文件目录如下 代码按照顺序分别如下 webworker.html <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><meta name"viewpo…...

机器看世界

博主简介 博主是一名大二学生&#xff0c;主攻人工智能研究。感谢让我们在CSDN相遇&#xff0c;博主致力于在这里分享关于人工智能&#xff0c;c&#xff0c;Python&#xff0c;爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主&#xff0c;博主会继续更新的&#xff0c…...

18、指数移动平均——EMA

简介 在深度学习中&#xff0c;经常会使用EMA&#xff08;指数移动平均&#xff09;这个方法对模型的参数做平均&#xff0c;以求提高测试指标并增加模型鲁棒。 指数移动平均&#xff08;Exponential Moving Average&#xff09;也叫权重移动平均&#xff08;Weighted Moving…...

用Go快速搭建IM即时通讯系统

WebSocket的目标是在一个单独的持久连接上提供全双工、双向通信。在Javascript创建了Web Socket之后&#xff0c;会有一个HTTP请求发送到浏览器以发起连接。在取得服务器响应后&#xff0c;建立的连接会将HTTP升级从HTTP协议交换为WebSocket协议。由于WebSocket使用自定义的协议…...

2023年江苏省职业院校技能大赛中职网络安全赛项试卷-学生组-任务书

2023年江苏省职业院校技能大赛中职网络安全赛项试卷-学生组-任务书 2023年江苏省职业院校技能大赛中职网络安全赛项试卷-学生组-任务书第一阶段 (300分) [手敲的任务书 点个赞吧]任务一:主机发现与信息收集 (50分)任务二: 应急响应 (60分)任务三:数字取证与分析(80分)任务四:…...

如何使用码匠连接 MariaDB

MariaDB 是一个免费的、开源的关系型数据库管理系统&#xff0c;由 MariaDB 的创始人 Michael Widenius 于 2010 年创建。它基于 MariaDB&#xff0c;但在对数据存储的处理中加入了一些自己的特性。MariaDB 相对于 MariaDB 而言&#xff0c;具有更好的性能和更好的兼容性&#…...

JavaEE简单示例——Bean的实例化

简单介绍&#xff1a; 在我们之前使用某个对象&#xff0c;那么就要创建这个类的对象&#xff0c;创建对象的过程就叫做实例化。对于Spring来说&#xff0c;实例化Bean的方式有三种&#xff0c;分别是构造方法实例化&#xff0c;静态方法实例化&#xff0c;实例工厂实例化。我…...

1229. 日期问题

目录 题目链接 一些话 流程 套路 ac代码 题目链接 1229. 日期问题 - AcWing题库 一些话 切入点 // 小明知道这些日期都在1960年1月1日至2059年12月31日。 // 这些日期采用的格式非常不统一&#xff0c;有采用年/月/日的&#xff0c;有采用月/日/年的&#xff0c;还有采用…...

Java 中的浅拷贝和深拷贝

无论是浅拷贝还是深拷贝&#xff0c;都可以通过 Object 类的 clone() 方法来完成&#xff1a; /*** 拷贝** author qiaohaojie* date 2023/3/5 15:58*/ public class CloneTest {public static void main(String[] args) throws Exception {Person person1 new Person(23, &…...

【java】 java开发中 常遇到的各种难点 思路方案

文章目录逻辑删除如何建立唯一索引唯一索引失效问题加密字段模糊查询问题maven依赖冲突问题&#xff08;jar包版本冲突问题&#xff09;sql in条件查询时 将结果按照传入顺序排序作为一个开发人员 总会遇到各种难题 本文列举博主 遇见/想到 的例子 &#xff0c;也希望同学们可以…...

ViewBinding 和 DataBinding的使用

1.ViewBinding:视图绑定 通过视图绑定功能&#xff0c;您可以更轻松地编写可与视图交互的代码。在模块中启用视图绑定之后&#xff0c;系统会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。在大多数情况下&…...

HTML+CSS入门

CSS概述 CSS指层叠样式表 (Cascading Style Sheets)&#xff0c;用来定义HTML网页中的内容用什么样式来显示。 HTML: 指定网页显示的内容 CSS: 指定内容显示的样式CSS入门案例 <html><head><meta charset"UTF-8"><title>入门案例</tit…...

【Vue】vue2导出页面内容为pdf文件,自定义选中页面内容导出为pdf文件,打印选中页面内容,预览打印内容

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、安装html2canvas和jspdf二、导出pdf使用步骤1.在utils文件夹下创建htmlToPdf.js2.在main.js中引入3.在页面中使用三、打印预览1. 引入print-js2.页面中impor…...

保姆级使用PyTorch训练与评估自己的Replknet网络教程

文章目录前言0. 环境搭建&快速开始1. 数据集制作1.1 标签文件制作1.2 数据集划分1.3 数据集信息文件制作2. 修改参数文件3. 训练4. 评估5. 其他教程前言 项目地址&#xff1a;https://github.com/Fafa-DL/Awesome-Backbones 操作教程&#xff1a;https://www.bilibili.co…...

1/4车、1/2车、整车悬架PID控制仿真合集

目录 前言 1. 1/4悬架系统 1.1数学模型 1.2仿真分析 2. 1/2悬架系统 2.1数学模型 2.2仿真模型 2.3仿真分析 3. 整车悬架系统 3.1数学模型 3.2仿真分析 参考文献 前言 前面几篇文章介绍了LQR、SkyHook、H2/H∞控制&#xff0c;接下来会继续介绍滑模、反步法、MPC、…...

媒体邀约的形式和步骤

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 做媒体服务很多年&#xff0c;今天就与大家分享下媒体邀约都有哪些形式&#xff1a; 1&#xff0c;电话邀约&#xff1a;通过电话与媒体记者进行沟通&#xff0c;邀请其参加活动或接受采…...

Unity合批处理

一.静态合批标记为Batching Static的物体&#xff08;标记后物体运行不能移动、旋转、缩放&#xff09;在使用相同材质球的条件下在项目打包的时候unity会自动将这些物体合并到一个大Mesh*缺点打包后体积增大运行时内存占用增大二.动态批处理不超过300个顶点不超过900个属性不包…...

Android 进阶——Binder IPC之Native 服务的启动及代理对象的获取详解(六)

文章大纲引言一、Binder线程池的启动1、ProcessState#startThreadPool函数来启动线程池2、IPCThreadState#joinThreadPool 将当前线程进入到线程池中去等待和处理IPC请求二、Service 代理对象的获取1、获取Service Manager 代理对象BpServiceManager2、调用BpServiceManager#ge…...

企业官网怎么做?

企业官网是企业展示形象和吸引潜在客户的重要渠道之一&#xff0c;因此如何打造一款优秀的企业官网显得尤为重要。本文将从策划、设计、开发和上线等方面&#xff0c;为您介绍企业官网的制作步骤。 一、策划 1.明确目标 企业官网的制作需要明确目标&#xff0c;即确定官网的主…...