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

Android 12 S 系统开机流程分析 - SetupSelinux(二)

Android 12 S 系统开机流程分析-FirstStageMain(一)

本文接着上文开始讲解,上文中最后一步执行后会执行init启动过程中的第二步SetupSelinux(Selinux配置阶段),这样又会走到main.cpp中的main方法。

目录

1. SetupSelinux

1.1 初始化kernel log系统

1.2 ReadPolicy

1.3 加载策略

1.4 使能/关闭Selinux并写入节点

1.5 selinux_android_restorecon

1.6  重新执行init进程并携带参数second_stage


1. SetupSelinux

由于上一篇中最后一步在重新执行init的时候携带了参数selinux_setup,所以此处会走入SetupSelinux方法,加载selinux的策略。

SetupSelinux主要功能:

1)open sepolicy

2)load sepolicy

3)使能/关闭selinux

int main(int argc, char** argv) {
...if (!strcmp(argv[1], "selinux_setup")) {
//执行此处return SetupSelinux(argv);}if (!strcmp(argv[1], "second_stage")) {return SecondStageMain(argc, argv);}}return FirstStageMain(argc, argv);
}

1.1 初始化kernel log系统

1)初始化kernel log

2)若系统发生了panic,则走InstallRebootSignalHandlers

InstallRebootSignalHandlers中主要做了以下几件事:

    1. 初始化了一个自定义信号集,将其所有信号都填充满,即将信号集中的所有的标志位都置为1,使得这个集合包含所有可接受的信号,也就是阻塞所有信号。这个函数可以用于快速创建一个包含所有信号的信号集,然后可以根据需要删除其中的某些信号。

    2. init创建出来的子进程不做处理,直接exit;如果不是子进程,则代表是init进程,则执行InitFatalReboot

    3. 通过syscall向内核发送重启命令

    4. 捕获一些信号

int SetupSelinux(char** argv) {SetStdioToDevNull(argv);InitKernelLogging(argv);if (REBOOT_BOOTLOADER_ON_PANIC) {InstallRebootSignalHandlers();}boot_clock::time_point start_time = boot_clock::now();
//这是R上为了R system.img/system_ext.img作为system_ext.img工作在old vendor.img上。
//我们在init第二阶段挂载system_ext,因为在system-only OTA场景中boot.img的init第一阶段是不会被更新的。MountMissingSystemPartitions();SelinuxSetupKernelLogging();LOG(INFO) << "Opening SELinux policy";

 SelinuxGetVendorAndroidVersion主要作用是读/vendor/etc/selinux/plat_sepolicy_vers.txt中的第一行,获取Selinux vendor version。

int SelinuxGetVendorAndroidVersion() {static int vendor_android_version = [] {if (!IsSplitPolicyDevice()) {
//如果这个设备不拆分sepolicy文件,它就不是一个Treble设备return __ANDROID_API_FUTURE__;}std::string version;
//获取vendor Selinux version
//读/vendor/etc/selinux/plat_sepolicy_vers.txt中的第一行
//可以adb看下vendor版本:
//adb shell cat /vendor/etc/selinux/plat_sepolicy_vers.txt
//32.0if (!GetVendorMappingVersion(&version)) {LOG(FATAL) << "Could not read vendor SELinux version";}
//解析version值,version一般是带小数点的,比如32.0。int major_version;std::string major_version_str(version, 0, version.find('.'));if (!ParseInt(major_version_str, &major_version)) {PLOG(FATAL) << "Failed to parse the vendor sepolicy major version "<< major_version_str;}
//返回version中整数return major_version;}();return vendor_android_version;
}

1.2 ReadPolicy

ReadPolicy主要做了两件事

1)open split policy

2)读sepolicy

这个是SetupSelinux的主要功能之一。

    // Read the policy before potentially killing snapuserd.std::string policy;ReadPolicy(&policy);auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();if (snapuserd_helper) {// Kill the old snapused to avoid audit messages. After this we cannot// read from /system (or other dynamic partitions) until we call// FinishTransition().snapuserd_helper->StartTransition();}

ReadPolicy的主要功能是:

    如果设备中存在/system/etc/selinux/plat_sepolicy.cil文件,并可以访问,则IsSplitPolicyDevice为true,会走OpenSplitPolicy流程,这个设备中默认都有这个文件的。

void ReadPolicy(std::string* policy) {PolicyFile policy_file;
//constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";//bool IsSplitPolicyDevice() {
//    return access(plat_policy_cil_file, R_OK) != -1;
//}
//IsSplitPolicyDevice如上,如果能访问设备中的/system/etc/selinux/plat_sepolicy.cil文件,
//则IsSplitPolicyDevice为true,会走OpenSplitPolicybool ok = IsSplitPolicyDevice() ? OpenSplitPolicy(&policy_file): OpenMonolithicPolicy(&policy_file);if (!ok) {LOG(FATAL) << "Unable to open SELinux policy";}if (!android::base::ReadFdToString(policy_file.fd, policy)) {PLOG(FATAL) << "Failed to read policy file: " << policy_file.path;}
}

OpenSplitPolicy的主要功能是:

1)如果设备是userdebug版本+设备unlock+存在/debug_ramdisk/adb_debug.prop并且可以访问,则加载userdebug system sepolicy

 2)从代码看是access system,vendor,system_ext和vendor下etc/selinux/mapping/下的.cil文件
