【Android 10 源码】healthd 模块 HAL 1.0 分析

2024-05-01 20:58

本文主要是介绍【Android 10 源码】healthd 模块 HAL 1.0 分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

health@1.0:android.hardware.health@1.0 的缩写。指的是 Android 8.0 中发布的运行状况 HIDL 的 1.0 版 HAL。

Android 8.x 中的“运行状况”服务

在 Android 8.x 中,运行状况组件的工作原理详情如下图所示:

在这里插入图片描述
在此图中:

  1. 框架通过一次 Binder 调用和一次 hwbinder 调用与硬件进行通信。
  2. healthd 静态关联到 libhealthd_android、libbatterymonitor 和 libbatteryservice。
  3. health@1.0-impl 静态关联到 libhealthd.BOARD。

每个开发板都可以自定义不同的 libhealthd.BOARD;charger、health@1.0-impl 和 recovery 关联到哪个开发板是在构建时确定的。

对于其他模式:

在这里插入图片描述
charger 静态关联到 libhealthd.BOARD、libhealthd_charger 和 libbatterymonitor。

recovery 静态关联到 libhealthd.BOARD 和 libbatterymonitor。

health@1.0 HAL 中的定义:

hardware/interfaces/health/1.0/types.hal

package android.hardware.health@1.0;/*** Possible return values for optional HAL method(s) like* IHealth::energyCounter()* 可选HAL方法的可能返回值*/
enum Result : int32_t {SUCCESS,NOT_SUPPORTED,UNKNOWN,
};/*** Possible values for Battery Status.* “电池状态”的取值范围。* Note: These are currently in sync with BatteryManager and must not* be extended / altered.* 注意:这些目前与 BatteryManager 同步,不能扩展/更改。*/
@export(name="", value_prefix="BATTERY_STATUS_")
enum BatteryStatus : int32_t {UNKNOWN = 1,CHARGING = 2,DISCHARGING = 3,/*** Battery is *not* charging - special case when charger is present* but battery isn't charging*/NOT_CHARGING = 4,FULL = 5,
};/*** Possible values for Battery Health.* 电池运行状况的可能值。* Note: These are currently in sync with BatteryManager and must not* be extended / altered.* 注意:这些目前与 BatteryManager 同步,不能扩展/更改。*/
@export(name="", value_prefix="BATTERY_HEALTH_")
enum BatteryHealth : int32_t {UNKNOWN = 1,GOOD = 2,OVERHEAT = 3,DEAD = 4,OVER_VOLTAGE = 5,/*** Battery experienced an unknown/unspecifid failure.*/UNSPECIFIED_FAILURE = 6,COLD = 7,
};struct HealthConfig {/*** periodicChoresIntervalFast is used while the device is not in* suspend, or in suspend and connected to a charger (to watch for battery* overheat due to charging)* 当设备不处于挂起状态或挂起并连接到充电器时使用 periodicChoresIntervalFast* (观察电池因充电而过热)*/int32_t periodicChoresIntervalFast;/*** periodicChoresIntervalSlow is used when the device is in suspend and* not connected to a charger (to watch for a battery drained to zero* remaining capacity).* 当设备处于挂起状态且未连接充电器时,* 使用 periodicChoresIntervalSlow (观察电池耗尽至剩余容量为零)。*/int32_t periodicChoresIntervalSlow;/*** power_supply sysfs attribute file paths. Set these to specific paths* to use for the associated battery parameters. Clients must search* for appropriate power_supply attribute files to use, for any paths* left empty after the HAL is initialized.*//*** batteryStatusPath - file path to read battery charging status.* 读取电池充电状态的文件路径。* (POWER_SUPPLY_PROP_STATUS)*/string batteryStatusPath;/*** batteryHealthPath - file path to read battery health.* 读取电池运行状况的文件路径。* (POWER_SUPPLY_PROP_HEALTH)*/string batteryHealthPath;/*** batteryPresentPath - file path to read battery present status.* 读取电池当前状态的文件路径。* (POWER_SUPPLY_PROP_PRESENT)*/string batteryPresentPath;/*** batteryCapacityPath - file path to read remaining battery capacity.* 读取剩余电池容量的文件路径。* (POWER_SUPPLY_PROP_CAPACITY)*/string batteryCapacityPath;/*** batteryVoltagePath - file path to read battery voltage.* 读取电池电压的文件路径。* (POWER_SUPPLY_PROP_VOLTAGE_NOW)*/string batteryVoltagePath;/*** batteryTemperaturePath - file path to read battery temperature in tenths* of degree celcius. (POWER_SUPPLY_PROP_TEMP)* 读取电池温度摄氏度乘以10后的文件路径。*/string batteryTemperaturePath;/*** batteryTechnologyPath - file path to read battery technology.* 读取电池技术的文件路径。* (POWER_SUPPLY_PROP_TECHNOLOGY)*/string batteryTechnologyPath;/*** batteryCurrentNowPath - file path to read battery instantaneous current.* 读取电池瞬时电流的文件路径。* (POWER_SUPPLY_PROP_CURRENT_NOW)*/string batteryCurrentNowPath;/*** batteryCurrentAvgPath - file path to read battery average current.* 读取电池平均电流的文件路径。* (POWER_SUPPLY_PROP_CURRENT_AVG)*/string batteryCurrentAvgPath;/*** batteryChargeCounterPath - file path to read battery accumulated charge.* 读取电池累计充电电量的文件路径。* (POWER_SUPPLY_PROP_CHARGE_COUNTER)*/string batteryChargeCounterPath;/*** batteryFullChargerPath - file path to read battery charge value when it* is considered to be full. (POWER_SUPPLY_PROP_CHARGE_FULL)* 读取电池充满时的对应值文件路径。*/string batteryFullChargePath;/*** batteryCycleCountPath - file path to read battery charge cycle count.* 读取电池充电周期计数文件路径。* (POWER_SUPPLY_PROP_CYCLE_COUNT)*/string batteryCycleCountPath;
};/*** The parameter to healthd mainloop update calls* healthd 主循环更新调用的参数*/
struct HealthInfo {/** AC charger state - 'true' if online */ /** 交流充电状态 */bool chargerAcOnline;/** USB charger state - 'true' if online */ /** USB 充电状态 */bool chargerUsbOnline;/** Wireless charger state - 'true' if online */ /** 无线充电状态 */bool chargerWirelessOnline;/** Maximum charging current supported by charger in uA *//** 充电器支持的最大充电电流(uA) */int32_t maxChargingCurrent;/** Maximum charging voltage supported by charger in uV *//** 充电器支持的最大充电电压(uV) */int32_t maxChargingVoltage;BatteryStatus batteryStatus;BatteryHealth batteryHealth;/** 'true' if battery is present *//** 如果有电池,则为 true */bool batteryPresent;/** Remaining battery capacity in percent *//** 剩余电池容量百分比 */int32_t batteryLevel;/*** Instantaneous battery voltage in millivolts (mV).* 瞬间电池电压(毫伏)。** Historically, the unit of this field is microvolts (uV), but all* clients and implementations uses millivolts in practice, making it* the de-facto standard.*/int32_t batteryVoltage;/** Instantaneous battery temperature in tenths of degree celcius *//** 瞬间电池温度 */int32_t batteryTemperature;/** Instantaneous battery current in uA *//** 瞬时电池电流(uA) */int32_t batteryCurrent;/** Battery charge cycle count *//** 电池充电周期计数 */int32_t batteryCycleCount;/** Battery charge value when it is considered to be "full" in uA-h *//** 当认为电池“满”时的电池充电值(uA-h) */int32_t batteryFullCharge;/** Instantaneous battery capacity in uA-h *//** 瞬时电池容量(uA-h) */int32_t batteryChargeCounter;/** Battery technology, e.g. "Li-ion, Li-Poly" etc. *//** 电池技术 */string batteryTechnology;
};

