OVH Cloud OVH Cloud

Pipe qui change ?

4 réponses
Avatar
gg
Bonjour,

J'ai un problème bizarre : J'ai un programme qui lance 2 process, à partir
de 2 threads, ces 2 process lisent sur le pipe des infos, les traitent et
renvoient des résultats. Tout marche bien, mais si je tue le premier
process créé (pour simuler un plantage par exemple), le premier thread qui
lit sur le pipe du process mort se met à lire sur le pipe du second, il ne
détecte pas que le pipe est fermé. Quand je tue le deuxième process, il
détecte bien que le pipe est fermé.

J'utilise les fonctions DuplicateHandle et CreateProcess, d'après ce que
j'ai pu voir dans les docs, sur ce même forum. Mais je n'arrive pas à
trouver pourquoi j'ai ce comportement, est-ce j'oublie quelque chose, un
paramètre, ou autre chose ?

Je vous met le code source, nettoyé, de ce que je fais, si vous voyez un
truc qui cloche, je suis preneur.

HANDLE ReadPipeChild;
HANDLE ReadPipeFather;
HANDLE WritePipeChild;
HANDLE WritePipeFather;

SECURITY_ATTRIBUTES sa;

memset( &sa, 0x00, sizeof(SECURITY_ATTRIBUTES) );

sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;

if ( !CreatePipe(&ReadPipeChild, &WritePipeFather, &sa, 0) )
return Err_CreatePipe;

if (DuplicateHandle( GetCurrentProcess(), // duplicate from this
process
WritePipeFather, // this handle
GetCurrentProcess(), // into this process
&_WritePipeFather, // as this handle
0, // no access flags
(subsumed by DUPLICATE_SAME_ACCESS)
FALSE, // not inheritable
DUPLICATE_SAME_ACCESS) == 0) // same handle access
{
return Err_CreatePipe;
}

int iRes = CloseHandle(WritePipeFather);
WritePipeFather = 0;

if ( !CreatePipe(&ReadPipeFather, &WritePipeChild, &sa, 0) )
return Err_CreatePipe;

