Is C++'s system() synchronized? - c++

I'm writing a small utility which is supposed to launch several commands in parallel using system() and wait for their results for logging purposes. However, even though I'm calling system() on different threads, by looking at my Activity Monitor I only see one instance of each command at a time. It looks like system is internally synchronized on a mutex, and only one execution is allowed at each time, however this looks like a huge limitation, can someone confirm this behavior? Do you have any ideas on how to solve it?
Update by looking at the threads execution flow, it looks like they're effectively synchronizing on a mutex. Is there an alternative system() which doesn't do that?
I should mention I'm using C++11 (w/ clang and libc++) on Mac OS 10.7.5.
Update code is:
void Batch::run()
{
done.clear();
generator->resetGeneration();
while(generator->hasMoreParameters())
{
// Lock for accessing active
unique_lock<mutex> lock(q_mutex, adopt_lock);
// If we've less experiments than threads
if (active.size() < threads)
{
Configuration conf = generator->generateParameters();
Experiment e(executable, conf);
thread t(&Experiment::run, e, reference_wrapper<Batch>(*this));
thread::id id = t.get_id();
active.insert(id);
t.detach();
}
// Condition variable
q_control.wait(lock, [this] { return active.size() < threads; } );
}
}
void Batch::experimentFinished(std::thread::id pos)
{
unique_lock<mutex> lock(q_mutex, adopt_lock);
active.erase(pos);
lock.unlock();
q_control.notify_all();
}
void Experiment::run(Batch& caller)
{
// Generate run command
stringstream run_command;
run_command << executable + " ";
ParameterExpression::printCommandLine(run_command, config);
if (system(run_command.str().c_str()))
stats["success"] = "true";
else
stats["success"] = "false";
caller.experimentFinished(this_thread::get_id());
}
Just to be clear: the spawning and handling of threads works fine and does what it needs to do, but it looks like you can have just one instance of system() running at a time.
Thanks

POSIX has this to say about system(3):
Using the system() function in more than one thread in a process or when the SIGCHLD signal is being manipulated by more than one thread in a process may produce unexpected results.
Due to the way that SIGCHLD must be blocked during the execution, running system calls concurrently doesn't really work. If you want multiple threads to run external tasks, you'll need to write a bit more code (handling fork/exec/wait yourself).

To whoever comes later, popen did the trick, as it doesn't internally keep a mutex. The code to make it work is
FILE* proc;
char buff[1024];
// Keep track of the success or insuccess of execution
if (!(proc = popen(run_command.str().c_str(), "r")))
stats["success"] = "false";
else
stats["success"] = "true";
// Exhaust output
while(fgets(buff, sizeof(buff), proc) != nullptr);
pclose(proc);

In case this helps, I wrote some fork/exec/wait code in C++ a while back. It captures output into a std::string.
As #Mat points out, fork, exec, and wait aren't really designed to be uses in a multi-threaded process.
So this is more useful if multi-process can be a substitute for multi-threaded in your application.
bool Utility::execAndRedirect(std::string command, std::vector<std::string> args, std::string& output, int& status)
{
int error;
int pipefd[2];
int localStatus;
if (pipe(pipefd) == -1)
{
error = errno;
cerr << "Executing command '" << command << "' failed: " << strerror(error) << endl;
return false;
}
pid_t pid = fork();
if (pid == 0)
{
char** argsC;
argsC = new char*[args.size() + 2];
argsC[0] = new char[command.size() + 1];
strncpy(argsC[0], command.c_str(), command.size());
argsC[0][command.size()] = '\0';
for (size_t count = 0; count < args.size(); count++)
{
argsC[count + 1] = new char[args[count].size() + 1];
strncpy(argsC[count + 1], args[count].c_str(), args[count].size());
argsC[count + 1][args[count].size()] = '\0';
}
argsC[args.size() + 1] = NULL;
close(pipefd[0]);
if (dup2(pipefd[1], STDOUT_FILENO) == -1)
{
error = errno;
cerr << "Executing command '" << command << "' failed: " << strerror(error) << endl;
exit(1);
}
if (dup2(pipefd[1], STDERR_FILENO) == -1)
{
error = errno;
cerr << "Executing command '" << command << "' failed: " << strerror(error) << endl;
exit(1);
}
close(pipefd[1]);
if (execvp(command.c_str(), argsC) == -1)
{
error = errno;
cerr << "Executing command '" << command << "' failed: " << strerror(error) << endl;
exit(1);
}
}
else if (pid > 0)
{
size_t BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE + 1];
close(pipefd[1]);
ostringstream oss;
ssize_t num_b;
while ((num_b = read(pipefd[0], buffer, BUFFER_SIZE)) != 0)
{
buffer[num_b] = '\0';
oss << buffer;
}
output = oss.str();
waitpid(pid, &localStatus, 0);
close(pipefd[0]);
}
else
{
error = errno;
cerr << "Executing command '" << command << "' failed: " << strerror(error) << endl;
return false;
}
if(WIFEXITED(localStatus))
{
status = WEXITSTATUS(localStatus);
//DateTime current = DateTime::now(); //this is a custom class
if(status == 0)
{
return true;
}
else
{
return false;
}
}
else
{
error = errno;
cerr << "Executing command '" << command << "' failed: child didn't terminate normally" << endl;
return false;
}
}

