鸿蒙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;
}