hardware/interfaces/health/1.0/IHealth.hal

package android.hardware.health@1.0;interface IHealth {/*** This function lets you change healthd configuration from default if* desired. It must be called exactly once at startup time.* 如果需要,这个功能可以让你改变默认的 healthd 配置。它必须在启动时调用一次。** The configuration values are described in 'struct HealthConfig'.* To use default configuration, simply return without modifying the* fields of the config parameter.* 配置值在“struct HealthConfig”中描述。* 要使用默认配置,只需返回而不修改 config 参数的字段。** @param default healthd configuration.*/init(HealthConfig config) generates (HealthConfig configOut);/*** This function is a hook to update/change device's HealthInfo (as described* in 'struct HealthInfo').* 这个函数是一个 hook 来更新/改变设备的 HealthInfo** 'HealthInfo' describes device's battery and charging status, typically* read from kernel. These values may be modified in this call.* “HealthInfo”描述设备的电池和充电状态,通常从内核读取。* 这些值可以在这个调用中修改。** @param   Device Health info as described in 'struct HealthInfo'.* @return  skipLogging Indication to the caller to add 'or' skip logging the health*          information. Return 'true' to skip logging the update.* @return  infoOut HealthInfo to be sent to client code. (May or may*          not be modified).*/update(HealthInfo info) generates (bool skipLogging, HealthInfo infoOut);/*** This function is called by healthd when framework queries for remaining* energy in the Battery through BatteryManager APIs.* 当框架通过 BatteryManager API 查询电池中的剩余电量时,* healthd 会调用这个函数。** @return  result Result of querying enery counter for the battery.* @return  energy Battery remaining energy in nanowatt-hours.*          Must be '0' if result is anything other than Result::SUCCESS.*/energyCounter() generates (Result result, int64_t energy);
};