3)再查看对应目录下的.cil文件内容是否为空,不为空则将对应目录下的.cil文件内容push进compile_args
4)执行compile_args并等待,决定include哪个mapping下的.cil文件

5)将临时文件/dev/sepolicy.XXXXXX的fd和path保存进policy_file->fd和 policy_file->path。

bool OpenSplitPolicy(PolicyFile* policy_file) {
//若存在/force_debuggable,则在init启动第一阶段,force_debuggable_env 环境变量已经设置好了
//setenv("INIT_FORCE_DEBUGGABLE", "true", 1);const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
//是userdebug版本+设备unlock+存在/debug_ramdisk/adb_debug.prop并且可以访问,
//则加载userdebug system sepolicybool use_userdebug_policy =((force_debuggable_env && "true"s == force_debuggable_env) &&AvbHandle::IsDeviceUnlocked() && access(kDebugRamdiskSEPolicy, F_OK) == 0);if (use_userdebug_policy) {LOG(WARNING) << "Using userdebug system sepolicy";}if (!use_userdebug_policy) {if (auto res = FindPrecompiledSplitPolicy(); res.ok()) {unique_fd fd(open(res->c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));if (fd != -1) {policy_file->fd = std::move(fd);policy_file->path = std::move(*res);return true;}} else {LOG(INFO) << res.error();}}// No suitable precompiled policy could be loaded
//没有合适的策略可以加载LOG(INFO) << "Compiling SELinux policy";// We store the output of the compilation on /dev because this is the most convenient tmpfs// storage mount available this early in the boot sequence.char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
//mkostemp:创建临时文件
//创建临时文件/dev/sepolicy.XXXXXX,其fd为compiled_sepolicy_fdunique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));if (compiled_sepolicy_fd < 0) {PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;return false;}
...
//从代码看是access system,vendor,system_ext和vendor下etc/selinux/mapping/下的.cil文件
//再查看对应目录下的.cil文件内容是否为空,不为空则将对应目录下的.cil文件内容push进compile_args
//执行compile_args并等待
//将临时文件/dev/sepolicy.XXXXXX的fd和path保存进policy_file->fd和 policy_file->path。
//决定include哪个mapping下的.cil文件std::string vend_plat_vers;if (!GetVendorMappingVersion(&vend_plat_vers)) {return false;}std::string plat_mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");std::string plat_compat_cil_file("/system/etc/selinux/mapping/" + vend_plat_vers +".compat.cil");if (access(plat_compat_cil_file.c_str(), F_OK) == -1) {plat_compat_cil_file.clear();}std::string system_ext_policy_cil_file("/system_ext/etc/selinux/system_ext_sepolicy.cil");if (access(system_ext_policy_cil_file.c_str(), F_OK) == -1) {system_ext_policy_cil_file.clear();}std::string system_ext_mapping_file("/system_ext/etc/selinux/mapping/" + vend_plat_vers +".cil");if (access(system_ext_mapping_file.c_str(), F_OK) == -1) {system_ext_mapping_file.clear();}std::string system_ext_compat_cil_file("/system_ext/etc/selinux/mapping/" + vend_plat_vers +".compat.cil");if (access(system_ext_compat_cil_file.c_str(), F_OK) == -1) {system_ext_compat_cil_file.clear();}std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");if (access(product_policy_cil_file.c_str(), F_OK) == -1) {product_policy_cil_file.clear();}std::string product_mapping_file("/product/etc/selinux/mapping/" + vend_plat_vers + ".cil");if (access(product_mapping_file.c_str(), F_OK) == -1) {product_mapping_file.clear();}// vendor_sepolicy.cil and plat_pub_versioned.cil are the new design to replace// nonplat_sepolicy.cil.std::string plat_pub_versioned_cil_file("/vendor/etc/selinux/plat_pub_versioned.cil");std::string vendor_policy_cil_file("/vendor/etc/selinux/vendor_sepolicy.cil");if (access(vendor_policy_cil_file.c_str(), F_OK) == -1) {// For backward compatibility.// TODO: remove this after no device is using nonplat_sepolicy.cil.vendor_policy_cil_file = "/vendor/etc/selinux/nonplat_sepolicy.cil";plat_pub_versioned_cil_file.clear();} else if (access(plat_pub_versioned_cil_file.c_str(), F_OK) == -1) {LOG(ERROR) << "Missing " << plat_pub_versioned_cil_file;return false;}// odm_sepolicy.cil is default but optional.std::string odm_policy_cil_file("/odm/etc/selinux/odm_sepolicy.cil");if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {odm_policy_cil_file.clear();}const std::string version_as_string = std::to_string(SEPOLICY_VERSION);//有一些代码,我们并不想让clang-format调整格式,这时可以使用注释临时禁用clang-format// clang-format offstd::vector<const char*> compile_args {"/system/bin/secilc",use_userdebug_policy ? kDebugRamdiskSEPolicy: plat_policy_cil_file,"-m", "-M", "true", "-G", "-N","-c", version_as_string.c_str(),plat_mapping_file.c_str(),"-o", compiled_sepolicy,// We don't care about file_contexts output by the compiler"-f", "/sys/fs/selinux/null",  // /dev/null is not yet available};// clang-format on//将对应目录下的.cil文件内容push进compile_argsif (!plat_compat_cil_file.empty()) {compile_args.push_back(plat_compat_cil_file.c_str());}if (!system_ext_policy_cil_file.empty()) {compile_args.push_back(system_ext_policy_cil_file.c_str());}if (!system_ext_mapping_file.empty()) {compile_args.push_back(system_ext_mapping_file.c_str());}if (!system_ext_compat_cil_file.empty()) {compile_args.push_back(system_ext_compat_cil_file.c_str());}if (!product_policy_cil_file.empty()) {compile_args.push_back(product_policy_cil_file.c_str());}if (!product_mapping_file.empty()) {compile_args.push_back(product_mapping_file.c_str());}if (!plat_pub_versioned_cil_file.empty()) {compile_args.push_back(plat_pub_versioned_cil_file.c_str());}if (!vendor_policy_cil_file.empty()) {compile_args.push_back(vendor_policy_cil_file.c_str());}if (!odm_policy_cil_file.empty()) {compile_args.push_back(odm_policy_cil_file.c_str());}compile_args.push_back(nullptr);
//执行compile_args并等待if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {unlink(compiled_sepolicy);return false;}unlink(compiled_sepolicy);
//将临时文件/dev/sepolicy.XXXXXX的fd和path保存进policy_file->fd和 policy_file->path。policy_file->fd = std::move(compiled_sepolicy_fd);policy_file->path = compiled_sepolicy;return true;
}

