Translate

Chủ Nhật, 4 tháng 12, 2016

Signals

// Created by Flash -NDQ

1.Definition
Linux provides a mechanism to interrupt a running program. Some of the interrupts are the result of user programs doing something bad, and some of the interrupts are external events that are intending to get the attentions of a running program.


Numerous conditions can generate a signal:
  • Memory segment violations : Process attempts to access a memory address that does not belong to it. SIGSEGV signal
  • Hardware exceptions generate signals: devide by 0 (detected by hardware and the kernel is notified. The kernel then generates the appropriate signal for the process that was running at the time condition occurred)
  • Software conditions can generate signals when a process should be notified of various events: Ex SIGURG (generated when out-of-band data arrives over a network conditions).
  • From user space or from some other process when someone calls a function like kill()
  • When you send the signal from the process to itself using a function like abort()
  • When a child process exits the operating system and then sends the SIGCHLD signal to its parent process.
  • When user interrupts program from the keyboard, SIGINT is sent.
  • When the program behaves incorrectly, one of SIGILL, SIGFPE, SIGSEGV is delivered.

2. The Wide World of Signals

Every signal has a name, it starts with SIG and ends with a description. 

Name
Number
Default
Action
Description
SIGHUP
1
Exit
Hangup (ref termio(7I))
SIGINT
2
Exit
Interrupt (ref termio(7I))
SIGQUIT
3
Core
Quit (ref termio(7I))
SIGILL
4
Core
Illegal Instruction
SIGTRAP
5
Core
Trace or breakpoint trap
SIGABRT
6
Core
Abort
SIGEMT
7
Core
Emulation trap
SIGFPE
8
Core
Arithmetic exception
SIGKILL
9
Exit
Kill
SIGBUS
10
Core
Bus error--a misaligned address error
SIGSEGV
11
Core
Segmentation fault, an address reference boundary error
SIGSYS
12
Core
Bad system call
SIGPIPE
13
Exit
Broken pipe
SIGALRM
14
Exit
Alarm clock
SIGTERM
15
Exit
Terminated
SIGUSR1
16
Exit
User defined signal 1
SIGUSR2
17
Exit
User defined signal 2
SIGCHLD
18
Ignore
Child process status changed
SIGPWR
19
Ignore
Power fail or restart
SIGWINCH
20
Ignore
Window size change
SIGURG
21
Ignore
Urgent socket condition
SIGPOLL
22
Exit
Pollable event (ref streamio(7I))
SIGSTOP
23
Stop
Stop (cannot be caught or ignored)
SIGTSTP
24
Stop
Stop (job control, e.g., CTRL-z))
SIGCONT
25
Ignore
Continued
SIGTTIN
26
Stop
Stopped--tty input (ref termio(7I))
SIGTTOU
27
Stop
Stopped--tty output (ref termio(7I))
SIGVTALRM
28
Exit
Virtual timer expired
SIGPROF
29
Exit
Profiling timer expired
SIGXCPU
30
Core
CPU time limit exceeded (ref getrlimit(2))
SIGXFSZ
31
Core
File size limit exceeded (ref getrlimit(2))
SIGWAITING
32
Ignore
Concurrency signal used by threads library
SIGLWP
33
Ignore
Inter-LWP signal used by threads library
SIGFREEZE
34
Ignore
Checkpoint suspend
SIGTHAW
35
Ignore
Checkpoint resume
SIGCANCEL
36
Ignore
Cancellation signal used by threads library
SIGLOST
37
Ignore
Resource lost
SIGRTMIN
38
Exit
Highest priority real-time signal
SIGRTMAX
45
Exit
Lowest priority real-time signal
  • Exit: forces the process to exit.
  • Core: forces the process to exit and create a core file.
  • Stop: stops the process.
  • Ignore: ignores the signal; no action taken.
If a process receives one of these signal without first arranging to catch it, the process can be terminated immediately( or a core dump file is generated, placed in current directory, used in debugging).

