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

init的service 启动顺序

inti.rc 各阶段的执行顺序,从 SecondStageMain 方法开始分析

/android-14.0.0_r21/xref/system/core/init/init.cpp

912  int SecondStageMain(int argc, char** argv) {
913      if (REBOOT_BOOTLOADER_ON_PANIC) {
914          InstallRebootSignalHandlers();
915      }
。。。。
// 创建 ActionManager 对象
1032      ActionManager& am = ActionManager::GetInstance();
1033      ServiceList& sm = ServiceList::GetInstance();
1034  
// 1)首先执行  LoadBootScripts 方法
1035      LoadBootScripts(am, sm);
。。。
// 2) 执行 QueueBuiltinAction 方法
1053      am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
1054      am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
1055      am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
1056      am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
// 3)执行 QueueEventTrigger 方法
1057      am.QueueEventTrigger("early-init");
1058  
1059      // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
1060      am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
1061      // ... so that we can start queuing up actions that require stuff from /dev.
1062      am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
1063      Keychords keychords;
1064      am.QueueBuiltinAction(
1065              [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
1066                  for (const auto& svc : ServiceList::GetInstance()) {
1067                      keychords.Register(svc->keycodes());
1068                  }
1069                  keychords.Start(&epoll, HandleKeychord);
1070                  return {};
1071              },
1072              "KeychordInit");
1073  
1074      // Trigger all the boot actions to get us started.
1075      am.QueueEventTrigger("init");
1076  
1077      // Don't mount filesystems or start core system services in charger mode.
1078      std::string bootmode = GetProperty("ro.bootmode", "");
1079      if (bootmode == "charger") {
1080          am.QueueEventTrigger("charger");
1081      } else {
1082          am.QueueEventTrigger("late-init");
1083      }
1084  
1085      // Run all property triggers based on current state of the properties.
1086      am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
。。。
1090      while (true) {
。。。
1103  
1104          if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
// 4)执行所有的command 命令:ExecuteOneCommand
1105              am.ExecuteOneCommand();
1106              // If there's more work to do, wake up again immediately.
// 如果还有命令,则下一次执行的时间就是现在;循环执行命令
1107              if (am.HasMoreCommands()) {
1108                  next_action_time = boot_clock::now();
1109              }
1110          }

1)首先执行 LoadBootScripts 方法

337  static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
338      Parser parser = CreateParser(action_manager, service_list);
340      std::string bootscript = GetProperty("ro.boot.init_rc", "");
341      if (bootscript.empty()) {
342          parser.ParseConfig("/system/etc/init/hw/init.rc");

// 将 三个处理的指令 service、on、import保存到 Parser 中

270  Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
271      Parser parser;
272  
273      parser.AddSectionParser("service", std::make_unique<ServiceParser>(
274                                                 &service_list, GetSubcontext(), std::nullopt));
275      parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
276      parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
277  
278      return parser;
279  }

// AddSectionParser 方法,缓存对应的on 名字和处理对象 ActionParser
/system/core/init/parser.cpp

37  void Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser) {
38      section_parsers_[name] = std::move(parser);
39  }

// 然后调用 ParseConfig 去解析所有的rc 文件,会走到 ParseData 方法中