1.3 加载策略

这也是SetupSelinux中最重要的功能之一。

   LoadSelinuxPolicy(policy);

 上一节中对policy进行了赋值。

static void LoadSelinuxPolicy(std::string& policy) {LOG(INFO) << "Loading SELinux policy";set_selinuxmnt("/sys/fs/selinux");if (security_load_policy(policy.data(), policy.size()) < 0) {PLOG(FATAL) << "SELinux:  Could not load policy";}
}

 security_load_policy主要功能是:

1)open $selinux_mnt/load,获取其对应fd

2)根据fd将/dev/sepolicy.XXXXXX中的内容写入$selinux_mnt/load中。selinux_mnt如下#define SELINUXMNT "/sys/fs/selinux"

external/selinux/libselinux/src/load_policy.c
int security_load_policy(void *data, size_t len)
{char path[PATH_MAX];int fd, ret;if (!selinux_mnt) {errno = ENOENT;return -1;}snprintf(path, sizeof path, "%s/load", selinux_mnt);fd = open(path, O_RDWR | O_CLOEXEC);if (fd < 0)return -1;ret = write(fd, data, len);close(fd);if (ret < 0)return -1;return 0;
}

1.4 使能/关闭Selinux并写入节点

获取selinux状态是否是enforce,然后将selinux状态is_enforcing写入 $selinux_mnt/enforce中。selinux_mnt如下

