I have been trying to develop a 3 player game in C using signals but it is not giving desired output.
#define _POSIX_SOURCE //to use functionality from the POSIX.1 standard as ANCI C does not support kill()
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
void action(){}
void child(char *);
int main(){
pid_t pid1, pid2, pid3;
printf("This is a 3-players game with a referee\n\n");
if((pid1=fork()) == 0) child("TOTO");
sleep(1);
if((pid2=fork()) == 0) child("TITI");
sleep(1);
if((pid3=fork()) == 0) child("TUTU");
sleep(1);
while(1){
signal(SIGUSR1, action);
printf("Refree: TOTO plays\n\n");
kill(pid1, SIGUSR1);
pause();
printf("Refree: TITI plays\n\n");
kill(pid2, SIGUSR1);
pause();
printf("Refree: TUTU plays\n\n");
kill(pid3, SIGUSR1);
pause();
}
}
void child(char *s){
int points=0, dice;
srand(time(NULL));
while(1){
signal(SIGUSR1, action); // block myself
pause();
sleep(1);
printf("%s: playing my dice\n", s);
dice = rand() % 10 + 1;
printf("%s: got %d points\n", s, dice);
points+=dice;
printf("%s: Total so far %d\n\n", s, points);
sleep(3);
if(points >= 100){
printf("%s: game over I won\n", s);
kill(0, SIGTERM);
}
kill(getppid(), SIGUSR1);
}
}
Output I get is:
This is a 3-players game with a referee
Refree: TOTO plays
TOTO: playing my dice
TOTO: got 8 points
TOTO: Total so far 8
Refree: TITI plays
TITI: playing my dice
TITI: got 2 points
TITI: Total so far 2
User defined signal 1
It never shows "TUTU" playing dice and terminates using User Defined Signal 1 which is registered as a blank signal. The program should terminate only after a player wins.
Any suggestions?
When the referee catches TOTO's signal, it's disposition is reset to SIG_DFL, so TITI's signal really kills him. The referee must call signal(SIGUSR1, action) three times per loop, before each kill().
An alternative is to #define _BSD_SOURCE (read the Portability section of man 2 signal carefully), which imposes a BSD semantics of not resetting a disposition
Related
If the subprocess does not call the system call, will the signal sent by kill still take effect?
It worked. But I want to know when did the subprocess enter the kernel mode.
Code as follows.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
int pid;
if ((pid = fork()) == 0)
{
while (1)
continue;
exit(0);
}
sleep(3);
kill(pid, SIGINT);
int wid, status;
while ((wid = wait(&status)) != -1)
{
printf("child %d: exited with ", wid);
if (WIFEXITED(status))
printf("%d\n", WEXITSTATUS(status));
if (WIFSIGNALED(status))
printf("%d\n", WTERMSIG(status));
}
return 0;
}
Yes, processes that do not make system calls can still receive signals. You can easily test this by writing an infinite loop program, running it, then pressing Ctrl-C, or using the kill command from another terminal window.
I'm trying to communicate between two processes in C using a pipe. Everything works fine until it is supposed to print "hi\n". The output is
(8841) Child here stopping self
(8841) SAYS: 19
DATA WRITED
C: 8
(8841) CONTINUING
This is a simplified version of the program. I know for a fact the reading part works, but it seems that the writing call does not, because it never prints "hi\n". Any clues on why is that?
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
volatile sig_atomic_t sigchld = 0;
void sigchldHandler(){
sigchld = 1;
return;
}
int main(){
sigset_t mask,prev;
signal(SIGCHLD, sigchldHandler);
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
int pid = fork();
int fd[2];
pipe(fd);
sigprocmask(SIG_BLOCK, &mask, &prev);
if (pid == 0){
dup2(STDIN_FILENO,fd[0]);
printf("(%d) Child here stopping self\n",getpid());
raise(SIGSTOP);
printf("(%d) CONTINUING\n",getpid());
char* hello = malloc(sizeof("hi\n"));
read(STDIN_FILENO,hello,sizeof("hi\n"));
printf("%s",hello);
exit(0);
}
sleep(0.1);
sigprocmask(SIG_SETMASK, &prev,NULL);
while(1){
if (sigchld){
int status;
int p = waitpid(-1,&status,WNOHANG|WUNTRACED);
if (WIFSTOPPED(status)){
if (WSTOPSIG(status) == SIGSTOP){
printf("(%d) SAYS: %d\n",p, WSTOPSIG(status));
kill(pid,SIGCONT);
printf("DATA WRITED\n");
char* h = "hi\n";
int c=write(fd[1],h,sizeof(h));
printf("C: %i\n",c);
break;
}
}
sigchld = 0;
}
}
}
Primary problem
Your key problem is that you call pipe() after you've called fork(). That means the two processes have completely separate pipes; they are not talking to each other.
Secondary issues
There are other issues too, of course.
You have (in the parent): int c=write(fd[1],h,sizeof(h));. You're writing 8 bytes (your output includes C: 8 because the variable h is a pointer of size 8 (you're on a 64-bit system). However, the string only points to 4 bytes — you should be using strlen() or thereabouts to limit the amount of data written.
You aren't closing enough file descriptors for comfort.
You have the arguments to dup2() reversed. This too is crucial.
It seems weird to be using dynamic allocation for just 4 bytes of data, but it should work.
You should print the PID along with the value in hello in the child (for consistency, if nothing else). It's good you do that with the other printing.
The parent should probably wait for the child after the loop (after closing the pipe).
The sleep() function takes an integer; calling sleep(0.1) sleeps for zero seconds. For sub-second sleeping, you need nanosleep() or maybe. usleep() (older, no longer part of POSIX, but widely available and easier to use).
Here's working code:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
volatile sig_atomic_t sigchld = 0;
static void sigchldHandler(int signum)
{
sigchld = signum;
}
int main(void)
{
sigset_t mask, prev;
signal(SIGCHLD, sigchldHandler);
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
int fd[2];
pipe(fd);
int pid = fork();
sigprocmask(SIG_BLOCK, &mask, &prev);
if (pid == 0)
{
/* Child */
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
printf("(%d) Child here stopping self\n", getpid());
raise(SIGSTOP);
printf("(%d) CONTINUING\n", getpid());
char *hello = malloc(sizeof("hi\n"));
int nbytes = read(STDIN_FILENO, hello, sizeof("hi\n"));
printf("(%d) received %d bytes: %.*s\n", getpid(), nbytes, nbytes, hello);
exit(0);
}
/* Parent */
close(fd[0]);
nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 100000000}, NULL);
sigprocmask(SIG_SETMASK, &prev, NULL);
while (1)
{
if (sigchld)
{
int status;
int p = waitpid(-1, &status, WNOHANG | WUNTRACED);
if (WIFSTOPPED(status))
{
if (WSTOPSIG(status) == SIGSTOP)
{
printf("(%d) SAYS: %d\n", p, WSTOPSIG(status));
kill(pid, SIGCONT);
char *h = "hi\n";
int c = write(fd[1], h, strlen(h));
printf("DATA WRITTEN: %i\n", c);
close(fd[1]);
break;
}
}
sigchld = 0;
}
}
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("PID %d exited with status 0x%.4X\n", corpse, status);
return 0;
}
Sample output:
(66589) Child here stopping self
(66589) SAYS: 17
DATA WRITTEN: 3
(66589) CONTINUING
(66589) received 3 bytes: hi
PID 66589 exited with status 0x0000
The difference between 17 (on a Mac running macOS Mojave 10.14.6) and 19 (on a Linux box) is normal; the actual values for signal numbers is not standardized by POSIX (though signals 1 SIGHUP through 15 SIGTERM are the same across systems because they were standard in 7th Edition Unix).
I'm facing an error with the folllowing code.
It only executes the handler related to the odd numbers (in case the parent produces a odd random number included between 1 and 10), while the one for the even is always "mute".
Could someone help me out?
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <signal.h>
#include <math.h>
pid_t pid1,pid2;
int r, ric1, ric2;
int fd[4]; //per 2 processi figli
void handler_one(){
read(fd[0], &ric1, sizeof(int));
printf("Im the first child process...the even received numebr is...%d\n", ric1);
sleep(1);
}
void handler_two(){
read(fd[2], &ric2, sizeof(int));
printf("I'm the second child process...the odd receiver number is...%d\n", ric2);
sleep(1);
}
void main(){
pipe(fd);
pipe(fd+2);
pid1=fork();
if(pid1)
pid2=fork();
while(1){
if(pid1<0 || pid2<0){
perror("AN ERROR OCCURRED!!!!!!!\n");
exit(1);
}
else if(!pid1 && pid2){ //child #1
signal(SIGUSR1, handler_one);
}
else if(pid1 && !pid2){ //child #2
signal(SIGUSR2, handler_two);
}
else{ //padre
printf("I'm the parent and I'm gonna send a random number\n");
r=rand()%10+1;
if(r%2==0){
write(fd[1], &r, sizeof(int));
kill(pid1, SIGUSR1);
}
else{
write(fd[3], &r, sizeof(int));
kill(pid2, SIGUSR2);
}
sleep(1);
}
}
}
Your condition for setting up the child 1 signal handler is incorrect. Instead of
else if(!pid1 && pid2){ //child #1
it should be
else if(!pid1 && !pid2){ //child #1
Not sure if you are guaranteed pid1 and pid2 are initialized to zero, you may want to do that explicitly.
I have read the manual of kill, and I know that it is a system call for sending a signal. I write a simple multi process code, each child process will do the handler function if it catch a specified signal, which is SIGUSR1 in my code.
In my code, I have made 3 processes, each process will print out "yo" if they catch SIGUSR1 signal, but the output only print out one time or two time..? That really confuse me, thanks for your help!
Here is my code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <signal.h>
#include <sys/time.h>
#include <string.h>
#define N 3
int pid_id[N];
void handler (int signum)
{
printf("yo\n");
}
void child(int process_index)
{
struct sigaction sa;
/* Register */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handler;
sigaction(SIGUSR1, &sa, NULL);
printf("I am %d.\n", getpid());
pid_id[process_index] = getpid();
sleep(5);
exit(0);
}
int main()
{
int i, status;
pid_t pid[N];
pid_t pid_wait;
for (i=0;i<N;i++)
{
pid[i] = fork();
if (pid[i]==0)
{
child(i);
}
}
for (i=0;i<N;i++)
kill(pid_id[i], SIGUSR1);
for (i=0;i<N;i++)
{
do
{
pid_wait = waitpid(pid[i], &status, WNOHANG);
}while(pid_wait != pid[i]);
}
printf("all done\n");
return 0;
}
Remember that you're dealing with multiple processes now. Just because in the code it looks like you ran child before kill doesn't mean that it happened in that order. The order of execution is entirely dependent on how the OS schedules CPU time for these processes.
What's happening is that some of the child processes are killed before they can install their signal handler. This is an example of a race condition, much like the sort you get when starting new threads.
This can be solved by synchronising the parent with its children, so that it doesn't continue until all children have notified back that they have completed their necessary initialisation steps.
I'm made this code, and I have to use the alarm signal (SIGALRM) to make the program print the message “I am alive.” every 3 seconds.
But it doesn't work, it sends the message "I'm Alive" only when I press CTR-C, I'm guessing
I didn't put the SIGALRM function in the right place, can you help me?
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
unsigned Count = 0; //Counts the number of times it receives the signal SIGINT.
void mypause(int sign); //prototype of the function my pause.
void mypause(int sign) {
signal(SIGALRM, mypause); //Set alarm clock for 3 seconds.
alarm(3);
printf("I'm Alive");
signal(SIGINT, mypause);
switch (sign) {
case SIGINT:
printf("\nPressed CTR-C\n");
printf("I'm running, waiting for a sign\n");
Count++;
break;
case SIGQUIT:
printf("\nPressed CTR-\\n");
printf("You pressed CTR-C %d times", Conta);
exit(0); //Exit program.
break;
}
}
int main() {
signal(SIGALRM, mypause);
signal(SIGINT, mypause);
signal(SIGQUIT, mypause);
printf("\nI'm running waiting for a signal\n");
while (1) {}
return (0);
}
Maybe add alarm(3) in your main() ?