Zombie process is not cleanup with waitpid call - c++

I am watching the processes with htop and I see that child process stays as zombie even though I clean up with waitpid call. Any idea why this might happen?
Thank you very much!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
void child_signal_handler(int signal) {
printf("Someone is stabbed me with signal %d\n", signal);
}
int main(int argc, char** argv)
{
const pid_t child = fork();
if (child == 0) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = &child_signal_handler;
sigaction(SIGTERM, &sa, NULL);
printf("Child is started in busy loop\n");
while (true)
;
} else {
const int mercy_period = 3;
printf("Parent is angry and gonna kill his child in %d sec\n", mercy_period);
sleep(mercy_period);
kill(child, SIGTERM);
// clean-up zombie child process as it is terminated before we can wait on
// it
int status = 0;
while(waitpid(-1, &status, WNOHANG) > 0);
}
return EXIT_SUCCESS;
}

waitpid glibc implementation comments
If PID is (pid_t) -1, match any process. If the WNOHANG bit is set in OPTIONS, and that child
is not already dead, return (pid_t) 0.
The while loop clearly exits immediately as 0 > 0 is false.
Change the else and the signal to SIGKILL
} else {
const int mercy_period = 3;
printf("Parent is angry and gonna kill his child in %d sec\n", mercy_period);
sleep(mercy_period);
kill(child, SIGKILL);
int status = 0;
pid_t pid = waitpid(-1, &status, WNOHANG);
while(!pid) {
pid = waitpid(-1, &status, WNOHANG);
printf("%d\n", pid);
}
}
After few attempts waitpid will return the pid of the child process. A success.

Related

Why in this case the pid of the calling process and the new thread are the same?

//compile and link with -pthread
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *pthread_function( void *ptr ){
printf("Hello from the new thread, my pid is %d\n", getpid());
}
int main() {
printf("Hello from the calling process, my pid is %d\n", getpid());
pthread_t thread;
int ret;
ret = pthread_create( &thread, NULL, pthread_function, NULL);
if (ret){
printf("ERROR; return code from pthread_create() is %d\n", ret);
exit(-1);
}
/* Last thing that main() should do */
pthread_exit(NULL);
}
When I run this program, I got this result
Hello from the calling process, my pid is 4445
Hello from the new thread, my pid is 4445
Why in this case the pid of the calling process and the new thread are the same?

Detecting child process creation in fork()

The fork() system call makes two identical copies of the address space, one for parent, the other for the child.
When using fork with an if statement, how many times will the child process be created in the following code?
fork();
pid=fork();
if(pid==0)
{
fork();
}
Add a little extra code to get something like:
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = fork();
if (pid != 0) printf("%d\n", pid);
pid = fork();
if (pid != 0) printf("%d\n", pid);
if(pid==0)
{
pid = fork();
if (pid != 0) printf("%d\n", pid);
}
return 0;
}
Then compile, execute, and check its output: 5 child process IDs.

Is there a way to test whether pclose() will succeed?

In my C++ application, I am seeing a pclose() that hangs because the pipe's process hung and never exited. Is there anyway I could do something like select() to test whether the pclose() will return because the child process has completed? I'd rather not do a fork() instead of popen() if possible. If fork() is the only solution, are there any examples of using fork() to replace a popen() / pclose() scenario?
Probably the easiest way, particularly if you only have one child process, is to catch SIGCHLD and set a flag that the process has terminated and pclose() can be called.
Here's a simple example:
sillyprog.c:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("This is some data from the child.\n");
fflush(stdout);
sleep(5);
return 0;
}
pc.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
volatile sig_atomic_t child_done = 0;
void handler(int signum)
{
if ( signum == SIGCHLD ) {
child_done = 1;
}
}
int main(void)
{
/* Set signal handler */
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if ( sigaction(SIGCHLD, &sa, NULL) == -1 ) {
perror("couldn't set signal handler");
return EXIT_FAILURE;
}
/* Open pipe */
FILE * fp = popen("./sillyprog", "r");
if ( !fp ) {
fprintf(stderr, "Couldn't open pipe\n");
return EXIT_FAILURE;
}
/* Get a line from pipe */
char buffer[100];
if ( !fgets(buffer, 100, fp) ) {
fprintf(stderr, "Error calling fgets()\n");
return EXIT_FAILURE;
}
const size_t len = strlen(buffer);
if ( len && buffer[len - 1] == '\n' ) {
buffer[len - 1] = 0;
}
printf("Got '%s' from pipe.\n", buffer);
/* Wait for child to finish */
while ( !child_done ) {
printf("Child not ready, waiting...\n");
sleep(1);
}
/* Close pipe */
if ( pclose(fp) == -1 ) {
fprintf(stderr, "Error calling pclose()\n");
return EXIT_FAILURE;
}
else {
printf("pclose() successfully called.\n");
}
return 0;
}
which outputs:
paul#horus:~/src/sandbox$ ./pc
Got 'This is some data from the child.' from pipe.
Child not ready, waiting...
Child not ready, waiting...
Child not ready, waiting...
Child not ready, waiting...
Child not ready, waiting...
pclose() successfully called.
paul#horus:~/src/sandbox$

