reproc(重定向进程)是一个跨平台 C/C++ 库,可简化外部程序的启动、停止和通信。主要用例是直接从 C 或 C++ 代码执行命令行应用程序并检索其输出。
reproc 由两个库组成:reproc 和 reproc++。 reproc 是一个 C99 库,包含使用外部程序的实际代码。 reproc++ 依赖于 reproc 并将其 API 调整为惯用的 C++11 API。它还添加了一些额外功能,可以简化 C++ 外部程序的使用。
#include <reproc/run.h>
int main ( void )
{
const char * args [] = { "echo" , "Hello, world!" , NULL };
return reproc_run ( args , ( reproc_options ) { 0 });
}
如果您在阅读自述文件和文档后有任何疑问,您可以提出问题或直接在 reproc gitter 频道中提问。
注意:构建 reproc 需要 CMake 3.12 或更高版本。
有多种方法可以将 reproc 引入到您的项目中。一种方法是使用 CMake 将 reproc 构建为项目的一部分。为此,我们首先必须将 reproc 源代码放入项目中。可以使用以下任一选项来完成此操作:
FetchContent
API 下载 reproc。有关示例,请参阅 https://cliutils.gitlab.io/modern-cmake/chapters/projects/fetch.html。在项目中包含 reproc 的源代码后,可以从根 CMakeLists.txt 文件构建它,如下所示:
add_subdirectory (< path -to-reproc>) # For example: add_subdirectory(external/reproc)
可以在调用add_subdirectory
之前指定 CMake 选项:
set (REPROC++ ON )
add_subdirectory (< path -to-reproc>)
注意:如果该选项已在之前的 CMake 运行中缓存,则必须清除 CMake 的缓存才能应用新的默认值。
有关配置 reproc 构建的更多信息,请参阅 CMake 选项。
您还可以依赖已安装的 reproc 版本。您可以自己构建和安装 reproc,也可以通过包管理器安装 reproc。 reproc 可在以下软件包存储库中找到:
如果无法使用包管理器,您可以从源代码构建并安装 reproc (CMake 3.13+):
cmake -B build
cmake --build build
cmake --install build
启用REPROC_TEST
选项并构建test
目标以运行测试(CMake 3.13+):
cmake -B build -DREPROC_TEST=ON
cmake --build build
cmake --build build --target test
安装 reproc 后,您的构建系统将必须找到它。 reproc 提供 CMake 配置文件和 pkg-config 文件,以简化分别使用 CMake 和 pkg-config 查找 reproc 安装的过程。请注意,reproc 和 reproc++ 是单独的库,因此也有单独的配置文件。请务必搜索您要使用的那个。
要使用 CMake 查找已安装的 reproc 版本:
find_package (reproc) # Find reproc.
find_package (reproc++) # Find reproc++.
将 reproc 构建为项目的一部分或找到已安装的 reproc 版本后,您可以从 CMakeLists.txt 文件中链接到它,如下所示:
target_link_libraries (myapp reproc) # Link against reproc.
target_link_libraries (myapp reproc++) # Link against reproc++.
从 Meson 0.53.2 开始,reproc 可以作为 CMake 子项目包含在 Meson 构建脚本中。请参阅 https://mesonbuild.com/CMake-module.html 了解更多信息。
默认情况下,reproc 依赖于 POSIX 系统上的 pthreads ( -pthread
) 和 Windows 系统上的 Winsock 2.2 ( -lws2_32
) 依赖项。 CMake 和 pkg-config 自动处理这些依赖项。
reproc 的构建可以使用以下 CMake 选项进行配置:
REPROC++
:构建 reproc++ (默认值: ${REPROC_DEVELOP}
)
REPROC_TEST
:构建测试(默认值: ${REPROC_DEVELOP}
)
通过运行测试二进制文件来运行测试,该test
可以在构建 reproc 后在构建目录中找到。
REPROC_EXAMPLES
:构建示例(默认值: ${REPROC_DEVELOP}
)
生成 reproc 后,生成的二进制文件将位于生成目录中每个项目子目录的示例文件夹中。
REPROC_OBJECT_LIBRARIES
:构建 CMake 对象库(默认值: ${REPROC_DEVELOP}
)
这对于直接将 reproc 包含在另一个库中非常有用。当将 reproc 构建为静态或共享库时,它必须与使用库一起安装,这使得分发使用库变得更加困难。使用对象库时,reproc 的对象文件直接包含在使用库中,无需额外安装。
注意:reproc 的对象库只能从 CMake 3.14 开始正确链接。
注意:此选项会覆盖BUILD_SHARED_LIBS
。
REPROC_INSTALL
:生成安装规则(默认值: ON
,除非启用REPROC_OBJECT_LIBRARIES
)
REPROC_INSTALL_CMAKECONFIGDIR
:CMake 配置文件安装目录(默认: ${CMAKE_INSTALL_LIBDIR}/cmake
)
REPROC_INSTALL_PKGCONFIG
:安装 pkg-config 文件(默认值: ON
)
REPROC_INSTALL_PKGCONFIGDIR
:pkg-config 文件安装目录(默认: ${CMAKE_INSTALL_LIBDIR}/pkgconfig
)
REPROC_MULTITHREADED
:使用pthread_sigmask
并链接到系统的线程库(默认值: ON
)
REPROC_DEVELOP
:配置开发选项默认值(默认值: OFF
除非设置了REPROC_DEVELOP
环境变量)REPROC_SANITIZERS
:使用消毒剂构建(默认值: ${REPROC_DEVELOP}
)REPROC_TIDY
:构建时运行 clang-tidy (默认值: ${REPROC_DEVELOP}
)REPROC_WARNINGS
:启用编译器警告(默认值: ${REPROC_DEVELOP}
)REPROC_WARNINGS_AS_ERRORS
:添加 -Werror 或等效于编译标志和 clang-tidy (默认值: OFF
) 每个函数和类都在其头文件中进行了详细记录。示例可以在reproc 和reproc++ 的examples 子目录中找到。
失败时,reproc API 中的大多数函数都会返回负errno
(POSIX) 或GetLastError
(Windows) 样式的错误代码。对于可操作的错误,reproc 提供常量( REPROC_ETIMEDOUT
、 REPROC_EPIPE
等),可用于匹配错误,而无需编写特定于平台的代码。要获取错误的字符串表示形式,请将其传递给reproc_strerror
。
reproc++ 的 API 与 C++ 标准库错误代码机制( std::error_code
和std::error_condition
)集成。 reproc++ API 中的大多数方法都会返回包含实际发生的系统错误的std::error_code
值。您可以使用std::errc
枚举中的值来测试这些错误代码。
有关如何在使用 reproc 时处理错误的更多信息,请参阅示例。
笔记:
reproc 和 reproc++ API 都采用options
参数,该参数可以定义一个或多个stop
操作,例如terminate
或kill
。因此,如果使用 POSIX 上的信号终止或杀死子进程,错误代码将不会反映错误。
由下游项目来解释反映意外行为的状态代码以及错误代码(请参阅此示例)。
不要同时从多个线程对同一个子进程调用相同的操作。例如:从不同线程读取和写入子进程没问题,但同时从两个不同线程等待同一个子进程会导致问题。
(POSIX) 强烈建议不要对 reproc 启动的进程的 pid 调用waitpid
。
reproc 使用waitpid
等待进程退出。不幸的是, waitpid
不能在同一进程上调用两次。这意味着如果在 reproc 之外之前已经在子进程上调用了waitpid
,则reproc_wait
将无法正常工作。
强烈建议使用reproc_wait
或reproc_stop
确保每个子进程实际退出。
在 POSIX 上,已退出的子进程是僵尸进程,直到父进程使用waitpid
等待它。僵尸进程会占用资源,可以被视为资源泄漏,因此确保所有进程及时正确退出非常重要。
强烈建议尝试通过等待子进程退出或在诉诸reproc_kill
之前调用reproc_terminate
来终止子进程。
使用reproc_kill
时,子进程没有机会执行清理,这可能会导致资源泄漏。这些泄漏中最主要的是子进程将无法停止自己的子进程。始终尝试通过在调用reproc_kill
之前调用reproc_terminate
让子进程正常退出。 reproc_stop
是一个方便的辅助函数,可用于连续执行多个停止操作,中间有超时。
(POSIX) 强烈建议忽略父进程中的SIGPIPE
信号。
在 POSIX 上,默认情况下,写入子进程的关闭 stdin 管道将使用SIGPIPE
信号终止父进程。为了避免这种情况,必须在父进程中忽略SIGPIPE
信号。如果忽略SIGPIPE
信号,则在写入关闭的标准输入管道时, reproc_write
将按预期返回REPROC_EPIPE
。
虽然reproc_terminate
允许子进程执行清理,但由子进程自行完成正确的清理工作。 reproc 只向子进程发送终止信号。子进程本身负责清理自己的子进程和其他资源。
(Windows) reproc_kill
不能保证在 Windows 上立即终止子进程。有关详细信息,请阅读 Windows TerminateProcess
函数文档中的“备注”部分,reproc 使用该函数来终止 Windows 上的子进程。
通过 reproc 生成的子进程继承一个额外的文件句柄,用于等待子进程退出。如果子进程手动关闭这个文件句柄,reproc会错误地检测到子进程已经退出。如果该句柄进一步被比子进程寿命更长的其他进程继承,reproc 将检测到子进程仍在运行,即使它已经退出。如果向这个句柄写入数据,reproc也会错误地检测到子进程已经退出。
(Windows) 无法检测子进程在退出之前是否关闭其 stdout 或 stderr 流。仅当子进程退出时,父进程才会收到子进程输出流已关闭的通知。
(Windows) reproc 假定 Windows 创建可用作文件系统对象的套接字。更具体地说, WSASocket
返回的默认套接字应该设置XP1_IFS_HANDLES
标志。如果 Windows 计算机上安装了外部 LSP 提供程序,则情况可能并非如此。如果是这种情况,我们建议删除提供额外服务提供商的软件,因为它们已被弃用,不应再使用(请参阅 https://docs.microsoft.com/en-us/windows/win32/winsock/对分层服务提供商和应用程序进行分类)。