Terminate a thread that activates a process in the shell - c

Using pthreads, I created a thread that does audio recording through shell:
void *thread_function(void *arg) {
system("arecord data.wav");
}
However, when I call pthread_cancel(&thread_ID); to terminate the thread, the audio recorder still works on its own (Until I terminate the whole C program of course).
How do I stop a pthread that does a system call? Thanks in advance!

Your thread start function should do the following:
pid_t pid;
int status;
posix_spawnp(&pid, "arecord", 0, 0, (char *[]){"arecord", "data.wav", 0}, environ);
pthread_cleanup_push(cleanup, &pid);
while (waitpid(pid, &status, 0)<0 && errno==EINTR);
pthread_cleanup_pop(0);
With a cleanup function like:
static void cleanup(void *p)
{
pid_t pid = *(pid_t *)p;
kill(pid, SIGTERM);
while (waitpid(pid, &status, 0)<0 && errno==EINTR);
}
Then cancelling the thread with pthread_cancel will kill the child process.

system("arecord data.wav");
It will make a separate process (not a thread in your program) in your system, and terminating that thread will not affect that process. You should kill that process by another system call.
However making the process with spawn* functions in non-waiting mode is a bit better than your way and in this case and you don't need an extra thread.
spawnl(P_NOWAIT, "arecord data.wav", .... );
But, killing the created process is ugly.

You can use pthread_kill to send a signal to a specific thread. The problem with thread cancellation is that it will be delayed until the program reaches a cancellation point (see this answer).
If you do use pthread_kill then you can choose which signal to send. I think it should be possible to end the thread with a kill signal which would then also end the child process spawned with system, although I'm not certain about that.

Related

Why does waitpid in a signal handler need to loop?

I read in an ebook that waitpid(-1, &status, WNOHANG) should be put under a while loop so that if multiple child process exits simultaniously , they are all get reaped.
I tried this concept by creating and terminating 2 child processes at the same time and reaping it by waitpid WITHOUT using loop. And the are all been reaped .
Question is , is it very necessary to put waitpid under a loop ?
#include<stdio.h>
#include<sys/wait.h>
#include<signal.h>
int func(int pid)
{
if(pid < 0)
return 0;
func(pid - 1);
}
void sighand(int sig)
{
int i=45;
int stat, pid;
printf("Signal caught\n");
//while( (
pid = waitpid(-1, &stat, WNOHANG);
//) > 0){
printf("Reaped process %d----%d\n", pid, stat);
func(pid);
}
int main()
{
int i;
signal(SIGCHLD, sighand);
pid_t child_id;
if( (child_id=fork()) == 0 ) //child process
{
printf("Child ID %d\n",getpid());
printf("child exiting ...\n");
}
else
{
if( (child_id=fork()) == 0 ) //child process
{
printf("Child ID %d\n",getpid());
printf("child exiting ...\n");
}
else
{
printf("------------Parent with ID %d \n",getpid());
printf("parent exiting ....\n");
sleep(10);
sleep(10);
}
}
}
Yes.
Okay, I'll elaborate.
Each call to waitpid reaps one, and only one, child. Since you put the call inside the signal handler, there is no guarantee that the second child will exit before you finish executing the first signal handler. For two processes that is okay (the pending signal will be handled when you finish), but for more, it might be that two children will finish while you're still handling another one. Since signals are not queued, you will miss a notification.
If that happens, you will not reap all children. To avoid that problem, the loop recommendation was introduced. If you want to see it happen, try running your test with more children. The more you run, the more likely you'll see the problem.
With that out of the way, let's talk about some other issues.
First, your signal handler calls printf. That is a major no-no. Very few functions are signal handler safe, and printf definitely isn't one. You can try and make your signal handler safer, but a much saner approach is to put in a signal handler that merely sets a flag, and then doing the actual wait call in your main program's flow.
Since your main flow is, typically, to call select/epoll, make sure to look up pselect and epoll_pwait, and to understand what they do and why they are needed.
Even better (but Linux specific), look up signalfd. You might not need the signal handler at all.
Edited to add:
The loop does not change the fact that two signal deliveries are merged into one handler call. What it does do is that this one call handles all pending events.
Of course, once that's the case, you must use WNOHANG. The same artifacts that cause signals to be merged might also cause you to handle an event for which a signal is yet to be delivered.
If that happens, then once your first signal handler exists, it will get called again. This time, however, there will be no pending events (as the events were already extracted by the loop). If you do not specify WNOHANG, your wait block, and the program will be stuck indefinitely.