现在重点来分析 hardware/interfaces/health/1.0/default/ 下的实现。Android.mk 中编译了两个模块,分别为 android.hardware.health@1.0-impl 和 android.hardware.health@1.0-service(可执行文件)。android.hardware.health@1.0-impl 主要使用了 Health.cpp 源文件,android.hardware.health@1.0-service 则使用了 HealthService.cpp 源文件。

hardware/interfaces/health/1.0/default/Android.mk

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)
LOCAL_MODULE := android.hardware.health@1.0-impl
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_C_INCLUDES := system/core/base/include
LOCAL_SRC_FILES := \Health.cpp \LOCAL_HEADER_LIBRARIES := libhealthd_headersLOCAL_SHARED_LIBRARIES := \libcutils \libhidlbase \libhidltransport \liblog \libutils \android.hardware.health@1.0 \LOCAL_STATIC_LIBRARIES := android.hardware.health@1.0-convertLOCAL_HAL_STATIC_LIBRARIES := libhealthdinclude $(BUILD_SHARED_LIBRARY)include $(CLEAR_VARS)
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE := android.hardware.health@1.0-service
LOCAL_INIT_RC := android.hardware.health@1.0-service.rc
LOCAL_SRC_FILES := \HealthService.cpp \LOCAL_SHARED_LIBRARIES := \liblog \libcutils \libdl \libbase \libutils \libhidlbase \libhidltransport \android.hardware.health@1.0 \include $(BUILD_EXECUTABLE)include $(call first-makefiles-under,$(LOCAL_PATH))

android.hardware.health@1.0-impl 包含了 HAL 的实现以及一个导出函数 HIDL_FETCH_IHealth(…) ,HIDL_FETCH_IHealth(…) 获取 HAL 实现的对象,只是 new 了一个 Health 结构体返回。

Health::init(…) 实现主要流程:

  1. 创建一个 healthd_config struct;
  2. 为了使得 healthd 静态 HAL 工作正常,调用 convertFromHealthConfig(…) 将 HealthConfig 转换为 healthd_config;
  3. 调用 healthd_board_init(…) 进一步板级初始化;
  4. 给 mGetEnergyCounter 字段赋值;
  5. 调用 convertToHealthConfig(…) 再将 healthd_config 转换为 HealthConfig 结构,并返回。

Health::update(…) 实现主要流程:

  1. 创建 android::BatteryProperties 结构;
  2. 调用 convertFromHealthInfo(…) 将 HealthInfo 转为 android::Batteryproperties;
  3. 调用板级更新 healthd_board_battery_update(…);
  4. 调用 convertToHealthInfo(…) 将 android::Batteryproperties 转为 HealthInfo。

