I want to fork a process and then do the following in the parent:
Wait until it terminates naturally or timeout period set by the parent expires (something like waitforsingalobject in windows) after which I will kill the process using kill(pid);
Get the exit code of the child process (assuming it exited naturally)
I need to have access to the std::cout of the child process from the parent.
I attempted to use waitpid() however while this allows me access to the return code I cannot implement a timeout using this function.
I also looked at the following solution (https://www.linuxprogrammingblog.com/code-examples/signal-waiting-sigtimedwait) which allows me to implement a time-out however there doesnt seem a way to get the return code.
I geuss my question boils down to, Whats the correct way achieving this in linux?
You can do #1 and #2 with sigtimedwait function and #3 with pipe:
#include <unistd.h>
#include <signal.h>
#include <iostream>
int main() {
// Block SIGCHLD, so that it only gets delivered while in sigtimedwait.
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGCHLD);
sigprocmask(SIG_BLOCK, &sigset, nullptr);
// Make a pipe to communicate with the child process.
int child_stdout[2];
if(pipe(child_stdout))
abort();
std::cout.flush();
std::cerr.flush();
auto child_pid = fork();
if(-1 == child_pid)
abort();
if(!child_pid) { // In the child process.
dup2(child_stdout[1], STDOUT_FILENO); // Redirect stdout into the pipe.
std::cout << "Hello from the child process.\n";
std::cout.flush();
sleep(3);
_exit(3);
}
// In the parent process.
dup2(child_stdout[0], STDIN_FILENO); // Redirect stdin to stdout of the child.
std::string line;
getline(std::cin, line);
std::cout << "Child says: " << line << '\n';
// Wait for the child to terminate or timeout.
timespec timeout = {1, 0};
siginfo_t info;
auto signo = sigtimedwait(&sigset, &info, &timeout);
if(-1 == signo) {
if(EAGAIN == errno) { // Timed out.
std::cout << "Killing child.\n";
kill(child_pid, SIGTERM);
}
else
abort();
}
else { // The child has terminated.
std::cout << "Child process terminated with code " << info.si_status << ".\n";
}
}
Outputs:
Child says: Hello from the child process.
Killing child.
If sleep is commented out:
Child says: Hello from the child process.
Child process terminated with code 3.
Related
Following this documentation, I am testing how to stop and resume a process. I have basic code to test as follows:
#include <iostream>
#include <csignal>
#include <unistd.h>
int main() {
std::cout << "Hello" << std::endl;
int pid = getpid();
kill(pid, SIGSTOP);
kill(pid, SIGCONT);
std::cout << "Bye" << std::endl;
return 0;
}
The output is:
Hello
It stops the process, but it never resumes it. How should I fix it?
A solution, if a bit complicated, is to create a child process to start and stop the parent. Here is a small code example, that might help:
#include <iostream>
#include <csignal>
#include <unistd.h>
int pid; //Include declaration outside so it transfers to the child process
int main() {
std::cout << "Hello" << std::endl;
pid = getpid();
int returned_pid = fork(); //Duplicate process into 2 identical processes
if(returned_pid) {
// If it is the parent process, then fork returns the child process pid
// This is executed by the parent process
usleep(1000); // Sleep a millisecond to allow for the stop command to run
} else {
// If fork returns 0, then it is the child process
// The else is executed by the child process
kill(pid, SIGSTOP); // Stop parent process
usleep(3000000); // Delay 3 seconds
kill(pid, SIGCONT); // Resume parent process
}
if(returned_pid) { // Only print if parent process
std::cout << "Bye" << std::endl;
}
return 0;
}
Clarification: The fork command returns 2 different values in the 2 processes: 0 in the child, and the pid of the child process in the parent.
Other note: When running this in a terminal, it will look weird, as the terminal may note that the process was stopped and give a new command line, but then the process resumes, so prints Bye over it. Just a note.
I was writing a code for a research program. I have following requirement:
1. Main binary execution begins at main()
2. main() fork()
3. child process runs a linpack benchmark binary using execvp()
4. parent process runs some monitoring process and wait for child to exit.
The code is below:
main.cpp
extern ServerUncorePowerState * BeforeStates ;
extern ServerUncorePowerState * AfterStates;
int main(int argc, char *argv[]) {
power pwr;;
procstat st;
membandwidth_t data;
int sec_pause = 1; // sample every 1 second
pid_t child_pid = fork();
if (child_pid >= 0) { //fork successful
if (child_pid == 0) { // child process
int exec_status = execvp(argv[1], argv+1);
if (exec_status) {
std::cerr << "execv failed with error "
<< errno << " "
<< strerror(errno) << std::endl;
}
} else { // parent process
int status = 1;
waitpid(child_pid, &status, WNOHANG);
write_headers();
pwr.init();
st.init();
init_bandwidth();
while (status) {
cout << " Printing status Value: " << status << endl;
sleep (sec_pause);
time_t now;
time(&now);
struct tm *tinfo;
tinfo = localtime(&now);
pwr.loop();
st.loop();
data = getbandwidth();
write_samples(tinfo, pwr, st, data.read_bandwidth + data.write_bandwidth);
waitpid(child_pid, &status, WNOHANG);
}
wait(&status); // wait for child to exit, and store its status
//--------------------This code is not executed------------------------
std::cout << "PARENT: Child's exit code is: "
<< WEXITSTATUS(status)
<< std::endl;
delete[] BeforeStates;
delete[] AfterStates;
}
} else {
std::cerr << "fork failed" << std::endl;
return 1;
}
return 0;
}
What is expected that the child will exit and then parent exits but due to some unknown reason after 16 mins parent exits but child is still running.
Normally It is said that when parent exits the child dies automatically.
What could be the reason for this strange behavior???
Normally It is said that when parent exits the child dies automatically.
Well this is not always true, it depends on the system. When a parent process terminates, the child process is called an orphan process. In a Unix-like OS this is managed by relating the parent process of the orphan process to the init process, this is called re-parenting and it's automatically managed by the OS. In other types of OS, orphan processes are automatically killed by the system. You can find more details here.
From the code snippet I would think that maybe the issue is in the wait(&status) statement. The previous loop would end (or not be executed) when the return status is 0, which is the default return value from your final return 0 at the end, that could be yielded by the previous waitpid(child_pid, &status, WNOHANG) statements. This means that the wait(&status) statement would wait on a already terminated process, this may cause some issues.
I'm redirecting output from a child process:
int pipefd[2];
pipe(pipefd);
pid_t pid = fork(); /* Create a child process */
switch (pid) {
case -1: /* Error */
cout << "Uh-Oh! fork() failed.\n";
exit(1);
case 0: /* Child process */
close(pipefd[0]);
dup2(pipefd[1], 1);
dup2(pipefd[1], 2);
close(pipefd[1]);
execv(args[0], (char * const *)args);
cout << "execv() error" << endl;
exit(1);
default: /* Parent process */
close(pipefd[1]);
char buffer[1024];
size_t bytes_read = 0;
bytes_read = read(pipefd[0], buffer, sizeof(buffer));
if(bytes_read == -1) {
cout << "read() error" << endl;
exit(1);
}
close(pipefd[0]);
if(bytes_read > 0) {
buffer[bytes_read-1] = '\0'; // Overwrite the newline
}
int status, exit_pid;
while(true) {
exit_pid = waitpid(pid, &status, 0);
if(exit_pid == -1) {
cout << "waitpid() error: " << strerror(errno) << endl;
exit(1);
}
else {
return WEXITSTATUS(status);
}
}
}
This works fine when I ran it as an isolated piece of code. But when I integrate it into my multithreaded environment, a horrible thing happens: the read() calls somehow reads output of other threads of the parent process, as if it were the output from the pipe of the child process.
Anyone encountered such a thing?
I'm on OS X.
Well, I have a solution even though I don't completely understand why this happened.
But first, it should be clear that this behavior is neither normal nor expectable. A child process created with fork() does not inherit any running threads from its parent (so the unexpected output must come from the parent threads). And it has its own descriptor table. So when the child process calls dup2() to alter its output descriptors, that shouldn't have any effect on the threads in the parent process.
The problem occurred only in cases where execv() call failed. In those cases, I expected the termination of the child process to close all its file descriptors. But that didn't happen, or at least it didn't have the same effect as calling close() explicitly. So adding explicit close() calls after execv() solved the problem:
execv(args[0], (char * const *)args);
close(1);
close(2);
exit(1);
The close of the write-end descriptor of the pipe is what will cause the read operation on the read-end to receive 0, thus knowing not to read anymore.
However, I still don't know the following:
Why isn't the call to exit() in the child process equivalent to explicitly calling close() ?
Even if the pipe write-end isn't closed, why does reading from the read-end produces output of threads in the parent process, instead of blocking, or returning some error ?
If anybody can shed light on this, it will be appreciated.
First off, allow me to describe my scenario:
I developed a supervisory program on Linux that forks and then uses execv(), in the child process, to launch my multi-threaded application. The supervisory program is acting as a watchdog to the multi-threaded application. If the multi-threaded application does not send a SIGUSR1 signal to the supervisor after a period of time then the supervisory program will kill the child using the pid_t from the fork() call and repeat the process again.
Here is the code for the Supervisory Program:
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <iostream>
#include <cerrno>
time_t heartbeatTime;
void signalHandler(int sigNum)
{
//std::cout << "Signal (" << sigNum << ") received.\n";
time(&heartbeatTime);
}
int main(int argc, char *argv[])
{
pid_t cpid, ppid;
int result = 0;
bool programLaunched = false;
time_t now;
double timeDiff;
int error;
char ParentID[25];
char *myArgv[2];
// Get the Parent Process ID
ppid = ::getpid();
// Initialize the Child Process ID
cpid = 0;
// Copy the PID into the char array
sprintf(ParentID, "%i", ppid);
// Set up the array to pass to the Program
myArgv[0] = ParentID;
myArgv[1] = 0;
// Print out of the P PID
std::cout << "Parent ID: " << myArgv[0] << "\n";
// Register for the SIGUSR1 signal
signal(SIGUSR1, signalHandler);
// Register the SIGCHLD so the children processes exit fully
signal(SIGCHLD, SIG_IGN);
// Initialize the Heart Beat time
time(&heartbeatTime);
// Loop forever and ever, amen.
while (1)
{
// Check to see if the program has been launched
if (programLaunched == false)
{
std::cout << "Forking the process\n";
// Fork the process to launch the application
cpid = fork();
std::cout << "Child PID: " << cpid << "\n";
}
// Check if the fork was successful
if (cpid < 0)
{
std::cout << "Error in forking.\n";
// Error in forking
programLaunched = false;
}
else if (cpid == 0)
{
// Check if we need to launch the application
if (programLaunched == false)
{
// Send a message to the output
std::cout << "Launching Application...\n";
// Launch the Application
result = execv("./MyApp", myArgv);
std::cout << "execv result = " << result << "\n";
// Check if the program launched has failed
if (result != -1)
{
// Indicate the program has been launched
programLaunched = true;
// Exit the child process
return 0;
}
else
{
std::cout << "Child process terminated; bad execv\n";
// Flag that the program has not been launched
programLaunched = false;
// Exit the child process
return -1;
}
}
}
// In the Parent Process
else
{
// Get the current time
time(&now);
// Get the time difference between the program heartbeat time and current time
timeDiff = difftime(now, heartbeatTime);
// Check if we need to restart our application
if ((timeDiff > 60) && (programLaunched == true))
{
std::cout << "Killing the application\n";
// Kill the child process
kill(cpid, SIGINT);
// Indicate that the process was ended
programLaunched = false;
// Reset the Heart Beat time
time(&heartbeatTime);
return -1;
}
// Check to see if the child application is running
if (kill(cpid, 0) == -1)
{
// Get the Error
error = errno;
// Check if the process is running
if (error == ESRCH)
{
std::cout << "Process is not running; start it.\n";
// Process is not running.
programLaunched = false;
return -1;
}
}
else
{
// Child process is running
programLaunched = true;
}
}
// Give the process some time off.
sleep(5);
}
return 0;
}
This approach worked fairly well until I ran into a problem with the library I was using. It didn't like all of the killing and it basically ended up tying up my Ethernet port in an endless loop of never releasing - not good.
I then tried an alternative method. I modified the supervisory program to allow it to exit if it had to kill the multi-threaded application and I created a script that will launch the supervisor program from crontab. I used a shell script that I found on Stackoverflow.
#!/bin/bash
#make-run.sh
#make sure a process is always running.
export DISPLAY=:0 #needed if you are running a simple gui app.
process=YourProcessName
makerun="/usr/bin/program"
if ps ax | grep -v grep | grep $process > /dev/null
then
exit
else
$makerun &
fi
exit
I added it to crontab to run every minute. That was very helpful and it restarted the supervisory program which in turn restarted multi-threaded application but I noticed a problem of multiple instances of the multi-threaded application being launched. I'm not really sure why this was happening.
I know I'm really hacking this up but I'm backed into a corner with this implementation. I'm just trying to get it to work.
Suggestions?
The goal of this program is to fork and have the child sleep while parent loops infinitely waiting for an interrupt. When I hit ^C, it calls the void parent function. This part works however, the message from the kill ( pid, SIGALRM ) is not working. I checked and pid is the correct process ID for the child.
I've searched for awhile, but I haven't found what I'm doing wrong. I used the kill ( pid, SIGALRM ) before from the child process to the parent but I can't figure out why this isn't working..
#include <signal.h>
#include <unistd.h>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int pid;
void parent ( int sig )
{
kill ( pid, SIGALRM );
cout << "I'm a parent " << getpid() << " My child is " << pid << endl;
}
void child ( int sig )
{
cout << "I am " << getpid() << "my parent is " << getppid()<< endl;
cout << "Use ctrl+backslash to actually end the program" << endl;
}
int main()
{
pid = fork();
if(pid == 0)
{ //Child process
cout << "Child pid = " << getpid() << " Waiting for interrupt." << endl;
(void) signal ( SIGALRM, child );
pause();
}
else if(pid > 0)
{ //Parent
sleep(2);
cout << "child pid = " << pid << endl;
struct sigaction act;
act.sa_handler = parent;
sigemptyset ( &act.sa_mask);
sigaction (SIGINT, &act, 0);
while(1)
{
sleep ( 1 );
}
}
return 0;
}
Ok, so I figured out the problem.
When I was pressing ^C, it would catch the interrupt in the main process, but kill the child process. When I ran a system("ps") from inside the program, it showed the child a.out process to be defunct.
To fix this I added the following to the child's process:
struct sigaction act;
act.sa_handler = CHILD_PRESERVER;
sigemptyset ( &act.sa_mask);
sigaction (SIGINT, &act, 0);
Where CHILD PRESERVER was a dummy function that did nothing except keep it alive.
It doesn't see that this solution is very elegant, so if anyone has a more correct way of doing this please post it.
You can do the same thing as your sigaction solution by just using signal(SIGINT, SIG_IGN);
The thing that tripped you up initially (and often trips up new programmers dealing with ctrl-C and signals) is that ctrl-C sends a signal to AN ENTIRE PROCESS GROUP, rather than to a single process -- every process in the group will get the signal. The process group the signal is sent to is the foreground process group of the terminal.
So this gives you lots of ways of dealing with/controlling ctrl-C interrupts. You can have each process install its own SIGINT handler (as you have done). Or you can carefully manage your process groups, putting children into their own process group (which will generally not be the foreground process group), so they won't get the signal in the first place.
You manage process groups with the setpgrp(2)/setpgid(2) system call.