if (DuplicateHandle( GetCurrentProcess(), // duplicate from this
process
ReadPipeFather, // this handle
GetCurrentProcess(), // into this process
&_ReadPipeFather, // as this handle
0, // no access flags
(subsumed by DUPLICATE_SAME_ACC
FALSE, // not inheritable
DUPLICATE_SAME_ACCESS) == 0)
{
return Err_CreatePipe;
}

iRes = CloseHandle(ReadPipeFather);
ReadPipeFather = 0;

_ReadPipeChild = ReadPipeChild;
_WritePipeChild = WritePipeChild;


// Now we create the child
boost::format formatCmdLine("\"%s\" %x %x %s");
formatCmdLine % _ExePath.native_file_string().c_str() % _ReadPipeChild
% _WritePipeChild % _strCmdLine;

string CmdLine = formatCmdLine.str();

ProcessCreate process(CmdLine, _exec_dir.native_directory_string(),
_priority, _bDebugMode);

// Create the child process.
if ( !process.ok() )
{
string mes = boost::io::str(boost::format("return code
=%d")%GetLastError());
_pi.hProcess = 0;
_pi.hThread = 0;
return Err_CreateProcess;
}
else
{
CloseHandle(_ReadPipeChild); _ReadPipeChild = 0;
CloseHandle(_WritePipeChild); _WritePipeChild = 0;
}


La fonction ProcessCreate fait ceci :

// Set up members of the PROCESS_INFORMATION structure.
::ZeroMemory(&m_pi, sizeof(PROCESS_INFORMATION) );

// Set up members of the STARTUPINFO structure.
STARTUPINFO si;
::ZeroMemory( &si, sizeof(STARTUPINFO) );
si.cb = sizeof(STARTUPINFO);

// Create the child process.
if ( (res=::CreateProcess(NULL,
(char *)CmdLine.c_str(), //
command line to child process
NULL, //
process security attributes
NULL, //
primary thread security attributes
TRUE, //
handles are inherited
DETACHED_PROCESS | iDefaultPriority, //
creation flags
NULL, // use
parent's environment
pCurDir, // use
parent's current directory
&si, //
STARTUPINFO pointer
&m_pi)) == FALSE) //
receives PROCESS_INFORMATION
{
m_iErrorCode = GetLastError();

LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, m_iErrorCode, MAKELANGID(LANG_NEUTRAL,
SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf, 0, NULL );

m_strError = (char*)lpMsgBuf;

LocalFree( lpMsgBuf );

A2iATrace("ProcessCreate", ("Error : "+m_strError).c_str() );
m_bSuccess = false;
}
else
{
m_strError = "OK";
m_bSuccess = true;
}

--
Gérald


--
Posté via http://www.webatou.net/
Usenet dans votre navigateur !
Complaints-To: abuse@webatou.net

4 réponses

Avatar
Frédéric Lachasse
"GeraldGg" wrote in message
news:
Bonjour,

J'ai un problème bizarre : J'ai un programme qui lance 2 process, à partir
de 2 threads, ces 2 process lisent sur le pipe des infos, les traitent et
renvoient des résultats. Tout marche bien, mais si je tue le premier
process créé (pour simuler un plantage par exemple), le premier thread qui
lit sur le pipe du process mort se met à lire sur le pipe du second, il ne
détecte pas que le pipe est fermé. Quand je tue le deuxième process, il
détecte bien que le pipe est fermé.

J'utilise les fonctions DuplicateHandle et CreateProcess, d'après ce que
j'ai pu voir dans les docs, sur ce même forum. Mais je n'arrive pas à
trouver pourquoi j'ai ce comportement, est-ce j'oublie quelque chose, un
paramètre, ou autre chose ?

Je vous met le code source, nettoyé, de ce que je fais, si vous voyez un
truc qui cloche, je suis preneur.

HANDLE ReadPipeChild;
HANDLE ReadPipeFather;
HANDLE WritePipeChild;
HANDLE WritePipeFather;

SECURITY_ATTRIBUTES sa;

memset( &sa, 0x00, sizeof(SECURITY_ATTRIBUTES) );

sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
if ( !CreatePipe(&ReadPipeChild, &WritePipeFather, &sa, 0) )
return Err_CreatePipe;

if (DuplicateHandle( GetCurrentProcess(), // duplicate from this
process WritePipeFather, // this handle
GetCurrentProcess(), // into this process
&_WritePipeFather, // as this handle
0, // no access flags
(subsumed by DUPLICATE_SAME_ACCESS)
FALSE, // not inheritable
DUPLICATE_SAME_ACCESS) == 0) // same handle access
{
return Err_CreatePipe;
}

int iRes = CloseHandle(WritePipeFather);
WritePipeFather = 0;

if ( !CreatePipe(&ReadPipeFather, &WritePipeChild, &sa, 0) )
return Err_CreatePipe;

if (DuplicateHandle( GetCurrentProcess(), // duplicate from this
process
ReadPipeFather, // this handle
GetCurrentProcess(), // into this process
&_ReadPipeFather, // as this handle
0, // no access flags
(subsumed by DUPLICATE_SAME_ACC
FALSE, // not inheritable
DUPLICATE_SAME_ACCESS) == 0)
{
return Err_CreatePipe;
}

iRes = CloseHandle(ReadPipeFather);
ReadPipeFather = 0;

_ReadPipeChild = ReadPipeChild;
_WritePipeChild = WritePipeChild;
// Now we create the child
boost::format formatCmdLine(""%s" %x %x %s");
formatCmdLine % _ExePath.native_file_string().c_str() % _ReadPipeChild %
_WritePipeChild % _strCmdLine;

string CmdLine = formatCmdLine.str();

ProcessCreate process(CmdLine, _exec_dir.native_directory_string(),
_priority, _bDebugMode);

// Create the child process. if ( !process.ok() )
{
string mes = boost::io::str(boost::format("return code
=%d")%GetLastError());
_pi.hProcess = 0;
_pi.hThread = 0;
return Err_CreateProcess;
}
else
{
CloseHandle(_ReadPipeChild); _ReadPipeChild = 0;
CloseHandle(_WritePipeChild); _WritePipeChild = 0;
}


La fonction ProcessCreate fait ceci :
// Set up members of the PROCESS_INFORMATION structure.
::ZeroMemory(&m_pi, sizeof(PROCESS_INFORMATION) );

// Set up members of the STARTUPINFO structure. STARTUPINFO si;
::ZeroMemory( &si, sizeof(STARTUPINFO) );
si.cb = sizeof(STARTUPINFO);
// Create the child process. if ( (res=::CreateProcess(NULL, (char
*)CmdLine.c_str(), // command line to child process
NULL, //
process security attributes NULL, //
primary thread security attributes TRUE, //
handles are inherited DETACHED_PROCESS | iDefaultPriority, // creation
flags NULL, // use parent's environment
pCurDir, // use parent's current directory
&si, // STARTUPINFO pointer &m_pi)) ==
FALSE) // receives PROCESS_INFORMATION {
m_iErrorCode = GetLastError();

LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, m_iErrorCode, MAKELANGID(LANG_NEUTRAL,
SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf, 0, NULL );

m_strError = (char*)lpMsgBuf;
LocalFree( lpMsgBuf );

A2iATrace("ProcessCreate", ("Error : "+m_strError).c_str() );
m_bSuccess = false;
}
else
{
m_strError = "OK";
m_bSuccess = true;
}



Dans l'ensemble, pas de problème majeur dans le code à part que la partie où
le STARTUPINFO est initialisé manque (redirection de sdtout/stderr/stdin
entre autre)

Détail sans impotance: je prefère créer les pipes non-héritable (plus
facile: SECURITY_ATTRIBUTES nul) et créer un duplicata héritable (argument
boolean dans DuplicateHandle()).

Pistes possibles:
- Après le démarrage d'un process, fermer le pipe héritable utilisé pour le
process fils (non présent dans le code fourni)
- Si tu crées d'abord les pipes pour les 2 process, puis les process, alors
chaque process va hériter des 2 pipes. Les pipes seront fermé quand tous les
handles seront fermé, donc quand les 2 process auront terminé.

Conclusion: pour chaque process, crée les pipes, démarre le process, ferme
les handles non-utilisé.

--
Frédéric Lachasse - ECP86
Avatar
gg
Frédéric Lachasse wrote:

Dans l'ensemble, pas de problème majeur dans le code à part que la partie où
le STARTUPINFO est initialisé manque (redirection de sdtout/stderr/stdin
entre autre)



Ok, je vais regarder ça.

Détail sans impotance: je prefère créer les pipes non-héritable (plus
facile: SECURITY_ATTRIBUTES nul) et créer un duplicata héritable (argument
boolean dans DuplicateHandle()).



J'ai essayé les 2, j'ai le même comportement.

Pistes possibles:
- Après le démarrage d'un process, fermer le pipe héritable utilisé pour le
process fils (non présent dans le code fourni)



CloseHandle(_ReadPipeChild); _ReadPipeChild = 0;
CloseHandle(_WritePipeChild); _WritePipeChild = 0;
Je ferme ces 2 handles. Lequel est-ce que je devrais fermer encore ?

- Si tu crées d'abord les pipes pour les 2 process, puis les process, alors
chaque process va hériter des 2 pipes. Les pipes seront fermé quand tous les
handles seront fermé, donc quand les 2 process auront terminé.



Conclusion: pour chaque process, crée les pipes, démarre le process, ferme
les handles non-utilisé.



La création de chaque process, et donc des pipes, se fait dans un thread à
part, est-ce que ca peut être un problème ?

Merci,

Gérald


--
Posté via http://www.webatou.net/
Usenet dans votre navigateur !
Complaints-To:
Avatar
Frédéric Lachasse
"GeraldGg" wrote in message
news:
Frédéric Lachasse wrote:

Dans l'ensemble, pas de problème majeur dans le code à part que la partie
où le STARTUPINFO est initialisé manque (redirection de
sdtout/stderr/stdin entre autre)



Ok, je vais regarder ça.

Détail sans impotance: je prefère créer les pipes non-héritable (plus
facile: SECURITY_ATTRIBUTES nul) et créer un duplicata héritable
(argument boolean dans DuplicateHandle()).



J'ai essayé les 2, j'ai le même comportement.

Pistes possibles:
- Après le démarrage d'un process, fermer le pipe héritable utilisé pour
le process fils (non présent dans le code fourni)



CloseHandle(_ReadPipeChild); _ReadPipeChild = 0;
CloseHandle(_WritePipeChild); _WritePipeChild = 0;
Je ferme ces 2 handles. Lequel est-ce que je devrais fermer encore ?



N'ayant pas le code complet, en particulier la méthode ProcessCreate::ok(),
impossible de savoir si cela est fait juste après le CreateProcess() ou
après la fin du process. Code incomplet --> reponse incomplète.

- Si tu crées d'abord les pipes pour les 2 process, puis les process,
alors chaque process va hériter des 2 pipes. Les pipes seront fermé quand
tous les handles seront fermé, donc quand les 2 process auront terminé.



Conclusion: pour chaque process, crée les pipes, démarre le process,
ferme les handles non-utilisé.



La création de chaque process, et donc des pipes, se fait dans un thread à
part, est-ce que ca peut être un problème ?



Oui, car il est probable que l'un des process va hériter d'un pipe destiné à
l'autre process. Il faut prévoir une section critique pour éviter le
problème.

--
Frédéric Lachasse - ECP86
Avatar
gg
Frédéric Lachasse wrote:

N'ayant pas le code complet, en particulier la méthode ProcessCreate::ok(),



Elle ne fait que renvoyer un boolean qui est à true si le process a été
créé sans problème, false sinon.

impossible de savoir si cela est fait juste après le CreateProcess() ou
après la fin du process. Code incomplet --> reponse incomplète.



Il n'y a rien de plus que ce que j'ai donné dans mon premier message, je
ne ferme pas d'autres handle par la suite.

Oui, car il est probable que l'un des process va hériter d'un pipe destiné à
l'autre process. Il faut prévoir une section critique pour éviter le
problème.



J'ai l'impression que c'est ce qui se passe en effet. Il faudrait que la
création du process du 2ème commence seulement quand la première est
complètement finie si je comprends bien?

Merci en tout cas, je vais travailler sur ça pour voir ce que je peux en
tirer.

Gérald

--
Posté via http://www.webatou.net/
Usenet dans votre navigateur !
Complaints-To: