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

四、驱动篇-HDF驱动介绍2

步骤5 启动Host的每个Device

下面介绍启动一个Host的每个Device的流程,具体包括以下两个子流程

  1. InstallDevices 子流程:在一个while 循环中遍历 hostClnt->unloadDevInfos 链
    表,判断链表上的设备节点是否符合在这一步骤启动的条件,如果符合就执行
    AddDevice子流程,否则就查看下一个设备节点。
  2. AddDevice 子流程:对符合条件的设备节点执行加载驱动和启动设备的流程
1 InstallDevices 子流程

InstallDevices 子流程的入口是DevHostServiceClntInstallDriver()函数,该函数的实现如下所示。

/*** @brief 安装设备主机(Host)下的所有符合条件的设备驱动* * 此函数遍历主机客户端的设备列表,根据预加载策略(preload)动态加载设备驱动。* 是HDF驱动框架实现设备按需加载的核心逻辑。* * @param hostClnt 设备主机服务客户端(包含设备列表和主机服务接口)* @return int 操作状态(HDF_SUCCESS/HDF_FAILURE)*/
int DevHostServiceClntInstallDriver(struct DevHostServiceClnt *hostClnt)
{int ret;struct HdfSListIterator it;       // 单链表迭代器,用于遍历设备列表struct HdfDeviceInfo *deviceInfo = NULL;struct IDevHostService *devHostSvcIf = NULL;// 空指针安全校验if (hostClnt == NULL) {HDF_LOGE("failed to install driver, hostClnt is null");return HDF_FAILURE;}// 加锁保护主机客户端资源OsalMutexLock(&hostClnt->hostLock);  // 防止多线程竞争// 获取设备主机服务接口devHostSvcIf = (struct IDevHostService *)hostClnt->hostService;if (devHostSvcIf == NULL || devHostSvcIf->AddDevice == NULL) {OsalMutexUnlock(&hostClnt->hostLock);HDF_LOGE("devHostSvcIf or devHostSvcIf->AddDevice is null");return HDF_FAILURE;  // 关键接口缺失则终止}// 初始化设备列表迭代器// HdfSListIteratorInit 将迭代器关联到 hostClnt->unloadDevInfos 链表HdfSListIteratorInit(&it, &hostClnt->unloadDevInfos);// 遍历设备列表并加载驱动while (HdfSListIteratorHasNext(&it)) {  // 检查是否存在下一个设备节点deviceInfo = (struct HdfDeviceInfo *)HdfSListIteratorNext(&it);  // 获取设备信息/* 步骤5.1:判断设备是否可以在这里启动,如果不行则跳过,到下一个设备 */if ((deviceInfo == NULL) || (deviceInfo->preload == DEVICE_PRELOAD_DISABLE)) {continue;  // DEVICE_PRELOAD_DISABLE(2)表示动态设备,延迟加载}/* 步骤5.2:快速启动模式下的特殊处理 */// 若系统启用快速启动,则跳过标记为DEVICE_PRELOAD_ENABLE_STEP2的设备if (DeviceManagerIsQuickLoad() == DEV_MGR_QUICK_LOAD &&deviceInfo->preload == DEVICE_PRELOAD_ENABLE_STEP2) {  // 值为1continue;}/* 步骤5.3:调用设备添加接口加载驱动 */// AddDevice 触发驱动入口函数(Bind/Init)的执行//如果符合条件,则执行AddDevice子流程//AddDevice函数指针指向具体的函数://内核态驱动框架:DevHostServiceAddDevice//用户态驱动框架:DevHostServiceProxyAddDeviceret = devHostSvcIf->AddDevice(devHostSvcIf, deviceInfo);if (ret != HDF_SUCCESS) {HDF_LOGE("failed to AddDevice %{public}s, ret = %{public}d", deviceInfo->svcName, ret);  // 记录错误但继续处理其他设备continue;}// 步骤5.4:更新设备状态并清理资源deviceInfo->status = HDF_SERVICE_USABLE;  // 标记设备为可用状态
#ifndef __USER__  // 内核态特有操作:移除已加载设备节点并释放资源HdfSListIteratorRemove(&it);  // 从链表中移除已加载设备HdfDeviceInfoFreeInstance(deviceInfo);  // 释放设备信息内存
#endif}// 步骤6:释放锁并返回OsalMutexUnlock(&hostClnt->hostLock);return HDF_SUCCESS;  // 所有设备处理完成
}

在上面的代码中,通过一个 while循环遍历当前 Host 的 hostclnt->unloadDevInfos 有序单向链表,启动链表上的每一个符合条件的设备叶子节点。

在 while 循环中,首先对 hostclnt->unloadDevInfos 链表上的每一个 HdfDeviceInfo* deviceInfo 节点进行启动条件检査(主要是根据 deviceInfo->preload 字段的配置来确认当前设备叶子节点是否符合启动条件)。如果不符合条件,则执行continue语句继续遍历下一个设备叶子节点(不符合条件的设备叶子节点不启动或延缓到快速启动的后期阶段再启动);如果符合条件,则调用 hostclnt->hostService->AddDevice(devHostSvcIfdeviceInfo)接口(即 **DevHostServiceAddDevice() **),把该设备叶子节点的相关信息挂载到DevHostService.devices 链表上的 HdfDevice device.devNodes 链表上

当 while 循环遍历到 hostClnt->unloadDevInfos 链表末尾时(即迭代器返回 FALSE),则表示当前 Host 中符合条件的设备叶子节点已经启动完毕,将会逐层返回到 while循环中启动下一个 Host 。