Health::energyCounter(…) 实现非常简单主要调用了 mGetEnergyCounter 指向的函数指针获取剩余电量。

hardware/interfaces/health/1.0/default/Health.cpp

using ::android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
using ::android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;// Methods from ::android::hardware::health::V1_0::IHealth follow.
Return<void> Health::init(const HealthConfig& config, init_cb _hidl_cb)  {struct healthd_config healthd_config = {};HealthConfig configOut;// To keep working with existing healthd static HALs,// convert the new HealthConfig to the old healthd_config// and back.convertFromHealthConfig(config, &healthd_config);healthd_board_init(&healthd_config);mGetEnergyCounter = healthd_config.energyCounter;convertToHealthConfig(&healthd_config, configOut);_hidl_cb(configOut);return Void();
}Return<void> Health::update(const HealthInfo& info, update_cb _hidl_cb)  {struct android::BatteryProperties p = {};HealthInfo infoOut;// To keep working with existing healthd static HALs,// convert the new HealthInfo to android::Batteryproperties// and back.convertFromHealthInfo(info, &p);int skipLogging = healthd_board_battery_update(&p);convertToHealthInfo(&p, infoOut);_hidl_cb(!!skipLogging, infoOut);return Void();
}Return<void> Health::energyCounter(energyCounter_cb _hidl_cb) {int64_t energy = 0;Result result = Result::NOT_SUPPORTED;if (mGetEnergyCounter) {int status = mGetEnergyCounter(&energy);if (status == 0) {result = Result::SUCCESS;}}_hidl_cb(result, energy);return Void();
}IHealth* HIDL_FETCH_IHealth(const char* /* name */) {return new Health();
}

上面提到的转换适配函数定义在 convert.cpp 中,实现了 healthd_config 和 HealthConfig 互转,
BatteryProperties 和 HealthInfo 互转。

hardware/interfaces/health/1.0/default/convert.cpp

