Android HAL到Framework
一、为什么需要Framwork?
Framework实际上是⼀个应⽤程序的框架,提供了很多服务:
1、丰富⽽⼜可扩展的视图(Views),
可以⽤来构建应⽤程序,它包括列表(lists),⽹格(grids),⽂本框(text boxes),按钮(buttons),甚⾄可嵌⼊的web浏览器。
2、内容提供器(Content Providers)
使得应⽤程序可以访问另⼀个应⽤程序的数据(如联系⼈数据库),或者共享它们⾃⼰的数据
3、资源管理器(Resource Manager)
提供⾮代码资源的访问,如本地字符串,图形,和布局⽂件(layout files)。
4、通知管理器(Notification Manager)
使得应⽤程序可以在状态栏中显⽰⾃定义的提⽰信息。
5、活动管理器(Activity Manager)
⽤来管理应⽤程序⽣命周期并提供常⽤的导航回退功能。
二、应用层访问硬件,如何自定义系统Service?
1、应用层如何访问硬件

(1)Linux
对于Linux来说的话,就比较简单,应用层的APP直接通过open一类的接口直接访问我们底层的驱动文件
(2)Android
对于Android来说的话,它就会有多种方式去访问,
1) APP ----- JNI ----- Kernel:
这种就很直接明了,上层app访问JNI,再去访问kernel
2)APP ----- Service ----- JNI ----- Kernel:
当我们要往系统里添加一个硬件的话,我们更希望把它封装为一个系统的服务,就可以以这种方式去访问到底层
3)APP ----- Service ----- JNI ----- HAL ----- Kernel:
一些驱动厂商的一个源码呢他是不希望开放给我们的一个开发者是吧,但是他们又依赖着Android的开源框架,所以就有一种比较好的方法,既不需要公开源码,又可以实现同样的功能。就是把它封装成库,这样可以让厂家去提供一个现成的库,然后我们直接去使用,他就不用开放这一层的源码,这就是HAL层的存在意义。
为什么需要JNI?
应⽤使⽤java编写,驱动⼀般使⽤c/cpp编写,提供⼀种Java访问c/cpp的⽅法。也就是Java代码可通过JNI接⼝调⽤C/C++⽅法。
JNI开发流程的步骤:
1)编写JNI⽅法表并注册
2)实现JNI的.c⽂件

2、自定义系统Service
Framework还有一个很重要的功能,就是系统server。所有的硬件呢都是通过我们的系统server去进行管理,那我们怎样为我们的硬件接口去添加一个自定义的系统serve呢?
(1)建立aidl通信接口;
(2)在system_server中注册service到servicemanager;
(3)实现service,对应aidl中的接口函数。
(4)client向servicemanager请求service,成功后,调用aidl接口函数,建立client进程和service进程的通信关系。