2 AddDevices 子流程
AddDevice 子流程的入口是·DevHostServiceAddDevice()·函数,该函数的实现如下所示。
/*** 向指定的 Host 服务中添加一个新设备* @param inst       指向 IDevHostService 接口的指针(Host 服务)* @param deviceInfo 设备信息结构体(包含设备ID、模块名等)* @return           操作状态(HDF_SUCCESS 表示成功)*/
int DevHostServiceAddDevice(struct IDevHostService *inst, const struct HdfDeviceInfo *deviceInfo)
{int ret = HDF_FAILURE; // 默认返回失败状态struct HdfDevice *device = NULL;      // 设备对象struct HdfDeviceNode *devNode = NULL; // 设备节点(连接设备与驱动的桥梁)struct HdfDriver *driver = NULL;       // 驱动对象// 获取 Host 服务实例(通过容器宏从接口指针反向解析宿主结构体)struct DevHostService *hostService = CONTAINER_OF(inst, struct DevHostService, super);// 步骤1:获取driverLoader对象struct IDriverLoader *driverLoader = HdfDriverLoaderGetInstance();// 参数校验:确保输入有效且驱动加载器可用if (inst == NULL || deviceInfo == NULL || driverLoader == NULL || driverLoader->GetDriver == NULL) {HDF_LOGE("failed to add device, input param is null");return ret;}// 步骤2:获取device对象// 在 Host 服务中查询或创建设备对象(根据设备 ID)device = DevHostServiceQueryOrAddDevice(hostService, DEVICEID(deviceInfo->deviceId));if (device == NULL || device->super.Attach == NULL) {return HDF_DEV_ERR_NO_DEVICE; // 设备不存在或 Attach 接口无效}// 步骤3. 获取devNode对象devNode = device->super.GetDeviceNode(&device->super, deviceInfo->deviceId);if (devNode != NULL) {HDF_LOGE("failed to add device %d, device already exist", deviceInfo->deviceId);return HDF_ERR_DEVICE_BUSY; // 设备节点已存在}// 通过驱动加载器获取驱动实例(根据设备信息中的模块名)driver = driverLoader->GetDriver(deviceInfo->moduleName);if (driver == NULL) {ret = HDF_DEV_ERR_NODATA; // 驱动未找到goto ERROR;}// 创建设备节点实例(绑定设备信息与驱动)devNode = HdfDeviceNodeNewInstance(deviceInfo, driver);if (devNode == NULL) {driverLoader->ReclaimDriver(driver); // 回收驱动资源return HDF_DEV_ERR_NO_MEMORY;        // 内存分配失败}// 关联设备节点与 Host 服务及设备对象devNode->hostService = hostService;devNode->device = device;devNode->driver = driver;// 步骤4 挂载devNode对象ret = device->super.Attach(&device->super, devNode);if (ret != HDF_SUCCESS) {HdfDeviceNodeFreeInstance(devNode); // 释放设备节点goto ERROR;}return HDF_SUCCESS; // 添加成功// 错误处理逻辑
ERROR:// 若设备节点链表为空(无有效节点),则释放设备对象if (DListIsEmpty(&device->devNodes)) {DevHostServiceFreeDevice(hostService, device);}return ret; // 返回错误码
}

下面对 DevHostServiceAddDevice()函数中的4个步骤进行简单解释。

2.1 获取 driverLoader 对象

画板

HdfDriverLoaderGetInstance()通过 HdfDriverLoaderCreate 获取一个 HdfDriverLoader 对象并将其引用记录到 IDriverLoader *driverLoader中

这一步会同时获取device对应的driver以及driver的入口entry

2.2 获取device对象

调用 DevHostServiceQueryOrAddDevice() 为每一个 deviceInfo 对应的设备叶子节点(deviceNode)获取(查找已存在的或创建一个新的)对应的设备(device)节点。

/*** 在指定的宿主服务(DevHostService)中查询或动态创建设备实例* @param inst     宿主服务实例指针(管理设备的核心对象)* @param deviceId 待查询/添加的设备ID(逻辑设备标识)* @return         成功返回设备对象指针,失败返回NULL*/
static struct HdfDevice *DevHostServiceQueryOrAddDevice(struct DevHostService *inst,   // 宿主服务实例 uint16_t deviceId             // 设备逻辑ID
) {// 1. 在宿主服务的设备链表中查找是否已存在该设备ID对应的设备struct HdfDevice *device = DevHostServiceFindDevice(inst, deviceId);// 2. 若未找到设备,则创建新设备实例if (device == NULL) {// 2.1 调用设备创建接口实例化HdfDevice对象device = HdfDeviceNewInstance();if (device == NULL) {HDF_LOGE("Dev host service failed to create driver instance");return NULL; // 内存分配失败,返回空指针}// 2.2 构造复合设备ID(含HostID、DeviceID、设备序号)// MK_DEVID宏将HostID(宿主服务ID)、DeviceID(逻辑ID)、设备序号(0表示主设备)组合为全局唯一ID device->deviceId = MK_DEVID(inst->hostId, deviceId, 0);// 2.3 将新设备插入宿主服务的设备链表头部(DList为鸿蒙双向链表结构)DListInsertHead(&device->node, &inst->devices); // 维护设备链表 }// 3. 返回设备对象(可能是查找到的或新建的)return device;
}

在 DevHostServiceQueryOrAddDevice() 中,首先调用 DevHostServiceFindDevice() 在DevHostService.devices 双向链表中查找匹配deviceId的设备(device)节点,如果该设备(device)节点已经存在则直接返回对该节点的引用,反之则返回NULL。如果 DevHostServiceFindDevice() 返回的查找结果为 NULL,则需要调用HdfDeviceNewInstance() 创建一个新的 HdfDevice device 节点并配置好 hostId 和deviceId ,然后再调用 DListInsertHead(&device->node,&inst->devices) 将新设备节点的device->node 字段插入到DevHostService.devices 链表中,表示这个设备(device)节点开始受到所在 Host 的 DevHostservice 的管理。

2.3 创建devNode对象

画板

调用 driverLoader->GetDriver() 接口,查找并加载匹配 deviceInfo->moduleName 的设备驱动入口对象。

在执行 driverLoader->GetDriver() 之后,再把当前的 hostservice 对象绑定到 devNode->hostService 上以备后继步骤使用。

