# 创建/终止Native子进程（C/C++）

本模块提供了两种创建Native子进程的方式，以及一种终止子进程的方式。

*   创建支持IPC通信的Native子进程：创建子进程，并在父子进程间建立IPC通道，适用于父子进程需要IPC通信的场景。对IPCKit存在依赖。
*   创建支持参数传递的Native子进程：创建子进程，并传递字符串和fd句柄参数到子进程。适用于需要传递参数到子进程的场景。
*   终止子进程：终止当前进程创建的Native子进程或ArkTS子进程。

创建的子进程会随着父进程的退出而退出，无法脱离父进程独立运行。

#### 创建支持IPC通信的Native子进程

#### 场景介绍

本章节介绍如何在主进程中创建Native子进程，并在父子进程间建立IPC通道，方便开发者在Native层进行多进程编程。

#### 接口说明

| 名称 | 描述 |
| --- | --- |
| int OH_Ability_CreateNativeChildProcess (const char *libName, OH_Ability_OnNativeChildProcessStarted onProcessStarted) | 创建子进程并加载参数中指定的动态链接库文件，进程启动结果通过参数中的回调函数onProcessStarted异步通知。回调函数运行在独立线程，如果需要访问共享资源在实现时需要注意线程同步，由于系统对于单个进程拥有的回调线程数量有限制，因此不建议在回调函数中执行高耗时操作。 |

从API version 14开始，支持2in1和Tablet设备。API version 13及之前版本，仅支持2in1设备。

从API version 15开始，单个进程最多支持启动50个Native子进程。API version 14及之前版本，单个进程只能启动1个Native子进程。

#### 开发步骤

基于已创建完成的Native应用开发工程，在此基础上介绍如何使用AbilityKit提供的C API接口，创建Native子进程，并同时在父子进程间建立IPC通道。

**动态库文件**

```
libipc_capi.so
libchild_process.so
```

**头文件**

```
#include <IPCKit/ipc_kit.h>
#include <AbilityKit/native_child_process.h>
```

1.  子进程-实现必要的导出方法。
    
    在子进程中，实现必要的两个函数**NativeChildProcess\_OnConnect**及**NativeChildProcess\_MainProc**并导出（假设代码所在的文件名为ChildProcessSample.cpp）。其中NativeChildProcess\_OnConnect方法返回的OHIPCRemoteStub对象负责与主进程进行IPC通信，具体实现方法请参考IPC通信开发指导（C/C++)，本文不再赘述。
    
    子进程启动后会先调用NativeChildProcess\_OnConnect获取IPC Stub对象，之后再调用NativeChildProcess\_MainProc移交主线程控制权，该函数返回后子进程随即退出。
    
    ```
    #include <IPCKit/ipc_kit.h>
    // ...
    #include <IPCKit/ipc_cremote_object.h>
    #include <IPCKit/ipc_cparcel.h>
    #include <IPCKit/ipc_error_code.h>
    
    class IpcCapiStubTest {
    public:
        explicit IpcCapiStubTest();
        ~IpcCapiStubTest();
        OHIPCRemoteStub *GetRemoteStub();
        static int OnRemoteRequest(uint32_t code, const OHIPCParcel *data, OHIPCParcel *reply, void *userData);
    
    private:
        OHIPCRemoteStub *stub_{nullptr};
    };
    
    IpcCapiStubTest::IpcCapiStubTest()
    {
        // 创建stub对象
        stub_ = OH_IPCRemoteStub_Create("testIpc", &IpcCapiStubTest::OnRemoteRequest, nullptr, this);
    }
    
    IpcCapiStubTest::~IpcCapiStubTest()
    {
        if (stub_ != nullptr) {
            OH_IPCRemoteStub_Destroy(stub_);
        }
    }
    
    OHIPCRemoteStub *IpcCapiStubTest::GetRemoteStub() { return stub_; }
    
    int IpcCapiStubTest::OnRemoteRequest(uint32_t code, const OHIPCParcel *data, OHIPCParcel *reply, void *userData)
    {
        return OH_IPC_SUCCESS;
    }
    
    IpcCapiStubTest g_ipcStubObj;
    
    extern "C" {
    OHIPCRemoteStub *NativeChildProcess_OnConnect()
    {
        // ipcRemoteStub指向子进程实现的ipc stub对象，用于接收来自主进程的IPC消息并响应
        // 子进程根据业务逻辑控制其生命周期
        return g_ipcStubObj.GetRemoteStub();
    }
    
    void NativeChildProcess_MainProc()
    {
        // 相当于子进程的Main函数，实现子进程的业务逻辑
        // ...
        // 函数返回后子进程随即退出
    }
    
    } // extern "C"
    ```
    
