Files
startup_appspawn/docs/modules/module_standard.md
yh 703135e9f2 initmd
Co-Authored-By: Agent
Reviewed-by: yh
Signed-off-by: yh <yanghao236@huawei.com>

AI[0%] Human Fixed[0%] Human[100%] AI Adopted[0%]
2026-07-24 17:18:39 +08:00

31 KiB
Raw Permalink Blame History

Module: standard

返回: 索引

Overview

standard 模块是 appspawn 在标准系统(而非小型系统)上的核心实现。它负责解析启动参数、创建 socket 服务、接收客户端孵化请求、管理 fork 流程、处理子进程生命周期以及管理已孵化进程。

该模块是整个 appspawn 系统的控制中枢,通过 Hook 机制将沙箱、权限、SPM 等功能模块编排到孵化流程的各个阶段。

Source Location

  • Directory: standard/
  • Files: 7 C 源文件, 5 头文件
  • Estimated LOC: ~4,137

Dependencies

  • Depends on: common(通用服务层), modulemgr_engineHook 引擎), util(工具类), client_api(消息定义)
  • Used by: 无(顶层入口模块)

KP-1: 进程孵化主流程

Priority: P0

Summary

appspawn 的主入口位于 appspawn_main.cmain() 函数。它解析命令行参数选择孵化模式(appspawn/nwebspawn/nativespawn/hybridspawn/cold run),然后调用 StartSpawnService() 初始化服务,最后通过 content->runAppSpawn() 进入事件循环。

Key Code

模式选择与启动参数模板

// standard/appspawn_main.c:31
static AppSpawnStartArgTemplate g_appSpawnStartArgTemplate[PROCESS_INVALID] = {
    {APPSPAWN_SERVER_NAME, {MODE_FOR_APP_SPAWN, MODULE_APPSPAWN, APPSPAWN_SOCKET_NAME, APPSPAWN_SERVER_NAME, 1}},
    {NWEBSPAWN_SERVER_NAME, {MODE_FOR_NWEB_SPAWN, MODULE_NWEBSPAWN, NWEBSPAWN_SOCKET_NAME, NWEBSPAWN_SERVER_NAME, 1}},
    {"app_cold", {MODE_FOR_APP_COLD_RUN, MODULE_APPSPAWN, APPSPAWN_SOCKET_NAME, APPSPAWN_SERVER_NAME, 0}},
    ...
};

g_appSpawnStartArgTemplate 定义了所有支持的孵化模式,每个模式包含运行模式(RunMode)、模块类型、socket 名称、服务名称以及是否为孵化服务(initArg1=服务进程,0=冷启动子进程)。

主函数入口

// standard/appspawn_main.c:101
int main(int argc, char *const argv[])
{
    InitCommonEnv();
    CheckPreload(argv);
    (void)signal(SIGPIPE, SIG_IGN);
    // ...根据编译宏选择默认模式模板...
    argTemp = GetAppSpawnStartArg(argv[MODE_VALUE_INDEX], ...);
    arg = &argTemp->arg;
    AppSpawnContent *content = StartSpawnService(arg, argvSize, argc, argv);
    if (content != NULL) {
        content->runAppSpawn(content, argc, argv);
    }
    return 0;
}

main() 首先初始化通用环境(InitCommonEnv),处理 LD_PRELOADCheckPreload 确保加载 helper 库),然后根据命令行 -mode 参数选择孵化模式。

LD_PRELOAD 处理

// standard/appspawn_main.c:54
static void CheckPreload(char *const argv[])
{
    char *preload = getenv("LD_PRELOAD");
    char *pos = preload ? strstr(preload, APPSPAWN_PRELOAD) : NULL;
    // ...如果已设置 preload,重新组合后 execv 自身...
    ret = execv(buf, argv);
}

该函数确保 libappspawn_helper.z.so 被 LD_PRELOAD 加载,通过 execv 重启自身来应用新的环境变量。

Data Structures

