鸿蒙linux内核的L1设备服务开发详解

鸿蒙linux内核的L1设备服务开发详解

​ 鸿蒙基于linux内核的L1设备的系统基于面向服务架构,提供了服务开发、服务的子功能开发、对外接口的开发、以及多服务进程、进程间服务调用的开发能力。现对此基座下的服务进行剖析并以实例方式进行讲解。

一、简介

在L1设备中服务分为两类:

  • core system service

    foundation\systemabilitymgr\safwk_lite\BUILD.gn

    deps添加依赖,由foundation进程启动加载。此种方式的服务挂载在foundation进程中。

  • system and application service

​ 以应用的方式进行启动,可以在/base/startup/init/service/etc/init.cfg中添加start service方式启动,或者以可执行程序的方式进行启动。此种方式的服务是以本身的进程启动。

二、服务的开发

首先SA服务包含两部份:服务端、子功能端,服务提供了子功能的接口有默认的接口和自定义子功能接口,子功能接口又包含了进程内接口与进程间接口。后续章节会一一进行说明。

2.1 服务端实现

2.1.1 服务声明

#include <service.h>
#include <message.h>
#include <common.h>typedef struct ExampleService {INHERIT_SERVICE;Identity identity;
} ExampleService;
  • INHERIT_SERVICE在foundation\systemabilitymgr\samgr_lite\interfaces\kits\samgr\service.h中定义如下:
#define INHERIT_SERVICE                                          \const char *(*GetName)(Service * service);                   \BOOL (*Initialize)(Service * service, Identity identity);    \BOOL (*MessageHandle)(Service * service, Request * request); \TaskConfig (*GetTaskConfig)(Service * service)

该宏定义了服务的生命周期函数的函数指针。

  • Identity在foundation\systemabilitymgr\samgr_lite\interfaces\kits\samgr\message.h中定义如下:
struct Identity {/** Service ID */int16 serviceId;/** Feature ID */int16 featureId;/** Message queue ID */MQueueId queueId;
};

该宏定义了服务ID、默认子功能ID及消息队列ID。

  • MQueueId定义在foundation\industrialbus\modules\common.h
typedef void *MQueueId;

关于默认子功能及消息队列指针,此处按下不表,在相应部份再进行说明,此时服务结构定义只需要添加两个宏即可。

2.1.2 实现服务的生命周期函数

实现该服务对象指向的生命周期函数。

  • 服务生命周期函数,实现示例如下:
#define EXAMPLE_SERVICE "moduleservice"
// 返回服务名称
static const char *GetName(Service *service)
{(void)service;return EXAMPLE_SERVICE;
}
// 初始化服务
static BOOL Initialize(Service *service, Identity identity)
{ExampleService *example = (ExampleService *)service;// 保存服务的唯一身份标识,用来自己的IUnknown接口对服务发消息时使用。example->identity = identity;return TRUE;
}
static BOOL MessageHandle(Service *service, Request *msg)
{ExampleService *example = (ExampleService *)service;(void)example;switch (msg->msgId) {default:break;}return FALSE;
}
static TaskConfig GetTaskConfig(Service *service)
{(void)service;TaskConfig config = {LEVEL_HIGH, PRI_NORMAL,0x800, MODULE_QUEUE_SIZE, SINGLE_TASK};return config;
}

2.1.3 创建服务对象

实例化ExampleService服务对象,将对象的生命函数指针指向其实现其实现的生命周期函数。

// 创建服务对象
static ExampleService g_example = {.GetName = GetName,.Initialize = Initialize,.MessageHandle = MessageHandle,.GetTaskConfig = GetTaskConfig,{-1, -1, NULL}
};

identity初始化时serviceId,featureId均为-1,queueId为空,在生命周期函数Initialize中赋值系统分配的identity。

2.1.4 注册服务

向SAMGR注册服务及接口

static void Init(void)
{SAMGR_GetInstance()->RegisterService((Service *)&g_example);
}

定义服务的初始化入口:

SYSEX_SERVICE_INIT(Init);

2.2 子功能端

通过以上操作后,服务驻存在系统中,服务没有对外的接口,此时需要添加子功能feature,提供服务对外的接口。

子功能按分为两类:进程内子功能、跨进程子功能

2.2.1 定义子功能

子功能按通信方式分为两类:进程内子功能、跨进程子功能。