Related

CreateProcess and redirecting output

There are 2 apps.
AppCMD is a command line app and AppMAIN starts AppCMD with some command line args.
Unfortunately AppMAIN does not seem to handle the output off AppCMD very well and something is going wrong.
I'd like to log the calls to AppCMD and its output to see what is going on.
In order to do so I want to replace AppCMD with another binary AppWRAP that forwards the calls to a renamed AppCMD and logs it's output.
AppWRAP should act like a transparent Man-In-The-Middle.
For testing purposes I wrote a simple AppCMD that just outputs it's command line args:
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
cout << "#### Hello, I'm the test binary that wants to be wrapped." << endl;
if (argc < 2) {
cout << "#### There where no command line arguments." << endl;
}
else {
cout << "#### These are my command line arguments:";
for (int i = 1; i < argc; ++i) cout << " " << argv[i];
cout << endl;
}
cout << "#### That's pretty much everything I do ... yet ;)" << endl;
return 0;
}
I followed MSDN: Creating a Child Process with Redirected Input and Output to implement AppWrap but I got stuck since it does not return and I cant figure out why:
#include <iostream>
#include <sstream>
#include <Windows.h>
using namespace std;
const string TARGET_BINARY("TestBinary.exe");
const size_t BUFFSIZE = 4096;
HANDLE in_read = 0;
HANDLE in_write = 0;
HANDLE out_read = 0;
HANDLE out_write = 0;
int main(int argc, char *argv[])
{
stringstream call;
cout << "Hello, I'm BinTheMiddle." << endl;
//-------------------------- CREATE COMMAND LINE CALL --------------------------
call << TARGET_BINARY;
for (int i = 1; i < argc; ++i) {
call << " " << argv[i];
}
cout << "Attempting to call '" << call.str() << "'" << endl;
//------------------------------ ARRANGE IO PIPES ------------------------------
SECURITY_ATTRIBUTES security;
security.nLength = sizeof(SECURITY_ATTRIBUTES);
security.bInheritHandle = NULL;
security.bInheritHandle = TRUE;
security.lpSecurityDescriptor = NULL;
if (!CreatePipe(&out_read, &out_write, &security, 0)) {
cout << "Error: StdoutRd CreatePipe" << endl;
return -1;
}
if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
cout << "Stdout SetHandleInformation" << endl;
return -2;
}
if (!CreatePipe(&in_read, &in_write, &security, 0)) {
cout << "Stdin CreatePipe" << endl;
return -3;
}
if (!SetHandleInformation(in_write, HANDLE_FLAG_INHERIT, 0)) {
cout << "Stdin SetHandleInformation" << endl;
return -4;
}
//------------------------------ START TARGET APP ------------------------------
STARTUPINFO start;
PROCESS_INFORMATION proc;
ZeroMemory(&start, sizeof(start));
start.cb = sizeof(start);
start.hStdError = out_write;
start.hStdOutput = out_write;
start.hStdInput = in_read;
start.dwFlags |= STARTF_USESTDHANDLES;
ZeroMemory(&proc, sizeof(proc));
// Start the child process.
if (!CreateProcess(NULL, (LPSTR) call.str().c_str(), NULL, NULL, TRUE,
0, NULL, NULL, &start, &proc))
{
cout << "CreateProcess failed (" << GetLastError() << ")" << endl;
return -1;
}
// Wait until child process exits.
WaitForSingleObject(proc.hProcess, INFINITE);
// Close process and thread handles.
CloseHandle(proc.hProcess);
CloseHandle(proc.hThread);
//----------------------------------- OUTPUT -----------------------------------
DWORD dwRead;
CHAR chBuf[127];
while (ReadFile(out_read, chBuf, 127, &dwRead, NULL)) {
cout << "Wrapped: " << chBuf << endl;
}
return 0;
}
It seems like it is waiting for ReadFile to return. Can anybody spot what I'm doing wrong?
I call the binary this way:
> shell_cmd_wrapper.exe param1 param2
This is the console output but the binary does not return.
Hello, I'm BinTheMiddle.
Attempting to call 'TestBinary.exe param1 param2'
Wrapped:#### Hello, I'm the test binary that wants to be wrapped.
#### These are my command line arguments: param1 param2
#### That'sD
Wrapped: pretty much everything I do ... yet ;)
s to be wrapped.
#### These are my command line arguments: param1 param2
#### That'sD
(Please ignore that I don't clear the buffer)
Close the out_write and in_read handles after calling CreateProcess. Otherwise ReadFile on out_read will block when the pipe is empty because there's still a potential writer even after the child has exited -- the out_write handle in the current process.
Also, as noted by Harry Johnston in a comment, waiting for the process to exit before reading from the pipe can potentially cause a deadlock. The child will block on WriteFile if the pipe fills up.

How to find out whether child process still is running?

I am spawning a process in my application:
int status = posix_spawnp(&m_iProcessHandle, (char*)strProgramFilepath.c_str(), NULL, NULL, argsWrapper.m_pBuffer, NULL);
When I want to see if the process is still running, I use kill:
int iReturn = kill(m_iProcessHandle,0);
But after the spawned process has finished its work, it hangs around. The return value on the kill command is always 0. Not -1. I am calling kill from within the code, but if I call it from the command line, there is no error - the spawned process still exists.
Only when my application exits does the command-line kill return "No such process".
I can change this behavior in my code with this:
int iResult = waitpid(m_iProcessHandle, &iStatus, 0);
The call to waitpd closes down the spawned process and I can call kill and get -1 back, but by then I know the spawned process is dead.
And waitpd blocks my application!
How can I test a spawned processes to see if it is running, but without blocking my application?
UPDATE
Thanks for the help! I have implemented your advise and here is the result:
// background-task.cpp
//
#include <spawn.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>
#include "background-task.h"
CBackgroundTask::CBackgroundTask()
{
// Initialize member variables
m_iProcessHandle = 0;
}
CBackgroundTask::~CBackgroundTask()
{
// Clean up (kill first)
_lowLevel_cleanup(true);
}
bool CBackgroundTask::IsRunning()
{
// Shortcuts
if (m_iProcessHandle == 0)
return false;
// Wait for the process to finish
int iStatus = 0;
int iResult = waitpid(m_iProcessHandle, &iStatus, WNOHANG);
return (iResult != -1);
}
void CBackgroundTask::Wait()
{
// Wait (clean up without killing)
_lowLevel_cleanup(false);
}
void CBackgroundTask::Stop()
{
// Stop (kill and clean up)
_lowLevel_cleanup(true);
}
void CBackgroundTask::_start(const string& strProgramFilepath, const string& strArgs, int iNice /*=0*/)
{
// Call pre-start
_preStart();
// Split the args and build array of char-strings
CCharStringAarray argsWrapper(strArgs,' ');
// Run the command
int status = posix_spawnp(&m_iProcessHandle, (char*)strProgramFilepath.c_str(), NULL, NULL, argsWrapper.m_pBuffer, NULL);
if (status == 0)
{
// Process created
cout << "posix_spawn process=" << m_iProcessHandle << " status=" << status << endl;
}
else
{
// Failed
cout << "posix_spawn: error=" << status << endl;
}
// If process created...
if(m_iProcessHandle != 0)
{
// If need to adjust nice...
if (iNice != 0)
{
// Change the nice
stringstream ss;
ss << "sudo renice -n " << iNice << " -p " << m_iProcessHandle;
_runCommand(ss.str());
}
}
else
{
// Call post-stop success=false
_postStop(false);
}
}
void CBackgroundTask::_runCommand(const string& strCommand)
{
// Diagnostics
cout << "Running command: " << COUT_GREEN << strCommand << endl << COUT_RESET;
// Run command
system(strCommand.c_str());
}
void CBackgroundTask::_lowLevel_cleanup(bool bKill)
{
// Shortcuts
if (m_iProcessHandle == 0)
return;
// Diagnostics
cout << "Cleaning up process " << m_iProcessHandle << endl;
// If killing...
if (bKill)
{
// Kill the process
kill(m_iProcessHandle, SIGKILL);
}
// Diagnostics
cout << "Waiting for process " << m_iProcessHandle << " to finish" << endl;
// Wait for the process to finish
int iStatus = 0;
int iResult = waitpid(m_iProcessHandle, &iStatus, 0);
// Diagnostics
cout << "waitpid: status=" << iStatus << " result=" << iResult << endl;
// Reset the process-handle
m_iProcessHandle = 0;
// Call post-stop with success
_postStop(true);
// Diagnostics
cout << "Process cleaned" << endl;
}
Until the parent process calls one of the wait() functions to get the exit status of a child, the child stays around as a zombie process. If you run ps during this time, you'll see that the process is still there in the Z state. So kill() returns 0 because the process exists.
If you don't need to get the child's status, see How can I prevent zombie child processes? for how you can make the child disappear immediately when it exits.

piping stockfish misbehaves in fedora

Somewhere in my project I use fork and pipe to execute another process and pipe its I/O to communicate with it (I'm writing it in C++). There is no problem when I compile it in Ubuntu 14.04, it will work just fine, but I compiled it in fedora on a WMWare virtual machine and strange things began to happen. If I run the binary in terminal, there is no error but nothing will be written in the pipe (but getting streams of characters will work). I tried to debug my code in fedora, I put a break point in my code, but then a broken pipe signal was given when process tried to read from pipe (there were no signals when executing in terminal).
So, have any of you encountered such problems before? Is there any difference in piping between debian and red hat linux? Or is it because I'm running fedora on a virtual machine?
CODE:
int mFD_p2c [2];
int mFD_c2p [2];
int mEnginePID;
if (pipe(mFD_p2c) != 0 || pipe(mFD_c2p) != 0)
{
cout << "Failed to pipe";
exit(1);
}
mEnginePID = fork();
if (mEnginePID < 0)
{
cout << "Fork failed";
exit(-1);
}
else if (mEnginePID == 0)
{
if (dup2(mFD_p2c[0], 0) != 0 ||
close(mFD_p2c[0]) != 0 ||
close(mFD_p2c[1]) != 0)
{
cout << "Child: failed to set up standard input";
exit(1);
}
if (dup2(mFD_c2p[1], 1) != 1 ||
close(mFD_c2p[1]) != 0 ||
close(mFD_c2p[0]) != 0)
{
cout << "Child: failed to set up standard output";
exit(1);
}
string engine = "stockfish";
execlp(engine.c_str(), (char *) 0);
cout << "Failed to execute " << engine;
exit(1);
}
else
{
close(mFD_p2c[0]);
close(mFD_c2p[1]);
string str = "uci";
int nbytes = str.length();
if (write(mFD_p2c[1], str.c_str(), nbytes) != nbytes)
{
cout << "Parent: short write to child";
exit(1);
}
cout << "The following string has been written to engine:\n"
<< string(1, '\t') << str;
char readBuffer[2];
string output = "";
while (1)
{
int bytes_read = read(mFD_c2p[0], readBuffer, sizeof(char));
if (readBuffer[0] == '\n')
break;
readBuffer[bytes_read] = '\0';
output += readBuffer;
}
cout << "Got: " << output;
}
I see you're using Stockfish. I too have exactly experienced this behavior from Stockfish. The problem lies within how it handles output. Defined in misc.h:
#define sync_cout std::cout << IO_LOCK
And looking at the code again we'll see that IO_LOCK is an enum which is used in an overloaded friend operator for cout:
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
static Mutex m;
if (sc == IO_LOCK)
m.lock();
if (sc == IO_UNLOCK)
m.unlock();
return os;
}
What I see here is that during using cout, a mutex is locked. I don't know how exactly this affects cout's output in a pipe instead of stdout, but I'm positive that this is the cause for the problem. You can check it by removing the lock functionality.
Edit: I forgot to mention that the pipe behavior is not different in linux based systems as mentioned before, but there might be slight differences between distributions handling mutexes used with pipes.
There are no differences in piping between debian and red hat, but the following list of questions may help you:
-Are the Ubuntu and the Fedora using the same architecture (64 bit vs 32) ?
-Are you using the same version of gcc (or any other compiler) ?
(Suggestion: use cerr for your error outputs, and maybe your debug output too -> you dup the standard outputs and inputs, so if something fails you may not see it)
Anyhow, here's how you turn it into a self-contained, compilable example:
stockfish
#cat stockfish
tr a-z A-Z #just so we do something
echo #need to end with a "\n" or else the parent won't break out of the while loop
Run command:
make pipes && PATH=.:$PATH pipes
pipes.cc
//pipes.cc
#include <iostream>
#include <fstream>
#include <string>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
using namespace std;
int mFD_p2c [2];
int mFD_c2p [2];
int mEnginePID;
if (pipe(mFD_p2c) != 0 || pipe(mFD_c2p) != 0)
{
cout << "Failed to pipe";
exit(1);
}
mEnginePID = fork();
if (mEnginePID < 0)
{
cout << "Fork failed";
exit(-1);
}
else if (mEnginePID == 0)
{
if (dup2(mFD_p2c[0], 0) != 0 ||
close(mFD_p2c[0]) != 0 ||
close(mFD_p2c[1]) != 0)
{
cout << "Child: failed to set up standard input";
exit(1);
}
if (dup2(mFD_c2p[1], 1) != 1 ||
close(mFD_c2p[1]) != 0 ||
close(mFD_c2p[0]) != 0)
{
cout << "Child: failed to set up standard output";
exit(1);
}
string engine = "stockfish";
char *const args[]={};
int ret;
execvp(engine.c_str(), args);
//I need the endl here or else it doesn't show for me when the execvp fails; I wasn't able to compile the original exec command so I used a different one from the exec* family
cout << "Failed to execute " << engine << endl;
exit(1);
}
else
{
close(mFD_p2c[0]);
close(mFD_c2p[1]);
string str = "uci";
int nbytes = str.length();
if (write(mFD_p2c[1], str.c_str(), nbytes) != nbytes)
{
cout << "Parent: short write to child";
exit(1);
}
//My particular child process tries to read to the end, so give it the EOF
close(mFD_p2c[1]);
cout << "The following string has been written to engine:\n"
<< string(1, '\t') << str;
char readBuffer[2];
string output = "";
while (1)
{
int bytes_read = read(mFD_c2p[0], readBuffer, sizeof(char));
if (readBuffer[0] == '\n')
break;
readBuffer[bytes_read] = '\0';
output += readBuffer;
}
cout << "Got: " << output;
}
return 0;
}
output:
The following string has been written to engine:
uciGot: UCI