结构体 文件 用途
AppSpawnStartArgTemplate appspawn_service.h:73 模式启动参数模板
AppSpawnStartArg appspawn_service.h:65 启动参数(mode, moduleType, socketName, serviceName
RunMode appspawn_server.h:33 运行模式枚举

Flow

  1. init 进程启动 appspawn(通过 appspawn.cfg 配置)
  2. main() 解析 -mode 参数,选择孵化模式
  3. CheckPreload() 确保 helper 库已加载
  4. StartSpawnService() 初始化服务、加载模块
  5. runAppSpawn() 进入事件循环

KP-2: fork 与子进程管理

Priority: P0

Summary

核心孵化逻辑在 appspawn_service.c 中。appspawn 使用 fork() 创建子进程,父进程通过 pipe 等待子进程的执行结果,子进程在 fork 后执行 Hook 链(沙箱设置、权限配置等)并通过 pipe 返回结果。还支持 prefork 机制,预创建子进程池以加速启动。

Key Code

连接接受与 UID 检查

// standard/appspawn_service.c:425
static int OnConnection(const LoopHandle loopHandle, const TaskHandle server)
{
    LE_StreamInfo info = {};
    info.baseInfo.close = OnClose;
    info.disConnectComplete = OnDisConnect;
    info.sendMessageComplete = SendMessageComplete;
    info.recvMessage = OnReceiveRequest;
    info.handleRecvMsg = HandleRecvMessage;
    LE_STATUS ret = LE_AcceptStreamClient(loopHandle, server, &stream, &info);
    // 检查客户端 UID
    struct ucred cred = {-1, -1, -1};
    getsockopt(LE_GetSocketFd(stream), SOL_SOCKET, SO_PEERCRED, &cred, &credSize);
    if (!OnConnectionUserCheck(cred.uid)) { ... 拒绝连接 ... }
}

OnConnection 通过 SO_PEERCRED 获取客户端 UID,仅允许 root(0)、foundation(5523)、app_fwk_update(3350)、storage_manager(1090) 以及开发者模式下的 shell(2000) 连接。

UID 白名单检查

// standard/appspawn_service.c:402
APPSPAWN_STATIC bool OnConnectionUserCheck(uid_t uid)
{
    const uid_t uids[APPSPAWN_MSG_USER_CHECK_COUNT] = {
        0, 3350, 5523, 1090,
    };
    for (int i = 0; i < APPSPAWN_MSG_USER_CHECK_COUNT; i++) {
        if (uid == uids[i]) return true;
    }
    if (uid == 2000 && IsDeveloperModeOn()) return true;
    return false;
}

Fork 上下文初始化

// standard/appspawn_service.c:637
static int InitForkContext(AppSpawningCtx *property)
{
    if (pipe(property->forkCtx.fd) == -1) {
        return errno;
    }
    int flags = fcntl(property->forkCtx.fd[0], F_GETFL);
    if (flags >= 0) {
        (void)fcntl(property->forkCtx.fd[0], F_SETFL, (unsigned int)flags | O_NONBLOCK);
    }
    return 0;
}

创建非阻塞 pipe,用于子进程向父进程通知 fork 结果。

子进程结果监听

// standard/appspawn_service.c:735
static int AddChildWatcher(AppSpawningCtx *property)
{
    uint32_t defTimeout = IsChildColdRun(property) ? COLD_CHILD_RESPONSE_TIMEOUT : WAIT_CHILD_RESPONSE_TIMEOUT;
    LE_WatchInfo watchInfo = {};
    watchInfo.fd = property->forkCtx.fd[0];
    watchInfo.flags = WATCHER_ONCE;
    watchInfo.processEvent = ProcessChildResponse;
    LE_STATUS status = LE_StartWatcher(LE_GetDefaultLoop(), &property->forkCtx.watcherHandle, &watchInfo, property);
    // 创建超时定时器
    status = LE_CreateTimer(LE_GetDefaultLoop(), &property->forkCtx.timer, WaitChildTimeout, property);
    status = LE_StartTimer(LE_GetDefaultLoop(), property->forkCtx.timer, timeout * 1000, 0);
}

父进程在 fork 后注册 IO watcher 监听 pipe 读端,并设置超时定时器(正常 3s,冷启动 10s)。

Data Structures

结构体 文件 用途
AppSpawningCtx appspawn_manager.h:84 孵化过程中的上下文,包含 fork pipe、消息、状态等
AppSpawnForkCtx appspawn_manager.h:74 fork 上下文(pipe fd, watcher, timer, 共享内存)
AppSpawnConnection appspawn_service.h:59 客户端连接,包含消息接收上下文

Edge Cases & Error Handling

  • 客户端 UID 不在白名单内:直接关闭连接 (appspawn_service.c:448)
  • 子进程超时未响应:WaitChildTimeout 定时器触发,kill 子进程 (appspawn_service.c:63)
  • pipe 创建失败:返回 errno 给调用方 (appspawn_service.c:640)
  • 消息未完整接收:保存为 incompleteMsg,等待下一次数据到达 (appspawn_service.c:523)

KP-3: 消息接收与 TLV 解析

Priority: P0

Summary

消息接收和 TLV 解析逻辑分布在 appspawn_msgmgr.c 中。消息格式采用 TLVType-Length-Value),消息头为 AppSpawnMsg(包含 magic、msgType、msgLen、msgId、tlvCount、processName),后跟若干 TLV 字段。支持分片接收和扩展 TLV。

