
CANN Runtime 多进程 IPC Notify 共享与进程白名单校验实战0_ipc_notify_withpid 示例深度解析【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime导读本文基于 CANN Runtime 仓库中的example/2_advanced_features/notify/0_ipc_notify_withpid示例系统讲解如何在两个 Device、两个进程之间共享 IPC Notify 并启用进程白名单校验实现跨进程任务同步。文中不仅完整还原该示例的编译运行步骤、接口清单与验证输出还结合 notify.cpp、acl_rt.h 等源码剖析 IPC Notify 的导出、导入与 PID 白名单机制的底层调用链。读完本文你将掌握aclrtNotifyGetExportKey、aclrtNotifyImportByKey、aclrtNotifySetImportPid、aclrtRecordNotify、aclrtWaitAndResetNotify等接口的组合用法以及如何通过共享内存 共享 Notify 在双进程间完成数据写入与任务同步。示例背景多 Device 多进程场景下的任务同步在 Atlas A2/A3 系列产品的多卡训练或推理场景中常常需要两个进程各自绑定不同的 Device但彼此之间又要协同工作一个进程在 Device 上写入数据另一个进程需要等待数据就绪后再继续执行。CANN Runtime 为此提供了两类基础设施IPC 共享内存通过aclrtIpcMemGetExportKey/aclrtIpcMemImportByKey将一个进程内的 Device 内存导出为共享 key另一进程导入后即可获得可用的 Device 内存指针IPC Notify通过aclrtNotifyGetExportKey/aclrtNotifyImportByKey将本进程内的 Notify 导出为共享名称另一进程导入后即可在同一 Stream 上aclrtRecordNotify/aclrtWaitAndResetNotify实现跨进程的任务级同步。本示例0_ipc_notify_withpid的特殊之处在于共享 Notify 时启用了进程白名单校验with PID。也就是说Notify 导出方需要显式调用aclrtNotifySetImportPid把允许导入的进程 PID 加入白名单未被授权的外部进程即使拿到了共享 key 也无法导入该 Notify从而提升了跨进程共享的安全性。仓库中与该示例互为对照的是 1_ipc_notify_withoutpid后者通过导出 flagACL_RT_NOTIFY_EXPORT_FLAG_DISABLE_PID_VALIDATION关闭白名单校验。两个示例合在一起恰好覆盖了 IPC Notify 共享的两种安全策略。整体能力清单可参考 notify 目录说明。示例总体架构与运行流程本示例由两个可执行程序组成源码位于 proc_a.cpp 与 proc_b.cppProcess A绑定 Device 2创建 Stream 与 Notify导出共享内存 key 和 Notify key读取 Process B 的 PID 并加入白名单最后在 Stream 上挂起等待aclrtWaitAndResetNotify等待被唤醒后执行读任务Process B绑定 Device 3导入共享内存 key 得到 Device 内存指针把自身 PID 通过文件告知 Process A导入 Notify key 得到共享 Notify模拟耗时操作后向共享内存写入数值 123并aclrtRecordNotify唤醒 Process A。两个进程通过本地文件交换三类信息memory::WriteFile/memory::ReadFile实现位于 file_ops.cpp文件名内容生产方消费方file/memName.binIPC 共享内存 keyProcess AProcess Bfile/pid.binProcess B 的 PIDProcess BProcess Afile/notifyName.binIPC Notify 共享名称Process AProcess B完整时序如下两个进程分别aclInit→aclrtSetDevice→aclrtCreateStream双方各自调用aclrtDeviceCanAccessPeer确认对端 Device 支持 P2P 数据交互并用aclrtDeviceEnablePeerAccess使能canAccessPeer 1表示支持Process A 在 Device 2 上aclrtMalloc申请 1 MiB Device 内存aclrtIpcMemGetExportKey导出共享内存 key写入文件Process BaclrtIpcMemImportByKey导入共享内存获得本进程可用的 Device 内存指针Process B 通过aclrtDeviceGetBareTgid获取自身 PID写入文件Process A 读取该 PID 后调用aclrtNotifySetImportPid完成白名单授权Process A 创建 Notify 并aclrtNotifyGetExportKey导出共享名称写入文件Process BaclrtNotifyImportByKey导入获得共享 Notify 指针Process A 先在 Stream 上aclrtWaitAndResetNotify挂起Process Busleep模拟耗时任务后启动写 kernel 写入123再aclrtRecordNotify唤醒Process A 的等待被解除执行读 kernel 校验数据aclrtSynchronizeStream收尾双方释放资源并退出。环境准备与编译运行环境准备运行本示例前需要准备 CANN 开发环境并配置环境变量完整的环境准备与配置说明参见 example 目录下的 README_en.md。示例的编译运行依赖以下环境变量ASCEND_INSTALL_PATHCANN 安装路径run.sh 中通过source ${ASCEND_INSTALL_PATH}/bin/setenv.bash加载编译环境SOC_VERSION目标芯片型号由 set_sample_env.sh 自动识别ASCENDC_CMAKE_DIRAscendC 算子编译的 CMake 模块目录同样由 set_sample_env.sh 自动识别。编译与运行步骤按示例 README_en.md 提供的步骤执行# 将 ${install_root} 替换为 CANN 安装根目录默认安装在 /usr/local/Ascend source ${install_root}/cann/set_env.sh # 自动识别 SOC_VERSION 和 ASCENDC_CMAKE_DIR source ${git_clone_path}/example/set_sample_env.sh # 编译并运行 bash run.shrun.sh 做了什么run.sh 内部依次完成以下工作加载setenv.bash并打印当前编译的 SOC 版本通过 CMake 配置、编译并安装工程产物安装到out/目录随后创建file/目录用于进程间文件交换并行启动proc_a与proc_b两个进程分别用tee将标准输出落到output_msg_proc_a.txt和output_msg_proc_b.txt从proc_b的输出中提取Source data:后的数值从proc_a的输出中提取Destination data:后的数值比较两值相等则输出[SUCCESS]结论否则输出[FAILURE]并退出非零状态。这种启动双进程 日志回放 数值比对的验证方式使得示例本身即可作为自动化冒烟测试使用无需人工核对两端日志。核心 API 全解示例 README_en.md 中列出的关键接口按功能分类说明如下。初始化与去初始化接口作用aclInit(nullptr)初始化 AscendCL 配置参数传空指针表示使用默认配置aclFinalize()去初始化 AscendCL释放全局资源Device 管理接口作用aclrtSetDevice(deviceId)指定当前进程用于运算的 DeviceProcess A 为 2Process B 为 3aclrtResetDeviceForce(deviceId)强制复位当前运算 Device回收 Device 上资源即使仍有未完成任务也会被清理aclrtDeviceCanAccessPeer(canAccessPeer, dev, peerDev)查询两个 Device 之间是否支持数据交互返回 1 表示支持aclrtDeviceEnablePeerAccess(peerDeviceId, 0)使能当前 Device 与指定 Device 之间的 P2P 数据交互Stream 管理接口作用aclrtCreateStream(stream)创建 Stream任务在 Stream 上排队异步执行aclrtSynchronizeStream(stream)阻塞等待 Stream 上所有任务执行完成aclrtDestroyStreamForce(stream)强制销毁 Stream丢弃其上所有未完成任务内存管理接口作用aclrtMalloc(devPtr, size, ACL_MEM_MALLOC_HUGE_FIRST)在 Device 上申请内存本示例申请1 * 1024 * 10241 MiB分配策略为优先申请大页内存aclrtFree(devPtr)释放 Device 内存aclrtIpcMemGetExportKey(devPtr, size, key, len, flags)将指定 Device 内存设置为 IPC 共享内存并返回共享 keyaclrtIpcMemImportByKey(devPtr, key, flags)按 key 导入共享内存返回本进程可使用的 Device 内存地址指针aclrtIpcMemClose(key)关闭 IPC 共享内存解除本进程的导入关系aclrtDeviceGetBareTgid(pid)获取当前进程的真实进程 IDthread group ID用于后续白名单授权关于 IPC 内存的完整用法包括带 PID / 不带 PID 的多种共享变体可进一步参考 memory 目录下的 10_ipc_memory_withpid、11_ipc_memory_withoutpid 等示例。Notify 管理本示例核心接口作用aclrtCreateNotify(notify, 0)创建 Notify第二个参数为 flag本示例传 0aclrtNotifyGetExportKey(notify, key, len, flags)将本进程中指定 Notify 设置为 IPC Notify返回 key即 Notify 共享名称供多 Device 上不同进程实现任务同步aclrtNotifySetImportPid(notify, pid, 1)设置共享 Notify 的进程白名单num1表示一次授权一个 PIDaclrtNotifyImportByKey(notify, key, flags)在本进程中按 key 获取共享 Notify 的可用指针aclrtRecordNotify(notify, stream)在指定 Stream 上记录一个 Notify 事件aclrtWaitAndResetNotify(notify, stream, 0)阻塞指定 Stream 的执行直到指定的 Notify 完成随后复位该 Notifytimeout0表示无限等待底层实现视角接口如何落到 RTS从 notify.cpp 的实现可以看到这些 acl 层接口最终都转发给底层 RTSRuntime Service接口ACL 接口实现底层调用aclrtCreateNotifyImplrtsNotifyCreateaclrtDestroyNotifyImplrtsNotifyDestroyaclrtRecordNotifyImplrtsNotifyRecordaclrtWaitAndResetNotifyImplrtsNotifyWaitAndResetaclrtNotifyGetExportKeyImplrtsNotifyGetExportKeyaclrtNotifyImportByKeyImplrtsNotifyImportByKeyaclrtNotifySetImportPidImplrtsNotifySetImportPid以 notify.cpp 中 aclrtNotifyGetExportKeyImpl 的实现 为例其流程为记录 Profiling 事件 → 空指针校验ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT→ 调用rtsNotifyGetExportKey生成共享 key → 打印成功日志。白名单授权接口aclrtNotifySetImportPidImplnotify.cpp同样校验notify与pid指针后调用rtsNotifySetImportPid(notify, pid, num)。可以推断PID 白名单校验逻辑由 RTS/驱动层在导入时强制执行——ACL 层只负责传递授权列表。进程白名单校验机制详解导出侧flags 决定是否启用白名单在 acl_rt.h 中定义了 Notify 导出的两个 flag#define ACL_RT_NOTIFY_EXPORT_FLAG_DEFAULT 0x0UL #define ACL_RT_NOTIFY_EXPORT_FLAG_DISABLE_PID_VALIDATION 0x02ULACL_RT_NOTIFY_EXPORT_FLAG_DEFAULT0x0默认行为启用PID 白名单校验ACL_RT_NOTIFY_EXPORT_FLAG_DISABLE_PID_VALIDATION0x2移除PID 白名单校验。本示例在导出 Notify 时传入flags 0即采用默认行为、保留白名单校验因此必须配合aclrtNotifySetImportPid完成授权这也正是示例名称中withpid的含义。反过来1_ipc_notify_withoutpid 导出时使用ACL_RT_NOTIFY_EXPORT_FLAG_DISABLE_PID_VALIDATION任何进程拿到共享 key 都能导入。授权侧aclrtNotifySetImportPidaclrtNotifySetImportPid(notify, pid, 1);参数含义参数含义notify待授权的 IPC Notify导出方本进程内的指针pid允许导入该 Notify 的进程 PID 数组numPID 数组长度本示例为 1支持一次授权多个进程该接口的声明位于 acl_rt.h其实现notify.cpp会对notify与pid做空指针校验再透传rtsNotifySetImportPid。仓库中还提供了面向跨服务器场景的aclrtNotifySetImportPidInterServer对应aclrtServerPid白名单用于多机场景下授权对端服务器进程属于同一机制在服务器维度的扩展。导入侧aclrtNotifyImportByKey 的 flag导入 Notify 时同样有 flag 可选acl_rt.h#define ACL_RT_NOTIFY_IMPORT_FLAG_DEFAULT 0x0UL #define ACL_RT_NOTIFY_IMPORT_FLAG_ENABLE_PEER_ACCESS 0x02ULACL_RT_NOTIFY_IMPORT_FLAG_DEFAULT0x0默认导入行为ACL_RT_NOTIFY_IMPORT_FLAG_ENABLE_PEER_ACCESS0x2使能对 peer Device 上 Notify 分配的直接访问。本示例导入侧使用flags 0。需要强调导入侧传入的 key 必须与导出侧aclrtNotifyGetExportKey产出的 key 完全一致且导入进程必须已在导出进程设置的白名单中否则导入会失败。白名单校验的意义启用白名单后即使共享 Notify 名称key在进程间泄露或被第三方截获未授权进程调用aclrtNotifyImportByKey也无法获得可用 Notify从而防止外部进程通过伪造 key 干扰任务同步流程。对于多租户、多服务共存的部署环境建议保留默认的白名单校验仅当所有参与方完全可信时才考虑关闭。源码逐段解析Process Aproc_a.cpp导出与等待侧关键代码段完整源码见 proc_a.cppaclInit(nullptr); int32_t deviceId 2; aclrtSetDevice(deviceId); aclrtStream stream nullptr; aclrtCreateStream(stream); int32_t canAccessPeer 0; int32_t peerDeviceId 3; CHECK_ERROR(aclrtDeviceCanAccessPeer(canAccessPeer, deviceId, peerDeviceId)); if (canAccessPeer 1) { CHECK_ERROR(aclrtDeviceEnablePeerAccess(peerDeviceId, 0)); // 1. 申请 Device 内存并导出 IPC 共享内存 key uint64_t size 1 * 1024 * 1024; int* devPtr nullptr; CHECK_ERROR(aclrtMalloc((void**)devPtr, size, ACL_MEM_MALLOC_HUGE_FIRST)); char memName[256] name; size_t lenName 65; CHECK_ERROR(aclrtIpcMemGetExportKey(devPtr, size, memName, lenName, 1)); memory::WriteFile(file/memName.bin, file/memName.bin.done, memName, strlen(memName)); // 2. 创建 Notify 并导出 IPC Notify 共享名称 aclrtNotify notify nullptr; CHECK_ERROR(aclrtCreateNotify(notify, 0)); char notifyName[256] name; CHECK_ERROR(aclrtNotifyGetExportKey(notify, notifyName, lenName, 0)); // 3. 读取 Process B 的 PID 并加入白名单 int32_t pid 0; memory::ReadFile(file/pid.bin, file/pid.bin.done, pid, sizeof(pid)); CHECK_ERROR(aclrtNotifySetImportPid(notify, pid, 1)); memory::WriteFile(file/notifyName.bin, file/notifyName.bin.done, notifyName, strlen(notifyName)); // 4. 挂起等待 Process B 的 RecordNotify被唤醒后执行读任务 CHECK_ERROR(aclrtWaitAndResetNotify(notify, stream, 0)); uint32_t blockDim 1; ReadDo(blockDim, stream, devPtr); // 5. 同步并释放资源 CHECK_ERROR(aclrtSynchronizeStream(stream)); CHECK_ERROR(aclrtIpcMemClose(memName)); CHECK_ERROR(aclrtFree(devPtr)); CHECK_ERROR(aclrtDestroyNotify(notify)); } aclrtDestroyStreamForce(stream); aclrtResetDeviceForce(deviceId); aclFinalize();实现细节值得注意导出时机Process A 在创建 Notify 后立即aclrtNotifyGetExportKey而aclrtNotifySetImportPid在读取到 Process B 的 PID 之后才执行——白名单授权与 Notify 导出的顺序不强制但授权必须在 Process B 执行aclrtNotifyImportByKey之前完成否则 Process B 会导入失败文件握手协议memory::WriteFile的第二个参数是.done标记文件memory::ReadFile会轮询等待该标记出现从而在两个并发进程中实现简单可靠的写后读同步资源清理即使中途失败如 Device 不支持 P2P 走 else 分支也会走到aclrtDestroyStreamForce和aclrtResetDeviceForce保证强制回收。Process Bproc_b.cpp导入与唤醒侧关键代码段完整源码见 proc_b.cppaclInit(nullptr); int32_t deviceId 3; aclrtSetDevice(deviceId); aclrtStream stream nullptr; aclrtCreateStream(stream); int32_t canAccessPeer 0; int32_t peerDeviceId 2; CHECK_ERROR(aclrtDeviceCanAccessPeer(canAccessPeer, deviceId, peerDeviceId)); if (canAccessPeer 1) { CHECK_ERROR(aclrtDeviceEnablePeerAccess(peerDeviceId, 0)); // 1. 导入 IPC 共享内存获得本进程可用的 Device 指针 char memName[256] name; memory::ReadFile(file/memName.bin, file/memName.bin.done, memName, sizeof(memName)); void* devPtr nullptr; uint64_t size 1 * 1024 * 1024; CHECK_ERROR(aclrtIpcMemImportByKey(devPtr, memName, 0)); // 2. 获取自身 PID 并告知 Process A int32_t pid 0; CHECK_ERROR(aclrtDeviceGetBareTgid(pid)); memory::WriteFile(file/pid.bin, file/pid.bin.done, pid, sizeof(pid)); // 3. 导入 IPC Notify char notifyName[256] name; memory::ReadFile(file/notifyName.bin, file/notifyName.bin.done, notifyName, sizeof(notifyName)); aclrtNotify notify nullptr; CHECK_ERROR(aclrtNotifyImportByKey(notify, notifyName, 0)); // 4. 模拟耗时任务后写数据再 RecordNotify 唤醒 Process A uint32_t waitTime 5000000; (void)usleep(waitTime); uint32_t blockDim 1; int32_t writeValue 123; WriteDo(blockDim, stream, static_castint*(devPtr), writeValue); CHECK_ERROR(aclrtRecordNotify(notify, stream)); // 5. 同步并释放资源 CHECK_ERROR(aclrtSynchronizeStream(stream)); CHECK_ERROR(aclrtIpcMemClose(memName)); CHECK_ERROR(aclrtDestroyNotify(notify)); } aclrtDestroyStreamForce(stream); aclrtResetDeviceForce(deviceId); aclFinalize();Process B 的要点aclrtDeviceGetBareTgid返回的是线程组 IDTGID即常规意义上的进程 PID它被写入文件后由 Process A 用于白名单授权usleep(5000000)5 秒刻意让 Process B 落后于 Process A 的等待任务以验证aclrtWaitAndResetNotify确实让 Process A 阻塞——若 Process A 未真正等待它会读到尚未写入的旧值导致校验失败WriteDo/ReadDo是示例自带的 Device 侧 kernel详见下文Kernel 与构建一节分别把数值写入、读出共享内存并打印日志。Kernel 与构建两个进程共用的WriteDo/ReadDokernel 定义在 write_read_value.cpp函数声明见 kernel_ops.h。它们通过 CMakeLists.txt 中的ascendc_library(kernels STATIC ../../../kernel_func/write_read_value.cpp)编译为静态库并分别链接进proc_a与proc_badd_executable(proc_a proc_a.cpp ../../../1_basic_features/memory/file_ops.cpp) add_executable(proc_b proc_b.cpp ../../../1_basic_features/memory/file_ops.cpp) target_link_libraries(proc_a PRIVATE ascendcl kernels) target_link_libraries(proc_b PRIVATE ascendcl kernels)其中file_ops.cpp 提供跨进程文件读写工具memory::WriteFile/memory::ReadFilemem_utils.h 提供内存相关的公共工具函数内存申请使用ACL_MEM_MALLOC_HUGE_FIRST优先申请大页保证 IPC 导出内存满足页对齐要求编译依赖SOC_VERSION与ASCENDC_CMAKE_DIR环境变量由 set_sample_env.sh 自动识别链接目录指向${ASCEND_CANN_PACKAGE_PATH}/lib64下的ascendcl运行时库。运行结果与验证预期输出按示例 README_en.md 给出的预期输出成功运行时日志形如[INFO] Process A: enable data interaction between device 2 and device 3 [INFO] Process A: get a shareable identifier for IPC memory sharing successfully, shareable identifier ... [INFO] Process B: enable data interaction between device 3 and device 2 [INFO] Process B: get Process Bs pid successfully, pid ... [INFO] Process A: get Process Bs pid successfully, Process Bs pid ... [INFO] Process A: add Process B to the whitelist successfully [INFO] Process B: get the shareable identifier for IPC notify sharing successfully, shareable identifier ... [INFO] Process B: write data 123 to the device memory ... Destination data: 123 [SUCCESS] IPC notify for task synchronization is successful. Values at source and destination are equal: 123如何确认同步生效run.sh的最终校验依赖两条日志的数值比对Source data:由 Process B 的写 kernel 打印写入值为 123Destination data:由 Process A 的读 kernel 打印从共享 Device 内存读出的值。只有当 Process A 的aclrtWaitAndResetNotify真正等待到 Process B 完成写任务并aclrtRecordNotify之后读 kernel 才能读到 123。若 Process A 未正确等待读出的可能是共享内存的初始值脚本会输出[FAILURE]并返回非零状态。因此该示例不仅演示了 IPC Notify 的 API 用法其脚本本身也是一套可复用的跨进程任务同步自检方案。常见失败排查方向现象可能原因The target devices do not support data interaction两块 Device 之间不支持 P2P 数据交互如跨 NUMA 或机型限制可确认产品型号与组网方式Process B 导入 Notify 失败白名单未及时设置aclrtNotifySetImportPid晚于对端aclrtNotifyImportByKey或 PID 读取/传递错误Destination data与Source data不一致同步失效等待任务未真正阻塞可检查 Stream 是否同步、Notify 是否被正确 Record/Wait编译失败SOC_VERSION/ASCENDC_CMAKE_DIR未识别成功或ASCEND_INSTALL_PATH指向的 CANN 包不完整产品支持情况与已知问题产品支持情况示例 README_en.md 明确给出了产品支持矩阵产品是否支持Ascend 950PR/Ascend 950DT否Atlas A3 训练系列产品 / Atlas A3 推理系列产品是Atlas A2 训练系列产品 / Atlas A2 推理系列产品是即在 Atlas A2/A3 训练与推理系列产品上支持跨 Device、跨进程的 IPC Notify 共享与白名单校验Ascend 950PR/Ascend 950DT 当前不支持运行时需按此矩阵确认硬件环境。已知问题当前示例无已知问题Known Issues: None。延伸阅读与本示例对比学习关闭白名单校验的变体1_ipc_notify_withoutpid通过对比两个示例的导出 flag 差异即可理解白名单机制的全部开关路径IPC 共享内存的更多变体带 PID / 不带 PID / 跨服务器物理内存共享memory 示例目录IPC Event基于事件的双进程同步ipcevent 示例目录Notify 管理相关接口的完整声明与 flag 定义acl_rt.hNotify 系列接口的 ACL 层实现notify.cpp可继续追踪rtsNotify*进入 Runtime 底层。综上0_ipc_notify_withpid示例完整演示了 CANN Runtime 在多 Device、多进程场景下通过IPC 共享内存 IPC Notify 进程白名单校验实现安全任务同步的完整链路。无论是作为学习 IPC Notify 用法的入门样例还是作为双进程协作框架的参考实现它都具备直接的工程参考价值。【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考