#define SELINUXMNT "/sys/fs/selinux"

    if (snapuserd_helper) {// Before enforcing, finish the pending snapuserd transition.snapuserd_helper->FinishTransition();snapuserd_helper = nullptr;}SelinuxSetEnforcement();
void SelinuxSetEnforcement() {bool kernel_enforcing = (security_getenforce() == 1);
//check selinux是否是enforce状态(开启selinux)bool is_enforcing = IsEnforcing();if (kernel_enforcing != is_enforcing) {if (security_setenforce(is_enforcing)) {PLOG(FATAL) << "security_setenforce(" << (is_enforcing ? "true" : "false")<< ") failed";}}if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result.ok()) {LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();}
}
bool IsEnforcing() {
//如果关闭系统的selinux,即将selinux置为permissive,此处直接返回false即可
//return false;if (ALLOW_PERMISSIVE_SELINUX) {return StatusFromProperty() == SELINUX_ENFORCING;}return true;
}
/external/selinux/libselinux/src/setenforce.c
int security_setenforce(int value)
{int fd, ret;char path[PATH_MAX];char buf[20];if (!selinux_mnt) {errno = ENOENT;return -1;}snprintf(path, sizeof path, "%s/enforce", selinux_mnt);fd = open(path, O_RDWR | O_CLOEXEC);if (fd < 0)return -1;snprintf(buf, sizeof buf, "%d", value);ret = write(fd, buf, strlen(buf));close(fd);if (ret < 0)return -1;return 0;
}

1.5 selinux_android_restorecon

从kernel domain域过渡到init domain域
文件系统在xattrs中存储了SELabels,比如ext4不需要restorecon,但其他文件系统需要

//从kernel domain域过渡到init domain域
//文件系统在xattrs中存储了SELabels,比如ext4不需要restorecon,但其他文件系统需要if (selinux_android_restorecon("/system/bin/init", 0) == -1) {PLOG(FATAL) << "restorecon failed of /system/bin/init failed";}

1.6  重新执行init进程并携带参数second_stage

    setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);const char* path = "/system/bin/init";const char* args[] = {path, "second_stage", nullptr};execv(path, const_cast<char**>(args));// execv() only returns if an error happened, in which case we// panic and never return from this function.PLOG(FATAL) << "execv(\"" << path << "\") failed";return 1;
}

到了SetupSelinux的最后一步,这一步还是重新执行/system/bin/init,这样就会再次走init bin程序的main函数,由代码可以看到,此次携带了参数second_stage。