How to convert from popen() to fork() and not duplicate process memory?

I have a multi-threaded C++03 application that presently uses popen() to invoke itself (same binary) and ssh (different binary) again in a new process and reads the output, however, when porting to Android NDK this is posing some issues such as not not having permissions to access ssh, so I'm linking in Dropbear ssh to my application to try and avoid that issue. Further, my current popen solution requires that stdout and stderr be merged together into a single FD which is a bit messy and I'd like to stop doing that.
I would think the pipe code could be simplified by using fork() instead but wonder how to drop all of the parent's stack/memory which is not needed in the child of the fork? Here is a snippet of the old working code:
#include <iostream>
#include <stdio.h>
#include <string>
#include <errno.h>
using std::endl;
using std::cerr;
using std::cout;
using std::string;
void
doPipe()
{
// Redirect stderr to stdout with '2>&1' so that we see any error messages
// in the pipe output.
const string selfCmd = "/path/to/self/binary arg1 arg2 arg3 2>&1";
FILE *fPtr = ::popen(selfCmd.c_str(), "r");
const int bufSize = 4096;
char buf[bufSize + 1];
if (fPtr == NULL) {
cerr << "Failed attempt to popen '" << selfCmd << "'." << endl;
} else {
cout << "Result of: '" << selfCmd << "':\n";
while (true) {
if (::fgets(buf, bufSize, fPtr) == NULL) {
if (!::feof(fPtr)) {
cerr << "Failed attempt to fgets '" << selfCmd << "'." << endl;
}
break;
} else {
cout << buf;
}
}
if (pclose(fPtr) == -1) {
if (errno != 10) {
cerr << "Failed attempt to pclose '" << selfCmd << "'." << endl;
}
}
cout << "\n";
}
}
So far, this is loosely what I have done to convert to fork(), but fork needlessly duplicates the entire parent process memory space. Further, it does not quite work, because the parent never sees EOF on the outFD it is reading from the pipe(). Where else do I need to close the FDs for this to work? How can I do something like execlp() without supplying a binary path (not easily available on Android) but instead start over with the same binary and a blank image with new args?
#include <iostream>
#include <stdio.h>
#include <string>
#include <errno.h>
using std::endl;
using std::cerr;
using std::cout;
using std::string;
int
selfAction(int argc, char *argv[], int &outFD, int &errFD)
{
pid_t childPid; // Process id used for current process.
// fd[0] is the read end of the pipe and fd[1] is the write end of the pipe.
int fd[2]; // Pipe for normal communication between parent/child.
int fdErr[2]; // Pipe for error communication between parent/child.
// Create a pipe for IPC between child and parent.
const int pipeResult = pipe(fd);
if (pipeResult) {
cerr << "selfAction normal pipe failed: " << errno << ".\n";
return -1;
}
const int errorPipeResult = pipe(fdErr);
if (errorPipeResult) {
cerr << "selfAction error pipe failed: " << errno << ".\n";
return -1;
}
// Fork - error.
if ((childPid = fork()) < 0) {
cerr << "selfAction fork failed: " << errno << ".\n";
return -1;
} else if (childPid == 0) { // Fork -> child.
// Close read end of pipe.
::close(fd[0]);
::close(fdErr[0]);
// Close stdout and set fd[1] to it, this way any stdout of the child is
// piped to the parent.
::dup2(fd[1], STDOUT_FILENO);
::dup2(fdErr[1], STDERR_FILENO);
// Close write end of pipe.
::close(fd[1]);
::close(fdErr[1]);
// Exit child process.
exit(main(argc, argv));
} else { // Fork -> parent.
// Close write end of pipe.
::close(fd[1]);
::close(fdErr[1]);
// Provide fd's to our caller for stdout and stderr:
outFD = fd[0];
errFD = fdErr[0];
return 0;
}
}
void
doFork()
{
int argc = 4;
char *argv[4] = { "/path/to/self/binary", "arg1", "arg2", "arg3" };
int outFD = -1;
int errFD = -1;
int result = selfAction(argc, argv, outFD, errFD);
if (result) {
cerr << "Failed to execute selfAction." << endl;
return;
}
FILE *outFile = fdopen(outFD, "r");
FILE *errFile = fdopen(errFD, "r");
const int bufSize = 4096;
char buf[bufSize + 1];
if (outFile == NULL) {
cerr << "Failed attempt to open fork file." << endl;
return;
} else {
cout << "Result:\n";
while (true) {
if (::fgets(buf, bufSize, outFile) == NULL) {
if (!::feof(outFile)) {
cerr << "Failed attempt to fgets." << endl;
}
break;
} else {
cout << buf;
}
}
if (::close(outFD) == -1) {
if (errno != 10) {
cerr << "Failed attempt to close." << endl;
}
}
cout << "\n";
}
if (errFile == NULL) {
cerr << "Failed attempt to open fork file err." << endl;
return;
} else {
cerr << "Error result:\n";
while (true) {
if (::fgets(buf, bufSize, errFile) == NULL) {
if (!::feof(errFile)) {
cerr << "Failed attempt to fgets err." << endl;
}
break;
} else {
cerr << buf;
}
}
if (::close(errFD) == -1) {
if (errno != 10) {
cerr << "Failed attempt to close err." << endl;
}
}
cerr << "\n";
}
}
There are two kinds of child processes created in this fashion with different tasks in my application:
SSH to another machine and invoke a server that will communicate back to the parent that is acting as a client.
Compute a signature, delta, or merge file using rsync.
First of all, popen is a very thin wrapper on top of fork() followed by exec() [and some call to pipe and dup and so on to manage the ends of a pipe] .
Second, the memory is only duplicated in form of "copy-on-write" memory - meaning that unless one of the processes writes to some page, the actual physical memory is shared between the two processes.
It does mean, of course, the OS has to create a memory map with 4-8 bytes per 4KB [in typical cases] (probably plus some internal OS data to track how many copies there are of that page and stuff - but as long as the page remains the same one as the parent process, the child page uses the parent processes internal data). Compared to everything else involved in creating a new process and loading an executable file into the new process, it's a pretty small part of the time. Since you are almost immediately doing exec, not much of the parent process' memory will be touched, so very little will happen there.
My advice would be that if popen works, keep using popen. If popen doesn't quite do what you want for some reason, then use fork + exec - but make sure you know what the reason for doing so is.