子功能还可以分为服务默认子功能与自定义子功能。

默认子功能不需要子功能生命周期,其生命周期由服务的生命周期管理。

2.2.1.1 进程内子功能接口
#include <iunknown.h>
typedef struct {INHERIT_IUNKNOWN;BOOL (*FeatureCall)(IUnknown *iUnknown, const char *buff);
} DefaultFeatureApi;
2.2.1.2 跨进程子功能接口
#include <iproxy_server.h>
typedef struct {INHERIT_SERVER_IPROXY;BOOL (*FeatureCall)(IUnknown *iUnknown, const char *buff);
} CrossProcessFeatureApi;

进程内子功能与跨进程子功能的声明表现在使用两个宏INHERIT_IUNKNOWN与INHERIT_SERVER_IPROXY

  • INHERIT_IUNKNOWN定义在foundation\systemabilitymgr\samgr_lite\interfaces\kits\samgr\iunknown.h
#define INHERIT_IUNKNOWN                                                   \int (*QueryInterface)(IUnknown *iUnknown, int version, void **target); \int (*AddRef)(IUnknown *iUnknown);                                     \int (*Release)(IUnknown *iUnknown)
  • INHERIT_SERVER_IPROXY定义在foundation\systemabilitymgr\samgr_lite\interfaces\kits\samgr\iunknown.h
#define INHERIT_SERVER_IPROXY \INHERIT_IUNKNOWN; \int32 (*Invoke)(IServerProxy *iProxy, int funcId, void *origin, IpcIo *req, IpcIo *reply)

跨进程feature的定义相对进程内定义,多出来的Invoke函数指针,跨进程的方法通过此方法完成进程间通信、参数序列化与反序列化。

2.2.1.3 默认子功能

默认子功能需要将声明放在服务结构中进行声明。

以2.1.1中声明的服务为例,只需要将DefaultFeatureApi或CrossProcessFeatureApi添加至服务ExampleService中。

  • 默认进程内子功能
// 默认进程内子功能
typedef struct ExampleService {INHERIT_SERVICE;INHERIT_IUNKNOWNENTRY(DefaultFeatureApi);Identity identity;
} ExampleService;
  • 默认跨进程子功能
// 自定义跨进程子功能
typedef struct ExampleService {INHERIT_SERVICE;INHERIT_IUNKNOWNENTRY(CrossProcessFeatureApi);Identity identity;
} ExampleService;

INHERIT_IUNKNOWNENTRY宏添加了子功能的版本也引用计数,定义在foundation\systemabilitymgr\samgr_lite\interfaces\kits\samgr\iunknown.h

#define INHERIT_IUNKNOWNENTRY(T) \uint16 ver;                   \int16 ref;                   \T iUnknown
2.2.1.4 自定义子功能

首先自定义子功能结构,然后定义子功能接口ExampleFeatureApi,子功能接口也分为进程间接口与跨进程接口。

// 定义子功能结构
typedef struct ExampleFeature {INHERIT_FEATURE;INHERIT_IUNKNOWNENTRY(ExampleFeatureApi);Identity identity;
} ExampleFeature;
  • 进程间子功能接口定义
#include <iunknown.h>
typedef struct {INHERIT_IUNKNOWN;BOOL (*FeatureCall)(IUnknown *iUnknown, const char *buff);
} ExampleFeatureApi;
  • 跨进程子功能接口定义
#include <iproxy_server.h>
typedef struct {INHERIT_SERVER_IPROXY;BOOL (*FeatureCall)(IUnknown *iUnknown, const char *buff);
} ExampleFeatureApi;
  • INHERIT_FEATURE定义在foundation\systemabilitymgr\samgr_lite\interfaces\kits\samgr\feature.h
#define INHERIT_FEATURE                                                     \
const char *(*GetName)(Feature *feature);                                   \
void (*OnInitialize)(Feature *feature, Service *parent, Identity identity); \
void (*OnStop)(Feature *feature, Identity identity);                        \
BOOL (*OnMessage)(Feature *feature, Request *request);                      \
BOOL (*IsDistributed)(void)

该宏定义了子功能的生命周期函数的函数指针。

2.2.2 服务子功能实现

  • 进程间子功能接口实现