45  void Parser::ParseData(const std::string& filename, std::string* data) {
46      data->push_back('\n');
47      data->push_back('\0');
。。
85              case T_NEWLINE: {
86                  state.line++;
87                  if (args.empty()) break;
88                  // If we have a line matching a prefix we recognize, call its callback and unset any
89                  // current section parsers.  This is meant for /sys/ and /dev/ line entries for
90                  // uevent.
91                  auto line_callback = std::find_if(
92                      line_callbacks_.begin(), line_callbacks_.end(),
93                      [&args](const auto& c) { return android::base::StartsWith(args[0], c.first); });
94                  if (line_callback != line_callbacks_.end()) {
95                      end_section();
96  
97                      if (auto result = line_callback->second(std::move(args)); !result.ok()) {
98                          parse_error_count_++;
99                          LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
100                      }
101                  } else if (section_parsers_.count(args[0])) {
102                      end_section();
// section_parser 就是 ActionParser
103                      section_parser = section_parsers_[args[0]].get();
104                      section_start_line = state.line;
// 调用 ActionParser的 ParseSection 方法
105                      if (auto result =
106                                  section_parser->ParseSection(std::move(args), filename, state.line);
107                          !result.ok()) {
108                          parse_error_count_++;
109                          LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
110                          section_parser = nullptr;
111                          bad_section_found = true;
112                      }
113                  } else if (section_parser) {
// 接下列解析每一行字符串值ParseLineSection
114                      if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
115                          !result.ok()) {
116                          parse_error_count_++;
117                          LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
118                      }

// 调用 ActionParser的 ParseSection 方法

/system/core/init/action_parser.cpp

133  Result<void> ActionParser::ParseSection(std::vector<std::string>&& args,
134                                          const std::string& filename, int line) {
135      std::vector<std::string> triggers(args.begin() + 1, args.end());
136      if (triggers.size() < 1) {
137          return Error() << "Actions must have a trigger";
138      }
139  
140      Subcontext* action_subcontext = nullptr;
141      if (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) {
142          action_subcontext = subcontext_;
143      }
144  
145      // We support 'on' for only Vendor APEXes from /{vendor, odm}.
146      // It is to prevent mainline modules from using 'on' triggers because events/properties are
147      // not stable for mainline modules.
148      // Note that this relies on Subcontext::PathMatchesSubcontext() to identify Vendor APEXes.
149      if (StartsWith(filename, "/apex/") && !action_subcontext) {
150          return Error() << "ParseSection() failed: 'on' is supported for only Vendor APEXes.";
151      }
152  
153      std::string event_trigger;
154      std::map<std::string, std::string> property_triggers;
155  
// ParseTriggers 获取到 on 后面的string值和 property
// 比如:on init && property:ro.product.cpu.abilist32=*
156      if (auto result =
157                  ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers);
158          !result.ok()) {
159          return Error() << "ParseTriggers() failed: " << result.error();
160      }
161  
// 创建 Action 对象
162      auto action = std::make_unique<Action>(false, action_subcontext, filename, line, event_trigger,
163                                             property_triggers);
164  
// 将其保存到 action_ 
165      action_ = std::move(action);
166      return {};
167  }

// 接下列解析每一行字符串值ParseLineSection,最后走 EndSection

173  Result<void> ActionParser::EndSection() {
174      if (action_ && action_->NumCommands() > 0) {
175          action_manager_->AddAction(std::move(action_));
176      }
177  
178      return {};
179  }

/system/core/init/action_manager.cpp

// 将action 保存到 actions_ 中

39  void ActionManager::AddAction(std::unique_ptr<Action> action) {
40      actions_.emplace_back(std::move(action));
41  }

2) 执行 QueueBuiltinAction 方法

/system/core/init/action_manager.cpp

57  void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
58      auto lock = std::lock_guard{event_queue_lock_};
// 创建了action 对象
59      auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
60                                             std::map<std::string, std::string>{});
// 将func 函数放入到 AddCommand ,command 中
61      action->AddCommand(std::move(func), {name}, 0);
62  
// 将action 保存到了 event_queue_和 actions_ 中
63      event_queue_.emplace(action.get());
64      actions_.emplace_back(std::move(action));
65  }

// event_queue_ 可以保存下列 3中类型:

59      std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_
60              GUARDED_BY(event_queue_lock_);

3)执行 QueueEventTrigger 方法

/system/core/init/action_manager.cpp

43  void ActionManager::QueueEventTrigger(const std::string& trigger) {
44      auto lock = std::lock_guard{event_queue_lock_};
// 将string 值保存到了 event_queue_ 中 
45      event_queue_.emplace(trigger);
46  }

QueueEventTrigger 保存了 “early-init” “init “ 和 “late-init”

4)执行所有的command 命令:ExecuteOneCommand

/system/core/init/action_manager.cpp

// event_queue_ 的值由前面分析,通过 QueueBuiltinAction 和 QueueEventTrigger 增加了