C++ daemon won't receive SIGCHLD signal

I'm writing daemon with ability to recover work process CentOS release 5.7 (Final).
Here is example of code:
#define CHILD_NEED_WORK 1
#define CHILD_NEED_TERMINATE 2
int ReloadConfig()
{
....
return 0;
}
void DestroyWorkThread()
{...}
int InitWorkThread()
{
...
return 0;
}
int LoadConfig(char* FileName)
{
...
return 0;
}
void SetPidFile(char* Filename)
{
FILE* f;
f = fopen(Filename, "w+");
if (f)
{
fprintf(f, "%u\n", getpid());
fclose(f);
}
}
int SetFdLimit(int MaxFd)
{
struct rlimit lim;
int status;
lim.rlim_cur = MaxFd;
lim.rlim_max = MaxFd;
status = setrlimit(RLIMIT_NOFILE, &lim);
return status;
}
//Monitor process
int MonitorProc()
{
int pid;
int status;
int need_start = 1;
sigset_t sigset;
siginfo_t siginfo;
parent_pid = getpid();
sigemptyset(&sigset);
sigaddset(&sigset, SIGQUIT);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
sigaddset(&sigset, SIGCHLD);
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigset, NULL);
SetPidFile(PID_FILE);
for (;;)
{
if (need_start)
{
pid = fork();
}
need_start = 1;
if (pid == -1)
{
}
else if (!pid)
{
status = WorkProc();
exit(status);
}
else
{
sigwaitinfo(&sigset, &siginfo);
if (siginfo.si_signo == SIGCHLD)
{
wait(&status);
status = WEXITSTATUS(status);
if (status == CHILD_NEED_TERMINATE)
{
Write("[MONITOR] Child stopped");
break;
}
else if (status == CHILD_NEED_WORK)
{
Write("[MONITOR] Child restart");
}
}
else if (siginfo.si_signo == SIGUSR1)
{
kill(pid, SIGUSR1);
need_start = 0;
}
else if (siginfo.si_signo == 0)
{
need_start = 0;
continue;
}
else
{
Write("[MONITOR] Signal ", strsignal(siginfo.si_signo));
kill(pid, SIGTERM);
status = 0;
break;
}
}
}
Write("[MONITOR] Stop");
unlink(PID_FILE);
return status;
}
//Work process
int WorkProc()
{
struct sigaction sigact;
sigset_t sigset;
int signo;
int status;
sigact.sa_flags = SA_SIGINFO;
sigact.sa_sigaction = signal_error_for_backtrace;
sigemptyset(&sigact.sa_mask);
sigaction(SIGFPE, &sigact, 0);
sigaction(SIGILL, &sigact, 0);
sigaction(SIGSEGV, &sigact, 0);
sigaction(SIGBUS, &sigact, 0);
sigemptyset(&sigset);
sigaddset(&sigset, SIGQUIT);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigset, NULL);
SetFdLimit(FD_LIMIT);
status = InitWorkThread();
if (!status)
{
for (;;)
{
sigwait(&sigset, &signo);
if (signo == SIGUSR1)
{
status = ReloadConfig();
if (status)
{
Write("[DAEMON] Reload config failed");
}
else
{
Write("[DAEMON] Reload config OK");
}
}
else
{
break;
}
}
DestroyWorkThread();
}
else
{
Write("[DAEMON] Create work thread failed");
}
Write("[DAEMON] Stopped");
return CHILD_NEED_TERMINATE;
}
int main(int argc , char *argv[])
{
if (argc != 2)
{
printf("Usage: ./test_daemon.conf failed!\n");
return -1;
}
status = LoadConfig(argv[1]);
if (status)
{
printf("Error: Load config failed\n");
return -1;
}
if (CheckForAnotherInstance())
{
printf("Daemon is already running!\n");
return 1;
}
pid = fork();
if (pid == -1)
{
printf("Error: Start Daemon failed (%s)\n", strerror(errno));
return -1;
}
else if (!pid)
{
umask(0);
setsid();
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
//Monitor process startup
status = MonitorProc();
return status;
}
else
{
return 0;
}
return 0;
}
I use two processes: work process, which produces main work and monitor process, which waits for signals from work process, and restarts them, if it receives required signal. When i try to send a signal to parent process - monitor process - with command kill -s SIGCHLD, it receives this signal.
When i try to terminate child process, parent process doesn't receive SIGCHLD signal- it contunies to wait for signals, and child process transforms to zombie.
But when i use utility strace with parent process, all works fine - child process terminates successfully, and parent process receives SIGCHLD signal.
I read about function waitpid(), which uses to receive SIGCHLD signal, but i want to receive another signals in parent process too.
Any ideas?
My guess , signal handler is not installed before first fork?
You SIG_BLOCK the SIGCHLD so will no receive any signals. But this is ok as you go on to use sigwaitinfo() but you fail to use siginfo.si_pid when you do a wait(), you should use waitpid() for the PID you are cleaning up due to receiving the signal synchronously via sigwaitinfo().
You use WEXITSTATUS() without checking WIFEXITED(status) first. See wait() man page.
Your monitor and work process appear to use the same executable as you do a fork() with out exec() after. So be careful as you may been to restore the signal handler state in the child to get the code in the child to behave normally.
For example the monitor process is the parent? So to get a child it does a fork() and then calls into WorkProc(). Inside WorkProc() it proceeds to block a bunch of signals (but not SIGCLD). However the execution is the sigprocmask(SIG_BLOCK, ...) from MonitorProc() will still be active inside WorkProc().
I am not sure what "if (siginfo.si_signo == 0) " is all about.
So to another your main query the reason why SIGCHLD is not being delivered from the process running WorkProc() function, is because you blocked that signal already inside MonitorProc(). So fix this issue use the 3rd argument to sigprocmask() to save the original block/unblock mask in MonitorProc() and when you fork() and before jumping into WorkProc() restore the block/unblock mask.