/*** 在指定设备对象中查找特定设备ID对应的设备节点* @param device 设备接口指针(抽象接口)* @param devid  要查找的设备ID(复合ID,包含HostID+DeviceID)* @return       找到的设备节点指针,未找到返回NULL*/
static struct HdfDeviceNode *HdfDeviceGetDeviceNode(struct IHdfDevice *device, // 设备对象的接口(面向接口编程)devid_t devid              // 设备全局唯一标识符(16位复合ID)
) {struct HdfDeviceNode *devNode = NULL;// 1. 通过容器宏从接口指针反向获取宿主设备对象// CONTAINER_OF 宏解析:从接口指针device获取其所属的HdfDevice实例struct HdfDevice *dev = CONTAINER_OF(device, struct HdfDevice, super);// 2. 遍历设备节点链表(dev->devNodes)// DLIST_FOR_EACH_ENTRY 是鸿蒙标准双向链表遍历宏DLIST_FOR_EACH_ENTRY(devNode,       // 当前遍历节点指针&dev->devNodes, // 链表起始地址struct HdfDeviceNode, // 节点类型entry          // 节点在结构体中的链表成员名) {// 3. 检查当前节点ID是否匹配目标IDif (devNode->devId == devid) {return devNode; // 找到目标节点,立即返回}; // 注意:此处分号是宏扩展语法的一部分}// 4. 遍历结束未找到匹配节点return NULL;
}/*** 根据驱动名称获取已注册的驱动实例(支持动态加载)* @param driverName 驱动名称(需与 HCS 配置中的 moduleName 匹配)* @return 成功返回驱动指针,失败返回 NULL*/
struct HdfDriver *HdfDriverManagerGetDriver(const char *driverName) {struct HdfDriver *driver = NULL;// 1. 参数校验:驱动名不能为空if (driverName == NULL) {return NULL;}// 2. 在已注册驱动链表中查找目标驱动driver = HdfDriverManagerFoundDriver(driverName);if (driver != NULL) {return driver; // 找到则直接返回}// 3. 动态加载机制:若驱动未加载且系统支持事件通知if (HdfSysEventSend != NULL) {HDF_LOGI("%{public}s: try to dynamic load driver %{public}s", __func__, driverName);// 发送模块安装事件(触发动态加载)if (HdfSysEventSend(HDF_SYSEVENT_CLASS_MODULE, // 事件类别:模块管理KEVENT_MODULE_INSTALL,     // 事件类型:安装模块driverName,                // 驱动名称true                       // 同步等待加载完成) != HDF_SUCCESS) {return NULL; // 事件发送失败}// 4. 再次尝试查找(动态加载后)driver = HdfDriverManagerFoundDriver(driverName);}// 5. 最终状态检查if (driver == NULL) {HDF_LOGE("%{public}s: driver %{public}s not found", __func__, driverName);}return driver; // 返回驱动指针(可能为 NULL)
}
2.4 挂载 devNode 对象

结合步骤2和步骤3的执行结果,调用device->super.Attach(&device->super,devNode)接口(即 HafDeviceAttach() ),将devNode指针指向的设备叶子对象挂载到 device->devNodes 链表上,这里还要完成 driver 的 Bind 子流程。

在执行上述步骤1~步骤4的子流程中,如果没有出现异常,则表示该设备启动成功,并返回while循环启动下一个设备;如果出现了驱动程序加载、挂载失败等异常情况,相关的错误信息会逐层返回,最终上报到DevHostServicAddDevice() 函数,然后由该数的erro标签下的DevHostServiceFreeDevice() 函数释放为启动该设备所申请的相关资源( DevHostServiceFreeDevice() 函数最终会调用驱动入口对象中的Release 接口)

/*** 将设备节点附加到设备实例(关键绑定操作)* @param devInst 设备接口指针* @param devNode 待附加的设备节点* @return 成功返回HDF_SUCCESS,失败返回错误码*/
static int HdfDeviceAttach(struct IHdfDevice *devInst,   // 设备抽象接口 struct HdfDeviceNode *devNode // 包含驱动信息的设备节点
) {int ret;// 1. 类型转换:从接口获取具体设备实例struct HdfDevice *device = (struct HdfDevice *)devInst;// 获取设备节点接口(内含关键操作函数指针)struct IDeviceNode *nodeIf = (struct IDeviceNode *)devNode;// 2. 参数有效性检查if (device == NULL || nodeIf == NULL || nodeIf->LaunchNode == NULL) {HDF_LOGE("failed to attach device, input params invalid");return HDF_ERR_INVALID_PARAM;}// 3. 动态节点设备ID分配(适用于热插拔设备)if (devNode->devId == 0 && AcquireNodeDeviceId(device, &devNode->devId) != HDF_SUCCESS) {HDF_LOGE("failed to attach device, invalid device id");return HDF_ERR_INVALID_PARAM;}// 4. 同步令牌中的设备ID(用于跨进程通信标识)devNode->token->devid = devNode->devId;// 5. ★ 核心操作:启动设备节点(触发驱动初始化)ret = nodeIf->LaunchNode(devNode);// 6. 成功后管理节点if (ret == HDF_SUCCESS) {// 6.1 将节点加入设备链表(尾部插入保持添加顺序)DListInsertTail(&devNode->entry, &device->devNodes);// 6.2 更新设备ID索引(加速后续查找)UpdateDeviceNodeIdIndex(device, devNode->devId);}return ret; // 返回驱动初始化结果
}
2.5 Device的Bind子流程

在上节介绍挂载 devNode 对象时,说到这里要完成 device 的 Bind 流程。在 Bind 子流程中,会执行驱动入口对象中定义的 Bind() 接口。Bind子流程的入口是HdfDriverLoader。

首先 HdfDeviceAttach() 里会调用 nodeIf->LaunchNode(devNode) 。这里实际调用的是HdfDeviceLaunchNode 函数

void HdfDeviceNodeConstruct(struct HdfDeviceNode *devNode)
{if (devNode != NULL) {struct IDeviceNode *nodeIf = &devNode->super;HdfDeviceObjectConstruct(&devNode->deviceObject);devNode->token = HdfDeviceTokenNewInstance();nodeIf->LaunchNode = HdfDeviceLaunchNode;nodeIf->PublishService = HdfDeviceNodePublishPublicService;nodeIf->RemoveService = HdfDeviceNodeRemoveService;nodeIf->UnlaunchNode = HdfDeviceUnlaunchNode;}
}

HdfDeviceLaunchNode 函数的实现如下。