后面接着讲启动流程

相关文章:

Android 12 S 系统开机流程分析 - SetupSelinux(二)

Android 12 S 系统开机流程分析-FirstStageMain&#xff08;一&#xff09; 本文接着上文开始讲解&#xff0c;上文中最后一步执行后会执行init启动过程中的第二步SetupSelinux(Selinux配置阶段)&#xff0c;这样又会走到main.cpp中的main方法。 目录 1. SetupSelinux 1.1 …...

高速信号PCB布局怎么布?(电子硬件)

对于高速信号&#xff0c;pcb的设计要求会更多&#xff0c;因为高速信号很容易收到其他外在因素的干扰&#xff0c;导致实际设计出来的东西和原本预期的效果相差很多。 所以在高速信号pcb设计中&#xff0c;需要提前考虑好整体的布局布线&#xff0c;良好的布局可以很好的决定布…...

vue 子页面通过暴露属性,实现主页面的某事件的触发

目录 1.前言2.代码2-1 子页面2-2 主页面 1.前言 需求&#xff1a;当我在子页面定义了一个定时器&#xff0c;点击获取验证码&#xff0c;计时器开始倒计时&#xff0c;在这个定时器没有走完&#xff0c;退出关闭子页面&#xff0c;再次进入子页面&#xff0c;定时器此时会被刷…...

计算机丢失mfc140.dll是什么意思?附送修复教程

mfc140.dll是Microsoft Foundation Classes&#xff08;MFC&#xff09;库的一部分&#xff0c;是一种动态链接库&#xff08;DLL&#xff09;文件。MFC库是Microsoft提供的一种C编程框架&#xff0c;它为开发者提供了许多方便的工具和类&#xff0c;以简化Windows应用程序的开…...

R语言将向量横向转换为单行数据框,随后整合数量不确定的数据框

vector1 c(1, “karthik”, “IT”) names(vector1) c(“id”, “name”, “branch”) df data.frame(as.list(vector1)) print(df) 先给向量的元素命名&#xff0c;然后转换为列表&#xff0c;最后转换为数据框。 我的需求大概是这个样子&#xff1a;数量不确定的仅有单行…...

​怎么测试websocket接口

在部分业务中&#xff0c;我们需要使用长连接&#xff0c;我们可以使用http长连接或者websocket&#xff0c;开发结束后难免会遇到测试问题&#xff0c;这里推荐2个&#xff0c;一个是postman&#xff0c;一个是网站 postman 测试网站 测这边推荐测试网站&#xff0c;支持ws/w…...

21 移动网络的前世今生

1、移动网络的发展历程 发展过程就是&#xff1a;2G,3G,4G,5G的过程&#xff0c;用2G看txt&#xff0c;用3G看jpg&#xff0c;用4G看avi。 2、2G网络 手机本来是用来打电话的&#xff0c;不是用来上网的&#xff0c;所以原来在2G时代&#xff0c;上网使用的不是IP网络&#…...

里氏替换原则

定义:子类对象能够替换程序中父类对象出现的任何地方&#xff0c;并且*保证原来程序的逻辑行为不变及正确性不被破坏*。 public class Transporter {private HttpClient httpClient;public Transporter(HttpClient httpClient) {this.httpClient httpClient;}public Response…...

【JS】Chapter11-正则阶段案例

站在巨人的肩膀上 黑马程序员前端JavaScript入门到精通全套视频教程&#xff0c;javascript核心进阶ES6语法、API、js高级等基础知识和实战教程 &#xff08;十一&#xff09;正则&阶段案例 1. 正则表达式 1.1 介绍 正则表达式&#xff08;Regular Expression&#xff0…...

跨时钟域(Clock Domain Crossing,CDC)

本文参考&#xff1a;http://t.csdnimg.cn/VHga2 【数字IC基础】跨时钟域&#xff08;CDC&#xff0c;Clock Domain Crossing&#xff09;_ReRrain的博客-CSDN博客 同步设计&#xff1a;所有设计使用同一时钟源&#xff0c;频率相位可预知。 异步设计&#xff1a;设计中有两…...