How can I prevent zombie child processes?

I am writing a server that uses fork() to spawn handlers for client connections. The server does not need to know about what happens to the forked processes – they work on their own, and when they're done, they should just die instead of becoming zombies. What is an easy way to accomplish this?
There are several ways, but using sigaction with SA_NOCLDWAIT in the parent process is probably the easiest one:
struct sigaction sigchld_action = {
.sa_handler = SIG_DFL,
.sa_flags = SA_NOCLDWAIT
};
sigaction(SIGCHLD, &sigchld_action, NULL);
Use double forks. Have your children immediately fork another copy and have the original child process exit.
http://thinkiii.blogspot.com/2009/12/double-fork-to-avoid-zombie-process.html
This is simpler than using signals, in my opinion, and more understandable.
void safe_fork()
{
pid_t pid;
if (!pid=fork()) {
if (!fork()) {
/* this is the child that keeps going */
do_something(); /* or exec */
} else {
/* the first child process exits */
exit(0);
}
} else {
/* this is the original process */
/* wait for the first child to exit which it will immediately */
waitpid(pid);
}
}
How to get rid of zombie processes?
you can’t kill the zombie process with SIGKILL signal as you kill a normall process, As the zombie process can’t recive any signal. so having a good habit is very important.
Then when programming, how to get rid amount of zombie processes? According to the above description, the child process will send SIGCHLD signals to the parent process when its dies. by default, this signal is ignored by system, so the best way is that we can call wait() in the signal processing function, which could avoid the zombie stick around in the system.
see more about this: http://itsprite.com/how-to-deep-understand-the-zombie-process-in-linux/

After calling SIGTSTP on child, no response from parent [duplicate]