/*** 启动设备节点(驱动初始化和服务发布的核心入口)* @param devNode 设备节点对象指针* @return 成功返回HDF_SUCCESS,失败返回错误码*/
int HdfDeviceLaunchNode(struct HdfDeviceNode *devNode)
{const struct HdfDriverEntry *driverEntry = NULL; // 驱动入口结构指针int ret; // 返回值变量// 1. 参数有效性检查:确保设备节点不为空if (devNode == NULL) {HDF_LOGE("failed to launch service, device or service is null");return HDF_ERR_INVALID_PARAM;}// 打印调试日志,标识当前启动的设备节点名称(servName可能为空)HDF_LOGI("launch devnode %{public}s", devNode->servName ? devNode->servName : "");// 2. 获取设备节点关联的驱动入口结构driverEntry = devNode->driver->entry;// 检查驱动入口的有效性:必须存在且包含Init函数if (driverEntry == NULL || driverEntry->Init == NULL) {HDF_LOGE("failed to launch service, deviceEntry invalid");return HDF_ERR_INVALID_PARAM;}// 3. ★ 更新设备节点状态为"已启动"devNode->devStatus = DEVNODE_LAUNCHED;// 4. ★ 驱动绑定阶段:调用Bind函数建立驱动与设备的关联ret = DeviceDriverBind(devNode);if (ret != HDF_SUCCESS) {return ret; // 绑定失败则直接返回错误码}// 5. ★ 驱动初始化:调用驱动入口的Init函数ret = driverEntry->Init(&devNode->deviceObject);if (ret != HDF_SUCCESS) {return HDF_DEV_ERR_DEV_INIT_FAIL; // 初始化失败返回特定错误码}// 6. ★ 服务发布:向HDF框架注册该设备节点提供的服务ret = HdfDeviceNodePublishService(devNode);if (ret != HDF_SUCCESS) {return HDF_DEV_ERR_PUBLISH_FAIL; // 发布失败返回特定错误码}// 7. ★ 设备附加:将该设备节点附加到设备管理器服务(DevmgrService)ret = DevmgrServiceClntAttachDevice(devNode->token);if (ret != HDF_SUCCESS) {return HDF_DEV_ERR_ATTACHDEV_FAIL; // 附加失败返回特定错误码}return ret; // 返回最终执行结果
}
2.6 DeviceDriverBind 函数如下:
/*** 执行驱动与设备节点的绑定操作* @param devNode 设备节点对象指针* @return 成功返回HDF_SUCCESS,失败返回错误码*/
int DeviceDriverBind(struct HdfDeviceNode *devNode)
{int ret; // 返回值变量const struct HdfDriverEntry *driverEntry = NULL; // 驱动入口结构指针// 1. 参数有效性检查if (devNode == NULL) {return HDF_ERR_INVALID_PARAM; // 设备节点不能为空}// 2. 获取驱动入口结构(来自设备节点关联的驱动对象)driverEntry = devNode->driver->entry;// 3. ★ 检查设备节点的服务策略,确定是否需要绑定// 只有需要对外提供服务的设备节点才执行Bind操作if (devNode->policy == SERVICE_POLICY_PUBLIC ||    // 公开服务(内核态可见)devNode->policy == SERVICE_POLICY_CAPACITY)   // 能力服务(内核+用户态可见){// 4. 检查Bind函数是否实现(驱动开发者必须提供)if (driverEntry->Bind == NULL) {HDF_LOGE("driver %{public}s bind method not implement", driverEntry->moduleName);// 重置设备节点状态(绑定失败)devNode->devStatus = DEVNODE_NONE;return HDF_ERR_INVALID_OBJECT; // 接口未实现错误}// 5. ★ 核心操作:执行驱动Bind函数// 将设备对象(HdfDeviceObject)传递给驱动进行初始化ret = driverEntry->Bind(&devNode->deviceObject);// 6. 处理绑定结果if (ret != HDF_SUCCESS) {HDF_LOGE("bind driver %{public}s failed", driverEntry->moduleName);return HDF_DEV_ERR_DEV_INIT_FAIL; // 绑定失败错误码}}// 7. 返回成功(对于不需要服务的节点直接跳过绑定)return HDF_SUCCESS;
}

这一步开始对 deviceInfo->policy 字段进行判断。如果 policy 的配置既不是1也不是2,表示该设备驱动不会对外提供服务。这样一来,就不需要执行 driverEntry->Bind() 函数了,而是直接返回。如果 policy 的配置是1或2,则表示该设备驱动会在内核空间提供服务或者在内核空间和用户空间同时提供服务,这样的话需要先检测 driverEntry->Bind() 函数的有效性,若有效则执行。实际执行的就是具体设备驱动的bind函数。

2.7 Device的Init子流程

这一步直接调用 driverEntry->Init(&devNode->deviceobject) 对设备进行初始化,其中包括为设备申请内存、配置初始参数等。这个Init()就是上节中步骤1获取的设备驱动入口对象(即 g_XxxHdfEntry 对象)中的 Init() 接口。

传入 Init() 的参数 &devNode->deviceObject 携带的信息是非常重要的。在 Init() 中可以调用 hcs-parser 模块的接口从 deviceObject 中读取设备叶子节点的私有配置数据,以便针对性地对设备进行初始化配置。

2.8 发布devNode服务

这一步执行 HdfDeviceNodePublishService() ,以对外发布设备节点提供的服务,PublishService 子流程的入口是 HdfDeviceNodePublishService() 函数,其定义如下所示。

/*** 发布设备节点服务(根据策略决定发布方式)* @param devNode 设备节点对象指针* @return 成功返回HDF_SUCCESS,失败返回错误码*/
static int HdfDeviceNodePublishService(struct HdfDeviceNode *devNode)
{int status = HDF_SUCCESS; // 默认返回成功struct IDeviceNode *nodeIf = NULL; // 设备节点接口指针// 1. ★ 服务发布条件检查://    a) 策略为SERVICE_POLICY_NONE(不发布服务)//    b) 服务名为空字符串(无效服务名)if (devNode->policy == SERVICE_POLICY_NONE ||(devNode->servName != NULL && strlen(devNode->servName) == 0)) {return status; // 直接返回成功(不需要发布)}// 2. 获取设备节点的接口实现nodeIf = &devNode->super;// 3. ★ 处理标准服务发布策略:if (devNode->policy == SERVICE_POLICY_PUBLIC ||    // 内核态公开服务devNode->policy == SERVICE_POLICY_CAPACITY)    // 跨内核-用户态服务{// 检查并调用自定义发布接口(优先使用驱动实现的发布方法)if (nodeIf->PublishService != NULL) {status = nodeIf->PublishService(devNode);}}// 4. ★ 发布本地服务(无论是否使用自定义发布,都需要注册基础服务)if (status == HDF_SUCCESS) {status = HdfDeviceNodePublishLocalService(devNode);}return status; // 返回最终发布状态
}

从上面的代码可以看到 HdfDeviceNodePublishService 函数会根据 policy 配置的取值进行不同的操作。下面分别来看一下。

  • policy 配置是1或2

调用 nodeIf->PublishService() (即 DeviceNodeExtPublishService() )进入ExtPublishservice子流程。如果 ExtPublishService 子流程执行失败(返回非 HDF_SUCCESS 值给 status),则 PublishService 子流程也会执行失败,因此不再执行 PublishLocalService 子流程

如果 ExtPublishService 子流程执行成功(返回 HDF_SUCCESS 值给 status),则PublishService 子流程会继续执行PublishLocalService· 子流程。