67  void ActionManager::ExecuteOneCommand() {
68      {
69          auto lock = std::lock_guard{event_queue_lock_};
70          // Loop through the event queue until we have an action to execute
71          while (current_executing_actions_.empty() && !event_queue_.empty()) {
// 遍历所有的  actions_
72              for (const auto& action : actions_) {
73                  if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
74                                 event_queue_.front())) {
// 将action 保存到 current_executing_actions_ 中
75                      current_executing_actions_.emplace(action.get());
76                  }
77              }
78              event_queue_.pop();
79          }
80      }
81  
82      if (current_executing_actions_.empty()) {
83          return;
84      }
85  
86      auto action = current_executing_actions_.front();
87  
88      if (current_command_ == 0) {
89          std::string trigger_name = action->BuildTriggersString();
// 会打印下列的log
90          LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
91                    << ":" << action->line() << ")";
92      }
93  
// 执行命令
94      action->ExecuteOneCommand(current_command_);
95  
96      // If this was the last command in the current action, then remove
97      // the action from the executing list.
98      // If this action was oneshot, then also remove it from actions_.
99      ++current_command_;
100      if (current_command_ == action->NumCommands()) {
101          current_executing_actions_.pop();
102          current_command_ = 0;
103          if (action->oneshot()) {
104              auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
105              actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser),
106                             actions_.end());
107          }
108      }
109  }

// 执行的顺序为:
1-1)为下列 4 个方法

SetupCgroupsAction、SetKptrRestrictAction、TestPerfEventSelinuxAction、ConnectEarlyStageSnapuserdAction 方法

// 然后执行 QueueEventTrigger(“early-init”;
1-2)early-init 阶段

/system/core/rootdir/init.rc

15 on early-init
16     # Disable sysrq from keyboard
17     write /proc/sys/kernel/sysrq 0
18 
22     write /proc/sys/kernel/modprobe \n
23 
25     restorecon /adb_keys
28     restorecon /postinstall
29 
30     mkdir /acct/uid
。。。

1-3)下列 3 个方法
wait_for_coldboot_done、SetMmapRndBitsAction、KeychordInit

1-4)init 阶段
可以看出在 init 阶段启动了 logd、lmkd、servicemanager、hwservicemanager和vndservicemanager

97 on init
98     sysclktz 0
99 
100     # Mix device-specific information into the entropy pool
101     copy /proc/cmdline /dev/urandom
102     copy /system/etc/prop.default /dev/urandom
103 
104     symlink /proc/self/fd/0 /dev/stdin
105     symlink /proc/self/fd/1 /dev/stdout
106     symlink /proc/self/fd/2 /dev/stderr
107 
108     # Create energy-aware scheduler tuning nodes
109     mkdir /dev/stune/foreground
110     mkdir /dev/stune/background
111     mkdir /dev/stune/top-app
112     mkdir /dev/stune/rt
。。。。
468     # Start logd before any other services run to ensure we capture all of their logs.
469     start logd
470     # Start lmkd before any other services run so that it can register them
471     write /proc/sys/vm/watermark_boost_factor 0
472     chown root system /sys/module/lowmemorykiller/parameters/adj
473     chmod 0664 /sys/module/lowmemorykiller/parameters/adj
474     chown root system /sys/module/lowmemorykiller/parameters/minfree
475     chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
476     start lmkd
477 
478     # Start essential services.
479     start servicemanager
480     start hwservicemanager
481     start vndservicemanager

1-5)late-init 阶段

// 在late-init 阶段也会设置几个阶段

531 # Mount filesystems and start core system services.
532 on late-init
533     trigger early-fs
539     trigger fs
540     trigger post-fs
541 
547     trigger late-fs
548 
549     # Now we can mount /data. File encryption requires keymaster to decrypt
550     # /data, which in turn can only be loaded when system properties are present.
551     trigger post-fs-data
552 
553     # Should be before netd, but after apex, properties and logging is available.
554     trigger load_bpf_programs
555 
556     # Now we can start zygote for devices with file based encryption
557     trigger zygote-start
558 
559     # Remove a file to wake up anything waiting for firmware.
560     trigger firmware_mounts_complete
561 
562     trigger early-boot
563     trigger boot

