2.3.2匿名管道
//匿名管道
//father.cpp
#include <windows.h>
#include <iostream>
using namespace std;
#define BUF_SIZE 4096
#define CHILD_ADDR L"C:\\Users\\gyrt\\Documents\\Visual Studio 2008\\Projects\\unnamed_pipe2\\Debug\\child.exe"
//父进程到子进程的管道,父进程一端写入,子进程一端读出
HANDLE g_hFtoCStd_Wr,g_hFtoCStd_Rd;
//子进程到父进程的管道,子进程一端写入,父进程一端读出
HANDLE g_hCtoFStd_Wr,g_hCtoFStd_Rd;
int main(int argc,char ** argv)
{
//步骤1:创建匿名管道
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
//CreatePipe:参数是对管道读的HANDLE,参数是对管道写的HANDLE
if (!CreatePipe(&g_hFtoCStd_Rd,&g_hFtoCStd_Wr, &saAttr, 0))
{
cout << "Create Unnamed_Pipe Failed..." << endl;
return 1;
}
if (!CreatePipe(&g_hCtoFStd_Rd, &g_hCtoFStd_Wr, &saAttr, 0))
{
cout << "Create Unnamed_Pipe Failed..." << endl;
return 1;
}
//步骤2:设置父进程端的管道口不可继承
//设置句柄不可继承(子进程)
if (!SetHandleInformation(g_hFtoCStd_Wr, HANDLE_FLAG_INHERIT, 0))
{
cout << "hFtoCStdinWr is not inherited by child process." << endl;
return 1;
}
//设置句柄不可继承
if (!SetHandleInformation(g_hCtoFStd_Rd, HANDLE_FLAG_INHERIT, 0))
{
cout << "hCtoFStdoutRd is not inherited by child process." << endl;
return 1;
}
//步骤3:创建子进程
//TCHAR szCommandLINE[] = TEXT("child.exe");//TEXT("child")等价于L"child"
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.hStdError = g_hCtoFStd_Wr;
si.hStdOutput = g_hCtoFStd_Wr; //将子进程的标准输出重定向到匿名管道的写段
si.hStdInput = g_hFtoCStd_Rd; //将子进程的标准输入重定向到匿名管道的读端
si.dwFlags |= STARTF_USESTDHANDLES;
PROCESS_INFORMATION pi; //pi 获取子进程的信息
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
//CreateProcess方法1:通过路径来打开子进程
int ret = CreateProcess(CHILD_ADDR, NULL,NULL,NULL,TRUE,0,NULL,NULL,&si,&pi);
//CreateProcess方法2:子进程的exe文件必须放在父进程中,然后打开
//int ret = CreateProcess(NULL, szCommandLINE,NULL,NULL,TRUE,0,NULL,NULL,&si,&pi);
if (ret)
{
printf("child process created.\n");
//父进程不需要子进程的系统资源,因此将子进程句柄和子进程的线程句柄关闭
//当然某些程序可能会保留这些句柄来监视子进程状态
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
//步骤4:读写匿名管道
//4.1:打开my.txt文件的句柄
HANDLE h_mytxt = CreateFile(
L"my.txt",
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);
//4.2:读取文件my.txt,输出到匿名管道g_hFtoCStd_Wr,即向子进程写数据
{
char szBuffer[BUF_SIZE] = { 0 };
DWORD wLen = 0;
DWORD rLen = 0;
int rRet = 0;
int wRet = 0;
for(;;)
{
//ReadFile读文件句柄:第一次读取结束后,第二次读取若文件内容未改动,则不再读取
//ReadFile读管道句柄:第一次读取结束后,清除管道缓冲区,因此第二次读取管道若无数据,一直等待
rRet = ReadFile(h_mytxt, szBuffer, BUF_SIZE, &rLen, NULL);
if(!rRet || rLen == 0)
break;
wRet = WriteFile(g_hFtoCStd_Wr, szBuffer, strlen(szBuffer) + 1, &wLen, NULL);
if(!wRet)
break;
}
}
//4.3:输出重定向到句柄hStdout
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
//4.4:从子进程读取数据,再通过hStdout输出
{
char szBuffer[BUF_SIZE] = { 0 };
DWORD wLen = 0;
DWORD rLen = 0;
int rRet = 0;
int wRet = 0;
for(;;)
{
rRet = ReadFile(g_hCtoFStd_Rd, szBuffer, BUF_SIZE, &rLen, NULL);
if(!rRet || rLen == 0)
break;
else
cout << "Read Successed..." << endl;
wRet = WriteFile(hStdout, szBuffer, strlen(szBuffer) + 1, &wLen, NULL);
if(!wRet)
break;
else
cout <<"Write Successed..." << endl;
}
}
//步骤5:关闭句柄
CloseHandle(g_hFtoCStd_Wr);
CloseHandle(g_hFtoCStd_Rd);
CloseHandle(g_hCtoFStd_Wr);
CloseHandle(g_hCtoFStd_Rd);
CloseHandle(h_mytxt);
CloseHandle(hStdout);
return 0;
}
//匿名管道
//child.cpp
#include <windows.h>
#include <iostream>
using namespace std;
#define BUF_SIZE 4096
HANDLE h_Stdin,h_Stdout;
int main(int argc,char ** argv)
{
//步骤1:子进程输出输入重定向
h_Stdin = GetStdHandle(STD_INPUT_HANDLE);
h_Stdout = GetStdHandle(STD_OUTPUT_HANDLE);
if (h_Stdin == INVALID_HANDLE_VALUE
|| h_Stdout == INVALID_HANDLE_VALUE)
{
cout << "Get Std_Handle Failed..." << endl;
ExitProcess(1);
}
//相当于执行WriteFile(h_Stdout,...)
cout<<"*This is a message from the child process.*"<<endl;
_sleep(500);
cout<<"***This is a message from the child process.***"<<endl;
char szBuffer[BUF_SIZE] = { 0 };
DWORD wLen = 0;
DWORD rLen = 0;
int rRet = 0;
int wRet = 0;
//步骤2:读写匿名管道
for(;;)
{
rRet = ReadFile(h_Stdin, szBuffer, BUF_SIZE, &rLen, NULL);
if(!rRet || rLen == 0)
break;
wRet = WriteFile(h_Stdout, szBuffer, strlen(szBuffer) + 1, &wLen, NULL);
if(!wRet)
break;
}
//步骤3:关闭句柄
CloseHandle(h_Stdin);
CloseHandle(h_Stdout);
return 0;
}
运行结果:
child process created.
Read Successed...
*This is a message from the child process.*
Write Successed...
Read Successed...
***This is a message from the child process.***
Write Successed...
Read Successed...
hello world
hello C++
你好,中国! Write Successed...
父进程从my.txt中读取数据,然后输出到匿名管道,发送给子进程;子进程读取到数据后,再发送会父进程;父进程读取子进程中传来的数据,然后通过标准输出重定向的句柄来输出数据。
BOOL WINAPI CreatePipe(参数1,参数2,参数3,参数4):创建匿名管道,并得到读写管道。
参数1:对匿名管道读的句柄;
参数2:对匿名管道写的句柄;
参数3:指向SECURITY_ATTRIBUTES结构的指针,SECURITY_ATTRIBUTES是用来设置管道的访问权限。SECURITY_ATTRIBUTES成员bInheritHandle是设置句柄是否被子进程继承,若为ture则可以被继承;SECURITY_ATTRIBUTES成员lpSecurityDescriptor表示指向安全描述符(Security_Descriptor)的指针,若为NULL,则表示管道设置为默认安全属性。
参数4:管道缓冲区的大小,若设置0,则使用系统默认的缓冲区大小。
BOOL WINAPI SetHandleInformation(参数1,参数2,参数3):控制子进程获得对象句柄的继承权。
参数1:标识一个句柄;
参数2:若为HANDLE_FLAG_INHERIT表示创建的子进程可以获得对象句柄。
HANDLE GetStdHandle( DWORD nStdHandle ):用于从一个特定的标准设备(标准输入、标准输出或标准错误)中取得一个句柄。
BOOL CreateProcess
(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
函数作用:创建子进程。在宽字符版本中实际调用的是CreateProcessW,窄字符版本中调用的则是CreateProcessA。
参数1:lpApplicationName,指向可执行模块的字符串。这个字符串可以是可执行模块的绝对路径或相对路径。注:若设为NULL,则参数2必须要设置。
参数2:lpCommandLine,指定要运行的命令行。此命令行是子进程的可执行模块,并且需要放在父进程代码下。
参数3:lpProcessAttributes,指向SECURITY_ATTRIBUTES结构的指针,这里设为为NULL。
参数4:lpThreadAttributes,指向SECURITY_ATTRIBUTES结构的指针,这里设为为NULL。
参数5:bInheritHandles,指示子进程是否从父进程处继承句柄,这里设为TRUE。
参数6:dwCreationFlags,这里设为0。
参数7:lpEnvironment,指示子进程的环境块,若设为NULL,则使用父进程的环境。
参数8:lpCurrentDirectory,指定子进程的工作路径,若设为NULL,则子进程和父进程使用相同的驱动器和目录。
参数9:lpStartupInfo,指向StartupInfo结构的指针。StartupInfo结构体用于指定新进程的主窗口特性:设置了标准错误/输出/输入;dwFlags设置为STARTF_USESTDHANDLES表示使用hStdInput、hStdOutput和hStdError成员。
参数10:lpProcessInformation,指向PROCESS_INFORMATION结构的指针。PROCESS_INFORMATION结构体用于存放进程信息。
2.4信号量
//进程1
#include <iostream>
#include <windows.h>
#include <process.h>
using namespace std;
int g_var;
//步骤1:创建句柄
HANDLE H_Semaphore = NULL;
UINT WINAPI ThreadOne(LPVOID lpParameter)
{
printf("进程正在执行...\n");
//步骤3:释放信号量
ReleaseSemaphore(H_Semaphore, 1, NULL);
return 1;
}
int main(int argc, char **argv)
{
//步骤2:使用CreateSemaphore创建信号量
H_Semaphore = CreateSemaphore(NULL,0,1,L"pSemaphore");
if(H_Semaphore == NULL)
printf("创建失败\n");
unsigned int thread_id = 0;
HANDLE HOne =(HANDLE)_beginthreadex(NULL, 0, ThreadOne, NULL, 0, &thread_id);
WaitForSingleObject(HOne, INFINITE);
while(1)
{
}
CloseHandle(HOne);
//步骤4:使用CloseHandle关闭信号量句柄
CloseHandle(H_Semaphore);
return 0;
}
//进程2
#include <iostream>
#include <windows.h>
#include <process.h>
using namespace std;
int g_var;
//步骤1:创建句柄
HANDLE H_Semaphore2 = NULL;
UINT WINAPI ThreadOne(LPVOID lpParameter)
{
//步骤3:等待信号量释放
WaitForSingleObject(H_Semaphore2, INFINITE);
printf("获得信号量,进程开始执行\n...\n");
printf("进程执行结束!\n");
return 1;
}
int main(int argc, char **argv)
{
//步骤2:使用OpenSemaphore打开信号量
H_Semaphore2 = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE,L"pSemaphore");
if(H_Semaphore2 == NULL)
{
printf("打开信号量失败\n");
exit(1);
}
else
printf("打开信号量成功\n");
unsigned int thread_id = 0;
HANDLE HOne = (HANDLE)_beginthreadex(NULL, 0, ThreadOne, NULL, 0, &thread_id);
WaitForSingleObject(HOne, INFINITE);
CloseHandle(HOne);
//步骤4:使用CloseHandle关闭信号量句柄
CloseHandle(H_Semaphore2);
return 0;
}
进程1运行结果:
进程1正在执行...
进程2运行结果:
打开信号量成功
获得信号量,进程2开始执行
...
进程2执行结束!
必须进程1开始执行后,再运行进程2。
OpenSemaphore(参数1,参数2,参数3):打开创建的信号量。
参数1:若是SEMAPHORE_ALL_ACCESS,表示对信号量的完全访问。
参数2:信号量句柄是否被子进程继承。
参数3:信号量的名称,类型为宽字符指针。