Program terminates if I use scanf

I am trying to implement the timer in code, All the example that I found are using while(1) or for(;;) but when I tried with using scanf my program terminates. Is it getting any value on stdin because if I use scanf two times then timer is called two time before exiting from program.
Here is my sample code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <signal.h>
#include <time.h>
#include <linux/socket.h>
#include <time.h>
#define SIGTIMER (SIGRTMAX)
#define SIG SIGUSR1
static timer_t tid;
static timer_t tid2;
void SignalHandler(int, siginfo_t*, void* );
timer_t SetTimer(int, int);
int main(int argc, char *argv[]) {
int t;
printf("SIGRTMAX %d\n", SIGRTMAX);
printf("SIGRTMIN %d\n", SIGRTMIN);
struct sigaction sigact;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = SA_SIGINFO;
sigact.sa_sigaction = SignalHandler;
// set up sigaction to catch signal
if (sigaction(SIGTIMER, &sigact, NULL) == -1) {
perror("sigaction failed");
exit( EXIT_FAILURE );
}
// Establish a handler to catch CTRL+c and use it for exiting.
sigaction(SIGINT, &sigact, NULL);
tid=SetTimer(SIGTIMER, 1000);
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = SignalHandler;
// set up sigaction to catch signal
if (sigaction(SIG, &sa, NULL) == -1) {
perror("sa failed");
exit( EXIT_FAILURE );
}
// Establish a handler to catch CTRL+c and use it for exiting.
sigaction(SIGINT, &sa, NULL);
tid2=SetTimer(SIG, 1000);
// for(;;); or while(1) Working properly
scanf("%d", &t); /// Program terminates
return 0;
}
void SignalHandler(int signo, siginfo_t* info, void* context)
{
if (signo == SIGTIMER) {
printf("Command Caller has ticked\n");
}else if (signo == SIG) {
printf("Data Caller has ticked\n");
} else if (signo == SIGINT) {
timer_delete(tid);
perror("Crtl+c cached!");
exit(1); // exit if CRTL/C is issued
}
}
timer_t SetTimer(int signo, int sec)
{
static struct sigevent sigev;
static timer_t tid;
static struct itimerspec itval;
static struct itimerspec oitval;
// Create the POSIX timer to generate signo
sigev.sigev_notify = SIGEV_SIGNAL;
sigev.sigev_signo = signo;
sigev.sigev_value.sival_ptr = &tid;
if (timer_create(CLOCK_REALTIME, &sigev, &tid) == 0)
{
itval.it_value.tv_sec = sec / 1000;
itval.it_value.tv_nsec = (long)(sec % 1000) * (1000000L);
itval.it_interval.tv_sec = itval.it_value.tv_sec;
itval.it_interval.tv_nsec = itval.it_value.tv_nsec;
if (timer_settime(tid, 0, &itval, &oitval) != 0)
{
perror("time_settime error!");
}
}
else
{
perror("timer_create error!");
return NULL;
}
return tid;
}
So, How can I resolve this problem ?
Any help would be Appreciated.
Thanks, Yuvi
Signal interrupts scanf. If you add perror('scanf') after scanf output will be:
SIGRTMAX 64
SIGRTMIN 34
Command Caller has ticked
Data Caller has ticked
scanf: Interrupted system call
If you replace scanf with:
do {
errno = 0;
scanf("%d", &t);
} while(errno == EINTR);
scanf will be retried when it fail with Interrupted system call.
You absolutely cannot use printf and perror in a signal handler. They are not re-entrant.
Also, it's possible that scanf will return with an error if your program receives a signal while in it. If scanf returns EOF, check errno, it will probably be EINTR.