static void DeviceNodeExtConstruct(struct DeviceNodeExt *inst)
{struct IDeviceNode *nodeIf = (struct IDeviceNode *)inst;if (nodeIf != NULL) {HdfDeviceNodeConstruct(&inst->super);nodeIf->PublishService = DeviceNodeExtPublishService;nodeIf->RemoveService = DeviceNodeExtRemoveService;}
}/*** 设备节点扩展服务的发布实现(支持跨用户态-内核态服务)* @param devNode 设备节点指针(实际指向DeviceNodeExt扩展结构)* @return 成功返回HDF_SUCCESS,失败返回错误码*/
./hdf_core/framework/core/common/src/hdf_device_node_ext.c
static int DeviceNodeExtPublishService(struct HdfDeviceNode *devNode)
{// 1. 初始化关键对象struct HdfDeviceObject *deviceObject = NULL;// 类型转换:将基础设备节点转换为扩展结构(支持额外功能)struct DeviceNodeExt *devNodeExt = (struct DeviceNodeExt *)devNode;int ret;// 静态分发器:定义服务调度的核心函数(跨进程通信入口)static struct HdfIoDispatcher dispatcher = { .Dispatch = DeviceNodeExtDispatch  // 服务请求分发函数};// 2. 参数有效性检查if (devNodeExt == NULL) {return HDF_FAILURE; // 扩展结构转换失败}// 3. 获取设备对象(含服务接口)deviceObject = &devNodeExt->super.deviceObject;if (deviceObject->service == NULL) {HDF_LOGE("device service interface is null"); // 服务接口未实现return HDF_FAILURE; // 不符合服务发布条件}// 4. ★ 跨态服务发布(SERVICE_POLICY_CAPACITY策略)if (devNode->policy == SERVICE_POLICY_CAPACITY) { // 需同时支持内核态+用户态访问// 步骤1 发布用户态服务(创建/dev/hdf服务节点)devNodeExt->ioService = HdfIoServicePublish(devNode->servName,    // 服务名称(如"display_service")devNode->permission   // 设备节点权限(如0644));if (devNodeExt->ioService != NULL) {// 4.2 关联设备对象(用于后续服务路由)devNodeExt->ioService->target = (struct HdfObject *)(deviceObject);// 4.3 绑定请求分发器(用户态请求由此进入驱动)devNodeExt->ioService->dispatcher = &dispatcher;} else {HDF_LOGE("device remote service publish failed");return HDF_DEV_ERR_NO_DEVICE_SERVICE; // 服务发布失败(可能权限不足)}}// 步骤2 ★ 发布公共服务(内核态服务注册)ret = HdfDeviceNodePublishPublicService(devNode); // 注册到内核服务表if (ret != HDF_SUCCESS) {HDF_LOGE("failed to publish device service, ret is %{public}d", ret);// 6. 失败回滚:清理已创建的跨态服务HdfIoServiceRemove(devNodeExt->ioService); // 删除设备节点devNodeExt->ioService = NULL; // 重置指针return ret; // 返回错误码}return HDF_SUCCESS; // 双轨发布成功
}

步骤1 发布用户态服务

画板

只有在满足 policy 为2的条件时,才继续执行这一步,发布用户态服务。

在发布用户态服务之后, DeviceNodeExt 的 HdfIoService* ioService 字段就获得了有效的配

置,在ExtPublishService 子流程的执行过程中,如果出现异常情况,则表示当前设备节点发

布服务失败,失败的状态信息会逐层上报到 DevHostServiceAddDevice() 函数,由该函数对

错误信息进行处理。

如果没有出现异常情况,则表示当前设备节点发布服务成功,接下来还要执行一个PublishLocalService 子流程

struct HdfIoService *HdfIoServicePublish(const char *serviceName, uint32_t mode)
{if (HdfIoServiceAdapterPublish != NULL) {return HdfIoServiceAdapterPublish(serviceName, mode);}return NULL;
}// 函数功能:发布 IO 服务适配器,注册字符设备并返回 IO 服务接口
// 参数:
//   - serviceName:服务名称(用于生成设备节点路径,如 "sensor" → "/dev/sensor")
//   - mode:设备节点模式(如权限设置,需 ≤ MAX_MODE_SIZE)
// 返回:
//   - 成功:指向 HdfIoService 的指针(对外提供 IO 服务)
//   - 失败:NULL
struct HdfIoService *HdfIoServiceAdapterPublish(const char *serviceName, uint32_t mode)
{int nodePathLength;                                  // 设备节点路径的长度(含结束符)struct HdfVNodeAdapter *vnodeAdapter = NULL;         // V 节点适配器(管理设备节点的核心结构体)// 定义静态字符设备操作函数集(不可修改,用于关联设备节点的文件操作)static const struct OsalCdevOps fileOps = {.open = HdfVNodeAdapterOpen,                     // 打开设备节点的回调函数.release = HdfVNodeAdapterClose,                 // 关闭设备节点的回调函数.ioctl = HdfVNodeAdapterIoctl,                   // IO 控制(如发送命令/读取数据)的回调函数.poll = HdfVNodeAdapterPoll,                     // Poll 操作(异步通知)的回调函数};// 1. 参数合法性检查if ((serviceName == NULL) || (mode > MAX_MODE_SIZE)) {HDF_LOGE("input param is invalid, mode is %x", mode);  // 日志记录:参数无效(服务名为空或模式超出范围)return NULL;                                            // 直接返回,避免后续无效操作}// 2. 分配 HdfVNodeAdapter 内存(初始化全0)vnodeAdapter = (struct HdfVNodeAdapter *)OsalMemCalloc(sizeof(struct HdfVNodeAdapter));if (vnodeAdapter == NULL) {HDF_LOGE("alloc remote service is null");             // 日志记录:内存分配失败return NULL;                                            // 返回 NULL,表示失败}// 3. 计算设备节点路径长度(DEV_NODE_PATH + serviceName + '\0')//    DEV_NODE_PATH 通常为 "/dev/"(定义在 HDF 框架中,用于生成设备节点的根路径)nodePathLength = strlen(serviceName) + strlen(DEV_NODE_PATH) + 1;// 分配设备节点路径的内存(如 "/dev/serviceName")vnodeAdapter->vNodePath = (char *)OsalMemCalloc(nodePathLength);if (vnodeAdapter->vNodePath == NULL) {HDF_LOGE("alloc vnode path is null");                // 日志记录:路径内存分配失败OsalMemFree(vnodeAdapter);                           // 释放之前分配的 vnodeAdapter 内存(避免泄漏)return NULL;                                            // 返回 NULL}// 4. 拼接设备节点路径(如 DEV_NODE_PATH="/dev/" + serviceName="sensor" → "/dev/sensor")//    sprintf_s:安全的字符串格式化函数(避免缓冲区溢出)if (sprintf_s(vnodeAdapter->vNodePath, nodePathLength, "%s%s", DEV_NODE_PATH, serviceName) < 0) {HDF_LOGE("failed to get node path");                 // 日志记录:路径拼接失败(如缓冲区不足)OsalMemFree(vnodeAdapter->vNodePath);                // 释放路径内存OsalMemFree(vnodeAdapter);                           // 释放 vnodeAdapter 内存return NULL;                                            // 返回 NULL}// 5. 注册字符设备并关联 V 节点适配器//    HdfIoServiceAdapterRegCdev:内部函数,负责://    a. 注册字符设备(cdev)到系统;//    b. 将 vnodeAdapter 与字符设备关联;//    c. 创建设备节点(如 "/dev/serviceName");//    d. 返回 HdfIoService 接口(对外提供 IO 服务)return HdfIoServiceAdapterRegCdev(vnodeAdapter, &fileOps, mode);
}