I'm coding a basic shell in C, and I'm working on suspending a child process right now.
I think my signal handler is correct, and my child process is suspending, but after that, the terminal should return to the parent process and that's not happening.
The child is suspended, but my shell isn't registering any input or output anymore. tcsetpgrp() doesn't seem to be helping.
Here's my signal handler in my shell code for SIGTSTP:
void suspend(int sig) {
pid_t pid;
sigset_t mask;
//mpid is the pgid of this shell.
tcsetpgrp(STDIN_FILENO, mpid);
tcsetpgrp(STDOUT_FILENO, mpid);
sigemptyset(&mask);
sigaddset(&mask, SIGTSTP);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
signal(SIGTSTP, SIG_DFL);
//active.pid is the pid of the child currently in the fg.
if (active.pid != 0) {
kill(active.pid, SIGTSTP);
}
else{
//if this code is being run in the child, child calls SIGTSTP on itself.
pid = getpid();
if (pid != 0 && pid != mpid){
kill(pid, SIGTSTP);
}
}
signal(SIGTSTP, suspend);
}
Can anyone tell me what I'm doing wrong?
Am I suspending my shell along with the child, and do I need to return stdin and stdout to the shell somehow? How would I do this?
Thanks!
It's an old question but still I think I found an answer.
You didn't write your parent's code but I'm assuming its looks something like:
int main(){
pid_t pid = fork();
if(pid == 0) //child process
//call some program
else //parent process
wait(&status); //or waitpid(pid, &status, 0)
//continue with the program
}
the problem is with the wait() or waitpid(), it's look like if you run your program on OS like Ubuntu after using Ctrl+Z your child process is getting the SIGTSTP but the wait() function in the parent process is still waiting!
The right way of doing that is to replace the wait() in the parent with pause(), and make another handler that catch SIGCHLD. For example:
void sigHandler(int signum){
switch(signum){
case SIGCHLD:
// note that the last argument is important for the wait to work
waitpid(-1, &status, WNOHANG);
break;
}
}
In this case after the child process receive Ctrl+Z the parent process also receive SIGCHLD and the pause() return.
tcsetpgrp is to specify what is the foreground job. When your shell spawns a job in foreground (without &), it should create a new process group and make that the foreground job (of the controlling terminal, not whatever's on STDIN). Then, upon pressing CTRL-Z, that job will get the TSTP. It's the terminal that suspends the job, not your shell. Your shell shouldn't trap TSTP or send TSTP to anyone.
It should just wait() for the job it has spawned and detect when it has been stopped (and claim back the foreground group and mark the job as suspended internally). Your fg command would make the job's pgid the foreground process group again and send a SIGCONT to it and wait for it again, while bg would just send the SIGCONT
i used folk with signals for make process pause and resume with ctrl+c
video while is running : link
Code:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void reverse_handler(int sig);
_Bool isPause=0;
_Bool isRunning=1;
int main()
{
int ppid;
int counter=0;
//make parent respond for ctrl+c (pause,resume).
signal(SIGINT,reverse_handler);
while(isRunning){
while(isPause==0)
{
/*code exec while process is resuming */
printf("\nc:%d",counter++);
fflush(stdout);
sleep(1);
}
//close parent after child is alive.
if((ppid=fork())==0){ exit(0); }
//make child respond for ctrl+c (pause,resume).
signal(SIGINT,reverse_handler);
//keep child alive and listening.
while(isPause==1){ /*code exec while process is pausing */ sleep(1); }
}
return 0;
}
//if process is pause made it resume and vice versa.
void reverse_handler(int sig){
if(isPause==0){
printf("\nPaused");
fflush(stdout);
isPause=1;
}
else if(isPause==1){
printf("\nresuming");
fflush(stdout);
isPause=0;
}
}
i hope that's be useful.
please comment me if there's any questions
I might be late to answer the question here but this is what worked when I was stuck with the same problem. According to the man pages for tcsetpgrp()
The function tcsetpgrp() makes the process group with process group ID
pgrp the foreground process group on the terminal associated to fd,
which must be the controlling terminal of the calling process, and
still be associated with its session. Moreover, pgrp must be a
(nonempty) process group belonging to the same session as the calling
process.
If tcsetpgrp() is called by a member of a background process group in
its session, and the calling process is not blocking or ignoring
SIGTTOU, a SIGTTOU signal is sent to all members of this background
process group.
So, what worked for me was ignoring the signal SIGTTOU in the shell program, before I created the processes that would come to the foreground. If I do not ignore this signal, then the kernel will send this signal to my shell program and suspend it.

killing child processes at parent process exit

I'm very new to c and programming and need some help. In c on linux(cygwin) I am required to remove all child processes at exit. I have looked at the other similar questions but can't get it to work. I've tried-
atexit(killzombies); //in parent process
void killzombies(void)
{
printf("works");
kill(0, SIGTERM);
printf("works");
if (waitpid(-1, SIGCHLD, WNOHANG) < 0)
printf("works");
}
for some reason, "works" doesn't even print ever. I press ctrl + c to exit.
ALSO I have tried-
prctl(PR_SET_PDEATHSIG, SIGHUP); //in child process
signal(SIGHUP, killMe);
void killMe()
{
printf("works");
exit(1);
}
but because I'm using cygwin, when I #include <sys/prctl.h>, cygwin says it can't find the file or directory and I don't know what package to install for it.
Also, if my prctl() function were to work, would that kill all the zombies?
My program is a client server and my server forks() to handle each client. I'm suppose to leave no remaining zombies when the server shuts down.
Your waitpid does not supply the usual parameters, I'm surprised it does not crash. The prototype is:
pid_t waitpid(pid_t pid, int *status, int options);
The second parameter should be a pointer to an int, you are supplying an int.
Notice also that you should call waitpid for each child, you are only calling it for one.
atexit() is only called if you exit normally. If you are exiting through CTRL+C then you need to call your function from a handler on SIGINT.
From the Linux documentation of atexit(3):
Functions registered using atexit() (and on_exit(3)) are not called if
a process terminates abnormally because of the delivery of a signal.
If you want to cleanup when your application receives a SIGINT or SIGTERM, you'll need to install the appropriate signal handlers and do your work there.
You'll need to keep track of how many children your process has and then call wait that many times. Also, as others have said, your atexit() function won't be called if the process is terminated by a signal, so you'll need to call killzombies() from a signal handler as well. You'll need something like:
int n_children = 0; // global
void handle_sig(int sig) {
killzombies();
exit(sig);
}
// your atexit()
void killzombies() {
kill(0, SIGKILL);
while (n_children > 0) {
if (wait(NULL) != -1) {
n_children--;
}
}
}

How variables are shared between two process when the fork is involved

/* In alarm.c, the first function, ding, simulates an alarm clock. */
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
static int alarm_fired = 0;
void ding(int sig)
{
alarm_fired = 1;
}
/* In main, we tell the child process to wait for five seconds
before sending a SIGALRM signal to its parent. */
int main()
{
pid_t pid;
printf("alarm application starting\n");
pid = fork();
switch(pid) {
case -1:
/* Failure */
perror("fork failed");
exit(1);
case 0:
/* child */
sleep(5);
printf("getppid: %d\n", getppid());
kill(getppid(), SIGALRM);
exit(0);
}
/* The parent process arranges to catch SIGALRM with a call to signal
and then waits for the inevitable. */
printf("waiting for alarm to go off\n");
(void) signal(SIGALRM, ding);
printf("pid: %d\n", getpid());
pause();
if (alarm_fired)
printf("Ding!\n");
printf("done\n");
exit(0);
}
I have run the above code under Ubuntu 10.04 LTS
> user#ubuntu:~/Documents/./alarm
> alarm application starting
> waiting for alarm to go off
> pid: 3055
> getppid: 3055
> Ding!
> done
I have read the following statement from a book.
It’s important to be clear about the
difference between the fork system
call and the creation of new threads.
When a process executes a fork call, a
new copy of the process is created
with its own variables and its own
PID. This new process is scheduled
independently, and (in general)
executes almost independently of the
process that created it.
Question:
It seems to me that the variable alarm_fired is shared between the original process and the new created process.
Is that correct?
No. Each process gets its own copy of the variable (and pretty much everything else). If you change the variable in one process, it is changed only in that process, not in both. Each process has its own address space.
Compare that with threads, where all threads share a single address space, so a change in a variable in one thread will be visible in all other threads (within that process).
From the Linux fork(2) manpage:
fork() creates a child process that differs from the parent process only in its PID and PPID, and in the fact that resource utilizations are set to 0. File locks and pending signals are not inherited.
It is shared in the sense that immediately after the fork it has the same value in both processes. BUT when either writes to it the change is not propagated to the other process (that what different .
Also, see copy on write for interesting stuff.
EDIT
It seems that the new created process
modified the variable alarm_fired
which is then later seen by the old
process
The child is sending a signal to the parent. The parent then executes the handler and personally sets alarm_fired to one. The child itself never touches that variable.
No, variables are not shared across a fork(). In your code, the child process never touches alarm_fired. What the child does is send a signal to the parent. That signal fires a signal handler in the parent process' context, setting the variable.

Resources