android 14 apexd分析(1)apexd bootstrap

Apex的由来,我们都知道普通的apk我们可以通过应用商店playstore等进行更新,apex的引入是google希望也能通过playstore更新bin文件.so etc配置文件等类型文件. 这些文件的安装实际通过apexd来进行,现在我们来解析一下apexd, apexd的启动分为两个阶段,bootstrap和普通apexd启动,下面分析apexd bootstrap

1. rc文件启动
/system/core/rootdir/init.rc#80

 本阶段启动三个bootstrap apex,分别为com.android.i18n,com.android.runtime com.android.tzdata,在bootstrap 阶段主要就是提供 critical shared libraries


77     # Run apexd-bootstrap so that APEXes that provide critical libraries
78     # become available. Note that this is executed as exec_start to ensure that
79     # the libraries are available to the processes started after this statement.
80     exec_start apexd-bootstrap    

2. main函数
/system/apex/apexd/apexd_main.cpp#118

int main(int /*argc*/, char** argv) {
...
118    android::apex::SetConfig(android::apex::kDefaultConfig); → 全局config
...
151    if (has_subcommand) {
152      return HandleSubcommand(argv);    →  这里走 apexd --bootstrap
153    }

全局config 就是这些常量:

61  static const ApexdConfig kDefaultConfig = {
62      kApexStatusSysprop,       →    kApexStatusSysprop = "apexd.status" apex的状态
63      kApexPackageBuiltinDirs,   →  所有apex文件所在的目录data、product、system、system_ext、vendor
64      kActiveApexPackagesDataDir,   →  kActiveApexPackagesDataDir = "/data/apex/active"
65      kApexDecompressedDir,         →    kApexDecompressedDir = "/data/apex/decompressed";
66      kOtaReservedDir,             →        kOtaReservedDir = "/data/apex/ota_reserved";
67      kApexHashTreeDir,           →   kApexHashTreeDir = "/data/apex/hashtree";
68      kStagedSessionsDir,        →   StagedSessionsDir = "/data/app-staging"
69      kMetadataSepolicyStagedDir,  →  kMetadataSepolicyStagedDir = "/metadata/sepolicy/staged";
70      kVmPayloadMetadataPartitionProp,  →  kVmPayloadMetadataPartitionProp = "apexd.payload_metadata.path"
71      "u:object_r:staging_data_file",  → staging_data_file的 file contexts
72  };

3. OnBootstrap()
 

/system/apex/apexd/apexd_main.cpp#38
36  int HandleSubcommand(char** argv) {
37    if (strcmp("--bootstrap", argv[1]) == 0) {
38      SetDefaultTag("apexd-bootstrap");
39      LOG(INFO) << "Bootstrap subcommand detected";
40      return android::apex::OnBootstrap();
41    }/system/apex/apexd/apexd.cpp#2566
2566  int OnBootstrap() {
2567    ATRACE_NAME("OnBootstrap");
2568    auto time_started = boot_clock::now();
2569  
2570    ApexFileRepository& instance = ApexFileRepository::GetInstance(); → 创建个实例 啥也没做
2571    Result<void> status =
2572        instance.AddPreInstalledApex(gConfig->apex_built_in_dirs);  →  3.1 scan kApexPackageBuiltinDirs下的所有apex, 详见3.1.1
2573    if (!status.ok()) {
2574      LOG(ERROR) << "Failed to collect APEX keys : " << status.error();
2575      return 1;
2576    }
2577  
2578    const auto& pre_installed_apexes = instance.GetPreInstalledApexFiles(); → 从全局变量中得到 pre_installed_store_
2579    int loop_device_cnt = pre_installed_apexes.size();
2580    // Find all bootstrap apexes
2581    std::vector<ApexFileRef> bootstrap_apexes;
2582    for (const auto& apex : pre_installed_apexes) {      →  遍历 所有apexfile
2583      if (IsBootstrapApex(apex.get())) {      →    判断是否是bootstrap apex,有三个com.android.i18n com.android.runtime com.android.tzdata
2584        LOG(INFO) << "Found bootstrap APEX " << apex.get().GetPath();
2585        bootstrap_apexes.push_back(apex);
2586        loop_device_cnt++;
2587      }
2588      if (apex.get().GetManifest().providesharedapexlibs()) {
2589        LOG(INFO) << "Found sharedlibs APEX " << apex.get().GetPath();
2590        // Sharedlis APEX might be mounted 2 times:
2591        //   * Pre-installed sharedlibs APEX will be mounted in OnStart
2592        //   * Updated sharedlibs APEX (if it exists) will be mounted in OnStart
2593        //
2594        // We already counted a loop device for one of these 2 mounts, need to add
2595        // 1 more.
2596        loop_device_cnt++;
2597      }
2598    }
.........
2613    if (auto res = loop::PreAllocateLoopDevices(loop_device_cnt); !res.ok()) { -----> 通过ioctl /dev/loop-control,按apex数量预先分配loop设备
2614      LOG(ERROR) << "Failed to pre-allocate loop devices : " << res.error();
2615    }
2616  
2617    DeviceMapper& dm = DeviceMapper::Instance();              -----> 创建 DeviceMapper实例,其实就是open(/dev/device-mapper)
2618    // Create empty dm device for each found APEX.
2619    // This is a boot time optimization that makes use of the fact that user space
2620    // paths will be created by ueventd before apexd is started, and hence
2621    // reducing the time to activate APEXEs on /data.
2622    // Note: since at this point we don't know which APEXes are updated, we are
2623    // optimistically creating a verity device for all of them. Once boot
2624    // finishes, apexd will clean up unused devices.
2625    // TODO(b/192241176): move to apexd_verity.{h,cpp}
2626    for (const auto& apex : pre_installed_apexes) {
2627      const std::string& name = apex.get().GetManifest().name();
2628      if (!dm.CreateEmptyDevice(name)) {                         -----> 按照apex的名字,所有apex创建空的dm device,ioctl(fd_, DM_DEV_CREATE, &io)
2629        LOG(ERROR) << "Failed to create empty device " << name;
2630      }
2631    }
2632  
2633    // Create directories for APEX shared libraries.
2634    auto sharedlibs_apex_dir = CreateSharedLibsApexDir();        -----> 创建目录  /apex/sharedlibs/lib{,64} for SharedLibs APEXes
2635    if (!sharedlibs_apex_dir.ok()) {
2636      LOG(ERROR) << sharedlibs_apex_dir.error();
2637      return 1;
2638    }
2639  
2640    // Now activate bootstrap apexes.
2641    auto ret =
2642        ActivateApexPackages(bootstrap_apexes, ActivationMode::kBootstrapMode);  ----> 真正的重点来了 bootstrap阶段的ActivateApexPackages(),见3.1.2
2643    if (!ret.ok()) {
2644      LOG(ERROR) << "Failed to activate bootstrap apex files : " << ret.error();
2645      return 1;
2646    }
2647  
2648    OnAllPackagesActivated(/*is_bootstrap=*/true);
2649    auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
2650      boot_clock::now() - time_started).count();
2651    LOG(INFO) << "OnBootstrap done, duration=" << time_elapsed;
2652    return 0;
2653  }


3.1.1 ScanBuiltInDir 遍历

59  Result<void> ApexFileRepository::ScanBuiltInDir(const std::string& dir) {
60    LOG(INFO) << "Scanning " << dir << " for pre-installed ApexFiles";
.....
72    // TODO(b/179248390): scan parallelly if possible
73    for (const auto& file : *all_apex_files) {   →  遍历所有apex
74      LOG(INFO) << "Found pre-installed APEX " << file;
75      Result<ApexFile> apex_file = ApexFile::Open(file);     →  得到解析的ApexFile
.....
134      auto it = pre_installed_store_.find(name);
135      if (it == pre_installed_store_.end()) {
136        pre_installed_store_.emplace(name, std::move(*apex_file));  →  apex_file保存到全局变量中pre_installed_store_

普通apex解压如下:

1 普通apex:
com.android.runtime-arm64$ ls
AndroidManifest.xml  apex_build_info.pb  apex_manifest.pb  apex_payload.img  apex_pubkey  assets  META-INF  resources.arsc2 compressed apex解压如下:
compressed ape实际上是对origin_apex又包了一层
com.google.android.art_compressed$ ls
AndroidManifest.xml   apex_build_info.pb   apex_manifest.pb   apex_pubkey   META-INF   original_apex  'original_apex (1)'   stamp-cert-sha256解压original_apex
com.google.android.art_compressed/original_apex (1)$ ls
AndroidManifest.xml  apex_build_info.pb  apex_manifest.pb  apex_payload.img  apex_pubkey  assets  META-INF  stamp-cert-sha256关键文件是: apex_payload.img(可以直接挂载)  apex_pubkey(用于avb验证) apex_manifest.pb

主要解压以下内容,compressed apex只解压一层:
 

/system/apex/apexd/apex_file.cpp#81
81  Result<ApexFile> ApexFile::Open(const std::string& path) {
82    std::optional<uint32_t> image_offset;
83    std::optional<size_t> image_size;
84    std::string manifest_content;
85    std::string pubkey;
86    std::optional<std::string> fs_type;
87    ZipEntry entry;
88  
89    unique_fd fd(open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
90    if (fd < 0) {
91      return ErrnoError() << "Failed to open package " << path << ": "
92                          << "I/O error";
93    }
94  
95    ZipArchiveHandle handle;
96    auto handle_guard =
97        android::base::make_scope_guard([&handle] { CloseArchive(handle); });
98    int ret = OpenArchiveFd(fd.get(), path.c_str(), &handle,
99                            /*assume_ownership=*/false);
100    if (ret < 0) {
101      return Error() << "Failed to open package " << path << ": "
102                     << ErrorCodeString(ret);
103    }
104  
105    bool is_compressed = true;
106    ret = FindEntry(handle, kCompressedApexFilename, &entry);  ==> 常量为original_apex 只是check 一下是否是compressed apex啥也不做
107    if (ret < 0) {
108      is_compressed = false;
109    }
110  
111    if (!is_compressed) {                           
112      // Locate the mountable image within the zipfile and store offset and size.
113      ret = FindEntry(handle, kImageFilename, &entry);     ==> 不是compressed apex,解压apex_payload.img,获取文件系统类型 mount时使用
114      if (ret < 0) {
115        return Error() << "Could not find entry \"" << kImageFilename
116                       << "\" or \"" << kCompressedApexFilename
117                       << "\" in package " << path << ": "
118                       << ErrorCodeString(ret);
119      }
120      image_offset = entry.offset;
121      image_size = entry.uncompressed_length;
122  
123      auto fs_type_result = RetrieveFsType(fd, image_offset.value()); ==》 f2fs ext4 erofs
124      if (!fs_type_result.ok()) {
125        return Error() << "Failed to retrieve filesystem type for " << path
126                       << ": " << fs_type_result.error();
127      }
128      fs_type = std::move(*fs_type_result);
129    }
130  
131    ret = FindEntry(handle, kManifestFilenamePb, &entry);    ==>  解压apex_manifest.pb的内容保存起来
132    if (ret < 0) {
133      return Error() << "Could not find entry \"" << kManifestFilenamePb
134                     << "\" in package " << path << ": " << ErrorCodeString(ret);
135    }
136  
137    uint32_t length = entry.uncompressed_length;
138    manifest_content.resize(length, '\0');
139    ret = ExtractToMemory(handle, &entry,
140                          reinterpret_cast<uint8_t*>(&(manifest_content)[0]),
141                          length);
142    if (ret != 0) {
143      return Error() << "Failed to extract manifest from package " << path << ": "
144                     << ErrorCodeString(ret);
145    }
146  
147    ret = FindEntry(handle, kBundledPublicKeyFilename, &entry);  ==>  解压apex_pubkey的内容保存起来
148    if (ret >= 0) {
149      length = entry.uncompressed_length;
150      pubkey.resize(length, '\0');
151      ret = ExtractToMemory(handle, &entry,
152                            reinterpret_cast<uint8_t*>(&(pubkey)[0]), length);
153      if (ret != 0) {
154        return Error() << "Failed to extract public key from package " << path
155                       << ": " << ErrorCodeString(ret);
156      }
157    }
158  
159    Result<ApexManifest> manifest = ParseManifest(manifest_content);  
160    if (!manifest.ok()) {
161      return manifest.error();
162    }
163  
164    if (is_compressed && manifest->providesharedapexlibs()) {
165      return Error() << "Apex providing sharedlibs shouldn't be compressed";
166    }
167  
168    // b/179211712 the stored path should be the realpath, otherwise the path we
169    // get by scanning the directory would be different from the path we get
170    // by reading /proc/mounts, if the apex file is on a symlink dir.
171    std::string realpath;
172    if (!android::base::Realpath(path, &realpath)) {
173      return ErrnoError() << "can't get realpath of " << path;
174    }
175  
176    return ApexFile(realpath, image_offset, image_size, std::move(*manifest),  ==>  返回一个ApexFile
177                    pubkey, fs_type, is_compressed);
178  }


apex_manifest.pb的内容就是:
 

/system/apex/proto/apex_manifest.proto#17
syntax = "proto3";
18
19 package apex.proto;
20
21 option java_package = "com.android.apex";
22 option java_outer_classname = "Protos";
23
24 message ApexManifest {
25
26   // APEX Name. Note that this can be different from what PackageManager sees.
27   // This is used to identify an APEX and to mount under /apex directory.
28   string name = 1;
29
30   // Version Number
31   int64 version = 2;
32
33   // Pre Install Hook
34   string preInstallHook = 3;
35
36   // Post Install Hook
37   // This feature is not supported.
38   string postInstallHook = 4 [ deprecated = true ];
39
40   // Version Name
41   string versionName = 5;
42
43   // Signals whenever this APEX doesn't contain any executable code.
44   // If this field is set to true, then apexd will mount this apex
45   // with MS_NOEXEC flag.
46   bool noCode = 6;
47
48   // List of native libs which can be used by other apexes or system.
49   repeated string provideNativeLibs = 7;
50
51   // List of native libs which this apex uses from other apexes or system.
52   repeated string requireNativeLibs = 8;
53
54   // List of JNI libs.
55   // linkerconfig/libnativeloader use this field so that java libraries can
56   // load JNI libraries in the same apex.
57   // This is supposed to be filled by the build system with libraries which are
58   // marked as "is_jni: true" from the list of "native_shared_libs".
59   repeated string jniLibs = 9;
60
61   // List of libs required that are located in a shared libraries APEX.  The
62   // Android platform only checks whether this list is non-empty, and by default
63   // the Android build system never sets this. This field can be used when
64   // producing or processing an APEX using libraries in /apex/sharedlibs (see
65   // `provideSharedApexLibs` field) to store some information about the
66   // libraries.
67   repeated string requireSharedApexLibs = 10;
68
69   // Whether this APEX provides libraries to be shared with other APEXs. This
70   // causes libraries contained in the APEX to be made available under
71   // /apex/sharedlibs .
72   bool provideSharedApexLibs = 11;
73
74   message CompressedApexMetadata { ---> 只有compressed apex存在此字段,用于avb验证
75
76     // Valid only for compressed APEX. This field contains the root digest of
77     // the original_apex contained inside CAPEX.
78     string originalApexDigest = 1;
79   }
80
81   // Exists only for compressed APEX
82   CompressedApexMetadata capexMetadata = 12;
83
84   // Indicates that this APEX can be updated without rebooting device.
85   bool supportsRebootlessUpdate = 13;
86
87   // VNDK version for apexes depending on a specific version of VNDK libs.
88   string vndkVersion = 14;
89 }

3.1.2 bootstrap阶段的三个bootstrap apex的激活ActivateApexPackages()

1810    std::vector<std::future<std::vector<Result<const ApexFile*>>>> futures;
1811    futures.reserve(worker_num);
1812    for (size_t i = 0; i < worker_num; i++) {
1813      futures.push_back(std::async(std::launch::async, ActivateApexWorker,  ------> 使用ActivateApexWorker 异步 activate apexes
1814                                   std::ref(mode), std::ref(apex_queue),
1815                                   std::ref(apex_queue_mutex)));
1816    }
/system/apex/apexd/apexd.cpp#1744
1744  std::vector<Result<const ApexFile*>> ActivateApexWorker(
1745      ActivationMode mode, std::queue<const ApexFile*>& apex_queue,
1746      std::mutex& mutex) {
1747    ATRACE_NAME("ActivateApexWorker");
1748    std::vector<Result<const ApexFile*>> ret;
1749  
1750    while (true) {
1751      const ApexFile* apex;    
1752      { //为什么这写了个代码块,所有的线程操作同一个变量apex_queue,当然要加智能锁(代码块完事自动释放),多少个apex就开多少个线程,FIFO, pop
1753        std::lock_guard lock(mutex);  
1754        if (apex_queue.empty()) break;
1755        apex = apex_queue.front();
1756        apex_queue.pop();
1757      }
1758  
1759      std::string device_name;
1760      if (mode == ActivationMode::kBootMode) {
1761        device_name = apex->GetManifest().name();
1762      } else {
1763        device_name = GetPackageId(apex->GetManifest());
1764      }
1765      if (mode == ActivationMode::kOtaChrootMode) {
1766        device_name += ".chroot";
1767      }
1768      bool reuse_device = mode == ActivationMode::kBootMode;
1769      auto res = ActivatePackageImpl(*apex, device_name, reuse_device);  ----> 正式激活apex package
1770      if (!res.ok()) {
1771        ret.push_back(Error() << "Failed to activate " << apex->GetPath() << "("
1772                              << device_name << "): " << res.error());
1773      } else {
1774        ret.push_back({apex});
1775      }
1776    }
1777  
1778    return ret;
1779  }
/system/apex/apexd/apexd.cpp?#1361
1361  Result<void> ActivatePackageImpl(const ApexFile& apex_file,
1362                                   const std::string& device_name,
1363                                   bool reuse_device) {
...................................................................
1419    const std::string& mount_point =
1420        apexd_private::GetPackageMountPoint(manifest);
1421  
1422    if (!version_found_mounted) {
1423      auto mount_status = MountPackage(apex_file, mount_point, device_name,  ------------------> 实际的mount动作 见第四节
1424                                       reuse_device, /*temp_mount=*/false);
1425      if (!mount_status.ok()) {
1426        return mount_status;
1427      }
1428    }bind mount:
1419    const std::string& mount_point =
1420        apexd_private::GetPackageMountPoint(manifest);   -----------> /aepx/com.android.adbd@340912002
1421  
...........
1439      bool mounted_latest = false;
1440      // Bind mount the latest version to /apex/<package_name>, unless the
1441      // package provides shared libraries to other APEXs.
1442      if (is_newest_version) {
1443        const Result<void>& update_st = apexd_private::BindMount(
1444            apexd_private::GetActiveMountPoint(manifest), mount_point);  ----------> GetActiveMountPoint() /aepx/com.android.adbd  例:
mount --bind  /aepx/com.android.adbd  /aepx/com.android.adbd@340912002


4 这里是把各个目录的apex mount到根目录的/apex下 顺便介绍下avb验证
 

/system/apex/apexd/apexd.cpp#MountPackage1151  Result<void> MountPackage(const ApexFile& apex, const std::string& mount_point,
1152                            const std::string& device_name, bool reuse_device,
1153                            bool temp_mount) {
1154    auto ret =
1155        MountPackageImpl(apex, mount_point, device_name,
1156                         GetHashTreeFileName(apex, /* is_new= */ false),
1157                         /* verify_image = */ false, reuse_device, temp_mount);/system/apex/apexd/apexd.cpp#436
436  Result<MountedApexData> MountPackageImpl(const ApexFile& apex,
437                                           const std::string& mount_point,
438                                           const std::string& device_name,
439                                           const std::string& hashtree_file,
440                                           bool verify_image, bool reuse_device,
441                                           bool temp_mount = false) {
.......................
498    auto& instance = ApexFileRepository::GetInstance();
499  
500    auto public_key = instance.GetPublicKey(apex.GetManifest().name()); ----------------> 获取avbpubkey
501    if (!public_key.ok()) {
502      return public_key.error();
503    }
504  
505    auto verity_data = apex.VerifyApexVerity(*public_key); -------> 验证apex_payload.img的vbmeta,这个类似于chain partition的验证,验证摘要签名再对比pubkey......................
557      auto verity_table =
558          CreateVerityTable(*verity_data, loopback_device.name, hash_device, -----------> 创建hashtree
559                            /* restart_on_corruption = */ !verify_image);
560      Result<DmVerityDevice> verity_dev_res =
561          CreateVerityDevice(device_name, *verity_table, reuse_device);      -----------> 创建dm-verity

 

 

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

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

相关文章

JAVAEE——文件IO之文件操作

文章目录 文件的创建和销毁File概述构造方法常用的方法getAbsolutePath()exists()isDirectory()isFile()createNewFile()delete()deleteOnExit()list()listFiles()mkdir() 文件的创建和销毁 上面我们介绍了文件的读写操作那么文件的创建等的操作如何进行呢&#xff1f;这个操作…

.NET 设计模式—简单工厂(Simple Factory Pattern)

简介 简单工厂模式&#xff08;Simple Factory Pattern&#xff09;属于类的创建型模式&#xff0c;又叫静态工厂方法模式&#xff08;Static FactoryMethod Pattern&#xff09;,是通过一个工厂类来创建对象&#xff0c;根据不同的参数或条件返回相应的对象实例。这种模式隐藏…

前视声呐目标识别定位(四)-代码解析之启动识别模块

前视声呐目标识别定位&#xff08;一&#xff09;-基础知识 前视声呐目标识别定位&#xff08;二&#xff09;-目标识别定位模块 前视声呐目标识别定位&#xff08;三&#xff09;-部署至机器人 前视声呐目标识别定位&#xff08;四&#xff09;-代码解析之启动识别模块 …

C语言之分支语句和循环语句

前言 一、什么是语句&#xff1f; 二、分支语句&#xff08;选择结构&#xff09; 2.1 if语句 2.2 switch语句 三、循环语句 3.1 while循环 3.2 break与continue语句 3.3 getchar()与putchar() 3.3.1 缓冲区 3.4 for循环 3.4.1 一些for循环的变种 3.5 do...while循…

AcWing 785. 快速排序——算法基础课题解

AcWing 785. 快速排序 文章目录 题目描述CGo模板 题目描述 给定你一个长度为 n 的整数数列。 请你使用快速排序对这个数列按照从小到大进行排序。 并将排好序的数列按顺序输出。 输入格式 输入共两行&#xff0c;第一行包含整数 n。 第二行包含 n 个整数&#xff08;所有…

C语言中的结构体:高级特性与扩展应用

前言 结构体在C语言中的应用不仅限于基本的定义和使用&#xff0c;还包含一些高级特性和扩展应用&#xff0c;这些特性和应用使得结构体在编程中发挥着更加重要的作用。 一、位字段&#xff08;Bit-fields&#xff09; 在结构体中&#xff0c;我们可以使用位字段来定义成员…

AJAX —— 学习(二)

目录 一、利用 JSON 字符串 返回数据 &#xff08;一&#xff09;基础代码 &#xff08;二&#xff09;原理及实现 二、nodmon 工具 自动重启服务 &#xff08;一&#xff09;用途 &#xff08;二&#xff09;下载 &#xff08;三&#xff09;使用 三、IE 缓存问题 &a…

QA测试开发工程师面试题满分问答8: mysql数据库的索引定义、用途和使用场景

MySQL数据库索引是一种数据结构&#xff0c;用于提高数据库的查询效率。索引是基于表中的一个或多个列构建的&#xff0c;它们允许数据库系统快速定位和访问表中的特定数据&#xff0c;而无需扫描整个表。 索引的定义 在MySQL中&#xff0c;可以使用CREATE INDEX语句定义索引…

pulsar集群部署流程及方案

# 部署方案: 集群模式部署,broker和bookie混合部署,zk同时部署在三台机器上面(资源充足zk最好单独部署,注意pulsar和zk版本兼容性) # 1.集群资源使用(实际测试情况CPU使用率比内存高,建议CPU设置大于内存,不同业务场景有差异) 三台主机centos7 16核16GB,每台挂载1TB机械硬盘(不…

Linux:进程终止和等待

一、进程终止 main函数的返回值也叫做进程的退出码&#xff0c;一般0表示成功&#xff0c;非零表示失败。我们也可以用不同的数字来表示不同失败的原因。 echo $?//打印最近一次进程执行的退出码 而作为程序猿&#xff0c;我们更需要知道的是错误码所代表的错误信息&#x…

MongoDB聚合运算符:$lte

文章目录 语法举例 $lte聚合运算符用于比较两个值&#xff0c;如果第一个小于等于第二个&#xff0c;返回true&#xff1b;如果第一个值大于第二个值&#xff0c;返回false。 语法 { $lte: [ <expression1>, <expression2> ] }$lte可以用来比较任何类型的值&…

嵌入式算法开发系列之卡尔曼滤波算法

卡尔曼滤波算法 文章目录 卡尔曼滤波算法前言一、卡尔曼滤波算法原理二、算法应用三、C语言实现总结 前言 在嵌入式系统中&#xff0c;传感器数据通常受到噪声、误差和不确定性的影响&#xff0c;因此需要一种有效的方法来估计系统的状态。卡尔曼滤波算法是一种基于概率理论的…

网络安全系统之——PKI体系

一、公钥基础设施 公钥基础设施 PKI&#xff08;Public Key Infrastructure&#xff09;是通过使用公钥技术和数据证书来提供信息系统安全服务&#xff0c;并负责验证数字证书持有者身份的一种体系。PKI 基础设施采用证书管理公钥&#xff0c;通过第三方可信任认证中心&#xf…

【信号与系统 - 2】傅里叶变换与反变换

1 傅里叶变换与频谱密度函数 非周期信号可以看成是 T → ∞ T\to{\infty} T→∞ 的周期信号 由于 w ↓ 2 π T ↑ w\downarrow\frac{2\pi}{T\uparrow} w↓T↑2π​&#xff0c; T T T 无限大&#xff0c;则基波频率 w w w 无限小&#xff0c;可以视为 d w dw dw 在频谱图…

RL — 强化学习算法概述

目录 一、说明二、无模型算法三、基于模型的 RL四、价值学习五、策略梯度5.1 强度:5.2 局限性5.3 蒙特卡洛政策梯度六、TRPO公司七、行动者兼评论家八、基于模型的 RL一、说明 我们在本系列中研究了许多强化学习 (RL) 算法,例如,用于 MoJoCo 任务的策略梯度方法、用于 At…

【亲测有效】Ubuntu系统下调整/home分区大小(无损扩容)

一、引言 当时给系统分区的时候&#xff0c;home和根分区都是100GB左右&#xff0c;突然发现home 分区不够用了&#xff0c;于是决定将根分区的部分空间挪移到home分区。 常用调整分区大小的工具有&#xff1a;GParted工具&#xff0c;DiskGenius工具。如果GParted工具无法调…

Linux文件权限管理详解——以CentOS为例

Linux文件权限管理详解——以CentOS为例 一、引言 在Linux系统中,文件权限管理是一项非常重要的任务。正确的文件权限设置能够保护系统的安全性,防止未授权访问和数据泄露。本文将详细介绍Linux文件权限管理的基本概念、常用命令以及如何在CentOS系统中进行实际操作。 二、…

电工技术学习笔记——直流电路及其分析方法

一、直流电路 电路的组成 1. 电压和电流的参考方向 电压&#xff08;Voltage&#xff09;&#xff1a;电压是电场力对电荷产生的作用&#xff0c;表示为电荷单位正电荷所具有的能量。在电路中&#xff0c;电压通常被定义为两点之间的电势差&#xff0c;具有方向性&#xff0c;…

HomePlug AV

目录 HomePlug AV的基本概念基本术语网络概念网络实例 HomePlug AV物理层&#xff08;PHY&#xff09;HomePlug AV OFDM收发器架构PHY的调制模式FC调制和ROBO调制物理层的特点OFDM频域/时域转换开窗/槽式OFDM信号和噪声PHY发送控制——信道自适应PHY帧格式&#xff08;Symbol&a…

中间件复习之-RPC框架

什么是RPC框架&#xff1f; RPC(Remote Procedure Call):远程过程调用。当多个应用部署在多个服务器上时&#xff0c;由于他们不在一个内存空间上&#xff0c;因此需要网络来进行通信&#xff0c;而RPC允许它像调用本地方法一样调用远程服务。 RPC原理 服务消费方通过RPC客户…