Creating/Terminating Native Child Processes (C/C++)

This module provides two methods for creating a native child process and one method for terminating a child process.

NOTE

The created child process will exit when the parent process exits and cannot run independently.

Creating a Native Child Process That Supports IPC

When to Use

This topic describes how to create a native child process in the main process and establish an IPC channel between the main process and child process. It makes multi-process programming at the native layer easier.

Available APIs

Name Description
int OH_Ability_CreateNativeChildProcess (const char *libName, OH_Ability_OnNativeChildProcessStarted onProcessStarted) Creates a child process, loads a specified dynamic link library, and returns the startup result asynchronously through the onProcessStarted callback function in the parameter. The callback function runs on a separate thread. If access to shared resources is required, thread synchronization must be implemented. Due to system limitations on the number of callback threads for a single process, you are advised not to perform highly time-consuming operations within the callback function.

NOTE

Starting from API version 14, 2-in-1 devices and tablets are supported. In API version 13 and earlier versions, only 2-in-1 devices are supported. Starting from API version 15, a single process supports a maximum of 50 native child processes. In API version 14 and earlier versions, a single process supports only one native child process.

How to Develop

This section describes how to use the C APIs provided by Ability Kit to create a native child process and establish an IPC channel between the main process and child process based on an existing native application development project.

Linking Dynamic Libraries

libipc_capi.so
libchild_process.so

Including Header Files

#include <IPCKit/ipc_kit.h>
#include <AbilityKit/native_child_process.h>
  1. (Child process) Implement necessary export functions.

    In the child process, implement and export the functions NativeChildProcess_OnConnect and NativeChildProcess_MainProc. (It is assumed that the code file is named ChildProcessSample.cpp.) The OHIPCRemoteStub object returned by NativeChildProcess_OnConnect is responsible for IPC with the main process. For details, see the IPC development guide (C/C++).

    After the child process is started, NativeChildProcess_OnConnect is invoked to obtain an IPC stub object, and then NativeChildProcess_MainProc is called to transfer the control right of the main thread. After the second function is returned, the child process exits.

    #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()
    {
        // Create a stub object.
        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 points to the IPC stub object implemented by the child process. The object is used to receive and respond to IPC messages from the main process.
        // The child process controls its lifecycle according to the service logic.
        return g_ipcStubObj.GetRemoteStub();
    }
    
    void NativeChildProcessMainProc()
    {
        // Equivalent to the Main function of the child process. It implements the service logic of the child process.
        // ...
        // After the function is returned, the child process exits.
    }
    
    } // extern "C"
    
  2. (Child process) Compile a dynamic link library.

    Modify the CMakeList.txt file, compile the file into a dynamic link library (named libchildprocesssample.so in this example), and add the dependency of the dynamic link library of IPC Kit.

    add_library(childprocesssample SHARED
        # Source code file that implements the necessary export functions
        ChildProcessSample.cpp
        
        # Other source code files
        # ...
    )
    
    target_link_libraries(childprocesssample PUBLIC
        # Add the dependency of the dynamic link library of IPC Kit.
        libipc_capi.so
        
        # Dependencies of other dynamic link libraries
        # ...
    )
    
  3. (Main process) Implement the child process startup result callback.

    #include <IPCKit/ipc_kit.h>
    #include <AbilityKit/native_child_process.h>
    // ···
    static void OnNativeChildProcessStarted(int errCode, OHIPCRemoteProxy *remoteProxy)
    {
        if (errCode != NCP_NO_ERROR) {
            // Exception handling when the child process is not started normally
            // ...
            return;
        }
    
        // Save the remoteProxy object for IPC with the child process based on the APIs provided by IPC Kit.
        // You are advised to transfer time-consuming operations to an independent thread to avoid blocking the callback thread for a long time.
        // When the IPC object is no longer needed, call OH_IPCRemoteProxy_Destroy to release it.
        // ···
    }
    

    The second parameter OHIPCRemoteProxy in the callback function is used to establish an IPC channel with the OHIPCRemoteStub object returned by the NativeChildProcess_OnConnect method implemented by the child process. For details, see the IPC development guide (C/C++). When the OHIPCRemoteProxy object is no longer needed, call OH_IPCRemoteProxy_Destroy to release it.

  4. (Main process) Start the native child process.

    Call the API to start the native child process. Note that the return value NCP_NO_ERROR only indicates that the native child process startup logic is successfully called. The actual startup result is asynchronously notified through the callback function specified in the second parameter. Note that a child process can be created only on the main process.

    #include <IPCKit/ipc_kit.h>
    #include <AbilityKit/native_child_process.h>
    // ···
    static void OnNativeChildProcessStarted(int errCode, OHIPCRemoteProxy *remoteProxy)
    {
        if (errCode != NCP_NO_ERROR) {
            // Exception handling when the child process is not started normally
            // ...
            return;
        }
    
        // Save the remoteProxy object for IPC with the child process based on the APIs provided by IPC Kit.
        // You are advised to transfer time-consuming operations to an independent thread to avoid blocking the callback thread for a long time.
        // When the IPC object is no longer needed, call OH_IPCRemoteProxy_Destroy to release it.
        // ...
        // ···
    }
    
    void CreateNativeChildProcess()
    {
        // The first parameter libchildprocesssample.so indicates the name of the dynamic link library that implements the necessary export functions of the child process.
        int32_t ret = OH_Ability_CreateNativeChildProcess("libchildprocesssample.so", OnNativeChildProcessStarted);
        if (ret != NCP_NO_ERROR) {
            // Exception handling when the child process is not started normally
            // ...
        }
        g_result = ret;
    }
    
  5. (Main process) Add build dependencies.

    Modify the CMaklist.txt file to add the dependencies. The following assumes that the main process is implemented in the library file named libmainprocesssample.so. (The implementation of the main process and child processes can be compiled to the same dynamic link library file.)

    target_link_libraries(mainprocesssample PUBLIC
        # Add dependencies of the dynamic link library of IPC Kit and Ability Kit.
        libipc_capi.so
        libchild_process.so
        
        # Dependencies of other dynamic link libraries
        # ...
    )
    