步骤2 发布公共 服务

发布公共服务的入口是 HdfDeviceNodePublishPublicService ()函数。在该入口函数调用的DevSvcManagerClntAddService() 中,先获取一个 DevSvcManagerClnt 对象的指针 devSvcMgrClnt (在驱动框架中第一个发布公共服务的设备节点会在这一步创建一个 DevSvcManager 对象,见讲义2.3节),然后再调用该对象的 devSvcMgrClnt->devSvcMgrIf->AddService() (即 DevSvcManagerAddService() ),将当前设备节点的设备对象字段(即devNode->deviceObject )以 DevSvcRecord 节点的形式挂载到DevSveManager.services 链表上。

int HdfDeviceNodePublishPublicService(struct HdfDeviceNode *devNode)
{int ret;if (devNode == NULL || devNode->deviceObject.service == NULL) {HDF_LOGE("failed to publish public service, devNode is NULL");return HDF_FAILURE;}struct HdfServiceInfo servInfo;HdfServiceInfoInit(&servInfo, devNode);ret = DevSvcManagerClntAddService(&devNode->deviceObject, &servInfo);if (ret == HDF_SUCCESS) {devNode->servStatus = true;}return ret;
}// 函数:DevSvcManagerClntAddService
// 功能:向设备服务管理器客户端添加服务
// 参数:
//   service: 指向要添加的服务的设备对象(HdfDeviceObject)的指针
//   servinfo: 指向服务信息结构体(HdfServiceInfo)的指针,包含服务的元数据
// 返回值:成功返回HDF_SUCCESS,失败返回HDF_FAILURE或其他错误码
int DevSvcManagerClntAddService(struct HdfDeviceObject *service, const struct HdfServiceInfo *servinfo)
{// 获取设备服务管理器客户端的单例实例struct DevSvcManagerClnt *devSvcMgrClnt = DevSvcManagerClntGetInstance();struct IDevSvcManager *serviceManager = NULL;// 检查客户端实例是否获取成功if (devSvcMgrClnt == NULL) {HDF_LOGE("failed to add service, client is null");return HDF_FAILURE;}// 检查服务信息中的设备类(devClass)是否有效(在允许的范围内)// DEVICE_CLASS_MAX 是一个枚举的最大值,表示设备类的最大值(即设备类枚举值的数量)if (servinfo->devClass >= DEVICE_CLASS_MAX) {HDF_LOGE("failed to add service, invalid class");return HDF_FAILURE;}// 从客户端实例中获取服务管理器接口(IDevSvcManager)serviceManager = devSvcMgrClnt->devSvcMgrIf;// 检查服务管理器接口是否有效,以及其AddService函数指针是否有效if (serviceManager == NULL || serviceManager->AddService == NULL) {HDF_LOGE("serviceManager AddService function is null");return HDF_FAILURE;}// 调用服务管理器接口的AddService方法,将服务对象和服务信息传递给它// 由具体的服务管理器实现来执行添加服务的操作return serviceManager->AddService(serviceManager, service, servinfo);
}

步骤3:HdfDeviceNodePublishService子流程

PublishLocalService 子流程的入口为 HdfDeviceNodePublishLocalService() 函数,该函数的实现如下所示。

// 函数功能:将设备节点的服务发布为本地服务(仅宿主服务内部可见)
// 参数:devNode - 指向要发布服务的设备节点结构体(必须非空)
// 返回值:成功返回HDF_SUCCESS,失败返回HDF_FAILURE或其他错误码
static int HdfDeviceNodePublishLocalService(struct HdfDeviceNode *devNode)
{// 1. 校验设备节点指针合法性(防御性编程,避免空指针访问)if (devNode == NULL) {HDF_LOGE("failed to publish local service, device is null");  // 日志记录:设备节点为空return HDF_FAILURE;                                          // 返回失败}// 2. 校验设备节点所属的宿主服务(Host Service)是否存在//    hostService:设备节点的宿主服务实例(如驱动宿主,每个设备节点属于一个宿主)if (devNode->hostService == NULL) {HDF_LOGE("failed to publish local service, host service is null");  // 日志记录:宿主服务为空return HDF_FAILURE;                                                  // 返回失败}// 3. 核心逻辑:通过宿主服务的**服务观察者(Service Observer)**发布本地服务//    调用HdfServiceObserverPublishService函数,将设备服务注册到宿主服务的观察者中return HdfServiceObserverPublishService(&(devNode->hostService->observer),  // 服务观察者对象(宿主服务用于管理本地服务)devNode->servName,                  // 服务名称(唯一标识本地服务,如"sensor_temp")devNode->devId,                     // 设备ID(唯一标识设备节点,由HDF框架分配)devNode->policy,                    // 服务策略(如SERVICE_POLICY_PRIVATE,限制访问权限)(struct HdfObject *)devNode->deviceObject.service  // 设备服务对象(实际提供服务的实现,转成HdfObject基类指针));
}

从上面的代码中可以看到, HdfDeviceNodePublishuocalservice() 中首先对传入的 devNode的信息进行简单处理,然后再调用 HdfServiceObserverPublishService() 函数实现向服务的观察者发布服务的功能。

