XiZi's Blog
这里记录一个用CreatePipe的坑
1. 问题来源:在Windows上编译了一个cgit, 加了source-filter,代码高亮(使用highlight处理)之后,死活不显示代码。发现是execvp的问题,开始想好好弄,后来放弃了,反正只加个代码高亮,直接dirtry的解决方案算了。所以就参照微软的官方<Creating a Child Process with Redirected Input and Output>, 进行highlight进程的创建。测试小文件可以正常高亮,大文件就会卡住。
2.问题所在和解决方案:经过各种查找,定位到了WriteFile给子线程的Stdin pipe时发生了阻塞。查了查,发现CreatePipes时,有buffersize的设置,增大buffersize, 问题得到了解决。
贴出Dirty的核心代码吧:
-
#define _WIN32_WINNT 0x0600 #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winnt.h> #include <winternl.h> #include <stdio.h> #include <errno.h> #include <assert.h> #include <process.h> typedef struct ProcessPrepare_Info { PROCESS_INFORMATION child_pi; HANDLE IN_Rd; HANDLE IN_Wr; HANDLE OUT_Rd; HANDLE OUT_Wr; } PPInfo; #define BUFSIZE 4096 #define PIPE_BUFSIZE 1024*1024*1024
-
PPInfo prepare_highlight(const char* filename) { SECURITY_ATTRIBUTES saAttr; char file_ext[1024]; char *fext; PPInfo ppinfo; PROCESS_INFORMATION child_pi; HANDLE g_hChildStd_IN_Rd = NULL; HANDLE g_hChildStd_IN_Wr = NULL; HANDLE g_hChildStd_OUT_Rd = NULL; HANDLE g_hChildStd_OUT_Wr = NULL; fext = get_filename_ext(filename); if (!fext) strcpy(file_ext,"txt"); else strcpy(file_ext,fext); if (strstr(filename,"Makefile") == filename) strcpy(file_ext,"mk"); // printf("\n->Start of parent execution.\n"); // Set the bInheritHandle flag so pipe handles are inherited. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; // Create a pipe for the child process's STDOUT. if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, PIPE_BUFSIZE)) debug_log(TEXT("StdoutRd CreatePipe")); // Ensure the read handle to the pipe for STDOUT is not inherited. if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) debug_log(TEXT("Stdout SetHandleInformation")); // Create a pipe for the child process's STDIN. if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, PIPE_BUFSIZE)) debug_log(TEXT("Stdin CreatePipe")); // Ensure the write handle to the pipe for STDIN is not inherited. if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) debug_log(TEXT("Stdin SetHandleInformation")); // Create the child process. child_pi = CreateChildProcess(file_ext,g_hChildStd_OUT_Wr,g_hChildStd_IN_Rd); debug_log("Create highlight process finished.\n"); ppinfo.child_pi = child_pi; ppinfo.IN_Rd = g_hChildStd_IN_Rd; ppinfo.IN_Wr = g_hChildStd_IN_Wr; ppinfo.OUT_Rd = g_hChildStd_OUT_Rd; ppinfo.OUT_Wr = g_hChildStd_OUT_Wr; return ppinfo; }
-
void wait_highlight(PPInfo ppinfo) { //WaitForSingleObject(ppinfo.child_pi.hProcess, INFINITE); WaitForSingleObject(ppinfo.child_pi.hProcess, 10000); if (!CloseHandle(ppinfo.OUT_Wr)) debug_log(TEXT("Child StdOutWr CloseHandle")); // Read from pipe that is the standard output for child process. //printf("\n->Contents of child process STDOUT:\n\n", argv[1]); ReadFromPipe(ppinfo.OUT_Rd); //printf("\n->End of parent execution.\n"); // The remaining open handles are cleaned up when this process terminates. // To avoid resource leaks in a larger application, close handles explicitly. }
-
DWORD WriteBufToPipe(HANDLE wHandle,const char* buf, DWORD bufsize) // Read from a file and write its contents to the pipe for the child's STDIN. // Stop when there is no more data. { int i, dwRead; DWORD dwWritten; CHAR chBuf[BUFSIZE+1]; BOOL bSuccess = FALSE; chBuf[BUFSIZE] = 0; #if false bSuccess = WriteFile(wHandle, buf, bufsize, &dwWritten, NULL); #else i=0; for (;;) { //bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL); //if (!bSuccess || dwRead == 0) break; dwRead = (bufsize - BUFSIZE*i); if ( dwRead <= 0) break; dwRead = min(dwRead+1,BUFSIZE); memcpy(chBuf,&(buf[BUFSIZE*i]),dwRead); debug_log("Write %d bytes of chbuf %s to handle\n",dwRead,chBuf); bSuccess = WriteFile(wHandle, chBuf, dwRead, &dwWritten, NULL); debug_log("Write buf %d time,bSuccess=%d\n",i,bSuccess); if (!bSuccess) break; FlushFileBuffers(wHandle); debug_log("Flush write handle.\n"); i=i+1; } #endif debug_log("Write buf finished\n"); // Close the pipe handle so the child process stops reading. if (!CloseHandle(wHandle)) debug_log(TEXT("StdInWr CloseHandle")); return dwWritten; }
-
PROCESS_INFORMATION CreateChildProcess(const char* file_ext,HANDLE hOutput, HANDLE hInput) // Create a child process that uses the previously created pipes for STDIN and STDOUT. { TCHAR szCmdline[1024] = TEXT("D:\\Data\\WebRoot\\cgit\\highlight-3.39-x64\\highlight.exe --force --inline-css -f -I -O xhtml -S "); PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartInfo; BOOL bSuccess = FALSE; //Append file extension strcat(szCmdline,file_ext); debug_log("%s\n",szCmdline); // Set up members of the PROCESS_INFORMATION structure. ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); // Set up members of the STARTUPINFO structure. // This structure specifies the STDIN and STDOUT handles for redirection. ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.hStdError = hOutput;//g_hChildStd_OUT_Wr; siStartInfo.hStdOutput = hOutput;//g_hChildStd_OUT_Wr; siStartInfo.hStdInput = hInput;//g_hChildStd_IN_Rd; siStartInfo.dwFlags |= STARTF_USESTDHANDLES; // Create the child process. bSuccess = CreateProcess(TEXT("D:\\Data\\WebRoot\\cgit\\highlight-3.39-x64\\highlight.exe"), szCmdline, // command line NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited 0, // creation flags NULL, // use parent's environment NULL, // use parent's current directory &siStartInfo, // STARTUPINFO pointer &piProcInfo); // receives PROCESS_INFORMATION // If an error occurs, exit the application. if (!bSuccess) debug_log(TEXT("CreateProcess Failed.")); else { // Close handles to the child process and its primary thread. // Some applications might keep these handles to monitor the status // of the child process, for example. CloseHandle(piProcInfo.hProcess); CloseHandle(piProcInfo.hThread); } return piProcInfo; }
-
const char *get_filename_ext(const char *filename) { const char *dot = strrchr(filename, '.'); if(!dot || dot == filename) return NULL; return dot + 1; }
-
Test Main Code
int test_process2(const char* filename,const char* buf, DWORD bufsize) { PPInfo cIn; int i, dwRead; DWORD dwWritten; CHAR chBuf[BUFSIZE+1]; BOOL bSuccess = FALSE; chBuf[BUFSIZE] = 0; debug_log("test_process2 start.\n"); cIn=prepare_highlight(filename); //WriteToPipe(cIn.IN_Wr,g_hInputFile); i=0; for (;;) { //bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL); //if (!bSuccess || dwRead == 0) break; dwRead = (bufsize - BUFSIZE*i); if ( dwRead <= 0) break; dwRead = min(dwRead+1,BUFSIZE); memcpy(chBuf,&(buf[BUFSIZE*i]),dwRead); debug_log("Write %d bytes of chbuf %s to handle\n",dwRead,chBuf); bSuccess = WriteFile(cIn.IN_Wr, chBuf, dwRead, &dwWritten, NULL); debug_log("Write buf %d time\n",i); if (!bSuccess) break; i=i+1; } debug_log("Write buf finished\n"); // Close the pipe handle so the child process stops reading. if (!CloseHandle(cIn.IN_Wr)) debug_log(TEXT("StdInWr CloseHandle")); wait_highlight(cIn); debug_log("test_process2 finished.\n"); return 0; }
PS:
这里在顺便提一下,如何在Windows下编译cgit, 下载并安装git-sdk,然后在打开的shell里面make即可。之后拷贝到Apache相关目录并设置即可。