在late-init 阶段也分为下列几个阶段:early-fs、fs、post-fs、late-fs、post-fs-data、load_bpf_programs、zygote-start、firmware_mounts_complete、early-boot、boot

// 先看下 trigger 对应的方法的逻辑:

/system/core/init/builtins.cpp

798  static Result<void> do_trigger(const BuiltinArguments& args) {
// 也是调用 ActionManager 对象的 QueueEventTrigger 方法
799      ActionManager::GetInstance().QueueEventTrigger(args[1]);
800      return {};
801  }

1-5-1)early-fs 阶段
// 在此阶段启动了 vold

565 on early-fs
566     # Once metadata has been mounted, we'll need vold to deal with userdata checkpointing
567     start vold

1-5-2)fs 阶段

比如logd 的处理:
/system/logging/logd/logd.rc

29 on fs
30     write /dev/event-log-tags "# content owned by logd
31 "
32     chown logd logd /dev/event-log-tags
33     chmod 0644 /dev/event-log-tags

1-5-3)post-fs 阶段
// post-fs 阶段 会创建一些文件
/xref/system/core/rootdir/init.rc

569 on post-fs
570     exec - system system -- /system/bin/vdc checkpoint markBootAttempt
571 
572     # Once everything is setup, no need to modify /.
573     # The bind+remount combination allows this to work in containers.
574     mount rootfs rootfs / remount bind ro nodev
575 
576     # Mount default storage into root namespace
577     mount none /mnt/user/0 /storage bind rec
578     mount none none /storage slave rec
579 
580     # Make sure /sys/kernel/debug (if present) is labeled properly
581     # Note that tracefs may be mounted under debug, so we need to cross filesystems
582     restorecon --recursive --cross-filesystems /sys/kernel/debug
583 
584     # We chown/chmod /cache again so because mount is run as root + defaults
585     chown system cache /cache
586     chmod 0770 /cache

1-5-4)late-fs 阶段

643 on late-fs
644     # Ensure that tracefs has the correct permissions.
645     # This does not work correctly if it is called in post-fs.
646     chmod 0755 /sys/kernel/tracing
647     chmod 0755 /sys/kernel/debug/tracing
648 
649     # HALs required before storage encryption can get unlocked (FBE)
650     class_start early_hal
651 
652     # Load trusted keys from dm-verity protected partitions
653     exec -- /system/bin/fsverity_init --load-verified-keys

// class_start early_hal 启动配置了 early_hal 的hal进程
/system/core/init/builtins.cpp

164  static Result<void> do_class_start(const BuiltinArguments& args) {
165      // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
166      if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
167          return {};
168      // Starting a class does not start services which are explicitly disabled.
169      // They must  be started individually.
170      for (const auto& service : ServiceList::GetInstance()) {
// 遍历所有的service,找到设置了class 为 early_hal的 进程
171          if (service->classnames().count(args[1])) {
172              if (auto result = service->StartIfNotDisabled(); !result.ok()) {
173                  LOG(ERROR) << "Could not start service '" << service->name()
174                             << "' as part of class '" << args[1] << "': " << result.error();
175              }
176          }
177      }
178      return {};
179  }

// 比如keystore2 进程

/system/security/keystore2/keystore2.rc

9 service keystore2 /system/bin/keystore2 /data/misc/keystore
10     class early_hal
11     user keystore

1-5-5)post-fs-data 阶段

678 on post-fs-data
679 
680     mark_post_data
.。。
741     start logd
742     start logd-reinit
743 
748     trigger load_persist_props_action
。。。
790     chown bluetooth bluetooth /data/misc/bluedroid/bt_config.conf
791     mkdir /data/misc/bluetooth 0770 bluetooth bluetooth
792     mkdir /data/misc/bluetooth/logs 0770 bluetooth bluetooth
793     mkdir /data/misc/nfc 0770 nfc nfc
794     mkdir /data/misc/nfc/logs 0770 nfc nfc
795     mkdir /data/misc/credstore 0700 credstore credstore
796     mkdir /data/misc/gatekeeper 0700 system system
797     mkdir /data/misc/keychain 0771 system system
798     mkdir /data/misc/net 0750 root shell
799     mkdir /data/misc/radio 0770 system radio
800     mkdir /data/misc/sms 0770 system radio
801     mkdir /data/misc/carrierid 0770 system radio