Signals are classic example of asynchronous events. They occur at what appear to be random times to the process. When the signal occurs, the process has to tell the kernel what to do with it. There can be three options through which a signal can be disposed:
  •        The signal can be ignored:  This means that nothing will be done when signal occurs
  •         The signal can be caught : The process registers a function with kernel, this function is called by kernel when that signal occurs
  •         Let the default action apply: Every signal has a default action (may be to terminate process  or ignored etc)
Note:
  • SIGKILL and SIGSTOP can not be ignored or caughted. This because these two signals provide a way for root user or kernel to kill or stop any process in any situation.
  • In a process, action of a signal  can be modified by call exec function, it change signal's action to be the default.
  • Processes running in background are not terminated by a user pressing a Ctrl + C (because this is purpose of making a process run in background)

3. Signals from the command Line

- “kill” command is used to send any kind of signal from terminal end to a process identified by their pid.
- “killall” sends the signal to all processes running a specified command.
Examples:

kill -HUP 512 // send a hangup signal to a shell running on a different terminal with process id 512
killall -HUP inetd // to tell inet program to re-read it’s configuration option

4. Handling and Generating Signals

a. Handling Signals (Catching Signals)
Programs can handle signals using the signal library function.
void (*signal(int sig, void (*func)(int)))(int);
Where:
        -   sig: the signal to be caught or ignored
        -   func: the function to be call when the specified signal is received, func: can be a function created by user or one of 2 below defined types.

SIG_IGN
Ignore the signal.
SIG_DFL
Restore default behavior

Examples: with SIGINT signal when we type Ctrl-C. When a signal arrives, the process is interrupted, the current registers are saved, and the signal handler (or func) is invoked. When the signal handler returns, the interrupted activity is continued.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void ouch(int sig)
{
      printf("OUCH! - I got signal %d\n", sig);
      (void) signal(SIGINT, SIG_DFL);
}
int main()
{
    (void) signal(SIGINT, ouch);
     while(1) {
          printf("Hello World!\n");
          sleep(1);
    }
}

Note:

Functions in handler should be reentrant: A reentrant function is a function whose execution can be stopped in between due to any reason (like due to interrupt or signal) and then can be reentered again safely before its previous invocations complete the execution. 

Suppose a function func() is registered for a call back on a signal occurrence. Now assume that this func() was already in  execution when the signal occurred. Since this function is call back for this signal so the current execution on this signal will be stopped by the scheduler and this function will be called again (due to signal).

The problem can be if func() works on some global values or data structures that are left in inconsistent state when the execution of this function was stopped in middle then the second call to same function(due to signal) may cause some undesired results.

Example: Non-reentrant function scanf()


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void handler(int no){
     printf("\n\n------------ We are at handler function------------------\n");
     printf("Enter your number = ");
     int p;
     scanf("%d", &p);
     printf("\nMy number at handler function is %d\n",p);
}
int main()
{
     signal(SIGINT, handler);
     printf("Get number = ");
     int s;
     scanf("%d",&s);
     printf("\n--------------- Get back at main function------------------!\n");
     printf("My number at main function = %d\n", s);
     return 0;
}
Compile and Result:


$./testSignal
Get number = 
Press: Ctrl+C
------------------------------- We are at handler function------------------------------------
Enter your number = 1

My number at handler function is 1
2

-------------------------------- Get back at main function-------------------------------------!
My number at main function = 0

We see that, our result is wrong, number at the main function is 0, not 2 as we had entered. So scanf() is non-reentrant function.


b. Generating Signal (Sending Signals)

A program can send a signal defined by sig argument to a different program indentified by pid argument by using the kill() system call with prototype.The call will be fail if the program does’t have permision to send the signal.
  • int kill (pid_t pid, int sig); // return 0 if success, -1 on error
  • int raise(int sig); // sends the signal sig to the excuting programe