Key Code

消息头验证

// standard/appspawn_msgmgr.c:126
static inline int CheckRecvMsg(const AppSpawnMsg *msg)
{
    APPSPAWN_CHECK(msg->magic == APPSPAWN_MSG_MAGIC, return -1, ...);
    APPSPAWN_CHECK(msg->msgLen < MAX_MSG_TOTAL_LENGTH, return -1, ...);
    APPSPAWN_CHECK(msg->msgLen >= sizeof(AppSpawnMsg), return -1, ...);
    APPSPAWN_CHECK(msg->tlvCount < MAX_TLV_COUNT, return -1, ...);
    return 0;
}

验证 magic0xEF201234)、消息长度上限(64KB)、TLV 数量上限(128)。

TLV 解码

// standard/appspawn_msgmgr.c:264
int DecodeAppSpawnMsg(AppSpawnMsgNode *message)
{
    uint32_t bufferLen = message->msgHeader.msgLen - sizeof(AppSpawnMsg);
    uint32_t currLen = 0;
    while (currLen < bufferLen) {
        AppSpawnTlv *tlv = (AppSpawnTlv *)(message->buffer + currLen);
        ret = CheckMsgTlv(tlv, bufferLen - currLen);
        if (tlv->tlvType < TLV_MAX) {
            message->tlvOffset[tlv->tlvType] = currLen;  // 标准 TLV
        } else {
            message->tlvOffset[TLV_MAX + tlvCount] = currLen;  // 扩展 TLV
            tlvCount++;
        }
        currLen += tlv->tlvLen;
    }
}

解码器遍历 buffer,按 TLV 格式解析。标准 TLVtype < TLV_MAX)直接索引到 tlvOffset 数组,扩展 TLV 追加在 TLV_MAX 之后。

必需字段检查

// standard/appspawn_msgmgr.c:185
int CheckAppSpawnMsg(const AppSpawnMsgNode *message)
{
    if (message->tlvOffset[TLV_BUNDLE_INFO] == INVALID_OFFSET ||
        message->tlvOffset[TLV_MSG_FLAGS] == INVALID_OFFSET ||
        message->tlvOffset[TLV_ACCESS_TOKEN_INFO] == INVALID_OFFSET ||
        message->tlvOffset[TLV_DOMAIN_INFO] == INVALID_OFFSET ||
        message->tlvOffset[TLV_DAC_INFO] == INVALID_OFFSET) {
        return APPSPAWN_MSG_INVALID;
    }
    // bundle name 不能包含 \ 或 /
    if (strstr(bundleInfo->bundleName, "\\") != NULL ||
        strstr(bundleInfo->bundleName, "/") != NULL) {
        return APPSPAWN_MSG_INVALID;
    }
}

KP-4: AppSpawnMgr 核心数据结构

Priority: P0

Summary

AppSpawnMgr 是 appspawn 的全局管理器,维护服务进程信息、已孵化进程队列、孵化中队列、死亡队列等。AppSpawningCtx 是单个孵化请求的上下文,AppSpawnedProcess 是已孵化进程的跟踪信息。

Key Code

AppSpawnMgr 结构体

// standard/appspawn_manager.h:161
typedef struct TagAppSpawnMgr {
    AppSpawnContent content;          // 服务内容(模式、沙箱标志等)
    struct SpawnTime spawnTime;       // 孵化时间统计
    uint32_t diedAppCount;            // 死亡进程计数
    uint32_t flags;                   // 全局标志
    pid_t servicePid;                 // 服务进程 PID
    TaskHandle server;                // socket 服务 task
    SignalHandle sigHandler;          // 信号处理
    struct ListNode appQueue;         // 已孵化进程队列
    struct ListNode diedQueue;        // 死亡进程队列(nwebspawn 用)
    struct ListNode appSpawnQueue;    // 孵化中队列
    struct ListNode extData;          // 扩展数据
    struct ListNode dataGroupCtxQueue; // Data Group 上下文
    struct ListNode checkPointIdQueue; // Checkpoint 进程队列
    struct ListNode spawningFdsQueue;  // 孵化 FD 队列
} AppSpawnMgr;