1-5-6)load_bpf_programs 阶段

1253 on load_bpf_programs && property:sys.init.perf_lsm_hooks=1
1254     write /proc/sys/kernel/perf_event_paranoid -1

1-5-7)zygote-start 阶段

// 可以通过命令 adb shell getprop ro.crypto.state 获取到对应的值
init: processing action (ro.crypto.state=encrypted && ro.crypto.type=file && zygote-start) from (/system/etc/init/hw/init.rc:1130)
00E1A <14> [    6.557417] [11-24 16:44:06.557] vold: [libfs_mgr] __mount(source=/dev/block/dm-48,target=/data,type=f2fs)=0,during time: 280 ms: Success
00E1B <15> [    6.558446] [11-24 16:44:06.558] vold: Mounted /data
00E1C <14> [    6.574732] [11-24 16:44:06.574] init: Userdata mounted using /vendor/etc/fstab.ums9230_4h10 result : 7
00E1D <14> [    6.574850] [11-24 16:44:06.574] init: Keyring created with id 1023294143 in process 1
00E1E <14> [    6.575766] [11-24 16:44:06.575] init: Command 'mount_all /vendor/etc/fstab.${ro.hardware} --late' action=late-fs (/vendor/etc/init/init.md.rc:963) took 1050ms and succeeded
00E1F <14> [    6.575864] [11-24 16:44:06.575] init: Service 'insmod-sh' (pid 268) exited with status 0 oneshot service took 2.680000 seconds in background

// 可以指导是执行了下列的rc 文件
// 启动了下列几个进程为:statsd、netd、zygote、zygote_secondary

1090 on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
1091     wait_for_prop odsign.verification.done 1
1092     # A/B update verifier that marks a successful boot.
1093     exec_start update_verifier_nonencrypted
1094     start statsd
1095     start netd
1096     start zygote
1097     start zygote_secondary

1-5-8)firmware_mounts_complete 阶段

527 # Indicate to fw loaders that the relevant mounts are up.
528 on firmware_mounts_complete
529     rm /dev/.booting

1-5-9)early-boot 阶段

/frameworks/native/cmds/installd/installd.rc

7 on early-boot
8     mkdir /config/sdcardfs/extensions/1055
9     mkdir /config/sdcardfs/extensions/1056
10     mkdir /config/sdcardfs/extensions/1057
11     mkdir /config/sdcardfs/extensions/1056/3gpp

1-5-10)boot 阶段

1104 on boot
1105     # basic network init
1106     ifup lo
1107     hostname localhost
1108     domainname localdomain
。。。
1206     # Define default initial receive window size in segments.
1207     setprop net.tcp_def_init_rwnd 60
1208 
1209     # Start standard binderized HAL daemons
1210     class_start hal
1211 
1212     class_start core

// 执行 class_start hal 和 class_start core

/system/core/init/builtins.cpp

164  static Result<void> do_class_start(const BuiltinArguments& args) {
165      // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
166      if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
167          return {};
168      // Starting a class does not start services which are explicitly disabled.
169      // They must  be started individually.
170      for (const auto& service : ServiceList::GetInstance()) {
// 看是否由class 为 hal  或者 core
171          if (service->classnames().count(args[1])) {
// 调用 service 的 StartIfNotDisabled 方法
172              if (auto result = service->StartIfNotDisabled(); !result.ok()) {
173                  LOG(ERROR) << "Could not start service '" << service->name()
174                             << "' as part of class '" << args[1] << "': " << result.error();
175              }
176          }
177      }
178      return {};
179  }

// 调用 service 的 StartIfNotDisabled 方法
/system/core/init/service.cpp

818  Result<void> Service::StartIfNotDisabled() {
// 如果设置中没有 DISABLED 属性,则执行start 启动进程
819      if (!(flags_ & SVC_DISABLED)) {
820          return Start();
821      } else {
/// 有disable,则设置属性 SVC_DISABLED_START
822          flags_ |= SVC_DISABLED_START;
823      }
824      return {};
825  }

// 比如:bootanim,有 disabled属性
/frameworks/base/cmds/bootanimation/bootanim.rc

1 service bootanim /system/bin/bootanimation
2     class core animation
3     user graphics
4     group graphics audio
5     disabled
6     oneshot
7     ioprio rt 0
8     task_profiles MaxPerformance

/system/core/init/service_parser.cpp

589          {"disabled",                {0,     0,    &ServiceParser::ParseDisabled}},
134  Result<void> ServiceParser::ParseDisabled(std::vector<std::string>&& args) {
// 设置flags 有 SVC_DISABLED
135      service_->flags_ |= SVC_DISABLED;
136      service_->flags_ |= SVC_RC_DISABLED;
137      return {};
138  }

1-5-10-1)boot 阶段启动 hal 的 进程
/hardware/interfaces/graphics/composer/2.4/default/android.hardware.graphics.composer%402.4-service.rc

1 service vendor.hwcomposer-2-4 /vendor/bin/hw/android.hardware.graphics.composer@2.4-service
2     class hal animation
3     user system
4     group graphics drmrpc

/hardware/interfaces/sensors/2.1/multihal/android.hardware.sensors%402.1-service-multihal.rc

1 service vendor.sensors-hal-2-1-multihal /vendor/bin/hw/android.hardware.sensors@2.1-service.multihal
2     class hal
3     user system
4     group system wakelock context_hub input

1-5-10-2)boot 阶段启动 core 的 进程

/packages/modules/adb/apex/adbd.rc

1 service adbd /apex/com.android.adbd/bin/adbd --root_seclabel=u:r:su:s0
2     class core
3     socket adbd seqpacket 660 system system
4     disabled

/frameworks/native/services/surfaceflinger/surfaceflinger.rc

1 service surfaceflinger /system/bin/surfaceflinger
2     class core animation
3     user system
4     group graphics drmrpc readproc
5     capabilities SYS_NICE
6     onrestart restart --only-if-running zygote

/frameworks/av/media/audioserver/audioserver.rc

1 service audioserver /system/bin/audioserver
2     class core
3     user audioserver

/system/core/rootdir/init.usb.rc

14 # adbd is controlled via property triggers in init.<platform>.usb.rc
15 service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
16     class core
17     socket adbd seqpacket 660 system system
18     disabled
19     updatable
20     seclabel u:r:adbd:s0

http://www.kler.cn/a/540443.html

相关文章:

  • C语言学习笔记:子函数的调用实现各个位的累加和
  • C++20新特性
  • 工业相机在工业生产制造过程中的视觉检测技术应用
  • Python截图轻量化工具
  • 【韩顺平linux】部分上课笔记整理
  • leetcode 做题思路快查
  • 基于 gitee 的 CI/CD
  • 球弹跳高度的计算(信息学奥赛一本通-1085)
  • 【JavaScript】this 指向由入门到精通
  • HTML标题标签(<h1>、<h2>、<h3>)的正确使用策略与SEO优化指南
  • 网络安全 — 安全架构
  • 实现双向数据绑定
  • 局域网使用Ollama(Linux)
  • 智慧校园与理工大学:信息技术在高等教育中的应用
  • 使用Python爬虫获取淘宝商品评论API接口数据
  • 前瞻技术解密:未来生活的改变与机遇
  • 1-portal认证功能
  • CPLD实现SPI通信
  • 使用XMLHttpRequest发送带查询参数的 GET 请求并动态展示数据
  • [LLM面试题] 指示微调(Prompt-tuning)与 Prefix-tuning区别
  • ndk 编译opencv(去除libandroid.so mediandk依赖)
  • 单片机复杂项目的软件分层设计
  • 构建jdk17包含maven的基础镜像
  • [安装FlashAttention] CUDA版本 和 Nvidia驱动版本
  • [Unity角色控制专题] 详细解释如何使用Character Controller配合脚本实现跳跃功能与落地抖动?
  • docker的卷映射如何手动指定位置