// 函数功能:向服务观察者发布服务(注册或更新服务,并通知订阅者)
// 参数:
//   - observer:服务观察者对象(管理服务记录的容器)
//   - svcName:服务名称(唯一标识服务,用于生成哈希键)
//   - deviceId:设备ID(关联服务所属的设备节点)
//   - policy:服务策略(如SERVICE_POLICY_PRIVATE,限制访问权限)
//   - service:服务对象(实际提供服务的实现,如HdfSensorService)
// 返回值:成功返回HDF_SUCCESS,失败返回HDF_FAILURE
int HdfServiceObserverPublishService(struct HdfServiceObserver *observer,   // 服务观察者(核心管理对象)const char *svcName,                   // 服务名称(唯一标识)devid_t deviceId,                      // 设备ID(关联设备节点)uint16_t policy,                       // 服务策略(访问权限)struct HdfObject *service              // 服务对象(实现具体功能)
) {struct HdfServiceObserverRecord *serviceRecord = NULL;  // 服务记录(存储服务元数据)// 1. 生成服务名称的哈希键(用于快速查找服务记录)//    HdfStringMakeHashKey:将字符串转换为32位哈希值(避免字符串比较的开销)uint32_t serviceKey = HdfStringMakeHashKey(svcName, 0);// 2. 参数合法性检查(防御性编程,避免空指针访问)if ((observer == NULL) || (svcName == NULL)) {HDF_LOGE("observer or svcName is null");  // 日志记录:观察者或服务名称为空return HDF_FAILURE;                      // 返回失败}// 3. 在观察者的服务列表中查找是否存在该服务(通过哈希键)//    HdfSListSearch:遍历单向链表(observer->services),用比较函数查找目标记录//    HdfServiceObserverRecordCompare:比较函数(判断服务记录的哈希键是否等于serviceKey)serviceRecord = (struct HdfServiceObserverRecord *)HdfSListSearch(&observer->services,  // 观察者管理的服务链表serviceKey,           // 目标服务的哈希键HdfServiceObserverRecordCompare  // 哈希键比较函数);// 4. 若服务记录不存在(首次发布服务)if (serviceRecord == NULL) {// 4.1 分配新的服务记录(从内存池或动态分配)serviceRecord = HdfServiceObserverRecordObtain(serviceKey);if (serviceRecord == NULL) {  // 分配失败检查HDF_LOGE("PublishService failed, serviceRecord is null");return HDF_FAILURE;}// 4.2 初始化服务记录的元数据serviceRecord->publisher = service;  // 关联服务对象(实际提供服务的实现)serviceRecord->devId = deviceId;     // 关联设备ID(服务所属的设备节点)serviceRecord->policy = policy;      // 设置服务策略(访问权限)// 4.3 线程安全地将服务记录添加到观察者的服务链表OsalMutexLock(&observer->observerMutex);  // 加锁(防止并发修改链表)HdfSListAdd(&observer->services, &serviceRecord->entry);  // 添加到链表尾部OsalMutexUnlock(&observer->observerMutex);  // 解锁} else {// 5. 若服务记录已存在(服务更新,如重新初始化)serviceRecord->publisher = service;  // 更新服务对象(替换为新的实现)// 5.1 通知该服务的所有订阅者(服务更新)//    HdfServiceObserverRecordNotifySubscribers:遍历订阅者列表,调用其回调函数HdfServiceObserverRecordNotifySubscribers(serviceRecord, deviceId, policy);}// 6. 返回成功return HDF_SUCCESS;
}

传入 HdfServiceObserverPublishService() 的第一个参数 hostService->observer 是一个HdfserviceObserver 结构体(见讲义2.10节)。该结构体的 HdfSList services 字段是一个链表结构,链表上的每个节点都是一个 HdfServiceObserverRecord 结构体上的entry字段。所以,在 hostService->observer.services 链表上的每一个 HdfServiceObserverRecord 结构体,都记录了一组订阅者的信息,这些订阅者都订阅了当前 devNode 提供的服务。在 HdfServiceObserverPublishService() 中,首先是调用 HdfSListSearch() 函数在

hostservice->observer.services 链表中查找匹配 servicekey 的记录。 serviceKey 是对svcName (全系统唯一)执行哈希运算得到的一个哈希键(hashKey)。如果执行 HdfSListSearch() 函数返回的结果为空,表示在 hostService->observer.services链表上不存在与当前devNode提供的服务对应的 HdfServiceObserverRecoro 结构体。因此,需要调用 HdfServiceObserverRecordObtain(serviceKey) 创建一个HdfserviceObserverRecord 结构体对象 serviceRecord ,并把 devNode 的相关信息填写到serviceRecord中。

在填写完 serviceRecord 的信息后,再调用 HdfSListAdd(&observer->services&serviceRecord->entry) 把该 serviceRecord 对象插入到 hostService->observer.services 链表中。这样一来, hostService 就可以把对这个 devNode 提供的驱动服务感兴趣的订阅者相关信息记录在 serviceRecord -> subscribers 链表中。

如果执行 HdfSistSearch() 函数后返回的结果不为空,则表示在 hostService->observer.services 链表中已经存在与当前 devNode 提供的服务对应的HdfServiceObserverRecord 对象,此时可将返回的对象指针记录到serviceRecord 中。这时需要针对该 HdfServiceObserverRecord 对象执行HdfServiceObserverRecordNotifySubscriber(serviceRecord,deviceId,policy) ,即遍历serviceRecord->subscribers 链表对其中满足条件的订阅者逐一调用回调函数OnServiceConnected() 通知订阅者它们订阅的服务上线了。至于订阅者在回调函数中具体做什么事情,则是订阅者自己的行为。

2.9 挂载 devNode 的信息

画板

这一步执行 DevmgrServiceClntAttachDevice() ,以将该设备节点的重要信息挂载到当前Host 的 hostClnt->devices 链表上。

下面继续解析“挂载 devNode 的信息”步骤的 AttachDevice 子流程。

AttachDevice 子流程的入口是 DevmgrServiceClntAttachDevice() 函数,其定义代码如下所示。

// 函数功能:将设备令牌附着(注册)到设备管理器
// 参数:deviceToken - 设备令牌(设备的唯一标识,用于设备管理器识别和管理设备)
// 返回值:成功返回HDF_SUCCESS,失败返回HDF_FAILURE或其他错误码
int DevmgrServiceClntAttachDevice(struct IHdfDeviceToken *deviceToken)
{struct IDevmgrService *devMgrSvcIf = NULL;  // 设备管理服务的接口对象(定义了设备管理的核心方法)// 获取设备管理服务客户端的单例实例(DevmgrServiceClnt是设备管理服务的客户端,负责与设备管理器通信)struct DevmgrServiceClnt *inst = DevmgrServiceClntGetInstance();// 1. 校验客户端实例的有效性(单例是否创建成功,且服务接口是否存在)if ((inst == NULL) || (inst->devMgrSvcIf == NULL)) {HDF_LOGE("devmgr client failed to attach device, inst is null");  // 日志记录:客户端实例或服务接口为空return HDF_FAILURE;                                              // 返回失败}// 2. 获取设备管理服务的接口对象(从客户端实例中提取,用于调用设备管理方法)devMgrSvcIf = inst->devMgrSvcIf;// 3. 校验设备管理服务接口中的`AttachDevice`方法是否存在(避免空指针调用)if (devMgrSvcIf->AttachDevice == NULL) {HDF_LOGE("devmgr client failed to attach device, dmsOps->AttachDevice is null");  // 日志记录:`AttachDevice`方法未实现return HDF_FAILURE;                                                              // 返回失败}// 4. 调用设备管理服务的`AttachDevice`方法,将设备令牌附着到设备管理器//    参数说明://      - devMgrSvcIf:设备管理服务接口(`this`指针,指向服务实现)//      - deviceToken:设备令牌(设备的唯一标识,设备管理器通过它管理设备)return devMgrSvcIf->AttachDevice(devMgrSvcIf, deviceToken);
}

