文章目录
-
- [1. 概要](#1. 概要)
- [2. 进程管理接口详解](#2. 进程管理接口详解)
-
- [2.1 进程冻结与恢复的基本概念](#2.1 进程冻结与恢复的基本概念)
- [2.2 进程查找与PID获取](#2.2 进程查找与PID获取)
- [2.3 进程冻结与恢复的实现](#2.3 进程冻结与恢复的实现)
-
- [2.3.1 进程冻结](#2.3.1 进程冻结)
- [2.3.2 进程恢复](#2.3.2 进程恢复)
- [2.4 进程终止](#2.4 进程终止)
- [2.5 进程状态监控与控制](#2.5 进程状态监控与控制)
- [3. dummy_process的设计与实现](#3. dummy_process的设计与实现)
-
- [3.1 创建dummy_process脚本](#3.1 创建dummy_process脚本)
- [3.2 启动dummy_process](#3.2 启动dummy_process)
- [3.3 终止dummy_process](#3.3 终止dummy_process)
- [4. GTest单元测试代码详解](#4. GTest单元测试代码详解)
-
- [4.1 测试框架和测试结构](#4.1 测试框架和测试结构)
- [4.2 单元测试用例](#4.2 单元测试用例)
-
- [4.2.1 测试冻结功能 `FreezeProcessByPidTest`](#4.2.1 测试冻结功能
FreezeProcessByPidTest
) - [4.2.2 测试恢复功能 `TryToResumeProcessByPidTest`](#4.2.2 测试恢复功能
TryToResumeProcessByPidTest
) - [4.2.3 测试终止功能 `TerminateProcessTest`](#4.2.3 测试终止功能
TerminateProcessTest
)
- [4.2.1 测试冻结功能 `FreezeProcessByPidTest`](#4.2.1 测试冻结功能
- [5. 运行和验证](#5. 运行和验证)
1. 概要
在Linux系统中,进程的管理涉及到对进程状态的控制,特别是进程的冻结(暂停)和恢复(继续执行)功能对系统管理和调试非常重要。
本文将展示如何使用Google Test(gtest)编写针对进程冻结与恢复功能的单元测试。
- 完整代码见process_helper
- 提前阅读Linux C++编程-实现进程的冻结与恢复管理模块
2. 进程管理接口详解
2.1 进程冻结与恢复的基本概念
在Linux环境中,进程的管理涉及到对进程执行状态的控制,其中包括了进程的冻结(暂停)和恢复(继续执行)。这些操作通过发送特定的信号来实现,主要使用到 SIGSTOP
和 SIGCONT
信号。
2.2 进程查找与PID获取
为了准确控制目标进程,首先需要能够根据进程名称或者其他标识符获取到进程的PID。在Linux系统中,可以通过读取 /proc
文件系统来获取所有运行中进程的详细信息,包括进程的命令行参数和状态。
cpp
/**
* @brief Function to find the PID of a process by its name
* @param [in] processName process name
* @param [out] pid process id
* @return true - find pid by process name; false - not find pid by process name
*/
bool FindPidByProcessName(const std::string& processName, pid_t &pid);
2.3 进程冻结与恢复的实现
2.3.1 进程冻结
进程冻结通过发送 SIGSTOP
信号来实现,暂停进程的执行。实现方法包括使用 kill(pid, SIGSTOP)
函数调用,并等待进程状态确认操作成功。
cpp
/**
* @brief freeze process by pid
*
* @param [in] pid - pid find by process name.
* @return true - Freeze successfully; false - Freeze failed
*/
bool FreezeProcessByPid(pid_t pid);
2.3.2 进程恢复
一旦进程被冻结,可以通过发送 SIGCONT
信号来恢复其执行。恢复过程中可能需要多次尝试,以确保进程成功从暂停状态恢复到执行状态。
cpp
/**
* @brief try to resume a stopped process by pid
*
* @param [in] attempts - if resume failed, try another attempts times.
* @param [in] pid - pid find by process name.
* @return true - Resume successfully; false - Resume failed
*/
bool TryToResumeProcessByPid(pid_t pid, int attempts = 3);
2.4 进程终止
在某些情况下,需要强制终止一个进程以释放系统资源或确保安全性。可以使用 SIGKILL
信号立即终止进程的执行,但需要注意可能会造成数据丢失。
cpp
/**
* @brief terminate a process
*
* @param [in] processName - process name.
* @param [in] attempts - if resume failed, try another attempts times.
* @return 0 - successfully; -1 - not found process; -2 terminate failed
* -3 - Handle waitpid error
*/
int TerminateProcess(const std::string& processName, int attempts = 3);
2.5 进程状态监控与控制
在实现进程管理功能时,还需要监控进程的状态并根据需要进行控制。通过读取 /proc/[pid]/status
文件可以获取进程的详细状态信息,如是否处于停止状态。
cpp
/**
* @brief Function to check if the process is not in stopped state
*
* @param [in] pid - pid find by process name
* @return true - stopped; false - not stopped
*/
bool IsProcessStopped(pid_t pid);
确实,dummy_process是我们测试进程管理功能的一个重要组成部分。我们需要详细分析它的设计和实现,以便更好地理解整个测试框架。下面我们将详细介绍dummy_process的创建和管理过程,并分析其在单元测试中的作用。
3. dummy_process的设计与实现
为了测试进程管理功能,我们需要一个可以启动、冻结、恢复和终止的模拟进程。我们将创建一个名为dummy_process的shell脚本,该脚本将作为我们的模拟进程。通过这个脚本,我们可以在测试中验证进程管理功能的各个方面。
3.1 创建dummy_process脚本
dummy_process脚本是一个简单的无限循环脚本,它能够响应特定的信号(如SIGTERM)。我们使用C++程序来创建这个脚本,并确保它具有可执行权限。
cpp
void CreateDummyProcessScript(const std::string& scriptPath) {
std::ofstream scriptFile(scriptPath);
if (scriptFile.is_open()) {
scriptFile << "#!/bin/bash\n";
scriptFile << "\n";
scriptFile << "# Function to handle signals\n";
scriptFile << "function handle_signal() {\n";
scriptFile << " echo \"[dummy_process] Received signal: $1\"\n";
scriptFile << " exit 0\n"; // Exit gracefully on signal
scriptFile << "}\n";
scriptFile << "\n";
scriptFile << "# Trap signals\n";
scriptFile << "trap 'handle_signal SIGTERM' SIGTERM\n"; // Handle SIGTERM signal
scriptFile << "\n";
scriptFile << "# Main loop\n";
scriptFile << "while true; do\n";
scriptFile << " echo \"dummy_process is running...\"\n";
scriptFile << " sleep 1\n";
scriptFile << "done\n";
scriptFile.close();
// Add executable permission to the script
if (chmod(scriptPath.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) {
std::cerr << "Failed to set execute permission on " << scriptPath << std::endl;
exit(EXIT_FAILURE);
}
} else {
std::cerr << "Failed to create " << scriptPath << std::endl;
exit(EXIT_FAILURE);
}
}
3.2 启动dummy_process
在每个测试用例运行之前,我们需要启动dummy_process。这里我们使用fork()和execlp()函数来创建一个子进程,并在子进程中运行dummy_process脚本。
cpp
pid_t StartDummyProcess() {
pid_t pid = fork();
if (pid == -1) {
std::cerr << "Failed to fork() process." << std::endl;
return -1;
} else if (pid == 0) {
// Child process: execute dummy_process script
execlp(dummyScriptPath.c_str(), dummyScriptPath.c_str(), nullptr);
// execlp should not return if successful
std::cerr << "Failed to exec " << dummyScriptPath << std::endl;
exit(EXIT_FAILURE);
} else {
// Parent process: return child process PID
return pid;
}
}
3.3 终止dummy_process
在每个测试用例运行之后,我们需要终止dummy_process以清理环境。这里我们使用kill()函数发送SIGKILL信号,并使用waitpid()等待子进程终止。
cpp
void TerminateDummyProcess(pid_t pid) {
// Send SIGKILL to dummy_process
kill(pid, SIGKILL);
// Wait for child process to end
int status;
waitpid(pid, &status, 0);
}
4. GTest单元测试代码详解
4.1 测试框架和测试结构
测试夹具 ProcessHelperTest
管理测试过程中的环境准备和清理工作,包括创建、启动和终止dummy_process。
cpp
class ProcessHelperTest : public ::testing::Test {
protected:
pid_t dummyProcessPid;
std::string dummyScriptPath = "./dummy_process"; // Path to dummy_process shell script
void SetUp() override {
// 创建 dummy_process shell 脚本并启动
CreateDummyProcessScript(dummyScriptPath);
dummyProcessPid = StartDummyProcess();
ASSERT_NE(dummyProcessPid, -1); // 检查 dummy_process 启动成功
}
void TearDown() override {
// 结束 dummy_process
TerminateDummyProcess(dummyProcessPid);
// 清理 dummy_process shell 脚本
std::remove(dummyScriptPath.c_str());
}
};
4.2 单元测试用例
4.2.1 测试冻结功能 FreezeProcessByPidTest
cpp
TEST_F(ProcessHelperTest, FreezeProcessByPidTest) {
pid_t pid;
bool find = FindPidByProcessName("dummy_process", pid);
EXPECT_TRUE(find); // 预期找到进程的PID
bool result = FreezeProcessByPid(pid);
EXPECT_TRUE(result); // 预期冻结操作成功
ASSERT_TRUE(IsProcessRunning("dummy_process")); // 检查进程仍在运行
ASSERT_TRUE(IsProcessStopped(dummyProcessPid)); // 检查进程是否被成功冻结
}
4.2.2 测试恢复功能 TryToResumeProcessByPidTest
cpp
TEST_F(ProcessHelperTest, TryToResumeProcessByPidTest) {
pid_t pid;
bool find = FindPidByProcessName("dummy_process", pid);
EXPECT_TRUE(find); // 预期找到进程的PID
bool result = TryToResumeProcessByPid(pid, 3);
EXPECT_TRUE(result); // 预期恢复操作成功
ASSERT_TRUE(IsProcessRunning("dummy_process")); // 检查进程仍在运行
ASSERT_FALSE(IsProcessStopped(dummyProcessPid)); // 检查进程是否已恢复执行
}
4.2.3 测试终止功能 TerminateProcessTest
cpp
TEST_F(ProcessHelperTest, TerminateProcessTest) {
int result = TerminateProcess("dummy_process", 3);
EXPECT_EQ(result, 0); // 预期终止操作成功
ASSERT_FALSE(IsProcessRunning("dummy_process")); // 检查进程是否已终止
}
5. 运行和验证
测试程序的主函数调用了 RUN_ALL_TESTS()
来执行所有测试用例。在运行过程中,每个测试用例会被独立执行,并根据 EXPECT_
和 ASSERT_
宏的断言结果判断测试是否通过。
cpp
int main(int argc, char** argv) {
testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
单元测试结果:
bash
$ ./process_helper_test
[==========] Running 6 tests from 2 test suites.
[----------] Global test environment set-up.
[----------] 5 tests from ProcessHelperTest
[ RUN ] ProcessHelperTest.FreezeProcessByNameTest
dummy_process is running...
Success to send SIGSTOP to process 12780
Process 12780 status is T is in stopped state.
Process 12780 freeze success.
processName dummy_process pid 12780 freeze success.
Process 12780 status is T is in stopped state.
[ OK ] ProcessHelperTest.FreezeProcessByNameTest (9 ms)
[ RUN ] ProcessHelperTest.TryToResumeProcessByNameTest
dummy_process is running...
Process 12782 status is S is not in stopped state.
Process 12782 resumed successfully.
Process 12782 status is S is not in stopped state.
[ OK ] ProcessHelperTest.TryToResumeProcessByNameTest (25 ms)
[ RUN ] ProcessHelperTest.FreezeProcessByPidTest
dummy_process is running...
Success to send SIGSTOP to process 12784
Process 12784 status is T is in stopped state.
Process 12784 freeze success.
Process 12784 status is T is in stopped state.
[ OK ] ProcessHelperTest.FreezeProcessByPidTest (14 ms)
[ RUN ] ProcessHelperTest.TryToResumeProcessByPidTest
dummy_process is running...
Process 12786 status is S is not in stopped state.
Process 12786 resumed successfully.
Process 12786 status is S is not in stopped state.
[ OK ] ProcessHelperTest.TryToResumeProcessByPidTest (16 ms)
[ RUN ] ProcessHelperTest.TerminateProcessTest
dummy_process is running...
Process with PID: 12788 has been terminated.
[ OK ] ProcessHelperTest.TerminateProcessTest (6 ms)
[----------] 5 tests from ProcessHelperTest (74 ms total)
[----------] 1 test from IsProcessStoppedTest
[ RUN ] IsProcessStoppedTest.IsStoppedTest
[ OK ] IsProcessStoppedTest.IsStoppedTest (0 ms)
[----------] 1 test from IsProcessStoppedTest (1 ms total)
[----------] Global test environment tear-down
[==========] 6 tests from 2 test suites ran. (77 ms total)
[ PASSED ] 6 tests.