static BOOL FeatureCall(IUnknown *iUnknown, const char *buff)
{(void)iUnknown;HILOG_INFO(1,"buff:%s\n", buff);HILOG_INFO(1,"[LPC Test][SyncCall API] Default Success!\n");return TRUE;
}
  • 跨进程子功能接口实现
enum MessageId {MSG_PROC,MSG_TIME_PROC,
};
static BOOL FeatureCall(IUnknown *iUnknown, const char *body)
{Request request = {.msgId = MSG_PROC, .msgValue = 0};request.len = (uint32_t)(strlen(body) + 1);request.data = malloc(request.len);if (request.data == NULL) {return FALSE;}if (strcpy_s(request.data, request.len, body) != EOK) {free(request.data);return FALSE;}ExampleFeature *feature = GET_OBJECT(iUnknown, ExampleFeature, iUnknown);return SAMGR_SendRequest(&g_example.identity, &request, NULL);
}

FeatureCall由跨进程方法Invoke调用,然后子功能通过SAMGR_SendRequest发送请求至内核。

2.2.2 实现子功能的生命周期函数

默认子功能不需要子功能生命周期,实现子功能对象指向的生命周期函数。

  • 子功能生命周期函数,实现示例如下:
#define EXAMPLE_FEATURE "modulefeature"
enum MessageId {MSG_PROC,MSG_TIME_PROC,
};
static const char *FEATURE_GetName(Feature *feature)
{(void)feature;return EXAMPLE_FEATURE;
}
static void FEATURE_OnInitialize(Feature *feature, Service *parent, Identity identity)
{ExampleFeature *demoFeature = (ExampleFeature *)feature;demoFeature->identity = identity;printf("serviceId:%d,featureId:%d,queueId:%p\n", identity.serviceId, identity.featureId, identity.queueId);printf("demoFeature:%p,g_example:%p\n", demoFeature, &g_example);printf("serviceId:%d,featureId:%d,queueId:%p\n",g_example.identity.serviceId, g_example.identity.featureId, g_example.identity.queueId);
}
static void FEATURE_OnStop(Feature *feature, Identity identity)
{(void)feature;(void)identity;g_example.identity.queueId = NULL;g_example.identity.featureId = -1;g_example.identity.serviceId = -1;
}
static BOOL FEATURE_OnMessage(Feature *feature, Request *request)
{(void)feature;if (request->msgId == MSG_PROC) {printf("[LPC Test][OnMessage: S:%s, F:%s] msgId<MSG_PROC> %s \n",EXAMPLE_SERVICE, feature->GetName(feature),(char *)request->data);Response response = {.data = "Yes, you did!", .len = 0};SAMGR_SendResponse(request, &response);return TRUE;} else {if (request->msgId == MSG_TIME_PROC) {printf("[LPC Test] OnMessage: S:%s, F:%s] Time Message Get Value<%s>!",EXAMPLE_SERVICE, feature->GetName(feature),request->msgValue ? "TRUE" : "FALSE");// AsyncTimeCall(GET_IUNKNOWN(g_example));return FALSE;}}printf("[LPC Test][OnMessage S:%s, F:%s] Inner Error! \n",EXAMPLE_SERVICE, feature->GetName(feature));return FALSE;
}

FEATURE_OnMessage处理异步处理应答的消息。

2.2.3 创建功能对象

  • 创建进程内通信子功能对象
static ExampleFeature g_example = {.GetName = FEATURE_GetName,.OnInitialize = FEATURE_OnInitialize,.OnStop = FEATURE_OnStop,.OnMessage = FEATURE_OnMessage,DEFAULT_IUNKNOWN_ENTRY_BEGIN,.FeatureCall = FeatureCall,DEFAULT_IUNKNOWN_ENTRY_END,.identity = {-1, -1, NULL},
};
  • 创建跨进程通信子功能对象
static ExampleFeature g_example = {.GetName = FEATURE_GetName,.OnInitialize = FEATURE_OnInitialize,.OnStop = FEATURE_OnStop,.OnMessage = FEATURE_OnMessage,SERVER_IPROXY_IMPL_BEGIN,..Invoke = Invoke,.FeatureCall = FeatureCall,IPROXY_END,.identity = {-1, -1, NULL},
};
  • DEFAULT_IUNKNOWN_ENTRY_BEGIN…DEFAULT_IUNKNOWN_ENTRY_END
// foundation\systemabilitymgr\samgr_lite\interfaces\kits\samgr\iunknown.h
#define DEFAULT_VERSION 0x20
#define DEFAULT_IUNKNOWN_IMPL                  \.QueryInterface = IUNKNOWN_QueryInterface, \.AddRef = IUNKNOWN_AddRef,                 \.Release = IUNKNOWN_Release#define IUNKNOWN_ENTRY_BEGIN(version)   \.ver = (version),                   \.ref = 1,                           \.iUnknown = {                       \DEFAULT_IUNKNOWN_IMPL#define DEFAULT_IUNKNOWN_ENTRY_BEGIN IUNKNOWN_ENTRY_BEGIN(DEFAULT_VERSION)#define DEFAULT_IUNKNOWN_ENTRY_END IUNKNOWN_ENTRY_END

此宏表示实现接口查询与引用计数方法使用系统实现的IUNKNOWN_QueryInterface、IUNKNOWN_AddRef、IUNKNOWN_Release三个方法。

  • SERVER_IPROXY_IMPL_BEGIN…IPROXY_END
// foundation\systemabilitymgr\samgr_lite\interfaces\kits\registry\iproxy_server.h
#define SERVER_PROXY_VER 0x80
#define SERVER_IMPL_PROXY_VER ((uint16)SERVER_PROXY_VER | (uint16)DEFAULT_VERSION)#define SERVER_IPROXY_IMPL_BEGIN  IUNKNOWN_ENTRY_BEGIN(SERVER_IMPL_PROXY_VER)#define IPROXY_END IUNKNOWN_ENTRY_END

此宏表示实现接口查询与引用计数方法使用系统实现的IUNKNOWN_QueryInterface、IUNKNOWN_AddRef、IUNKNOWN_Release三个方法。

两个宏校验的版本号不同。

在跨进程子功能中,需要实现Invoke接口

typedef enum {FEATURE_CALL,MY_MODULE_ASYNC_CALLBACK,
} MyModuleMsgID;static int32 Invoke(IServerProxy *iProxy, int funcId, void *origin, IpcIo *req, IpcIo *reply)
{ExampleFeatureApi *api = (ExampleFeatureApi *)iProxy;ExampleFeature *feature = GET_OBJECT(api, ExampleFeature, iUnknown);printf("xxxxxxxxxxxxxxxxxxxxxxxxxxx api:%p,g_example:%p,feature:%p\n", api, &g_example, feature);BOOL ret;size_t tempLen = 0;size_t len = 0;switch (funcId) {case FEATURE_CALL:buf = ReadString(req, &tempLen);printf("[feature] tempLen:%u\n", tempLen);printf("[feature] req: %s\n", buf);break;default:WriteBool(reply, FALSE);break;}return 0;
}

2.2.4 注册子功能

  • 默认子功能

默认子功能的注册在服务注册之后。

static void Init(void)
{SAMGR_GetInstance()->RegisterService((Service *)&g_example);SAMGR_GetInstance()->RegisterDefaultFeatureApi(EXAMPLE_SERVICE, GET_IUNKNOWN(g_example));
}SYSEX_SERVICE_INIT(Init);
  • 自定义子功能
static void Init(void)
{SAMGR_GetInstance()->RegisterFeature(EXAMPLE_SERVICE, (Feature *)&g_example);SAMGR_GetInstance()->RegisterFeatureApi(EXAMPLE_SERVICE, EXAMPLE_FEATURE, GET_IUNKNOWN(g_example));printf("[Register Test][Reg S:%s, F:%s]!\n",EXAMPLE_SERVICE, EXAMPLE_FEATURE);
}SYSEX_FEATURE_INIT(Init);

2.2.5 服务与子功能权限

修改文件base\security\permission_lite\services\ipc_auth\include\policy_preset.h

FeaturePolicy mymoduleFeature[] = {{"modulefeature",{{.type = RANGE,.uidMin = 0,.uidMax = __INT_MAX__,},},},
};
// 在g_presetPolicies[]里面增加{"moduleservice", mymoduleFeature, 1},

注意,服务名“moduleservice"与子功能名"modulefeature”,长度最长15个字节。

2.2 服务调用

2.2.1 进程内调用

#include <iunknown.h>
static DefaultFeatureApi *g_defaultProxy; 
IUnknown *iUnknown1 = SAMGR_GetInstance()->GetDefaultFeatureApi(EXAMPLE_SERVICE);
if (iUnknown1 == NULL) {return -1;
}
int32_t ret = iUnknown1->QueryInterface(iUnknown1, DEFAULT_VERSION, (void **)&g_defaultProxy);
if (ret != 0 || g_defaultProxy == NULL) {return -1;
}
ret = g_defaultProxy->FeatureCall((IUnknown *)g_defaultProxy, "Hi L1 first SA");
if (ret != 0) {return -1;
}
ret = g_defaultProxy->Release((IUnknown *)g_defaultProxy);

2.2.2 跨进程调用

#include <iunknown.h>typedef struct {int result;size_t messageLen;uint8_t *message;
} ServiceRspMsg;
static CrossProcessFeatureApi *g_clientProxy;IUnknown *iUnknown2 = SAMGR_GetInstance()->GetFeatureApi(EXAMPLE_SERVICE, EXAMPLE_FEATURE);
if (iUnknown2 == NULL) {return -1;
}
int32_t ret = iUnknown2->QueryInterface(iUnknown2, CLIENT_PROXY_VER, (void **)&g_clientProxy);
if (ret != 0 || g_clientProxy == NULL) {return -1;
}
IpcIo request;
char data[250];
IpcIoInit(&request, data, sizeof(data), 0);
WriteString(&request, "I want to async call good result!");
ServiceRspMsg reply = { 0 };
ret = g_clientProxy->Invoke((IUnknown *)g_clientProxy, FEATURE_CALL, &request, NULL, NULL);
if (ret != EXAMPLE_SUCCESS) {printf("[AsyncCall] ret:%d\n\r", ret);
}
if (ret != 0) {return -1;
}
ret = g_clientProxy->Release((IUnknown *)g_defaultProxy);

执行步骤:g_clientProxy->Invoke---------->异步FeatureCall方法---------->SAMGR_SendRequest---------->FEATURE_OnMessage---------->(IPCRPC)---------->服务Invoke

2.3 参数序列化

// 序列化
IpcIo request;
char data[250];
IpcIoInit(&request, data, sizeof(data), 0);
WriteString(&request, "I want to async call good result!");
// 反序列化
buf = ReadString(req, &tempLen);

2.4 跨进程回调函数调用

  • 首先调用方定义回调函数指针与回调方法实现
typedef int (*AsyncHandlerFunc)(IOwner owner, int code);static int CurrentCallback(IOwner owner, int code, IpcIo *reply)
{struct CurrentNotify *notify = (struct CurrentNotify *)owner;int32_t ret;ReadUint32(reply, &ret);printf("CurrentCallback................ %d\n", ret);notify->handler(notify->data, ret);return 0;
}
struct CurrentNotify {IOwner data;AsyncHandlerFunc handler;
};
......
ReplyData dataReply;
dataReply.id = 55;
IOwner notify = &dataReply; //自定义结构体
struct CurrentNotify customData = {.data = notify, .handle = AsyncHandler
};
ret = g_clientProxy->Invoke((IUnknown *)g_clientProxy, MY_MODULE_ASYNC_CALLBACK, &request, &customData, CurrentCallback);
if (ret != 0) {printf("[AsyncCallBack] ret:%d\n\r", ret);
}
  • 实现子功能的回调接口
static BOOL AsyncCallBack(IUnknown *iUnknown, const char *body, Handler handler)
{Request request = {.msgId = MSG_PROC, .msgValue = 0};request.len = (uint32_t)(strlen(body) + 1);request.data = malloc(request.len);if (request.data == NULL) {return FALSE;}if (strcpy_s(request.data, request.len, body) != EOK) {free(request.data);return FALSE;}// ExampleFeature *feature = GET_OBJECT(iUnknown, ExampleFeature, iUnknown);printf("[LPC Test][AsyncCallBack API] Send request! \n");return SAMGR_SendRequest(&g_example.identity, &request, handler);
}
  • Invoke反序列化结果
static int32 Invoke(IServerProxy *iProxy, int funcId, void *origin, IpcIo *req, IpcIo *reply)
{ExampleFeatureApi *api = (ExampleFeatureApi *)iProxy;ExampleFeature *feature = GET_OBJECT(api, ExampleFeature, iUnknown);printf("xxxxxxxxxxxxxxxxxxxxxxxxxxx api:%p,g_example:%p,feature:%p\n", api, &g_example, feature);BOOL ret;size_t tempLen = 0;uint8_t *buf = NULL;switch (funcId) {case FEATURE_CALL:buf = ReadString(req, &tempLen);printf("[feature] tempLen:%u\n", tempLen);printf("[feature] req: %s\n", buf);break;case MY_MODULE_ASYNC_CALLBACK: { // convert to sync proxybuf = ReadString(req, &tempLen);printf("xxxxxxxxxxxxxxxxxxxxxxxxxxx buf:%s\n", buf);WriteUint32(reply, 1);break;default:WriteBool(reply, FALSE);break;}return 0;
}

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

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

相关文章

git忽略某些文件(夹)更改方法

概述 在项目中,常有需要忽略的文件、文件夹提交到代码仓库中,在此做个笔录。 一、在项目根目录内新建文本文件,并重命名为.gitignore,该文件语法如下 # 以#开始的行,被视为注释. # 忽略掉所有文件名是 a.txt的文件. a.txt # 忽略所有生成的 java文件, *.java # a.j…

Time Travel

题目链接 解题思路 由于所有边集中的边加起来的总和至多为&#xff0c;无向图即&#xff0c;可以存下所以直接对所有边集中的边进行建边&#xff0c;同时对于每条边&#xff0c;记录其所在边集号对于每个边集&#xff0c;由大到小维护其能通过的时间点然后从1号跑最短路到当前…

Flutter开发框架

Flutter 技术栈是指用于构建应用程序的一系列技术和工具&#xff0c;这些技术和工具都是围绕 Flutter 框架组织的。Flutter 是由谷歌开发的开源 UI 开发工具包&#xff0c;用于从单一代码库开发跨平台应用程序&#xff0c;包括 Android、iOS、Web、以及桌面应用程序&#xff08…

操作系统功能

▶1.进程管理 简单地说&#xff0c;进程是程序的执行过程。程序是静态的&#xff0c;它仅仅包含描述算法的代码&#xff1b;进程是动态的&#xff0c;它包含了程序代码、数据和程序运行的状态等信息。进程管理的主要任务是对CPU资源进行分配&#xff0c;并对程序运行进行有效的…

高级语言期末2011级A卷(软件学院)

1.编写函数&#xff0c;判定正整数m和n&#xff08;均至少为2&#xff09;是否满足&#xff1a;数m为数n可分解的最小质因数&#xff08;数n可分解的最小质因数为整除n的最小质数&#xff09; 提示&#xff1a;判定m为质数且m是n的最小因数 #include <stdio.h> #include…

DiffiT | 英伟达提出用于图像生成的扩散ViT架构DiffiT,达成新SOTA!

本文首发于AIWalker&#xff0c;欢迎关注。 https://arxiv.org/abs/2312.02139 https://github.com/NVlabs/DiffiT 扩散模型以其强大的表达能力和高样本质量在许多领域得到了新的应用。对于样本生成&#xff0c;这些模型依赖于通过迭代去噪生成图像的去噪神经网络。然而&#…

坚持刷题|二叉树展开为链表

文章目录 题目考察点代码实现实现总结扩展问题用递归的方式实现在展开二叉树为链表的过程中&#xff0c;递归和迭代两种方法各有什么优缺点&#xff1f;可能的扩展问题 Hello&#xff0c;大家好&#xff0c;我是阿月。坚持刷题&#xff0c;老年痴呆追不上我&#xff0c;今天刷&…

LTD营销枢纽2023年度功能升级回顾

在过去的2023年&#xff0c;我们的团队致力于不断进步和创新。经过一年的不懈努力&#xff0c;我们共发布了50次的系统升级&#xff0c;引入了16种全新的解决方案与业务应用&#xff0c;并实施了1363项各类细致优化。 这些更新和改进不仅在我们的营销枢纽系统现有功能的基础上实…

Groovy(第二节) Groovy之HelloWorld

目录 为什么要学习 Groovy? 入门非常容易 用 Groovy 编写的 Hello World 运行 Groovy 示例

PHP函数 “password_hash“ 哈希密码

哈希函数是一种将输入转换为固定长度字符串的方法&#xff0c;这个过程是不可逆的&#xff0c;也就是无法从哈希值还原出原始输入。通过将密码进行哈希处理&#xff0c;即使数据库泄露&#xff0c;攻击者也无法简单地获取到用户密码。 在PHP中&#xff0c;我们可以使用 "…

35岁程序员,深入理解JVM的核心知识点

网络&#xff1a;分层模型、TCP、UDP、HTTP、HTTPS 分层模型 应用层&#xff1a;负责处理特定的应用程序细节&#xff0c;如 HTTP、FTP、DNS运输层&#xff1a;为两台主机提供端到端的基础通信&#xff0c;如 TCP、UDP网络层&#xff1a;控制分组传输、路由选择等&#xff0c…

【大数据架构(1)】Lambda Architecture – Realtime Data Processing 论文重点翻译

文章目录 1. INTRODUCTION2. LAMBDA ARCHITECTUREA) BATCH LAYERB) SPEED LAYERC) SERVICE LAYER 3. LIMITATIONS OF THE TRADITIONAL LAMBDAARCHITECTURE4. A PROPOSED SOLUTION1. 架构说明2. 前后架构改进对比 1. INTRODUCTION Lambda架构背后的需求是由于虽然MR能够处理大数…