Another example with SIGALRM: child process sends a SIGALRM to parent process. and parent process alarm itself.
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
static int alarm_fired  = 0;
void ding(int sig)
{
    printf("Ding -------%d.\n", alarm_fired);
    alarm_fired++;
}
int main()
{
    pid_t pid;
    printf("\n\nAlarm application starting:\n");
    pid = fork();
    switch(pid)
    {
    case -1:
         perror("fork failed!");
         return(1);
    case 0:
         sleep(5);
         kill(getppid(), SIGALRM);
         return(0);
         default:
         printf("Parent PID  = %d\n", getpid());
    }
    printf("Waiting for alarms to go off.\n");
    (void) signal(SIGALRM,ding);
  
    alarm(2);
    pause();
    pause();
    printf("\nDone\n\n\n");
    return(0);
}

Output:
Alarm application starting:
Parent PID = 3128
Waiting for alarms to go off.
Ding --------0.
Ding --------1.
Done

5. Sigaction (reliable signal)

a. Reasons why we should use sigaction() instead of signal()
In early versions:
  1. The signal() function does not (necessarily) block other signals from arriving while the current handler is executing.
  2. The signal()function (usually) resets the signal action back to SIG_DFL (default) for almost all signals. This means that the signal()handler must reinstall itself as its first action. It also opens up a window of vulnerability between the time when the signal is detected and the handler is reinstalled during which if a second instance of the signal arrives, the default behavior (usually terminate, sometimes with prejudice - aka core dump) occurs.
  3. While processing a signal, we can get another signal. Also, there are times where it is very awkward dealing with signals.
To  deal with this problems we use sigaction() instead of signal(). However, the interface of sigaction() is undeniably more fiddly.

b. Sigaction function:

int sigaction(int sig, const struct sigaction *act, struct sigaction *oact); // return 0 on success and -1 if not


- The sigaction function sets the action associated with the signal sig (except sig = SIGKILL, SIGSTOP)
- If oact is not null, sigaction writes the previous signal action to the location it refers to.
- If act is null, this is all sigaction does.
- If act isn't null, the action for the specified signal is set.