Creating a Native Child Process That Supports Pass-by-Parameter

When to Use

This section describes how to create a native child process and pass parameters to it.

Available APIs

Name Description
Ability_NativeChildProcess_ErrCode OH_Ability_StartNativeChildProcess (const char *entry, NativeChildProcess_Args args, NativeChildProcess_Options options, int32_t *pid) Starts a child process and returns its PID.

How to Develop

Linking Dynamic Libraries

libchild_process.so

Including Header Files

#include <AbilityKit/native_child_process.h>
  1. (Child process) Implement necessary export functions.

    In the child process, implement and export the entry function NativeChildProcess_Args. (It is assumed that the code file is named ChildProcessSample.cpp.) After the child process is started, the entry function is invoked. After the second function is returned, the child process exits.

    #include <AbilityKit/native_child_process.h>
    extern "C" {
    /**
     * Entry function of a child process, which implements the service logic of the child process.
     * The function name can be customized and is specified when the main process calls the OH_Ability_StartNativeChildProcess method. In this example, the function name is Main.
     * After the function is returned, the child process exits.
     */
    void Main(NativeChildProcess_Args args)
    {
        // Obtain the input entryPrams.
        char *entryParams = args.entryParams;
        // Obtain the input FD list.
        NativeChildProcess_Fd *current = args.fdList.head;
        while (current != nullptr) {
            char *fdName = current->fdName;
            int32_t fd = current->fd;
            current = current->next;
            // Implement the service logic.
        }
    }
    } // extern "C"
    
  2. (Child process) Compile a dynamic link library.

    Modify the CMakeList.txt file, compile the file into a dynamic link library (named libchildprocesssample.so in this example), and add the dependency of the dynamic link library of Ability Kit.

    add_library(childprocesssample SHARED
        # Source code file that implements the necessary export functions
        ChildProcessSample.cpp
        
        # Other source code files
        # ...
    )
    
    target_link_libraries(childprocesssample PUBLIC
        # Add the dependency of the dynamic link library of Ability Kit.
        libchild_process.so
    
        # Dependencies of other dynamic link libraries
        # ...
    )
    
  3. (Main process) Start the native child process.

    Call the API to start the native child process. The return value NCP_NO_ERROR indicates that the native child process is successfully started.

    #include <AbilityKit/native_child_process.h>
    #include <cstdlib>
    #include <cstring>
    #include <fcntl.h>
    // ...
    int32_t g_fdNameMaxLength = 20;
    
    void StartNativeChildProcess()
    {
        // ...
        NativeChildProcess_Args args;
        // Set entryParams. The maximum amount of data that can be passed is 150 KB.
        const size_t entryParamsSize = 10;
        args.entryParams = (char *)malloc(sizeof(char) * entryParamsSize);
        if (args.entryParams != nullptr) {
            (void)strlcpy(args.entryParams, "testParam", entryParamsSize);
        }
    
        // Insert a node to the head node of the linked list.
        args.fdList.head = (NativeChildProcess_Fd *)malloc(sizeof(NativeChildProcess_Fd));
        // FD keyword, which contains a maximum of 20 characters.
        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);
        }
        // Obtain the FD logic.
        int32_t fd = open("/data/storage/el2/base/haps/entry/files/test.txt", O_RDWR | O_CREAT, 0644);
        args.fdList.head->fd = fd;
        // Insert only one FD record. You can insert a maximum of 16 FD records to the linked list as required.
        args.fdList.head->next = NULL;
        NativeChildProcess_Options options = {.isolationMode = NCP_ISOLATION_MODE_ISOLATED};
    
        // The first parameter libchildprocesssample.so:Main indicates the name of the dynamic link library file that implements the Main method of the child process and the name of the entry method.
        int32_t pid = -1;
        Ability_NativeChildProcess_ErrCode ret =
            OH_Ability_StartNativeChildProcess("libchildprocesssample.so:Main", args, options, &pid);
        if (ret != NCP_NO_ERROR) {
            // Release the memory space in NativeChildProcess_Args to prevent memory leakage.
            // Exception handling when the child process is not started normally
            // ...
        }
    
        // Other logic
    // ...
    
        // Release the memory space in NativeChildProcess_Args to prevent memory leakage.
    }
    
  4. (Main process) Add build dependencies.

    Modify the CMaklist.txt file to add the dependencies. The following assumes that the main process is implemented in the library file named libmainprocesssample.so. (The implementation of the main process and child processes can be compiled to the same dynamic link library file.)

    target_link_libraries(mainprocesssample PUBLIC
        # Add the dependency of the dynamic link library of Ability Kit.
        libchild_process.so
        
        # Dependencies of other dynamic link libraries
        # ...
    )
    