AppSpawningCtx 结构体

// standard/appspawn_manager.h:84
typedef struct TagAppSpawningCtx {
    AppSpawnClient client;            // 客户端标识
    struct ListNode node;             // 链表节点
    AppSpawnForkCtx forkCtx;          // fork 上下文(pipe fd, watcher, timer
    AppSpawnMsgNode *message;         // 孵化请求消息
    bool isPrefork;                   // 是否为 prefork 进程
    pid_t pid;                        // 子进程 PID
    int state;                        // 状态(IDLE, SPAWNING
    struct timespec spawnStart;       // 孵化开始时间
    bool allowDumpable;               // 是否允许 dump
    uint64_t checkPointId;            // 镜像进程 ID
    uint8_t spmRefAdded;              // SPM 引用计数位图
    bool lockBundleRefAdded;          // bundle 锁引用计数标志
    char *lockPath;                   // 沙箱锁路径
} AppSpawningCtx;

KP-5: 已孵化进程管理

Priority: P1

Summary

appspawn_appmgr.c 负责管理已孵化进程的生命周期,包括注册(AddSpawnedProcess)、查找(GetSpawnedProcess/GetSpawnedProcessByName)、终止(TerminateSpawnedProcess)和遍历(TraversalSpawnedProcess)。

Key Code

创建并注册进程

// standard/appspawn_appmgr.c:155
AppSpawnedProcess *AddSpawnedProcess(pid_t pid, const char *processName, uint32_t appIndex,
    bool isDebuggable, uint64_t tokenid)
{
    AppSpawnedProcess *node = calloc(1, sizeof(AppSpawnedProcess) + len + 1);
    node->pid = pid;
    node->appIndex = appIndex;
    node->isDebuggable = isDebuggable;
    node->tokenid = tokenid;
    OH_ListAddWithOrder(&g_appSpawnMgr->appQueue, &node->node, AppInfoCompareProc);
    return node;
}

进程按 PID 排序插入 appQueue 链表。

NWeb 死亡队列

// standard/appspawn_appmgr.c:183
void TerminateSpawnedProcess(AppSpawnedProcess *node)
{
    OH_ListRemove(&node->node);
    if (!IsNWebSpawnMode(g_appSpawnMgr)) {
        free(node);  // 非 nwebspawn 直接释放
        return;
    }
    // nwebspawn 模式移入 diedQueue(最多保留 5 个)
    if (g_appSpawnMgr->diedAppCount >= MAX_DIED_PROCESS_COUNT) {
        // 移除最旧的
    }
    OH_ListAddTail(&g_appSpawnMgr->diedQueue, &node->node);
}

nwebspawn 模式下保留最近 5 个死亡进程信息,用于查询渲染进程终止状态。


KP-6: FD 管理与传递

Priority: P1

Summary

appspawn_fd_manager.c 管理孵化过程中父子进程间的文件描述符传递。通过 recvmsg 的辅助消息(SCM_RIGHTS)接收客户端传递的 FD。

Key Code

FD 接收

// standard/appspawn_service.c:365
static int HandleRecvMessage(const TaskHandle taskHandle, uint8_t *buffer, int bufferSize, int flags)
{
    struct msghdr msg = { .msg_iov = &iov, .msg_control = ctrlBuffer, ... };
    int recvLen = recvmsg(socketFd, &msg, flags);
    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
        if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
            int fdCount = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
            memcpy_s(connection->receiverCtx.fds, fdCount * sizeof(int), fd, fdCount * sizeof(int));
            connection->receiverCtx.fdCount = fdCount;
        }
    }
}

KP-7: 信号处理与进程退出

Priority: P1

Summary

appspawn 使用 signalfd 机制处理信号,主要通过 SIGCHLD 处理子进程退出,SIGTERM 处理服务终止。

Key Code

SIGCHLD 处理

// standard/appspawn_service.c:202
APPSPAWN_STATIC void ProcessSignal(const struct signalfd_siginfo *siginfo)
{
    switch (siginfo->ssi_signo) {
        case SIGCHLD: {
            pid_t pid;
            int status;
            while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
                HandleDiedPid(pid, siginfo->ssi_uid, status);
            }
            break;
        }
        case SIGTERM: {
            StopAppSpawn();
            break;
        }
    }
}

进程死亡处理