总结来说就是:
1)system_server完成注册功能;
2)servicemanager完成服务管理功能;
3)aidl完成通讯功能;
(1)建立aidl通信接口
在frameworks/base/core/java/android/os/路径下新建对应名称的一个aidl文件
下面我们以顾凯歌的一个蓝牙模块的服务为大家举例:
路径:frameworks/base/core/java/android/os/IGocsdkService.aidl:
(因为他是Interface的一个接口,所有在前面加个 "I")+ package android.os;+ interface IEmbededService {
+ interface IFmService {
+
+ //蓝牙状态回调注册去注销
+ void registerCallback(IGocsdkCallback callback);
+ // 注销蓝牙状态
+ void unregisterCallback(IGocsdkCallback callback);
+
+ //注释后面带的为操作后相应的回调回复
+ //蓝牙协议软复位 ---》 onInitSucceed()
+ void restBluetooth();
+
+ //获取本地蓝牙名称 ---》onCurrentDeviceName()
+ void getLocalName();
+
+ //设置本地蓝牙名称 ---》onCurrentDeviceName()
+ void setLocalName(String name);+ ..................//等等一些,都为接口函数,会在下面实现}
编译到系统
路径:frameworks/base/Android.mk diff --git a/android/frameworks/base/Android.bp b/android/frameworks/base/Android.bp
old mode 100644
new mode 100755
index d8a7f06..953759c
--- a/android/frameworks/base/Android.bp
+++ b/android/frameworks/base/Android.bp
@@ -265,6 +265,11 @@ java_defaults {"core/java/android/os/IRecoverySystemProgressListener.aidl","core/java/android/os/IRemoteCallback.aidl","core/java/android/os/ISchedulingPolicyService.aidl",
+ "core/java/android/os/IGocsdkService.aidl",":statsd_aidl","core/java/android/os/ISystemUpdateManager.aidl","core/java/android/os/IThermalEventListener.aidl",
(2)在system_server中注册EmbededServicer到servicemanager
路径:frameworks/base/services/java/com/android/server/SystemServer.java
使用ServiceManager.addService添加我们自定义的server
@@ -1097,6 +1097,13 @@ public final class SystemServer {
}Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);+ try {+ Slog.i(TAG, "IGocsdkService");+ ServiceManager.addService("gocsdkService ", new GocsdkService());+ } catch (Throwable e) { + Slog.e(TAG, "Failure starting Gocsdk Service", e);+ }
(3)实现EmbededService,对应aidl中的接⼝函数
路径:frameworks/base/services/java/com/android/server/EmbededService.java
package com.android.server;
import android.content.Context;
import android.os.IGocsdkService;
import android.util.Slog;
public class GocsdkService extends IGocsdkService.Stub {private static final String TAG = "GocsdkService";GocsdkService(){Slog.i(TAG,"GocsdkService init");}public void registerCallback(IGocsdkCallback callback){return xxx;}public void unregisterCallback(IGocsdkCallback callback){return xxx;}public void getLocalName(){return xxx;}.......................
}
(4)在app中使⽤IEmbededService的大致流程如下
很好理解吧,把我们对应的一个服务导入,然后去初始化一个类,然后通过ServiceManager去找到我们自定义的这个server,然后使用自定义服务的函数获取数据。
import android.os.IGocsdkService; //导入private IGocsdkService mGocsdkService = null; //初始化类
mGocsdkService = IGocsdkService .Stub.asInterface(ServiceManager.getService("gocsdkService"));int version= mEmbededService.getLocalName();
String text = String.value(localName);
(5)编译service,烧录
直接全sdk编译,防止有遗漏
(6)验证
使⽤service list查看是否有EmbededService
xxx:/ $ service list | grep gocsdkServicegocsdkService: [android.os.IGocsdkService]
三、为什么需要Android HAL?
Hardware Abstract Layer 硬件抽象层,由于Linux Kernel需要遵循GPL开源协议,硬件⼚商为了保护⾃⼰硬件⽅⾯的各项参数不被外泄,⽽⼀个设备的驱动程序包含了硬件的⼀些重要参数,所以驱动的开源势必会使硬件⼚商蒙受损失,Google为了保护硬件⼚商的利益,所以在Android系统中加⼊了HAL层,在HAL层中不必遵循GPL协议,所以代码可以封闭。
所以如果硬件驱动开源的写在Kernel⾥,Framework直接调⽤,⽽不愿意开源的就写在HAL层⾥,实现闭源。也就是说,编写驱动分为两个部分,⼀个是HAL层的驱动代码,⼀个是Kernel层的驱动代码。
1、内核实现HAL驱动的⽅法有两种:
(1)采⽤直接调⽤so动态链接库⽅式
采⽤共享库形式,在编译时会调⽤到。由于采⽤function call形式调⽤,因此可被多个进程使⽤,但会被mapping到多个进程空间中,造成浪费,同时需要考虑代码能否安全重⼊的问题。
(2)采⽤Stub代理⽅式调⽤
采⽤HAL module和HAL stub结合形式,HAL stub不是⼀个share library,编译时
上层只拥有访问HAL stub的函数指针,并不需要HAL stub。上层通过HAL module提供的统⼀接⼝获取并操作HAL stub,so⽂件只会被mapping到⼀个进程,也不存在重复mapping和重⼊问题。
2、如何编写HAL层驱动
我们现在一般都是采用第二种方式,基于HAL框架提供了三个结构体,分别为hw_device_t、hw_module_t、hw_module_methods_t,编写HAL层驱动则是依据这三个结构体作扩展,我们创建⾃⼰驱动的device_t,module_t代码,并且写hw_module_methods_t这个结构体中⽅法的实现代码,最后JNI层通过hw_get_module调⽤。
(1)在 android/hardware/libhardware/modules/xxx 路径下创建我们的HAL文件夹,例如LED:
mkdirhardware/libhardware/modules/ledpath:hardware/libhardware/include/hardware/led_hal.h
path:hardware/libhardware/modules/embeded/led_hal.c
(1)led_hal.c:
#define LOG_TAG "dLed"
#include <hardware/hardware.h>
#include <hardware/led_hal.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
#define DEVICE_NAME "sys/led/embeded_blue_led"
#define MODULE_NAME "EmLed"/*设备打开和关闭接⼝*/
static int embededled_device_open(const struct hw_module_t* module, const
char* name, struct hw_device_t** device);
static int embededled_device_close(struct hw_device_t* device);/*设备访问接⼝*/
static int embededled_set_val(struct embededled_device_t* dev, int val);
static int embededled_get_val(struct embededled_device_t* dev, int* val);
static int embededled_device_open(const struct hw_module_t* module, const
char* name, struct hw_device_t** device) {struct embededled_device_t* dev;dev = (structembededled_device_t*)malloc(sizeof(struct embededled_device_t));if(!dev) {ALOGI("embededled Stub: failed to alloc space");return -EFAULT;}memset(dev, 0, sizeof(struct embededled_device_t));//初始化设备相关信息,实现访问接⼝函数dev->common.tag = HARDWARE_DEVICE_TAG;dev->common.version = 0;dev->common.module = (hw_module_t*)module;dev->common.close = embededled_device_close;dev->set_val = embededled_set_val;dev->get_val = embededled_get_val;if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {ALOGI("embededled Stub: failed to opensys/embededled/embeded_blue_led -- %s.", strerror(errno));free(dev);return -EFAULT;}int status = 0;write(dev->fd, &status, sizeof(status));*device = &(dev->common);ALOGI("embededled Stub: open sys/embededled/embeded_blue_ledsuccessfully.");return 0;
}static int embededled_device_close(struct hw_device_t* device) {struct embededled_device_t* embededled_device = (structembededled_device_t*)device;if(embededled_device) {close(embededled_device->fd);free(embededled_device);}return 0;
}static int embededled_set_val(struct embededled_device_t* dev, int val) {ALOGI("embededled Stub: set value %d to device.", val);write(dev->fd, &val, sizeof(val));return 0;}static int embededled_get_val(struct embededled_device_t* dev, int* val) {
if(!val) {ALOGI("embededled Stub: error val pointer");return -EFAULT;
}read(dev->fd, val, sizeof(*val));ALOGI("embededled Stub: get value %d from device", *val);return 0;
}/*模块⽅法表*/
static struct hw_module_methods_t embededled_module_methods = {
open: embededled_device_open
};/*模块实例变量*/
struct embededled_module_t HAL_MODULE_INFO_SYM = {
common: {tag: HARDWARE_MODULE_TAG,version_major: 1,version_minor: 0,id: EMBEDEDLED_HARDWARE_MODULE_ID,name: MODULE_NAME,author: MODULE_AUTHOR,methods: &embededled_module_methods,
}
};
led_hal.h:
path:hardware/libhardware/include/hardware/led_hal.h
#ifndef ANDROID_LED_INTERFACE_H
#define ANDROID_LED_INTERFACE_H
#include <hardware/hardware.h>
__BEGIN_DECLS
/*定义模块ID*/
#define EMBEDEDLED_HARDWARE_MODULE_ID "led_hal"
/*硬件模块结构体*/
struct led_module_t {struct hw_module_t common;
};
/*硬件接⼝结构体*/
struct embededled_device_t {struct hw_device_t common;int fd;int (*set_val)(struct led_device_t* dev, int val);int (*get_val)(struct led_device_t* dev, int* val);
};
__END_DECLS
#endif
四、JNI层添加
JNI开发流程的步骤:

里面呢就是我们要实现的三个函数,然后再把对应的方法注册到我们的server里面去
1 diff --git a/frameworks/base/services/core/jni/Android.mkb/frameworks/base/services/core/jni/Android.mk
2 index 0f0124bd46..305773298a 100644
3 --- a/frameworks/base/services/core/jni/Android.mk
4 +++ b/frameworks/base/services/core/jni/Android.mk
5 @@ -36,6 +36,7 @@ LOCAL_SRC_FILES += \
6 $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
7 $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
8 $(LOCAL_REL_DIR)/com_android_server_PersistentDataBlockService.cpp \
9 + $(LOCAL_REL_DIR)/com_android_server_EmbededLedService.cpp \
10 $(LOCAL_REL_DIR)/onload.cpp
11
12 LOCAL_SRC_FILES += \
1 diff --git a/frameworks/base/services/core/jni/onload.cppb/frameworks/base/services/core/jni/onload.cpp
2 index d5861f8c41..b52f7917fd 100644
3 --- a/frameworks/base/services/core/jni/onload.cpp
4 +++ b/frameworks/base/services/core/jni/onload.cpp
5 @@ -47,6 +47,7 @@ intregister_android_server_PersistentDataBlockService(JNIEnv* env);
6 int register_android_server_Watchdog(JNIEnv* env);
7 int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
8 int register_com_android_server_rkdisplay_RkDisplayModes(JNIEnv* env);
9 +int register_android_server_EmbededLedService(JNIEnv* env);
10 };
11
12 using namespace android;
13 @@ -89,7 +90,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
14 register_android_server_Watchdog(env);
15 register_android_server_HardwarePropertiesManagerService(env);
16 register_com_android_server_rkdisplay_RkDisplayModes(env);
17 -
18 -
19 + register_android_server_EmbededLedService(env);
20 return JNI_VERSION_1_4;
21 } 1、AIDL:
1 package android.os;
2
3 interface IEmbededLedService {
4 void setVal(int val);
5 int getVal();
6 } 1 diff --git a/frameworks/base/Android.mk b/frameworks/base/Android.mk
2 index b9692de0e1..c426a3cd99 100755
3 --- a/frameworks/base/Android.mk
4 +++ b/frameworks/base/Android.mk
5 @@ -240,6 +240,7 @@ LOCAL_SRC_FILES += \
6 core/java/android/os/IUpdateLock.aidl \
7 core/java/android/os/IUserManager.aidl \
8 core/java/android/os/IVibratorService.aidl \
9 + core/java/android/os/IEmbededLedService.aidl \
10 core/java/android/os/IDisplayDeviceManagementService.aidl \
11 core/java/android/os/IRkDisplayDeviceManagementService.aidl \
12 core/java/android/security/IKeystoreService.aidl \ 2、Service
1 package com.android.server;
2 import android.content.Context;
3 import android.os.IEmbededLedService;
4 import android.util.Slog;
5 public class EmbededLedService extends IEmbededLedService.Stub {
6 private static final String TAG = "EmbededLedService";
7 EmbededLedService() {
8
9 boolean status = init_native();
10 Slog.i(TAG,"EmbededLedService Stub init"+status);
11 }
12 public void setVal(int val) {
13 setVal_native(val);
14 }
15 public int getVal() {
16 return getVal_native();
17 }
18
19 //JNI⽅法
20 private static native boolean init_native();
21 private static native void setVal_native(int val);
22 private static native int getVal_native();
23 }; 3、添加Service到System启动
1 diff --git
a/frameworks/base/services/java/com/android/server/SystemServer.java
b/frameworks/base/services/java/com/android/server/SystemServer.java
2 index cc6f1850e6..b22ecda734 100644
3 --- a/frameworks/base/services/java/com/android/server/SystemServer.java
4 +++ b/frameworks/base/services/java/com/android/server/SystemServer.java
5 @@ -1086,6 +1086,15 @@ public final class SystemServer {
6 } catch (Throwable e) {
7 reportWtf("starting DiskStats Service", e);
8 }
9 +
10 + try {
11 + Slog.i(TAG, "Embededled Service");
12 + ServiceManager.addService("embededled", new
EmbededLedService());
13 + } catch (Throwable e) {
14 + Slog.e(TAG, "Failure starting Embededled Service", e);
15 + }
16 +
17 +
18 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
19
20 if (!disableSamplingProfiler) { 4、编译&烧写
相关文章:
Android HAL到Framework
一、为什么需要Framwork? Framework实际上是⼀个应⽤程序的框架,提供了很多服务: 1、丰富⽽⼜可扩展的视图(Views), 可以⽤来构建应⽤程序,它包括列表(lists),⽹格&am…...
Python数据可视化(七)
绘制 3D 图形 到目前为止,我们一直在讨论有关 2D 图形的绘制方法和绘制技术。3D 图形也是数据可视化的 一个很重要的应用方面,我们接下来就重点讲解有关 3D 图形的实现方法。绘制 3D 图形通常需要导 入 mpl_toolkits 包中的 mplot3d 包的相关模块&#x…...
StringMVC
目录 一,MVC定义 二,SpringMVC的基本使用 2.1建立连接 - RequestMapping("/...") 编辑 2.2请求 1.传递单个参数 2.传递多个参数 3.传递对象 4.参数重命名 5.传递数组 6. 传递集合 7.传递JSON数据 8. 获取url中数据 9. 传递文…...
前端基础入门三大核心之HTML篇 —— SVG的viewBox、width和height:绘制矢量图的魔法比例尺【含代码示例】
前端基础入门三大核心之HTML篇 —— SVG的viewBox、width和height:绘制矢量图的魔法比例尺【含代码示例】 基本概念与作用viewBoxwidth和height 代码示例与实践基础示例动态调整示例 不同角度的使用思路保持比例缩放自动适应容器 实际问题与解决方案结语与讨论 在前…...
Java-Zookeeper
zookeeper是什么 一个分布式、开源的分布式应用程序协调服务,具有配置维护、域名服务、分布式同步、组服务等 zookeeper有哪些功能 功能简介集群管理监控节点状态、运行请求等主节点选举主节点挂掉之后会执行新主选举分布式锁zookeeper提供两种锁:独占…...
Godot游戏引擎有哪些优势
哈喽呀,大家好呀,淼淼又来和大家见面啦,众所周知在当今游戏开发领域,各种游戏引擎如雨后春笋般涌现,为开发者提供了丰富的选择。而在这些众多的选择中,Godot游戏引擎以其独特的特性和开放源代码的优势&…...
一张图看懂大模型性价比:能力、价格、并发量全面PK
最近,国内云厂商的大模型掀起一场降价风暴。火山引擎、阿里云、百度云等纷纷宣布降价,部分模型价格降幅据称高达99%,甚至还有些模型直接免费。 五花八门的降价话术,一眼望去遍地黄金。但事实真的如此吗?今天我们就拨开…...
设计井字棋游戏(一)
创建游戏登录注册窗口 用户名admin 密码admin(可自行改变) 主页面 1. 导包 import pickle:导入 pickle 模块,这是一个 Python 的内置模块,用于将 Python 对象序列化和反序列化。序列化是指将对象转换为字节流&am…...
华为手机卡顿(仅针对于部分人来说,我也不清楚是否真的有用)
关机! 之前一段时间手机变得特别卡顿,然后网上搜了一堆教程一点用没有,结果因为昨天下午在考试所以把手机关机了一个多小时,再打开之后手机就变得很流畅,原因不详,但效果显著,如有需要可尝试一…...
7、按钮无法点击
不能点击,打开f12,删除disabled...
开源博客项目Blog .NET Core源码学习(25:App.Hosting项目结构分析-13)
本文学习并分析App.Hosting项目中后台管理页面的文章管理页面。 文章管理页面用于显示、检索、新建、编辑、删除文章数据,以便在前台页面的首页、文章专栏、文章详情页面显示文章数据。文章管理页面附带一新建及编辑页面,以支撑新建和编辑文章数据。…...
第七节 ConfigurationClassParser 源码分析
tips: ConfigurationClassParser 是 Springframework 中的重要类。 本章主要是源码理解,有难度和深度,也枯燥乏味,可以根据实际情况选择阅读。 位置:org.springframework.context.annotation.ConfigurationClassPars…...
零基础代码随想录【Day42】|| 1049. 最后一块石头的重量 II,494. 目标和,474.一和零
目录 DAY42 1049.最后一块石头的重量II 解题思路&代码 494.目标和 解题思路&代码 474.一和零 解题思路&代码 DAY42 1049.最后一块石头的重量II 力扣题目链接(opens new window) 题目难度:中等 有一堆石头,每块石头的重量都是正整…...
2024-5-24 石群电路-15
2024-5-24,星期五,22:15,天气:晴,心情:晴。今天最后一天上班,终于要放返校假啦,开心!!!!!!不过放假也不能耽误…...
功能测试:核心原理、挑战以及解决之道
在软件开发生命周期中,功能测试占据了至关重要的位置。它是确保软件应用按照既定的要求和规格运行的关键测试阶段。功能测试的目的在于验证软件的功能、行为和用户界面等是否达到了业务需求的标准。本文将深入探讨功能测试的概念,执行过程中可能遇到的挑…...
跨境电商赛道,云手机到底能不能化繁为简?
当下国内电商背景: 从零售额的数据来看:随着互联网的普及和消费者购物习惯的改变,国内电商市场规模持续扩大。据相关数据显示,网络消费亮点纷呈,一季度全国网上零售额达到了3.3万亿元,同比增长12.4%。这表…...
linux:信号深入理解
文章目录 1.信号的概念1.1基本概念1.2信号的处理基本概念1.3信号的发送与保存基本概念 2.信号的产生2.1信号产生的五种方式2.2信号遗留问题(core,temp等) 3.信号的保存3.1 信号阻塞3.2 信号特有类型 sigset_t3.3 信号集操作函数3.4 信号集操作函数的使用 4.信号的处理4.1 信号的…...
Android系统的/etc/mkshrc文件
/etc/mkshrc 文件是用于配置 mksh(MirBSD Korn Shell)环境的启动脚本。mksh 是 Android 默认使用的 shell,在 shell 启动时会读取并执行这个文件中的配置。以下是关于 /etc/mkshrc 文件的详细信息及其用途。 /etc/mkshrc 文件的作用 环境配…...
LeetCode199二叉树的右视图
题目描述 给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 解析 这一题的关键其实就是找到怎么去得到当前是哪一层级,可以利用队列对二叉树进行层次遍历,但…...
JavaScript 基础
一 JavaScript 的书写形式 1.1 行内式 <input type"button" value"点我一下" onclick"alert(hello akai);" > 注意,JS 中的字符串常量可以用单引号表示,也可以使用双引号表示.HTML 中推荐使用双引号,JS 中推荐使用单引号(使用双引号容易…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