SpringBoot+PDF.js实现按需分片加载预览(包含可运行示例源码)

SpringBootPDF.js实现按需分片加载预览 前言分片加载的效果前端项目前端项目结构前端核心代码前端项目运行 后端项目后端项目结构后端核心代码后端项目运行 项目运行效果首次访问分片加载 项目源码 前言 本文的解决方案旨在解决大体积PDF在线浏览加载缓慢、影响用户体验的难题…

C 程序结构

在我们学习 C 语言的基本构建块之前&#xff0c;让我们先来看看一个最小的 C 程序结构&#xff0c;在接下来的章节中可以以此作为参考。 C Hello World 实例 C 程序主要包括以下部分&#xff1a; 预处理器指令函数变量语句 & 表达式注释 让我们看一段简单的代码&#x…

Fabric V2.5 通用溯源系统——区块链部分设计

本节对Fabric V2.5 通用溯源系统的区块链部分做一个简单的介绍,包括目录结构、文件作用、设计思路。此节内容免费发布在TrueTechLabs Fabric学习交流QQ群。 购买专栏前请认真阅读:《Fabric项目学习笔记》专栏介绍 TrueTechLabs Fabric学习交流QQ群: 一、区块链部分文件目录简…

基于Python校园鲜花水果商城系统(Django框架)开题答辩常规问题和如何回答(答辩指导)