Where sigaction structure used to define the action to be taken on receipt of the signal specified by sig and have following members:
  • void (* sa_handler) (int)/* function called when signal sig is received
  • void (*sa_sigaction)(int , siginfo_t *, void * ) // Some architectures a union is involved.  
  • sigset_t sa_mask // a mask of the Signals that are blocked while processing this Signal
  • int sa_flags /* signal action modifiers: Ex: with sa_flags = SA_SIGINFO, sa_sigaction should be set instead of sa_handler
c. Signal Sets

These sets are used in sigaction() and other functions to modify process behavior on receipt of signals.
include <signal.h>
  • int sigaddset(sigset_t *set, int signo); // add a signal specified by signo
  • int sigemptyset(sigset_t *set); // initializes a signal set to be empty
  • int sigfillset(sigset_t *set); // initializes a signal set to contain all defined signals
  • int sigdelset(sigset_t *set, int signo); // delete a signal specified by signo
  • int sigismember(sigset_t *set, int signo); // determines whether the given signal is a member of a signal set
    Example: Catch SIGCHLD signal when child process has stopped or exited, SIGINT when we press Ctrl+C

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    #include <stdio.h>
    #include <stdlib.h>
    #include <signal.h>
    #include <unistd.h>
    void HandleSignal(int sig, siginfo_t *si, void *context)   // this is sa_sigaction function with three arguments. 
    {
       switch(sig)
       {
          case SIGINT:
             printf("\nControl-C was pressed: mypid = %d, mypgid = %d\n",getpid(), getpgid(getpid())); // getpgid() get the process group ID of a process
             _exit(0);
             break;
          case SIGCHLD:
             printf("\nSIGCHLD. mypid = %d, mypgid = %d\n",getpid(), getpgid(getpid()));
             if(si->si_code == CLD_EXITED || si->si_code == CLD_KILLED)
             {
                printf("   Process %d is done!\n", si->si_pid);
             }
             break;
       }
    }
    int main(int argc, char *argv[])
    {
       struct sigaction sVal;
       pid_t myPID;
       pid_t myG_PID;
       // Specify that we will use a signal handler that takes three arguments
       // instead of one, which is the default.
       sVal.sa_flags = SA_SIGINFO;
       // Indicate which function is the signal handler.
       sVal.sa_sigaction = HandleSignal;
       myPID = getpid();
       myG_PID = getpgid(myPID);
       printf("\nMy process id = %d.\n", myPID);
       printf("My process group id = %d.\n", myG_PID);
       if(fork() == 0)
       {
          myPID = getpid();
          myG_PID = getpgid(myPID);
          printf("\nChild: My process id = %d.\n", myPID);
          printf("Child: My process group id = %d.\n", myG_PID);
          // Create a new process group that contains this process
          setpgid(0,0);
          myPID = getpid();
          myG_PID = getpgid(myPID);
          printf("\nChild: My process id = %d.\n", myPID);
          printf("Child: My process group id = %d.\n", myG_PID);
       }
       else
       {
          // Register for SIGINT
          sigaction(SIGINT, &sVal, NULL);
          // Register for SIGCHLD
          sigaction(SIGCHLD, &sVal, NULL);
          myPID = getpid();
          myG_PID = getpgid(myPID);
          printf("\nParent: My process id = %d.\n", myPID);
          printf("Parent: My process group id = %d.\n", myG_PID);
          while(1)
          {
          }
       }
       return(0);
    }
    Output:
    ./sigaction1

    My process id = 2723.
    My process group id = 2723.

    Parent: My process id = 2723.
    Parent: My process group id = 2723.

    Child: My process id = 2724.
    Child: My process group id = 2723.

    Child: My process id = 2724.
    Child: My process group id = 2724.

    SIGCHLD. mypid = 2723, mygid = 2723
             Process 2724 is done!
    ^C

    Control -C was pressed: mypid = 2723, mygid = 2723

    6. SIGUSR1, SIGUSR2

    With these signals, we can do any thing which we want, it guarantees that the system will never deliver them to our process. Besides, there is no difference between these two signals.

    They can be designed for simple inter-process communication (IPC), for example: a child process could send it to its parent to indicate that it's completed a certain task, for instance.

    NOTE: Signal only provide a primitive means of communication. In particular, there is no way to attach any information to them and the recipient only knows the signal number (to add more information,we use pipes or socket..)

    The following example code demonstrates a parent and child alternately scheduling each other with SIGUSR1.
    // Code

    ?
    1. #include <unistd.h>  
    2. #include <signal.h>  
    3. #include <stdio.h>  
    4. #include <sys/types.h>  
    5.   
    6. int ntimes=0;  
    7. main()  
    8. {  
    9.     pid_t pid, ppid;  
    10.     void p_action(int);  
    11.     void c_action(int);  
    12.     static struct sigaction pact, cact;  
    13.        
    14.     /* set SIGUSR1 action for parent */;  
    15.     pact.sa_handler = p_action;  
    16.     sigaction(SIGUSR1, &pact, NULL);  
    17.        
    18.     switch (pid = fork())  
    19.     {  
    20.     case -1: // Shouldn't get here  
    21.         perror("synchro");  
    22.         _exit(1);  
    23.     case 0:  // child  
    24.         cact.sa_handler = c_action;  
    25.         sigaction(SIGUSR1, &cact, NULL);  
    26.            
    27.         // get parent process-id  
    28.         ppid = getppid();  
    29.         for (;;)  
    30.         {  
    31.             sleep(1);  
    32.             // wake up parent  
    33.             kill (ppid, SIGUSR1);  
    34.             pause(); // wait for parent signal  
    35.         }  
    36.     default// parent  
    37.         for (;;)  
    38.         {  
    39.             pause(); // wait for child signal  
    40.             sleep(1);  
    41.             // wake up child  
    42.             kill (pid, SIGUSR1);  
    43.         }        
    44.     }  
    45. // end of main  
    46. void p_action(int sig)  
    47. {  
    48.     printf("Parent caught signal %d\n", ++ntimes);  
    49. }  
    50. void c_action(int sig)  
    51. {  
    52.     printf("Child caught signal %d\n", ++ntimes);  
    53. }  

    Result:
    Parent caught signal 1
    Child caught signal 1

    Parent caught signal 2 

    Child caught signal 2

    Parent caught signal 3 

    Child caught signal 3

    Parent caught signal 4 

    Child caught signal 4

    Another example of SIGUSR1 and SIGUSR2 in IPC
    // Code p1.c
    ?
    1. #include <stdio.h>  
    2. #include <signal.h>  
    3. #include <time.h>  
    4.   
    5. /* Global variables!  We need these so that main() and sh1 & sh2 can communicate. */  
    6. char sym = 'X';  
    7. FILE *fp = NULL;  
    8.    
    9. void sh1(int st)  
    10. {  
    11.   signal(SIGUSR1,sh1);  
    12.   fscanf(fp,"%c",&sym);  
    13. }  
    14.    
    15. void sh2(int st)  
    16. {  
    17.   fp = fopen("foo","r");  
    18.   setbuf(fp,NULL);  
    19. }  
    20.    
    21. int main()  
    22. {  
    23.   /* Register signal handlers. */  
    24.   signal(SIGUSR2,sh2);  
    25.   signal(SIGUSR1,sh1);  
    26.    
    27.   setbuf(stdout,NULL);  
    28.   while(1)  
    29.     printf("     %c",sym);  
    30.   return 0;  
    31. }  


    // Code p2.c
    ?
    1. #include <stdio.h>  
    2. #include <signal.h>  
    3. #include <unistd.h>  
    4.   
    5. int main(int argc, char **argv)  
    6. {  
    7.   /* Get p1's process ID. */  
    8.   pid_t pid = atoi(argv[1]);  
    9.    
    10.   /* Open foo for unbuffered writing. */  
    11.   FILE *fp = fopen("foo","w");  
    12.   setbuf(fp,NULL);  
    13.    
    14.   /* Tell p1 to open foo. */  
    15.   kill(pid,SIGUSR2);  
    16.    
    17.   /* Read char, write to foo, notify p1. */  
    18.   char c;  
    19.   while(scanf("%c",&c) == 1)  
    20.   {  
    21.     if (c == '\n'continue;  
    22.     fprintf(fp,"%c",c);  
    23.     kill(pid,SIGUSR1);  
    24.   }  
    25.    
    26.   /* Delete file foo & kill p1. */  
    27.   unlink("foo");  
    28.   kill(pid,SIGTERM);  
    29.    
    30.   return 0;  
    31. }  


    Explanation
    it will consist of two programs. Program p1 will run in one terminal, printing X's endlessly. In another terminal, we'll launch program p2 with p1's PID as a command-line argument. Whenever we enter a new symbol as input to p2, it will write that symbol to a file foo and signal p1 with signal SIGUSR1. In response to receiving SIGUSR1, p1's signal handler will read a character from file foo and make that character the one that it prints. We also need to signal p1 when file foo is ready to be opened, since it can't successfully open it until p2 creates it. We use signal SIGUSR2 to indicate that foo is ready for p1 to open.
    References:

    [1] http://www.thegeekstuff.com/2012/03/linux-signals-fundamentals/
    [2] http://stackoverflow.com/questions/231912/what-is-the-difference-between-sigaction-and-signal
    [3] http://www.ibm.com/developerworks/library/l-reent/
    [4] http://man.yolinux.com/cgi-bin/man2html?cgi_command=sigaction
    [5] http://www.thegeekstuff.com/2011/02/send-signal-to-process/

    Không có nhận xét nào:

    Đăng nhận xét