void convertToHealthConfig(const struct healthd_config *hc, HealthConfig& config) {config.periodicChoresIntervalFast = hc->periodic_chores_interval_fast;config.periodicChoresIntervalSlow = hc->periodic_chores_interval_slow;config.batteryStatusPath        = hc->batteryStatusPath.string();config.batteryHealthPath        = hc->batteryHealthPath.string();config.batteryPresentPath       = hc->batteryPresentPath.string();config.batteryCapacityPath      = hc->batteryCapacityPath.string();config.batteryVoltagePath       = hc->batteryVoltagePath.string();config.batteryTemperaturePath   = hc->batteryTemperaturePath.string();config.batteryTechnologyPath    = hc->batteryTechnologyPath.string();config.batteryCurrentNowPath    = hc->batteryCurrentNowPath.string();config.batteryCurrentAvgPath    = hc->batteryCurrentAvgPath.string();config.batteryChargeCounterPath = hc->batteryChargeCounterPath.string();config.batteryFullChargePath    = hc->batteryFullChargePath.string();config.batteryCycleCountPath    = hc->batteryCycleCountPath.string();}void convertFromHealthConfig(const HealthConfig& c, struct healthd_config *hc) {hc->periodic_chores_interval_fast = c.periodicChoresIntervalFast;hc->periodic_chores_interval_slow = c.periodicChoresIntervalSlow;hc->batteryStatusPath =android::String8(c.batteryStatusPath.c_str(),c.batteryStatusPath.size());hc->batteryHealthPath =android::String8(c.batteryHealthPath.c_str(),c.batteryHealthPath.size());hc->batteryPresentPath =android::String8(c.batteryPresentPath.c_str(),c.batteryPresentPath.size());hc->batteryCapacityPath =android::String8(c.batteryCapacityPath.c_str(),c.batteryCapacityPath.size());hc->batteryVoltagePath =android::String8(c.batteryVoltagePath.c_str(),c.batteryVoltagePath.size());hc->batteryTemperaturePath =android::String8(c.batteryTemperaturePath.c_str(),c.batteryTemperaturePath.size());hc->batteryTechnologyPath =android::String8(c.batteryTechnologyPath.c_str(),c.batteryTechnologyPath.size());hc->batteryCurrentNowPath =android::String8(c.batteryCurrentNowPath.c_str(),c.batteryCurrentNowPath.size());hc->batteryCurrentAvgPath =android::String8(c.batteryCurrentAvgPath.c_str(),c.batteryCurrentNowPath.size());hc->batteryChargeCounterPath =android::String8(c.batteryChargeCounterPath.c_str(),c.batteryChargeCounterPath.size());hc->batteryFullChargePath =android::String8(c.batteryFullChargePath.c_str(),c.batteryFullChargePath.size());hc->batteryCycleCountPath =android::String8(c.batteryCycleCountPath.c_str(),c.batteryCycleCountPath.size());// energyCounter is handled through special means so all calls to// the function go across the HALs// boot_min_cap - never used in Android (only in charger-mode).// screen_on - never used in Android (only in charger mode).
}void convertToHealthInfo(const struct android::BatteryProperties *p,HealthInfo& info) {info.chargerAcOnline        = p->chargerAcOnline;info.chargerUsbOnline       = p->chargerUsbOnline;info.chargerWirelessOnline  = p->chargerWirelessOnline;info.maxChargingCurrent     = p->maxChargingCurrent;info.maxChargingVoltage     = p->maxChargingVoltage;info.batteryStatus          = static_cast<BatteryStatus>(p->batteryStatus);info.batteryHealth          = static_cast<BatteryHealth>(p->batteryHealth);info.batteryPresent         = p->batteryPresent;info.batteryLevel           = p->batteryLevel;info.batteryVoltage         = p->batteryVoltage;info.batteryTemperature     = p->batteryTemperature;info.batteryCurrent         = p->batteryCurrent;info.batteryCycleCount      = p->batteryCycleCount;info.batteryFullCharge      = p->batteryFullCharge;info.batteryChargeCounter   = p->batteryChargeCounter;info.batteryTechnology      = p->batteryTechnology;
}void convertFromHealthInfo(const HealthInfo& info,struct android::BatteryProperties *p) {p->chargerAcOnline          = info.chargerAcOnline;p->chargerUsbOnline         = info.chargerUsbOnline;p->chargerWirelessOnline    = info.chargerWirelessOnline;p->maxChargingCurrent       = info.maxChargingCurrent;p->maxChargingVoltage       = info.maxChargingVoltage;p->batteryStatus            = static_cast<int>(info.batteryStatus);p->batteryHealth            = static_cast<int>(info.batteryHealth);p->batteryPresent           = info.batteryPresent;p->batteryLevel             = info.batteryLevel;p->batteryVoltage           = info.batteryVoltage;p->batteryTemperature       = info.batteryTemperature;p->batteryCurrent           = info.batteryCurrent;p->batteryCycleCount        = info.batteryCycleCount;p->batteryFullCharge        = info.batteryFullCharge;p->batteryChargeCounter     = info.batteryChargeCounter;p->batteryTechnology        = android::String8(info.batteryTechnology.c_str());
}

libhealthd 模块 Android.bp 编译主要使用了 healthd_board_default.cpp。

hardware/interfaces/health/1.0/default/libhealthd/Android.bp

cc_library_static {srcs: ["healthd_board_default.cpp"],name: "libhealthd.default",vendor_available: true,recovery_available: true,cflags: ["-Werror"],include_dirs: ["system/core/base/include"],header_libs: ["libhealthd_headers"],
}

healthd_board_init(…) 和 healthd_board_battery_update(…) 仅仅都是空实现。

hardware/interfaces/health/1.0/default/libhealthd/healthd_board_default.cpp

void healthd_board_init(struct healthd_config*)
{// use defaults
}int healthd_board_battery_update(struct android::BatteryProperties*)
{// return 0 to log periodic polled battery status to kernel logreturn 0;
}

android.hardware.health@1.0-service 实现了 HAL service。主要使用了 HealthService.cpp 源文件和,关联了 android.hardware.health@1.0-service.rc 文件。

Binderized 绑定式可以用两种方式来绑定服务,第一种通过 defaultPassthroughServiceImplementation 调用来注册服务,另外一种是直接调用 RegisterAsService 来注册服务。此处显然是通过第一种。