PTA古风排版

中国的古人写文字&#xff0c;是从右向左竖向排版的。本题就请你编写程序&#xff0c;把一段文字按古风排版。 输入格式&#xff1a; 输入在第一行给出一个正整数N&#xff08;<100&#xff09;&#xff0c;是每一列的字符数。第二行给出一个长度不超过1000的非空字符串&a…...

SQL 注入漏洞详解

SQL 注入漏洞详解 漏洞描述 sql注入漏洞是指恶意用户在应用与数据库交互的地方利用非法的操作获取数据库内容从以下两点分析: 没有对用户输入的数据进行充分的过滤和验证&#xff0c;导致一些用户利用此漏洞向数据库插入恶意sql语句非法请求数据库从而获得一些敏感数据在与数…...

关于阿里云 ACK ingress部分补充

强调&#xff1a; 本文只是作为记录,过一段时间会删除 跟唐老师学习网络 一 Nginx Ingress管理 ① 流量走向 需求&#xff1a; 应用绑定LoadBalance,会自动创建或使用SLBeip:port --> nodeport_ip:port --> service_ip:port --> pod_ip:port 支持的注解 通过…...

轻量封装WebGPU渲染系统示例<22>- 渲染到纹理(RTT)(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/RTTTest.ts 当前示例运行效果: 此示例基于此渲染系统实现&#xff0c;当前示例TypeScript源码如下: export class RTTTest {private mRscene new RendererScene()…...

官方Redis视图化工具Redisinsight

一、下载最新版本的 docker pull redislabs/redisinsight mkdir /data/redisinsight docker run -d -u root -p 8001:8001 -v /etc/localtime:/etc/localtime -v /data/redisinsight:/db --restartunless-stopped redislabs/redisinsight:latest 二、浏览器打开 http://192…...

Vue+Django REST framework 打造生鲜电商项目课程下载树大根深

VueDjango REST framework 打造生鲜电商项目 链接&#xff1a;https://pan.baidu.com/s/1kEDxPsoTYSVWPYB2H0jbBw?pwd6666 提取码&#xff1a;6666Django是高水准的Python编程语言驱动的一个开源模型&#xff0e;视图&#xff0c;控制器风格的Web应用程序框架&#xff0c;它…...

react中遇到的分页问题

问题&#xff1a; 1.使用useState时不能够进行当前页码的改变&#xff0c;数据不会随着页码变化 2.删除当前页的最后一条数据时&#xff0c;页码返回上一页但是数据为空 解决&#xff1a; 1.由于useState和useRef的区别那我们就不考虑使用useState 2.再删除的逻辑当中添加判断条…...

变电站自动化系统中的安全措施分析及应用-安科瑞

安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;阐述变电运行中的问题&#xff0c;电气自动化系统与安全运行措施&#xff0c;包括自动控制设备的投入&#xff0c;电气自动 化与计算机技术相、设备数据的采集与处理、自动化系统的升级、人工智能技术的应用。 关键…...

【MongoDB】索引 – 文本索引

一、准备工作 这里准备一些数据 db.books.insertMany([{_id: 1, name: "Java", description: "java 入门图书", translation: [{ language: "english", description: "java basic book" }]},{_id: 2, name: "C", descript…...

【广州华锐互动】影视制作VR在线学习:身临其境,提高学习效率

随着科技的不断发展&#xff0c;影视后期制作技术也在日新月异。然而&#xff0c;传统的教学方式往往难以满足学员的学习需求&#xff0c;无法充分展现影视后期制作的魅力和潜力。近年来&#xff0c;虚拟现实(VR)技术的崛起为教学领域带来了新的机遇。通过VR教学课件&#xff0…...

exp1_code

#include <iostream> using namespace std; // 链栈节点结构 struct StackNode { int data; StackNode* next; StackNode(int val) : data(val), next(nullptr) {} }; // 顺序栈实现 class SeqStack { private: int* data; int top; int capac…...

C++----剖析list

前面学习了vector和string&#xff0c;接下来剖析stl中的list&#xff0c;在数据库中学习过&#xff0c;list逻辑上是连续的&#xff0c;但是存储中是分散的&#xff0c;这是与vector这种数组类型不同的地方。所以list中的元素设置为一个结构体&#xff0c;将list设计成双向的&…...

行为型设计模式之Interpreter(解释器)

行为型设计模式之Interpreter&#xff08;解释器&#xff09; 前言&#xff1a; 自己的话理解&#xff1a;自定义一个解释器用来校验参数或数据是否合法。 1&#xff09;意图 给定一个语言&#xff0c;定义它的文法的一种表示&#xff0c;并定义一个解释器&#xff0c;这个解…...

oracle数据恢复—oracle数据库执行truncate命令后的怎么恢复数据?

oracle数据库误执行truncate命令导致数据丢失是一种常见情况。通常情况下&#xff0c;oracle数据库误操作删除数据只需要通过备份恢复数据即可。也会碰到一些特殊情况&#xff0c;例如数据库备份无法使用或者还原报错等。下面和大家分享一例oracle数据库误执行truncate命令导致…...

深入理解汇编语言中的顺序与分支结构

本文将结合Visual Studio环境配置、顺序结构编程和分支结构实现&#xff0c;全面解析汇编语言中的核心编程概念。通过实际案例演示无符号/有符号数处理、分段函数实现和逻辑表达式短路计算等关键技术。 一、汇编环境配置回顾&#xff08;Win32MASM&#xff09; 在Visual Studi…...

Python爬虫实战:研究Hyper 相关技术

一、项目概述 本项目展示了如何结合 Python 的异步编程技术与 Hyper 框架开发一个高性能、可扩展的网络爬虫系统。该系统不仅能够高效地爬取网页内容,还提供了 RESTful API 接口,方便用户通过 API 控制爬虫的运行状态和获取爬取结果。 二、系统架构设计 1. 整体架构 系统采…...

为什么React列表项需要key?(React key)(稳定的唯一标识key有助于React虚拟DOM优化重绘大型列表)

文章目录 1. **帮助 React 识别列表项的变化**2. **性能优化**3. **避免组件状态混乱**4. **为什么使用 rpid 作为 key**5. **不好的做法示例**6. **✅ 正确的做法** 在 React 中添加 key{item.rpid} 是非常重要的&#xff0c;主要有以下几个原因&#xff1a; 1. 帮助 React 识…...

家政小程序开发——AI+IoT技术融合,打造“智慧家政”新物种

基于用户历史订单&#xff08;如“每周一次保洁”&#xff09;、设备状态&#xff08;如智能门锁记录的清洁频率&#xff09;&#xff0c;自动生成服务计划。 结合天气数据&#xff08;如“雨天推荐玻璃清洁”&#xff09;&#xff0c;动态推送服务套餐。 IoT设备联动&#x…...

使用osqp求解简单二次规划问题

文章目录 一、问题描述二、数学推导1. 目标函数处理2. 约束条件处理 三、代码编写 一、问题描述 已知&#xff1a; m i n ( x 1 − 1 ) 2 ( x 2 − 2 ) 2 s . t . 0 ⩽ x 1 ⩽ 1.5 , 1 ⩽ x 2 ⩽ 2.5 min(x_1-1)^2(x_2-2)^2 \qquad s.t. \ \ 0 \leqslant x_1 \leqslant 1.5,…...

Flask 基础与实战概述

一、Flask 基础知识 什么是 Flask? Flask 是一个基于 Python 的轻量级 Web 框架(微框架)。 特点:核心代码简洁,给予开发者更多选择空间。 与 Django 对比: Django 创建空项目生成多个文件,Flask 仅需一个文件即可实现简单应用(如 "Hello, World!")。 Flask …...