// standard/appspawn_service.c:171
APPSPAWN_STATIC void HandleDiedPid(pid_t pid, uid_t uid, int status)
{
    if (pid == content->reservedPid) {
        CleanupSpawningFdsByPid((AppSpawnMgr *)content, pid);
        content->reservedPid = 0;
    }
    AppSpawnedProcess *appInfo = GetSpawnedProcess(pid);
    if (appInfo == NULL) { WaitChildDied(pid, status); return; }
    WriteSignalInfoToFd(appInfo, content, signal);
    ProcessMgrHookExecute(STAGE_SERVER_APP_DIED, GetAppSpawnContent(), appInfo);
    ProcessMgrHookExecute(STAGE_SERVER_APP_CLEANUP, GetAppSpawnContent(), appInfo);
    TerminateSpawnedProcess(appInfo);
}

KP-8: 多种孵化模式

Priority: P0

Summary

appspawn 支持多种孵化模式,通过 RunMode 枚举区分。每种模式有对应的 socket 名称和服务名称。冷启动模式(cold run)是已 fork 子进程中直接执行应用。

Data Structures

模式 socket 名称 服务名称 模块类型 说明
MODE_FOR_APP_SPAWN AppSpawn appspawn MODULE_APPSPAWN 标准应用孵化
MODE_FOR_NWEB_SPAWN NWebSpawn nwebspawn MODULE_NWEBSPAWN NWeb 渲染进程孵化
MODE_FOR_NATIVE_SPAWN NativeSpawn nativespawn MODULE_NATIVESPAWN 原生进程孵化
MODE_FOR_HYBRID_SPAWN HybridSpawn hybridspawn MODULE_HYBRIDSPAWN 混合孵化
MODE_FOR_APP_COLD_RUN AppSpawn app_cold MODULE_APPSPAWN 应用冷启动
MODE_FOR_CJAPP_SPAWN CJAppSpawn cjappspawn MODULE_APPSPAWN CJ 应用孵化

模式判断函数

// standard/appspawn_manager.h:246
APPSPAWN_INLINE int IsAppSpawnMode(const AppSpawnMgr *content) {
    return content->content.mode == MODE_FOR_APP_SPAWN ||
           content->content.mode == MODE_FOR_APP_COLD_RUN;
}
APPSPAWN_INLINE int IsColdRunMode(const AppSpawnMgr *content) {
    return content->content.mode == MODE_FOR_APP_COLD_RUN ||
           content->content.mode == MODE_FOR_NWEB_COLD_RUN || ...;
}

KP-9: devicedebug 调试服务 (G-2 补全)

Priority: P2

Summary

devicedebug 是一个独立 CLI 可执行程序ohos_executable("devicedebug")),用于开发者模式下向可调试应用进程发送信号。它通过 appspawn 客户端 SDK 向主服务发送 MSG_DEVICE_DEBUG 消息,由主服务验证后转发 kill() 到目标进程。

源码位置

  • 客户端目录: service/devicedebug/
  • 服务端处理: standard/appspawn_service.c:2215-2244(消息分发)与 2183-2203kill 实现)
  • 构建定义: service/devicedebug/BUILD.gn:17

命令路由表

// service/devicedebug/devicedebug_main.c:46-50
DeviceDebugManagerCmdInfo g_deviceDebugManagerCmd[] = {
    {"help", DeviceDebugShowHelp},
    {"-h",   DeviceDebugShowHelp},
    {"kill", DeviceDebugCmdKill},
};

main() 解析 argv[1] 作为子命令,调用对应处理函数。

客户端 → 服务端协议

消息类型 MSG_DEVICE_DEBUGinterfaces/innerkits/include/appspawn.h:124),通过 AppSpawnReqMsgAddExtInfo(reqHandle, "devicedebug", jsonString, len) 携带 JSON 载荷:

{ "app": <pid>, "op": "kill", "args": { "signal": <n> } }

服务端处理链

步骤 函数 位置 行为
1. 消息分发 ProcessAppSpawnDeviceDebugMsg appspawn_service.c:2215 解析 ext-info JSON,提取 app/op/args
2. 操作路由 AppspawnDevicedebugDeal appspawn_service.c op 字符串路由("kill" → 下一步)
3. 进程查找 GetSpawnedProcess(pid) appspawn_appmgr.c appQueueAppSpawnedProcess
4. 可调试校验 appInfo->isDebuggable appspawn_service.c:2193 不满足返回 APPSPAWN_DEVICEDEBUG_ERROR_APP_NOT_DEBUGGABLE
5. 信号发送 kill(pid, signal) appspawn_service.c:2200 标准库调用