2.  子进程-编译为动态链接库。
    
    修改CMakeList.txt文件，编译为动态链接库（假设需要编译出的库文件名称为libchildprocesssample.so），并添加IPC动态库依赖。
    
    ```
    add_library(childprocesssample SHARED
        # 实现必要导出方法的源文件
        ChildProcessSample.cpp
        
        # 其它代码源文件
        # ...
    )
    
    target_link_libraries(childprocesssample PUBLIC
        # 添加依赖的IPC动态库
        libipc_capi.so
       
        # 其它所依赖的动态库
        # ...
    )
    ```
    
3.  主进程-实现子进程启动结果回调函数。
    
    ```
    #include <IPCKit/ipc_kit.h>
    #include <AbilityKit/native_child_process.h>
    // ···
    static void OnNativeChildProcessStarted(int errCode, OHIPCRemoteProxy *remoteProxy)
    {
        if (errCode != NCP_NO_ERROR) {
            // 子进程未能正常启动时的异常处理
            // ...
            return;
        }
    
        // 保存remoteProxy对象，后续基于IPC Kit提供的API同子进程间进行IPC通信
        // 耗时操作建议转移到独立线程去处理，避免长时间阻塞回调线程
        // IPC对象使用完毕后，需要调用OH_IPCRemoteProxy_Destroy方法释放
        // ···
    }
    ```
    
    回调函数传递的第二个参数OHIPCRemoteProxy对象，会与子进程实现的**NativeChildProcess\_OnConnect**方法返回的OHIPCRemoteStub对象间建立IPC通道，具体使用方法参考IPC通信开发指导（C/C++)，本文不再赘述；OHIPCRemoteProxy对象使用完毕后，需要调用OH\_IPCRemoteProxy\_Destroy函数释放。
    
4.  主进程-启动Native子进程。
    
    调用API启动Native子进程，需要注意返回值为NCP\_NO\_ERROR仅代表成功调用native子进程启动逻辑，实际的启动结果通过第二个参数中指定的回调函数异步通知。需注意**仅允许在主进程中创建子进程**。
    
    ```
    #include <IPCKit/ipc_kit.h>
    #include <AbilityKit/native_child_process.h>
    // ···
    static void OnNativeChildProcessStarted(int errCode, OHIPCRemoteProxy *remoteProxy)
    {
        if (errCode != NCP_NO_ERROR) {
            // 子进程未能正常启动时的异常处理
            // ...
            return;
        }
    
        // 保存remoteProxy对象，后续基于IPC Kit提供的API同子进程间进行IPC通信
        // 耗时操作建议转移到独立线程去处理，避免长时间阻塞回调线程
        // IPC对象使用完毕后，需要调用OH_IPCRemoteProxy_Destroy方法释放
        // ...
        // ···
    }
    
    void CreateNativeChildProcess()
    {
        // 第一个参数"libchildprocesssample.so"为实现了子进程必要导出方法的动态库文件名称
        int32_t ret = OH_Ability_CreateNativeChildProcess("libchildprocesssample.so", OnNativeChildProcessStarted);
        if (ret != NCP_NO_ERROR) {
            // 子进程未能正常启动时的异常处理
            // ...
        }
        g_result = ret;
    }
    ```
    