problem with popen

I have a strange problem. I have 2 binaries by the name cpp and another is called mnp_proxy_server.
cpp will start mnp_proxy_binary by calling a method executeScript. The code of this method is
int executeScript(string script, unsigned int scriptTmOut)
{
fd_set readfd;
const int BUFSIZE = 1024;
//stringstream strBuf;
char buf[ BUFSIZE];
time_t startTime = time(NULL);
struct timeval tv;
int ret, ret2 = 0;
FILE * pPipe = popen(script.c_str(), "r");
if (pPipe == NULL)
{
// cout << "popen() failed:"<< strerror(errno) << endl;
return -1;
}
while(1)
{
FD_ZERO(&readfd);
FD_SET(fileno(pPipe), &readfd);
/** Select Timeout Hardcode with 1 secs **/
tv.tv_sec = scriptTmOut;
tv.tv_usec = 0;
ret = select(fileno(pPipe)+1, &readfd, NULL, NULL, &tv);
if(ret < 0)
{
// cout << "select() failed " << strerror(errno) << endl;
}
else if (ret == 0)
{
// cout << "select() timeout" << endl;
break;
}
else
{
//cout << "Data is available now" <<endl;
if(FD_ISSET(fileno(pPipe), &readfd))
{
if(fgets(buf, sizeof(buf), pPipe) != NULL )
{
//cout << buf;
//strBuf << buf;
}
/** No Problem if there is no data ouput by script **/
#if 1
else
{
//ret2 = -1;
// cout << "fgets() failed " << strerror(errno) << endl;
break;
}
#endif
}
else
{
ret2 = -1;
// cout << "FD_ISSET() failed " << strerror(errno) << endl;
break;
}
}
/** Check the Script-timeout **/
if((startTime + scriptTmOut) < time(NULL))
{
// cout<<"Script Timeout"<<endl;
break ;
}
}
pclose(pPipe);
return ret2;
}
cpp is a server which listens on various ports 7001 and 7045. Once mnp_proxy_server is started it connects to 7001 port and starts sending messages.
Now coming to the problem. when i send ctr^c signal to cpp the signal is propagated to mnp_proxy_server and if i kill cpp process then all the ports on which cpp was listning now becomes the part of mnp_proxy_server process.
output of netstat after killing cpp process
[root#punith bin]# netstat -alpn | grep mnp_pr
tcp 0 0 0.0.0.0:7045 0.0.0.0:* LISTEN 26186/mnp_proxy_ser
tcp 0 0 0.0.0.0:7001 0.0.0.0:* LISTEN 26186/mnp_proxy_ser
I know it has something to do with the way I am executing the startup script of mnp_proxy_server through cpp.
There is a signal handler in both the binaries. And also to exit the socket select when ctr^c is pressed I have used pipes in select, so when ctr^c is pressed i close the write end of the pipe so that select is notified and select comes out and breaks the run loop.
Both of them are written in c++ and I am using rhel
Any clue will greatly help me in solving this. Thanking in advance.
You should set the flag CLOEXEC on the server sockets of cpp so that they are closed in the child process:
fcntl(fd, F_SETFD, FD_CLOEXEC);
While using socket like in your processes, I would suggest use fork and exec instead of popen to be able to close or manage all sockets between fork and exec, but the flag CLOEXEC might be enough to solve your problem.