关键校验

  • 开发者模式前置:客户端入口 IsDeveloperModeOpen()devicedebug_kill.c:133),未开启直接返回 DEVICEDEBUG_ERRNO_NOT_IN_DEVELOPER_MODE
  • 信号范围signal > 0 && signal <= SIGRTMAXdevicedebug_kill.c:148)。
  • 目标存在性:服务端必须能在 appQueue 中找到 pid,否则返回 APPSPAWN_DEVICEDEBUG_ERROR_APP_NOT_EXIST

与主孵化服务的关系

维度 关系
进程身份 独立可执行文件 devicedebug,非主服务进程
代码复用 共享 interfaces/innerkits/client 客户端库与 util:libappspawn_util
IPC Socket(与普通孵化请求同一 socket)
主服务改动 仅增加 MSG_DEVICE_DEBUG case 分支,无侵入性扩展

相关文件

  • service/devicedebug/devicedebug_main.c(主入口)
  • service/devicedebug/kill/src/devicedebug_kill.ckill 子命令)
  • service/devicedebug/base/devicedebug_base.h(公共头)
  • service/devicedebug/kill/include/devicedebug_kill.h
  • 测试: test/unittest/devicedebug_test/test/autotest/sub_startup_appspawn_devicedebug/

KP-10: Prefork 机制详解 (G-4 补全)

Priority: P1

Summary

prefork 是 appspawn 的启动加速机制:预先 fork 一个空闲子进程(名为 "PreforkProcess"),通过共享内存(mmap)+ 管道信号传递孵化请求,避免每次孵化都付出 fork() 系统调用开销。注意:本机制是"单进程预留"而非进程池,一次只维护 1 个 prefork 子进程。

启用条件