5.  主进程-添加编译依赖项。
    
    修改CMaklist.txt添加必要的依赖库，假设主进程所在的so名称为libmainprocesssample.so（主进程和子进程的实现也可以选择编译到同一个动态库文件）。
    
    ```
    target_link_libraries(mainprocesssample PUBLIC
        # 添加依赖的IPC及元能力动态库
        libipc_capi.so
        libchild_process.so
       
        # 其它依赖的动态库
        # ...
    )
    ```
    

#### 创建支持参数传递的Native子进程

#### 场景介绍

本章节介绍如何创建Native子进程，并传递参数到子进程。

#### 接口说明

| 名称 | 描述 |
| --- | --- |
| Ability_NativeChildProcess_ErrCode OH_Ability_StartNativeChildProcess (const char *entry, NativeChildProcess_Args args, NativeChildProcess_Options options, int32_t *pid) | 启动子进程并返回子进程pid。 |

#### 开发步骤

**动态库文件**

```
libchild_process.so
```

**头文件**

```
#include <AbilityKit/native_child_process.h>
```

1.  子进程-实现必要的导出方法。
    
    在子进程中，实现参数为NativeChildProcess\_Args的入口函数并导出（假设代码所在的文件名为ChildProcessSample.cpp）。子进程启动后会调用该入口函数，该函数返回后子进程随即退出。
    
    ```
    #include <AbilityKit/native_child_process.h>
    extern "C" {
    /**
     * 子进程的入口函数，实现子进程的业务逻辑
     * 函数名称可以自定义，在主进程调用OH_Ability_StartNativeChildProcess方法时指定，此示例中为Main
     * 函数返回后子进程退出
     */
    void Main(NativeChildProcess_Args args)
    {
        // 获取传入的entryPrams
        char *entryParams = args.entryParams;
        // 获取传入的fd列表
        NativeChildProcess_Fd *current = args.fdList.head;
        while (current != nullptr) {
            char *fdName = current->fdName;
            int32_t fd = current->fd;
            current = current->next;
            // 实现业务逻辑
        }
    }
    } // extern "C"
    ```
    
2.  子进程-编译为动态链接库。
    
    修改CMakeList.txt文件，编译为动态链接库（假设需要编译出的库文件名称为libchildprocesssample.so），并添加元能力动态库依赖。
    
    ```
    add_library(childprocesssample SHARED
        # 实现必要导出方法的源文件
        ChildProcessSample.cpp
        
        # 其它代码源文件
        # ...
    )
    
    target_link_libraries(childprocesssample PUBLIC
        # 添加依赖的元能力动态库
        libchild_process.so
    
        # 其它所依赖的动态库
        # ...
    )
    ```
    
3.  主进程-启动Native子进程。
    
    调用API启动Native子进程，返回值为NCP\_NO\_ERROR代表成功启动native子进程。
    
    ```
    #include <AbilityKit/native_child_process.h>
    #include <cstdlib>
    #include <cstring>
    #include <fcntl.h>
    // ...
    int32_t g_fdNameMaxLength = 20;
    
    void StartNativeChildProcess()
    {
        // ...
        NativeChildProcess_Args args;
        // 设置entryParams，支持传输的最大数据量为150KB
        const size_t entryParamsSize = 10;
        args.entryParams = (char *)malloc(sizeof(char) * entryParamsSize);
        if (args.entryParams != nullptr) {
            (void)strlcpy(args.entryParams, "testParam", entryParamsSize);
        }
    
        // 插入节点到链表头节点中
        args.fdList.head = (NativeChildProcess_Fd *)malloc(sizeof(NativeChildProcess_Fd));
        // fd关键字，最多不超过20个字符
        args.fdList.head->fdName = (char *)malloc(sizeof(char) * g_fdNameMaxLength);
        if (args.fdList.head->fdName != nullptr) {
            (void)strlcpy(args.fdList.head->fdName, "fd1", g_fdNameMaxLength);
        }
        // 获取fd逻辑
        int32_t fd = open("/data/storage/el2/base/haps/entry/files/test.txt", O_RDWR | O_CREAT, 0644);
        args.fdList.head->fd = fd;
        // 此处只插入一个fd记录，根据需求可以插入更多fd记录到链表中，最多不超过16个
        args.fdList.head->next = NULL;
        NativeChildProcess_Options options = {.isolationMode = NCP_ISOLATION_MODE_ISOLATED};
    
        // 第一个参数"libchildprocesssample.so:Main"为实现了子进程Main方法的动态库文件名称和入口方法名
        int32_t pid = -1;
        Ability_NativeChildProcess_ErrCode ret =
            OH_Ability_StartNativeChildProcess("libchildprocesssample.so:Main", args, options, &pid);
        if (ret != NCP_NO_ERROR) {
            // 释放NativeChildProcess_Args中的内存空间防止内存泄漏
            // 子进程未能正常启动时的异常处理
            // ...
        }
    
        // 其他逻辑
    // ...
    
        // 释放NativeChildProcess_Args中的内存空间防止内存泄漏
    }
    ```
    