hardware/interfaces/health/1.0/default/HealthService.cpp

using android::hardware::health::V1_0::IHealth;
using android::hardware::defaultPassthroughServiceImplementation;int main() {return defaultPassthroughServiceImplementation<IHealth>();
}

rc 文件中,class 分类为 hal,user 用户是 system,group 组也是 system,capabilities 能力则是 WAKE_ALARM,表明可以闹钟唤醒。系统启动后 vendor.health-hal-1-0 Service 将会注册到系统。

hardware/interfaces/health/1.0/default/android.hardware.health@1.0-service.rc

service vendor.health-hal-1-0 /vendor/bin/hw/android.hardware.health@1.0-serviceclass haluser systemgroup systemcapabilities WAKE_ALARM

Android.bp 文件中编译了 android.hardware.health@1.0-convert 静态库,它主要使用了 convert.cpp 文件。

hardware/interfaces/health/1.0/default/Android.bp

cc_library_static {name: "android.hardware.health@1.0-convert",vendor_available: true,recovery_available: true,srcs: ["convert.cpp"],include_dirs: ["system/core/base/include",],header_libs: ["libhealthd_headers"],export_header_lib_headers: ["libhealthd_headers"],export_include_dirs: ["include"],shared_libs: ["libcutils","libhidlbase","libhidltransport","libutils","android.hardware.health@1.0",],}

这篇关于【Android 10 源码】healthd 模块 HAL 1.0 分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/952651

相关文章

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案(最新整理)

《MyBatisPlus中update_time字段自动填充失效的原因分析及解决方案(最新整理)》在使用MyBatisPlus时,通常我们会在数据库表中设置create_time和update... 目录前言一、问题现象二、原因分析三、总结:常见原因与解决方法对照表四、推荐写法前言在使用 MyBATis

Python主动抛出异常的各种用法和场景分析

《Python主动抛出异常的各种用法和场景分析》在Python中,我们不仅可以捕获和处理异常,还可以主动抛出异常,也就是以类的方式自定义错误的类型和提示信息,这在编程中非常有用,下面我将详细解释主动抛... 目录一、为什么要主动抛出异常?二、基本语法:raise关键字基本示例三、raise的多种用法1. 抛

github打不开的问题分析及解决

《github打不开的问题分析及解决》:本文主要介绍github打不开的问题分析及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、找到github.com域名解析的ip地址二、找到github.global.ssl.fastly.net网址解析的ip地址三

Mysql的主从同步/复制的原理分析

《Mysql的主从同步/复制的原理分析》:本文主要介绍Mysql的主从同步/复制的原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录为什么要主从同步?mysql主从同步架构有哪些?Mysql主从复制的原理/整体流程级联复制架构为什么好?Mysql主从复制注意

java -jar命令运行 jar包时运行外部依赖jar包的场景分析

《java-jar命令运行jar包时运行外部依赖jar包的场景分析》:本文主要介绍java-jar命令运行jar包时运行外部依赖jar包的场景分析,本文给大家介绍的非常详细,对大家的学习或工作... 目录Java -jar命令运行 jar包时如何运行外部依赖jar包场景:解决:方法一、启动参数添加: -Xb

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

Apache 高级配置实战之从连接保持到日志分析的完整指南

《Apache高级配置实战之从连接保持到日志分析的完整指南》本文带你从连接保持优化开始,一路走到访问控制和日志管理,最后用AWStats来分析网站数据,对Apache配置日志分析相关知识感兴趣的朋友... 目录Apache 高级配置实战:从连接保持到日志分析的完整指南前言 一、Apache 连接保持 - 性

Linux中的more 和 less区别对比分析

《Linux中的more和less区别对比分析》在Linux/Unix系统中,more和less都是用于分页查看文本文件的命令,但less是more的增强版,功能更强大,:本文主要介绍Linu... 目录1. 基础功能对比2. 常用操作对比less 的操作3. 实际使用示例4. 为什么推荐 less?5.

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

《spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔)》:本文主要介绍spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔),本文通过实例图... 目录需求背景需求拆解设计流程及作用域逻辑处理代码逻辑需求背景公司要求,通过公司网络代理访问的请求需要做请