博主介绍&#xff1a;黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者&#xff0c;CSDN博客专家&#xff0c;在线教育专家&#xff0c;CSDN钻石讲师&#xff1b;专注大学生毕业设计教育和辅导。 所有项目都配有从入门到精通的基础知识视频课程&#xff…

React最常用的几个hook

React最常用的几个Hook包括&#xff1a;useState、useEffect、useRef以及useContext。 useState&#xff1a; 用于在函数组件中添加状态管理。它返回一个数组&#xff0c;第一个元素是当前状态的值&#xff0c;第二个元素是更新状态的函数。在使用时&#xff0c;可以通过解构赋…

代码库管理工具Git介绍

阅读本文同时请参阅-----免费的Git图形界面工具sourceTree介绍 Git是一个分布式版本控制系统&#xff0c;它可以帮助开发者跟踪和管理代码历史。Git的命令行工具是使用Git的核心方式&#xff0c;虽然它可能看起来有些复杂&#xff0c;但是一旦掌握了基本命令&#xff0c;你…

【C++私房菜】序列式容器的迭代器失效问题

目录 一、list的迭代器失效 二、vector的迭代器失效 1、空间缩小操作 2、空间扩大操作 三、总结 在C中&#xff0c;当对容器进行插入或删除操作时&#xff0c;可能会导致迭代器失效的问题。所谓迭代器失效指的是&#xff0c;原先指向容器中某个元素的迭代器&#xff0c;在…

【每日前端面经】2023-02-28

题目来源: 牛客 使用TS的目的 提供很好的智能提示方便进行代码重构明确定义参数类型和函数重载 Type和Interface的区别 type主要用于创建联合类型、交叉类型、以及定义复杂的类型别名interface主要用于定义对象和类的结构 Any和泛型 虽然用any类型能够接收任何类型的参数…