4.  主进程-添加编译依赖项。
    
    修改CMaklist.txt添加必要的依赖库，假设主进程所在的so名称为libmainprocesssample.so（主进程和子进程的实现也可以选择编译到同一个动态库文件）。
    
    ```
    target_link_libraries(mainprocesssample PUBLIC
        # 添加依赖的元能力动态库
        libchild_process.so
       
        # 其它依赖的动态库
        # ...
    )
    ```
    

#### 子进程获取启动参数

#### 场景介绍

从API version 17开始，支持子进程获取启动参数。

#### 接口说明

| 名称 | 描述 |
| --- | --- |
| NativeChildProcess_Args* OH_Ability_GetCurrentChildProcessArgs() | 返回子进程自身的启动参数。 |

#### 开发步骤

**动态库文件**

```
libchild_process.so
```

**头文件**

```
#include <AbilityKit/native_child_process.h>
```

**获取启动参数**

OH\_Ability\_StartNativeChildProcess创建子进程后，子进程内的任意so和任意子线程可以通过调用OH\_Ability\_GetCurrentChildProcessArgs()获取到子进程的启动参数NativeChildProcess\_Args，便于操作相关的文件描述符。

```
#include <AbilityKit/native_child_process.h>
#include <thread>

extern "C" {
void ThreadFunc()
{
    // 获取子进程的启动参数
    NativeChildProcess_Args *args = OH_Ability_GetCurrentChildProcessArgs();
    // 获取启动参数失败时返回nullptr
    if (args == nullptr) {
        return;
    }
    // 获取启动参数中的entryPrams
    char *entryParams = args->entryParams;
    // 获取fd列表
    NativeChildProcess_Fd *current = args->fdList.head;
    while (current != nullptr) {
        char *fdName = current->fdName;
        int32_t fd = current->fd;
        current = current->next;
        // 实现业务逻辑
    }
}

/**
 * 子进程的入口函数，实现子进程的业务逻辑
 * args是子进程的启动参数
 */
void Main(NativeChildProcess_Args args)
{
    // 实现业务逻辑

    // 创建线程
    std::thread tObj(ThreadFunc);
}

} // extern "C"
```

#### 终止子进程

#### 场景介绍

从API version 22开始，支持根据传入的pid终止当前进程创建的Native子进程或ArkTS子进程。

#### 接口说明

| 名称 | 描述 |
| --- | --- |
| Ability_NativeChildProcess_ErrCode OH_Ability_KillChildProcess(int32_t pid) | 终止当前进程创建的子进程，该接口既可以用来终止Native子进程，也可以用来终止ArkTS子进程。 |

#### 开发步骤

**头文件**

```
#include <AbilityKit/native_child_process.h>
```

**终止子进程**

通过native\_child\_process和childProcessManager（非SELF\_FORK模式）中的接口创建子进程后，主进程可以调用OH\_Ability\_KillChildProcess(int32\_t pid)根据传入的pid终止相应的子进程。

```
#include <AbilityKit/native_child_process.h>
// ...
void KillChildProcess(int32_t pid)
{
    Ability_NativeChildProcess_ErrCode ret = OH_Ability_KillChildProcess(pid);
    if (ret != NCP_NO_ERROR) {
        // 子进程未成功杀死的异常处理
    }
    // ...
}
```