在 DevmgrServiceClntAttachDevice() 函数中,首先调用 DevmgrServiceclntGetInstance()

获取一个 DevmgrServiceClnt 对象(将对象指针赋值到 inst),借助该对象可以使用DevmgrService 提供的 AttachDevice() 接口。然后调用 inst->devMgrSvcIf->AttachDevice()接口(即 DevmgrServiceAttachDevice() )完成挂载设备节点的工作

DevmgrServiceAttachDevice()函数的实现如下代码所示

// 函数功能:将设备令牌附着到对应的宿主服务客户端(完成设备与宿主的关联)
// 参数:
//   - inst:设备管理服务实例(`IDevmgrService`,管理所有宿主服务)
//   - token:设备令牌(`IHdfDeviceToken`,设备的唯一标识,包含设备ID)
// 返回值:成功返回HDF_SUCCESS,失败返回HDF_FAILURE
static int DevmgrServiceAttachDevice(struct IDevmgrService *inst, struct IHdfDeviceToken *token)
{struct DevHostServiceClnt *hostClnt = NULL;  // 宿主服务客户端(管理某一宿主下的所有设备)struct DeviceTokenClnt *tokenClnt = NULL;    // 设备令牌客户端(包装设备令牌,用于宿主服务管理)// 1. 校验设备令牌有效性(避免空指针访问)if (token == NULL) {return HDF_FAILURE;  // 设备令牌为空,返回失败}// 2. 根据设备令牌中的**宿主ID**查找对应的宿主服务客户端//    - `HOSTID(token->devid)`:从设备ID中提取宿主ID(设备ID格式为`[宿主ID][设备在宿主内的ID]`,宏定义用于拆分)//    - `DevmgrServiceFindDeviceHost`:设备管理服务的内部函数,通过宿主ID查找已注册的宿主服务客户端hostClnt = DevmgrServiceFindDeviceHost(inst, HOSTID(token->devid));if (hostClnt == NULL) {  // 未找到对应的宿主服务客户端HDF_LOGE("failed to attach device, hostClnt is null");  // 日志记录:宿主服务客户端不存在return HDF_FAILURE;                                      // 返回失败}// 3. 创建设备令牌客户端实例(包装设备令牌,添加宿主服务所需的管理信息)//    - `DeviceTokenClntNewInstance`:分配并初始化`DeviceTokenClnt`,将`token`封装其中tokenClnt = DeviceTokenClntNewInstance(token);if (tokenClnt == NULL) {  // 创建失败(内存不足或参数错误)HDF_LOGE("failed to attach device, tokenClnt is null");  // 日志记录:设备令牌客户端创建失败return HDF_FAILURE;                                      // 返回失败}// 4. 将设备令牌客户端添加到宿主服务客户端的**设备链表**中//    - `hostClnt->devices`:宿主服务客户端管理的设备链表(存储所有属于该宿主的设备令牌客户端)//    - `HdfSListAdd`:HDF框架的链表操作函数,将`tokenClnt->node`(链表节点)添加到链表尾部HdfSListAdd(&hostClnt->devices, &tokenClnt->node);// 5. 返回成功(设备已成功附着到宿主服务)return HDF_SUCCESS;
}

DevmgrServiceAttachDevice()在AttachDevice 子流程中做了以下几件事情。

  • 调用 DevmgrServiceFindDeviceHost() 在 DevmgrService.hosts 链表中找到匹配deviceInfo->hostId 的 Host 对应的 hostClnt (见讲义2.1节)。
  • 调用 DeviceTokenClntNewInstance() 创建一个 DeviceTokenClnt tokenClnt 对象并配置好相关信息(其中的tokenClnt->tokenIf绑定到HdfIOService展开图中的HdfDeviceToken 对象)。
  • 调用 HdfsListAdd() ,把新创建的 tokenClnt 对象插入 hostClnt->devices 链表中,用以表示设备节点(devNode)开始受到对应Host的管理。

当前设备节点执行完 AttachDevice 子流程即表明该设备节点的驱动程序已经完成启动可以

对外提供服务了。然后驱动框架启动流程跳转回“InstallDevices 子流程小节中的循环去

启动下一个设备节点的驱动程序。

http://www.dtcms.com/a/313650.html

相关文章:

  • sublime 乱码问题
  • JavaEE文件泄露与修复方案
  • Linux | i.MX6ULL移植 Gdb+Gdbserver 调试(第十四章)
  • 深入解析 Linux Kernel 中的设备树:使用、修改与实际应用
  • 经典文献阅读之--ViNT(视觉导航的基础模型)
  • 《汇编语言:基于X86处理器》第11章 MS-Windows编程(3)
  • 8.3 Java Web(JavaScript P15-P28)
  • Leetcode——365. 水壶问题
  • 决策树模型知识点整理:从原理到实战(含可视化与调参)
  • [硬件电路-134]:模拟电路 - 运算放大器常见运算:两模拟信号相加、相减、单模拟信号的积分、微分...
  • HTTPS的概念和工作过程
  • Ollama模型库模型下载慢完美解决(全平台)
  • 模型学习系列之参数
  • pytorch深度学习全流程:以简易数据、模型介绍
  • linux火焰图
  • vuhub Noob靶场攻略
  • 雪花算法重复id问题
  • Maxscript在选择的可编辑多边形每个面上绘制一个内部圆形
  • 自动驾驶中的传感器技术19——Camera(10)
  • OS21.【Linux】环境变量
  • CMake 命令行参数完全指南(5)
  • graph TD的规则
  • Linux Deepin深度操作系统应用商店加载失败,安装星火应用商店
  • io_getevents 和 io_pgetevents 系统调用及示例
  • [硬件电路-145]:模拟电路 - 常见的电源芯片、用途、管脚定义
  • 深度学习-读写模型网络文件
  • 大模型设计
  • 学习方法论
  • 智能化设备维护:开启高效运维新时代
  • 前端异步任务处理总结