Thread: A question about signals handling

  1. #1
    Registered User
    Join Date
    Oct 2007
    Posts
    100

    A question about signals handling

    Hello everybody! I have a small doubt about signals which hope can find an answer here! What I need is just a confirmation or denial of my understanding of what follows...

    In general, when we define a signal handler, we have to declare the function as:

    Code:
    void sig_hand(int signo)
    {
    }
    and register the function for a given signal using the following statement:

    Code:
    struct sigaction sa={sa_handler=sig_hand}
    sigaction(SIGUSR1,&sa,0); //example for SIGUSR1
    This means that every time a registered signal is received by the process who executed the sigaction, sig_hand is executed and after that the control is given back to the process' code. If I want the process to behave in a certain way according to the signal received (Eg SIGUSR1 or SIGUSR2) I have to embed the behaviour directly in the sig_hand, isn't it?
    In general, a process can be aware of the signal it received only by means of the handler function, isn't it?
    For example, if a process is designed as receiver of several differen signals and I want it to print "I received a SIGXXX" every time it gets a signal, the only solution I can see is to embed the printf in the various sig_handXXX...
    In the case of 2 clone processes (with roles signal sender and receiver) another possibility I considered was to use a volatile int global variabile which is set with the signo and then checked by the receiving clone, but this would not work in the case of multiple sender/receivers...
    Are there other/better ways to face the problem?

    Thanks!
    bye
    /* NO COMMENT */

  2. #2
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Code:
    struct sigaction sa={sa_handler=sig_hand}
    That's not exactly valid syntax, but I'll assume it was just for demonstration purposes.

    If I want the process to behave in a certain way according to the signal received (Eg SIGUSR1 or SIGUSR2) I have to embed the behaviour directly in the sig_hand, isn't it?
    In general, a process can be aware of the signal it received only by means of the handler function, isn't it?
    For example, if a process is designed as receiver of several differen signals and I want it to print "I received a SIGXXX" every time it gets a signal, the only solution I can see is to embed the printf in the various sig_handXXX...
    No, there's also sigwait() and friends. Also, be very careful what functions you call inside a signal handler. See the signal(7) manpage for a list of safe functions. (printf is not in there)

    another possibility I considered was to use a volatile int global variabile which is set with the signo and then checked by the receiving clone, but this would not work in the case of multiple sender/receivers...
    True. In addition, be aware that the only global variable you're allowed to touch from a signal handler is one of type volatile sigatomic_t.
    Not that signals are a very good way of interprocess communication. There's pipes, message queues, semaphores and shared memory.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  3. #3
    Registered User
    Join Date
    Oct 2007
    Posts
    100
    hi!
    I invented another exercise to train with signals...
    The text sounds like this:
    Code:
    The program receives from the command line a string and a list of directories (1 or more). 
    The father process creates 1 clone for every directory and gives it the task of searching a directory for a regular file containing 
    the given string in its name.
    Every clone has to write in a text file (whose name is the same than the directory's one where '/' have been replaced with '_') 
    the name of every scanned file. 
    Upon match, the clone writes in the file "FILE FOUND", closes it and sends a SIGUSR1 to the father, which in turn sends
     a SIGTERM to every other clone. 
    The action connected with the SIGTERM is to write "FILE NOT FOUND" in the file, close it and return.
    What I cannot understand is how to implement the action connected with the SIGTERM. the last write ("FILE NOT FOUND") and the file close action should be embedded in an handler function?
    In this case I should use global vars to hold the files descriptor of every process in a way which sounds not that "clean"... How else could I do?

    Thanks! Bye!
    /* NO COMMENT */

  4. #4
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Since every process has a loop over the files in the directory, have a global flag
    volatile sigatomic_t g_other_process_found_it = 0;
    that is checked on every iteration as well. In the SIGTERM handler, set the flag. This will cause the loop to break. Then simply check for the flag again and write FILE NOT FOUND.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  5. #5
    Registered User
    Join Date
    Oct 2007
    Posts
    100
    Quote Originally Posted by CornedBee View Post
    Since every process has a loop over the files in the directory, have a global flag
    volatile sigatomic_t g_other_process_found_it = 0;
    that is checked on every iteration as well. In the SIGTERM handler, set the flag. This will cause the loop to break. Then simply check for the flag again and write FILE NOT FOUND.
    thank you CornedBee! without this forum i'd be dead!
    Which is the difference between volatile sigatomic_t and volatile int? I had to use the second since siatomic_t didn't compile.. Which library has to be imported?
    here is my code.. The only thing I don't manage well is if 2 or more processes find the file at the same time.. To fix this, would the use of a lock before kill(ppid,SIGUSR1) be a good idea?

    Thanks for any comment on my code!

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <dirent.h>
    #include <signal.h>
    #include <sched.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <unistd.h>
    
    volatile int g_other_process_found_it = 0;
    volatile int firstToEnd = 0;
    
    struct args {
    	char pathname[4096];
    	char keyString[256];
    };
    typedef struct args Args;
    
    void replaceChar(char *str, char old, char new)
    {
    	while (*str!='\0')
    	{
    		if (*str==old)
    		{
    			*str=new;
    		}
    		str++;
    	}
    	
    }
    
    void handlTERM(int signo)
    {
    	++g_other_process_found_it;
    	printf("SIGTERM received by %d.\n",(int)getpid());
    }
    
    void handlerUSR1(int signo)
    {
    	printf("SIGUSR1 received from %d\n",(int)firstToEnd);
    }
    
    void printfilecontent(int fd)
    {
    	lseek(fd,0,SEEK_SET);
    	char ch;
    	while (read(fd,&ch,1)==1)
    	{
    		printf("%c",ch);
    	}
    }
    
    int clone_func(void *arguments)
    {
    	Args *a=(Args *) arguments;
    	pid_t pid=getpid(),ppid=getppid();
    	char filename[256],scanning[256];
    	size_t scanlen;
    	int fd;
    
    	*scanning='\0';
    	strcpy(filename, a->pathname);
    	replaceChar(filename,'/','_');
    	
    	printf("I am %d, son of %d, and I'll search for files whose name contains %s into %s. The results of my scan will be available in file '%s'\n",
    	(int)pid,(int)ppid,a->keyString,a->pathname,filename);
    	
    	//I register here my handler
    	struct sigaction saTERM ={.sa_handler=handlTERM};
    	sigaction (SIGTERM,&saTERM,NULL);
    
    	fd = open (filename,O_CREAT|O_WRONLY|O_TRUNC,0666);
    
    	if (fd<0)
    	{
    		printf("BAD FILE OPEN in CLONE\n");
    		return 1;
    	}
    	
    	struct dirent *entry;
    	DIR *dir;
    	struct stat statbuff;
    
    	dir=opendir(a->pathname);
    	if (dir==NULL)
    	{
    		printf("BAD OPENDIR. Probably directory doesn't exist\n");
    		return 1;
    	}
    
    	chdir(a->pathname); //I change the CWD of the process to its target
    	while (!g_other_process_found_it&&(entry=readdir(dir))!=NULL)
    	{
    		sleep(1);
    
    		if (lstat(entry->d_name,&statbuff)<0)
    		{
    			printf("BAD lstat\n");
    			return 1;
    		}
    		if (S_ISREG(statbuff.st_mode))
    		{
    			if (strcmp(".",entry->d_name)!=0 && strcmp ("..",entry->d_name)!=0) //I don't consider . and .. entries
    			{
    				//First I do write the filename into the file
    				strcpy(scanning,entry->d_name);
    				strcat(scanning,"\n");
    				scanlen = strlen(scanning);
    				write(fd,scanning,scanlen);
    				//then I chech whether I'm finished
    				if (strstr(scanning, a->keyString))
    				{
    					strcpy(scanning,"String found!!\n");
    					scanlen=strlen(scanning);
    					write(fd,scanning,scanlen);
    					//I don't care of coming back to my old CWD
    					close(fd);
    					firstToEnd=pid;
    					kill(ppid,SIGUSR1);// I notify the father
    
    					return 0;
    				}
    			}
    		}
    	}
    	//The search finished in an unsuccessfull way (file not found or another process found it)
    	strcpy(scanning,"String NOT found!!\n");
    	scanlen=strlen(scanning);
    	write(fd,scanning,scanlen);
    	close(fd);
    
    	return 0;
    }
    
    
    int main (int argc, char *argv[])
    {
    	if (argc<3)
    	{
    		printf("usage: filename keystring searchdir1 searchdir2 ...\n");
    		return 1;
    	}
    
    	int numClones=argc-2;
    
    	int *stackClones;
    	stackClones = malloc(sizeof(int)*1024*numClones);
    	if (stackClones==NULL)
    	{
    		return 1;
    	}
    
    	pid_t *pidClones;
    	pidClones = malloc (sizeof(pid_t)*numClones);
    	if (pidClones==NULL)
    	{
    		return 1;
    	}
    
    	struct sigaction sa={.sa_handler=handlerUSR1};
    	sigaction (SIGUSR1,&sa,0);
    	int i;
    	Args *ar;
    	ar=malloc (sizeof(Args)*numClones);
    	if (ar==NULL)
    	{
    		return 1;
    	}
    
    	for (i=0;i<numClones;i++)
    	{
    		strcpy(ar[i].pathname,argv[2+i]);
    		strcpy(ar[i].keyString,argv[1]);
    
    		pidClones[i]=clone(clone_func,stackClones+((i+1)*1023)*sizeof(int),CLONE_VM,&ar[i]);
    		if (pidClones[i]<0)
    		{
    			printf("BAD CLONE\n");
    			return 1;
    		}
    	}
    	pid_t pid;
    	int j;
    	//I do wait all the children to finish (supposing that none of them will find the file)
    	for (i=0;i<numClones;i++)
    	{	
    		pid=waitpid(-1,NULL,__WCLONE);
    		if (firstToEnd!=0) break;
    		printf("CLONE %d ENDED NORMALLY\n",(int)pid);
    		//Now I set the pid of the normally terminated clone to 0 so that I'll not send a SIGTERM to it in the case another clone will
    		//find the file
    		for(j=0;j<numClones;j++)
    			if (pidClones[j]==pid) pidClones[j]=0;
    		
    	}
    	//I am here either because I received a SIGUSR1 either because all the clones terminated
    	for (i=0;i<numClones;i++)
    	{
    		if (pidClones[i]!=firstToEnd&&pidClones[i]>0)
    		{
    			kill(pidClones[i],SIGTERM);
    			printf("SIGTERM sent to %d\n",(int)pidClones[i]);
    		}
    	}
    	printf("PROGRAM TERMINATED\n");
    	return 0;
    }
    /* NO COMMENT */

  6. #6
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Which is the difference between volatile sigatomic_t and volatile int?
    The spec says that only volatile sigatomic_t globals may be accessed from signal handlers.

    Seems like it's sig_atomic_t, though. It's defined in signal.h.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  7. #7
    Registered User
    Join Date
    Oct 2007
    Posts
    100
    I was thinking: what if I want the clone to be aware that he is exactly the recipient of the SIGTERM? Could I set the global variable with its pid and check this in the while()?
    thanks! Bye
    /* NO COMMENT */

  8. #8
    Registered User
    Join Date
    Oct 2007
    Posts
    100
    Quote Originally Posted by smoking81 View Post
    I was thinking: what if I want the clone to be aware that he is exactly the recipient of the SIGTERM? Could I set the global variable with its pid and check this in the while()?
    thanks! Bye
    nobody can help me? Please...
    /* NO COMMENT */

  9. #9
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by smoking81 View Post
    I was thinking: what if I want the clone to be aware that he is exactly the recipient of the SIGTERM?
    What do you mean? The clone is aware of being the recipient by virtue of receiving the signal.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  10. #10
    Registered User
    Join Date
    Oct 2007
    Posts
    100
    Quote Originally Posted by CornedBee View Post
    What do you mean? The clone is aware of being the recipient by virtue of receiving the signal.
    I mean: suppose that I want to terminate a loop like this

    Code:
    while (1)
    {
          pause(); //stop until you receive any signal
         if (I_received_a_sigterm)
         break;
    }
    Would it be a solution to traslate I_received_a_sigterm with a statement like my_pid==sigterm_recipient where sigterm_recipient is a global var which is set in the sig_handler by means of getpid()?

    thanks for your help!
    Bye
    /* NO COMMENT */

  11. #11
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Do your clones actually have a shared address space, like threads? (Yes, I know it's possible. Doesn't necessarily mean it's smart.)

    Anyway, if you did it like that, you'd have one hell of a race condition.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. execlp() vs excelp < 0 error handling question
    By Overworked_PhD in forum Linux Programming
    Replies: 2
    Last Post: 06-29-2008, 04:33 AM
  2. File handling question...
    By Raigne in forum C++ Programming
    Replies: 7
    Last Post: 04-10-2008, 02:02 AM
  3. Question...
    By TechWins in forum A Brief History of Cprogramming.com
    Replies: 16
    Last Post: 07-28-2003, 09:47 PM
  4. opengl DC question
    By SAMSAM in forum Game Programming
    Replies: 6
    Last Post: 02-26-2003, 09:22 PM
  5. String handling question
    By Chimpsag in forum C++ Programming
    Replies: 0
    Last Post: 12-28-2002, 05:16 PM