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的核心代码吧:
-
123456789101112131415161718192021
#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
-
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
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;
}
-
123456789101112131415161718
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.
}
-
1234567891011121314151617181920212223242526272829303132333435363738394041424344
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;
}
-
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
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;
}
-
12345
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
123456789101112131415161718192021222324252627282930313233343536373839404142434445int
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相关目录并设置即可。