Child Threads Obtaining Startup Parameters

When to Use

Starting from API version 17, child processes can obtain startup parameters.

Available APIs

Name Description
NativeChildProcess_Args* OH_Ability_GetCurrentChildProcessArgs() Returns startup parameters of a child process.

How to Develop

Linking Dynamic Libraries

libchild_process.so

Including Header Files

#include <AbilityKit/native_child_process.h>

Obtaining Startup Parameters

After a child process is created through OH_Ability_StartNativeChildProcess, it can call OH_Ability_GetCurrentChildProcessArgs() to obtain the startup parameters NativeChildProcess_Args from any .so file or child thread, facilitating operations on related file descriptors.

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

extern "C" {
void ThreadFunc()
{
    // Obtain startup parameters of the child process.
    NativeChildProcess_Args *args = OH_Ability_GetCurrentChildProcessArgs();
    // If the startup parameters fail to be obtained, nullptr is returned.
    if (args == nullptr) {
        return;
    }
    // Obtain the value of entryPrams in the startup parameters.
    char *entryParams = args->entryParams;
    // Obtain the FD list.
    NativeChildProcess_Fd *current = args->fdList.head;
    while (current != nullptr) {
        char *fdName = current->fdName;
        int32_t fd = current->fd;
        current = current->next;
        // Implement the service logic.
    }
}

/**
 * Entry function of a child process, which implements the service logic of the child process.
 * args is the startup parameters of the child process.
 */
void Main(NativeChildProcess_Args args)
{
    // Implement the service logic.

    // Create a thread.
    std::thread tObj(ThreadFunc);
}

} // extern "C"

Terminating a Child Process

When to Use

Starting from API version 22, you can terminate a native child process or an ArkTS child process created by the current process based on the input PID.

Available APIs

Name Description
Ability_NativeChildProcess_ErrCode OH_Ability_KillChildProcess(int32_t pid) Terminates a child process created by the current process. This API can be used to terminate a native child process or an ArkTS child process.

How to Develop

Including Header Files

#include <AbilityKit/native_child_process.h>

Terminating a Child Process

After a child process is created using native_child_process and childProcessManager (non-SELF_FORK mode), the main process can call OH_Ability_KillChildProcess(int32_t pid) to terminate the child process based on the input 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) {
        // Exception handling when the child process fails to be terminated
    }
    // ...
}