维度 取值
系统参数 persist.sys.prefork.enable = "true"(默认启用,appspawn_service.c:1949 IsEnablePrefork
编译开关 appspawn_support_prefork = trueappspawn.gni:35
运行时条件 IsBootFinished()IsSupportPrefork():模式为 MODE_FOR_APP_SPAWN,非冷启动,非 APP_FLAGS_CHILDPROCESSappspawn_service.c:1440-1458

不满足条件时回退到 NormalSpawnChild

关键数据结构

// common/appspawn_server.h:72-96
typedef struct AppSpawnPreforkMsg {
    uint32_t id;        // 客户端 ID
    uint32_t flags;     // 协商标志
    uint32_t msgLen;
} AppSpawnPreforkMsg;

typedef struct AppSpawnPipeMsg {
    AppSpawnMsgType type;   // MSG_APP_SPAWN 或 MSG_LOCK_STATUS
    union { AppSpawnPreforkMsg preforkMsg; AppSpawnUnlockMsg unlockMsg; } msg;
} AppSpawnPipeMsg;
// standard/appspawn_fd_manager.h:48-75
typedef enum { TYPE_CHILD_PARENT, TYPE_PARENT_CHILD, TYPE_INVALID } SpawningFdType;

typedef struct {
    struct ListNode node;
    SpawningFdType type;
    uint32_t count;
    pid_t pid;              // 0 表示全局 fd
    uint64_t timestamp;
    int fds[0];             // 柔性数组
} AppSpawnFds;

AppSpawnContent.reservedPid 字段保存当前可用的 prefork 子进程 PID。

完整调用链

# 函数 位置 作用
1 RunAppSpawnProcessMsg appspawn_service.c:1469 入口分流:prefork vs normal
2 IsSupportPrefork appspawn_service.c:1440 条件判定
3 AppSpawnProcessMsgForPrefork appspawn_service.c:1401 prefork 主逻辑
4 PreparePreforkMsg appspawn_service.c:1273 mmap 共享内存 + WritePreforkMsg
5 SendPipeMsgToChild appspawn_service.c:1312 通过 parentToChildFd[1] 写管道
6 TransferPreforkFdToForkCtx appspawn_service.c:1361 FD 队列迁移 + 设非阻塞
7 ProcessPreFork appspawn_service.c:1235 为下一个请求重建 prefork 子进程
8 ForkAndRegisterFds appspawn_service.c:1110 双管道创建 + fork + 注册到 spawningFdsQueue
9 PreforkChildLoop appspawn_service.c:1180 子进程主循环(阻塞读 parentToChildFd[0]
10 HandlePreforkForkMsg appspawn_service.c:1053 收到 MSG_APP_SPAWN,读共享内存,调用 AppSpawnChild
11 CleanupPreforkChild appspawn_service.c:1342 失败回退清理
12 TryLevel1PreforkUnlock appspawn_service.c:2457 Level 1 解锁优化(复用 prefork 进程,零 fork

与正常 fork 对比

维度 NormalSpawnChild Prefork
进程创建 每次请求 fork() 复用预留子进程
管道 forkCtx.fd 双管道:childToParentFd + parentToChildFd
消息传递 完整 socket 消息 mmap 共享内存 + 管道信号
进程名 应用进程名 "PreforkProcess"
生命周期 临时进程 长期阻塞读,使用一次后销毁重建
解锁挂载 Level 2fork Level 1(复用 prefork,零 fork
失败恢复 直接返回错误 自动回退到 normal fork

Mermaid 序列图

sequenceDiagram
    participant Client as 客户端
    participant Main as 主服务
    participant Prefork as Prefork 子进程
    participant Target as 目标应用

    Note over Main: 启动阶段
    Main->>Main: ProcessPreFork()
    Main->>Prefork: fork() + 双管道
    activate Prefork
    Prefork->>Prefork: PreforkChildLoop() 阻塞读

    Note over Main,Target: 孵化请求
    Client->>Main: 孵化请求 (socket)
    Main->>Main: STAGE_PARENT_PRE_FORK Hook
    Main->>Main: reservedPid > 0 命中 prefork
    Main->>Main: PreparePreforkMsg() mmap + WritePreforkMsg
    Main->>Prefork: SendPipeMsgToChild(MSG_APP_SPAWN)
    Prefork->>Prefork: HandlePreforkForkMsg() 读共享内存
    Prefork->>Prefork: TransferPreforkFdToForkCtx()
    Prefork->>Prefork: AppSpawnChild() 完整 Hook 链
    Prefork->>Target: execve()
    Prefork->>Prefork: ProcessExit()
    deactivate Prefork

    Main->>Main: ProcessPreFork() 重建下一个 prefork

注意事项

  1. 非进程池reservedPid 只保存一个 PID,无并发 prefork
  2. PID 继承prefork 子进程的 PID 最终成为目标应用进程的 PID
  3. 零拷贝:通过 mmap 共享内存避免消息二次序列化
  4. 自动回退:任何 prefork 失败都退回 NormalSpawnChild
  5. 路径:共享内存位于 /dev/shm/appspawn/prefork_{clientId},按 MAX_MSG_BLOCK_LEN4KB)对齐

KP-11: KickDog 看门狗 (G-7 补全)

Priority: P2

Summary

KickDog 是 appspawn 的内核看门狗看护机制:通过定时(默认 10s)向内核 proc 文件写入 "kick" 字符串,防止内核看门狗因 appspawn 长时间未响应而触发系统重启。

源码位置

  • 实现: standard/appspawn_kickdog.c147 行)
  • 头文件: standard/include/appspawn_kickdog.h

注册机制

// standard/appspawn_kickdog.c:145-147
MODULE_CONSTRUCTOR(void)
{
    AddPreloadHook(HOOK_PRIO_COMMON, SpawnKickDogStart);
}

通过 MODULE_CONSTRUCTOR(.so 加载时自动执行)注册到 preload hook 链,优先级 HOOK_PRIO_COMMON

启用条件

// standard/appspawn_kickdog.c:127-143
APPSPAWN_STATIC int SpawnKickDogStart(AppSpawnMgr *mgrContent) {
    APPSPAWN_CHECK_ONLY_EXPER(mgrContent->content.mode != MODE_FOR_NATIVE_SPAWN, return 0);
    APPSPAWN_CHECK((mgrContent->content.mode == MODE_FOR_APP_SPAWN) ||
                   (mgrContent->content.mode == MODE_FOR_NWEB_SPAWN) ||
                   (mgrContent->content.mode == MODE_FOR_HYBRID_SPAWN),
                   return 0, ...);
    if (CheckKernelType(&mgrContent->content.isLinux) != 0) return 0;
    DealSpawnWatchdog(&mgrContent->content, true);   // 首次打开
    CreateTimerLoopTask(&mgrContent->content);       // 启动定时器
    return 0;
}

仅在 appspawn / nwebspawn / hybridspawn 三种模式启用,nativespawn 不启用(独立 native 进程无需看护 appspawn 看门狗)。

关键调用链

步骤 函数 位置 行为
1 SpawnKickDogStart appspawn_kickdog.c:127 入口,模式与内核类型校验
2 CheckKernelType appspawn_kickdog.c:109 uname() 区分 Linux / 鸿蒙
3 DealSpawnWatchdog(content, true) appspawn_kickdog.c:73 首次写入"打开"内容
4 CreateTimerLoopTask appspawn_kickdog.c:98 创建 10s 周期定时器
5 ProcessTimerHandle(回调) appspawn_kickdog.c:86 首次:打开;之后:kick
6 DealSpawnWatchdog(content, false) appspawn_kickdog.c:73 写入"kick"内容
7 OpenAndWriteToProc appspawn_kickdog.c:20 open(O_WRONLY|O_CLOEXEC) + write + close

proc 文件与内容路由

通过 GetProcFileGetProcContent 按内核类型 / 孵化模式 / 操作类型(open/kick)三维度路由:

维度 Linux 鸿蒙 appspawn 鸿蒙 nwebspawn 鸿蒙 hybridspawn
文件 LINUX_APPSPAWN_WATCHDOG_FILE HM_APPSPAWN_WATCHDOG_FILE 同左 同左
打开内容 LINUX_APPSPAWN_WATCHDOG_ON HM_APPSPAWN_WATCHDOG_ON HM_NWEBSPAWN_WATCHDOG_ON HM_HYBRIDSPAWN_WATCHDOG_ON
kick 内容 LINUX_APPSPAWN_WATCHDOG_KICK HM_APPSPAWN_WATCHDOG_KICK HM_NWEBSPAWN_WATCHDOG_KICK HM_HYBRIDSPAWN_WATCHDOG_KICK

具体字符串常量定义在 appspawn_kickdog.h

状态机

stateDiagram-v2
    [*] --> 起钩: SpawnKickDogStart
    起钩 --> 已打开: DealSpawnWatchdog(isOpen=true)
    已打开 --> 已打开: 定时器 10s 触发\nDealSpawnWatchdog(isOpen=false)
    note right of 已打开
        content->wdgOpened = true
        后续每次定时器只 kick
    end note

content->wdgOpened 字段记录打开状态,仅在首次打开时设置;后续定时回调只执行 kick 路径。

配置项

常量 默认值 含义
APPSPAWN_WATCHDOG_KICKTIME 10s 定时器周期
LE_StartTimer 第 3 参数 INT64_MAX 循环次数(无限循环)

Key Data Structures

Structure File Purpose
AppSpawnMgr appspawn_manager.h:161 全局管理器,维护所有队列和服务状态
AppSpawningCtx appspawn_manager.h:84 孵化请求上下文
AppSpawnedProcess appspawn_manager.h:102 已孵化进程跟踪信息
AppSpawnContent appspawn_server.h:98 服务内容(函数指针表、模式、沙箱标志)
AppSpawnConnection appspawn_service.h:59 客户端连接
AppSpawnMsgReceiverCtx appspawn_service.h:50 消息接收上下文
AppSpawnForkCtx appspawn_manager.h:74 fork 上下文

Public Interface

StartSpawnService(arg, argvSize, argc, argv)

  • File: standard/appspawn_service.c
  • Purpose: 初始化 appspawn 服务,创建 socket、加载模块
  • Parameters: arg=启动参数, argvSize=参数内存大小, argc/argv=命令行
  • Returns: AppSpawnContent 指针
  • Called by: appspawn_main.c:156

CreateAppSpawnMgr(mode)

  • File: standard/appspawn_appmgr.c:42
  • Purpose: 创建全局 AppSpawnMgr 实例
  • Parameters: mode=运行模式
  • Returns: AppSpawnMgr 指针

AddSpawnedProcess(pid, processName, appIndex, isDebuggable, tokenid)

  • File: standard/appspawn_appmgr.c:155
  • Purpose: 注册新孵化的进程
  • Called by: service 层在 fork 成功后调用

Cross-Module Interactions

Interaction With Module Mechanism Direction
加载 Hook 模块 modulemgr_engine AppSpawnLoadAutoRunModules Outgoing
执行 Hook modulemgr_engine AppSpawnHookExecute Outgoing
创建服务 server_common AppSpawnCreateContent Outgoing
消息定义 client_api AppSpawnMsg 结构体 Incoming
日志/工具 util APPSPAWN_LOGI 宏 Outgoing

Module Relationship Link
modulemgr_engine depends_on module_modulemgr_engine.md
sandbox depends_on module_sandbox.md
spm depends_on module_spm.md
server_common depends_on module_server_common.md
client_api shares_with module_client_api.md
util depends_on module_util.md

另见: 本模块与上述模块存在依赖关系。具体交互细节请参考 系统架构 中的跨模块关系章节。