Linux Signal Explanation

Keywords: Attribute Unix Linux less

Types of a signal

Reliable and unreliable signals, real-time and non-real-time signals

Reliable signals are real-time signals, and those inherited from UNIX systems are unreliable signals, which are manifested in signals.

If queuing is not supported, the signal may be lost, such as sending the same signal many times, and the process can only receive it once. The signal value is less than 1.

All SIGRTMIN signals are unreliable.

Unreliable signals are non-real-time signals. Later, Linux improved the signal mechanism and added 32 new signals.

Signals are reliable signals, which show that the signals support queuing, will not be lost, and can be received as many times as they are sent.

All the signals located in the [SIGRTMIN, SIGRTMAX] range are reliable.

 

For reliable signals, you can also refer to a paragraph from WIKI:

 

  1. The real-time signals, ranging from SIGRTMIN to SIGRTMAX, are a set of signals that can be used for application-defined purposes.  
  2. Because SIGRTMIN may have different values on different Unix-like systems, applications should always refer to the signals in the form SIGRTMIN+n, where n is a constant integer expression.  
  3. The real-time signals have a number of properties that differentiate them from other signals and make them suitable for application-defined purposes:  
  4. * Multiple instances of a real-time signal can be sent to a process and all will be delivered.  
  5. * Real-time signals can be accompanied by an integer or pointer value (see sigqueue[2]).  
  6. * Real-time signals are guaranteed to be delivered in the order they were emitted.  
 


Enter kill-l from the command line to list all the signals supported by the system:

 

  1. ~> kill -l  
  2.  1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP  
  3.  6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1  
  4. 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM  
  5. 16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP  
  6. 21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ  
  7. 26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR  
  8. 31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3  
  9. 38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8  
  10. 43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13  
  11. 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12  
  12. 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7  
  13. 58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2  
  14. 63) SIGRTMAX-1  64) SIGRTMAX      

Unreliable signals generally have definite uses and meanings. Reliable signals can be customized by users.

 

Installation of two signals

 

Early Linux used system call signals to install signals


#include <signal.h>

void (*signal(int signum, void (*handler))(int)))(int); 

The function has two parameters. signum specifies the signal to be installed and handler specifies the signal processing function.

The return value of this function is a function pointer to the handler installed last time.


Classic installation mode:

if (signal(SIGINT, SIG_IGN) != SIG_IGN) {

    signal(SIGINT, sig_handler);

}

Get the last handler first, and if you don't ignore the signal, install the handler for the signal.


Since the signal is delivered, the system automatically resets the handler as the default action in order to make the signal in the handler.

During processing, it can still react to subsequent signals, often calling signal again in the first statement of handler


sig_handler(ing signum)

{

/* Re-install the signal*/

    signal(signum, sig_handler);

    ......

}

We know that at any point in the execution of the program, a signal can occur at any time, if the signal is reinstalled in sig_handler

If the signal is generated before, the signal will perform default action instead of sig_handler. This problem is unpredictable.


Use the library function sigaction to install the signal

In order to overcome the difference between unreliable signals and the same SVR4 and BSD, a POSIX signal installation method was developed.

After sigaction installs the action of the signal, the action remains until another call to sigaction establishes another

This overcomes the old problem of signal ing calls.


#include <signal.h> 
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));


Classic installation mode:


struct sigaction action, old_action;



/* Setting SIGINT*/

action.sa_handler = sig_handler;

sigemptyset(&action.sa_mask);

sigaddset(&action.sa_mask, SIGTERM);

action.sa_flags = 0;


/* Get the last handler and install the signal if the action is not ignored*/

sigaction(SIGINT, NULL, &old_action);

if (old_action.sa_handler != SIG_IGN) {

    sigaction(SIGINT, &action, NULL);

}


Library function based on sigaction: signal

sigaction is naturally powerful, but the installation signal is cumbersome. At present, signal() in linux is through sigation() function.

So, even if the signal is installed through signal (), it is not necessary at the end of the signal processing function.

Call the signal installation function again.

 

How to Shield Signals

 

The so-called shielding is not to prohibit the delivery of signals, but to temporarily block the delivery of signals.

After removing the shield, the signal will be delivered and will not be lost. The relevant API is


int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigaddset(sigset_t *set, int signum);

int sigdelset(sigset_t *set, int signum);

int sigismember(const sigset_t *set, int signum);

int sigsuspend(const sigset_t *mask);

int sigpending(sigset_t *set);

-----------------------------------------------------------------

int  sigprocmask(int  how,  const  sigset_t *set, sigset_t *oldset));

sigprocmask() function can operate the signal set according to the parameter how. There are three main operations:

* SIG_BLOCK adds set-pointing signal to the current blocking signal set of the process

* SIG_UNBLOCK is relieved if the process blocking signal set contains the set pointing signal set in the signal set.

Blocking of the signal

* SIG_SETMASK updates the process blocking signal set to set-pointed signal set


Shield the signal of the whole process:

 

  1. #include <signal.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <error.h>  
  5. #include <string.h>  
  6.   
  7. void sig_handler(int signum)  
  8. {  
  9.     printf("catch SIGINT\n");  
  10. }  
  11.   
  12. int main(int argc, char **argv)  
  13. {  
  14.     sigset_t block;  
  15.     struct sigaction action, old_action;  
  16.   
  17.     /* Installation signal*/  
  18.     action.sa_handler = sig_handler;  
  19.     sigemptyset(&action.sa_mask);  
  20.     action.sa_flags = 0;  
  21.   
  22.     sigaction(SIGINT, NULL, &old_action);  
  23.     if (old_action.sa_handler != SIG_IGN) {  
  24.         sigaction(SIGINT, &action, NULL);  
  25.     }  
  26.   
  27.     /* Shielding signal*/  
  28.     sigemptyset(&block);  
  29.     sigaddset(&block, SIGINT);  
  30.   
  31.     printf("block SIGINT\n");  
  32.     sigprocmask(SIG_BLOCK, &block, NULL);  
  33.   
  34.     printf("--> send SIGINT -->\n");  
  35.     kill(getpid(), SIGINT);  
  36.     printf("--> send SIGINT -->\n");  
  37.     kill(getpid(), SIGINT);  
  38.     sleep(1);  
  39.   
  40.     /* When the signal is released, the previously triggered signal will be delivered. 
  41.      * But SIGINT is an unreliable signal and can only be delivered once. 
  42.      */  
  43.     printf("unblock SIGINT\n");  
  44.     sigprocmask(SIG_UNBLOCK, &block, NULL);  
  45.   
  46.     sleep(2);  
  47.   
  48.     return 0;  
  49. }  

Running results:

 

  1. work> ./a.out   
  2. block SIGINT  
  3. --> send SIGINT -->  
  4. --> send SIGINT -->  
  5. unblock SIGINT  
  6. catch SIGINT  

Two SIGINT signals are sent here. You can see that after shielding SIGINT,

The signal can not be delivered. It is not delivered until the shield is removed, but only once.

Because SIGINT is an unreliable signal, queuing is not supported.


Shield other signals only during signal processing

During the handler execution of the signal, the system will automatically shield the signal, but if

What if you want to shield other signals?

The sa_mask attribute of.

 

  1. #include <signal.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <error.h>  
  5. #include <string.h>  
  6.   
  7. void sig_handler(int signum)  
  8. {  
  9.     printf("in handle, SIGTERM is blocked\n");  
  10.     /* SIGTERM will be blocked in this handler until this handler returns.*/  
  11.     printf("--> send SIGTERM -->\n");  
  12.     kill(getpid(), SIGTERM);  
  13.     sleep(5);  
  14.     printf("handle done\n");  
  15. }  
  16.   
  17. void handle_term(int signum)  
  18. {  
  19.     printf("catch sigterm and exit..\n");  
  20.     exit(0);  
  21. }  
  22.   
  23. int main(int argc, char **argv)  
  24. {  
  25.     struct sigaction action, old_action;  
  26.   
  27.     /* Setting SIGINT*/  
  28.     action.sa_handler = sig_handler;  
  29.     sigemptyset(&action.sa_mask);  
  30.     /* When installing handler, set handler 
  31.      * Shield SIGTERM signal during execution*/  
  32.     sigaddset(&action.sa_mask, SIGTERM);  
  33.     action.sa_flags = 0;  
  34.   
  35.     sigaction(SIGINT, NULL, &old_action);  
  36.     if (old_action.sa_handler != SIG_IGN) {  
  37.         sigaction(SIGINT, &action, NULL);  
  38.     }  
  39.   
  40.     /* Setting SIGTERM*/  
  41.     action.sa_handler = handle_term;  
  42.     sigemptyset(&action.sa_mask);  
  43.     action.sa_flags = 0;  
  44.   
  45.     sigaction(SIGTERM, NULL, &old_action);  
  46.     if (old_action.sa_handler != SIG_IGN) {  
  47.         sigaction(SIGTERM, &action, NULL);  
  48.     }  
  49.   
  50.     printf("--> send SIGINT -->\n");  
  51.     kill(getpid(), SIGINT);  
  52.   
  53.     while (1) {  
  54.         sleep(1);  
  55.     }  
  56.   
  57.     return 0;  
  58. }  

 

Operation results:

 

  1. work> ./a.out  
  2. --> send SIGINT -->  
  3. in handle, SIGTERM is blocked  
  4. --> send SIGTERM -->  
  5. handle done  
  6. catch sigterm and exit..  

After receiving SIGINT, enter sig_handler, at which time the SIGTERM signal will be shielded.

After sig_handler returns, the SIGTERM signal is received and the program exits.

 

 

Fourth, how to obtain pending signals

The so-called pending signal refers to the blocked signal, waiting for the signal to be delivered.


int sigsuspend(const sigset_t *mask));

Sigpending (sigset_t*set) gets the current delivered process.

All signals that are blocked return results in the set-directed signal set.

 

  1. #include <signal.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <error.h>  
  5. #include <string.h>  
  6.   
  7. /* Version 1. Reliable signals will be delivered many times.*/  
  8. //#define MYSIGNAL SIGRTMIN+5  
  9. /* Version 2, unreliable signals are delivered only once.*/  
  10. #define MYSIGNAL SIGTERM  
  11.   
  12. void sig_handler(int signum)  
  13. {  
  14.     psignal(signum, "catch a signal");  
  15. }  
  16.   
  17. int main(int argc, char **argv)  
  18. {  
  19.     sigset_t block, pending;  
  20.     int sig, flag;  
  21.   
  22.     /* handler setting signal*/  
  23.     signal(MYSIGNAL, sig_handler);  
  24.   
  25.     /* Shield this signal*/  
  26.     sigemptyset(&block);  
  27.     sigaddset(&block, MYSIGNAL);  
  28.     printf("block signal\n");  
  29.     sigprocmask(SIG_BLOCK, &block, NULL);  
  30.   
  31.     /* Send two signals to see how many times the signal will be triggered.*/  
  32.     printf("---> send a signal --->\n");  
  33.     kill(getpid(), MYSIGNAL);  
  34.     printf("---> send a signal --->\n");  
  35.     kill(getpid(), MYSIGNAL);  
  36.   
  37.     /* Check the current pending signal*/  
  38.     flag = 0;  
  39.     sigpending(&pending);  
  40.     for (sig = 1; sig < NSIG; sig++) {  
  41.         if (sigismember(&pending, sig)) {  
  42.             flag = 1;  
  43.             psignal(sig, "this signal is pending");  
  44.         }   
  45.     }  
  46.     if (flag == 0) {  
  47.         printf("no pending signal\n");  
  48.     }  
  49.   
  50.     /* Remove the shielding of this signal and the pending signal will be delivered.*/  
  51.     printf("unblock signal\n");  
  52.     sigprocmask(SIG_UNBLOCK, &block, NULL);  
  53.   
  54.     /* Check the pending signal again*/  
  55.     flag = 0;  
  56.     sigpending(&pending);  
  57.     for (sig = 1; sig < NSIG; sig++) {  
  58.         if (sigismember(&pending, sig)) {  
  59.             flag = 1;  
  60.             psignal(sig, "a pending signal");  
  61.         }   
  62.     }  
  63.     if (flag == 0) {  
  64.         printf("no pending signal\n");  
  65.     }  
  66.   
  67.     return 0;  
  68. }  

This program has two versions:

Reliable signal version, operation result:

  1. work> ./a.out   
  2. block signal  
  3. ---> send a signal --->  
  4. ---> send a signal --->  
  5. this signal is pending: Unknown signal 39  
  6. unblock signal  
  7. catch a signal: Unknown signal 39  
  8. catch a signal: Unknown signal 39  
  9. no pending signal  

Send two reliable signals, and finally receive two signals.


Unreliable signal version, operation result:

 

  1. work> ./a.out   
  2. block signal  
  3. ---> send a signal --->  
  4. ---> send a signal --->  
  5. this signal is pending: Terminated  
  6. unblock signal  
  7. catch a signal: Terminated  
  8. no pending signal  

Send two unreliable signals and eventually receive only one

 

Five interrupted system calls

When some IO system calls are executed, such as read waiting for input, if a signal is received,

The system will interrupt the read and then perform the signal processing function. When the signal processing returns, the system

A question arises: Do you want to restart the system call or fail it?


Early UNIX systems interrupted system calls and failed system calls, such as read.

Return to - 1 and set errno to EINTR


Interrupted system calls are incomplete calls whose failures are temporary if called again

It is possible to succeed, which is not a real failure, so to deal with this situation, the typical way is:


while (1) {

    n = read(fd, buf, BUFSIZ);

    if (n == -1 && errno != EINTR) {

        printf("read error\n");

        break;

    }

    if (n == 0) {

        printf("read done\n");

        break;

    }

}


In fact, we can do this from the signal point of view.

To solve this problem, when installing the signal, set SA_RESTART

Property, then when the signal processing function returns, the system interrupted by the signal

The call will be automatically restored.

 

  1. #include <signal.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <error.h>  
  5. #include <string.h>  
  6.   
  7. void sig_handler(int signum)  
  8. {  
  9.     printf("in handler\n");  
  10.     sleep(1);  
  11.     printf("handler return\n");  
  12. }  
  13.   
  14. int main(int argc, char **argv)  
  15. {  
  16.     char buf[100];  
  17.     int ret;  
  18.     struct sigaction action, old_action;  
  19.   
  20.     action.sa_handler = sig_handler;  
  21.     sigemptyset(&action.sa_mask);  
  22.     action.sa_flags = 0;  
  23.     /* Version 1: No SA_RESTART attribute is set 
  24.      * Version 2: Setting the SA_RESTART property*/  
  25.     //action.sa_flags |= SA_RESTART;  
  26.   
  27.     sigaction(SIGINT, NULL, &old_action);  
  28.     if (old_action.sa_handler != SIG_IGN) {  
  29.         sigaction(SIGINT, &action, NULL);  
  30.     }  
  31.   
  32.     bzero(buf, 100);  
  33.   
  34.     ret = read(0, buf, 100);  
  35.     if (ret == -1) {  
  36.         perror("read");  
  37.     }  
  38.   
  39.     printf("read %d bytes:\n", ret);  
  40.     printf("%s\n", buf);  
  41.   
  42.     return 0;  
  43. }  

 

Version 1, no SA_RESTART attribute is set:

 

  1. work> gcc signal.c   
  2. work> ./a.out   
  3. ^Cin handler  
  4. handler return  
  5. read: Interrupted system call  
  6. read -1 bytes:  

During read waiting for data, press ctrl + c to trigger SIGINT signal.

When handler returns, read is interrupted and - 1 is returned


Version 2, set the SA_RESTART attribute:

 

  1. work> gcc signal.c   
  2. work> ./a.out   
  3. ^Cin handler  
  4. handler return  
  5. hello, world  
  6. read 13 bytes:  
  7. hello, world  

After handler returns, read system calls are resumed and continue to wait for data.

 

 

Six Non-local Control Transfer


int setjmp(jmp_buf env);

int sigsetjmp(sigjmp_buf env, int savesigs);

void longjmp(jmp_buf env, int val);

void siglongjmp(sigjmp_buf env, int val);

--------------------------------------------------------

setjmp() saves the current stack environment and then marks the current address.

When long JMP is called elsewhere in the program, it jumps directly to this token location.

Then restore the stack and continue to execute the program.  


The setjmp call smells like fork, setjmp()return 0 if returning directly,

and non-zero when returning from longjmp using  the saved context.


if (setjmp(jmpbuf)) {

   printf("return from jmp\n");

} else {

   printf("return directly\n");

}


The only difference between setjmp and sigsetjmp is that setjmp does not necessarily restore the signal set.

sigsetjmp guarantees the recovery of signal set

 

  1. #include <signal.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <errno.h>  
  5. #include <string.h>  
  6. #include <setjmp.h>  
  7.   
  8. void sig_alrm(int signum);  
  9. void sig_usr1(int signum);  
  10. void print_mask(const char *str);  
  11.   
  12. static sigjmp_buf jmpbuf;  
  13. static volatile sig_atomic_t canjmp;  
  14. static int sigalrm_appear;  
  15.   
  16. int main(int argc, char **argv)  
  17. {  
  18.     struct sigaction action, old_action;  
  19.   
  20.     /* Setting SIGUSR1*/  
  21.     action.sa_handler = sig_usr1;  
  22.     sigemptyset(&action.sa_mask);  
  23.     action.sa_flags = 0;  
  24.   
  25.     sigaction(SIGUSR1, NULL, &old_action);  
  26.     if (old_action.sa_handler != SIG_IGN) {  
  27.         sigaction(SIGUSR1, &action, NULL);  
  28.     }  
  29.   
  30.     /* Setting SIGALRM*/  
  31.     action.sa_handler = sig_alrm;  
  32.     sigemptyset(&action.sa_mask);  
  33.     action.sa_flags = 0;  
  34.   
  35.     sigaction(SIGALRM, NULL, &old_action);  
  36.     if (old_action.sa_handler != SIG_IGN) {  
  37.         sigaction(SIGALRM, &action, NULL);  
  38.     }  
  39.   
  40.     print_mask("starting main:");  
  41.   
  42.     if (sigsetjmp(jmpbuf, 1) != 0) {  
  43.         print_mask("exiting main:");  
  44.     } else {  
  45.         printf("sigsetjmp return directly\n");  
  46.         canjmp = 1;  
  47.         while (1) {  
  48.             sleep(1);  
  49.         }  
  50.     }  
  51.   
  52.     return 0;  
  53. }  
  54.   
  55. void sig_usr1(int signum)  
  56. {  
  57.     time_t starttime;  
  58.     if (canjmp == 0) {  
  59.         printf("please set jmp first\n");  
  60.         return;  
  61.     }  
  62.   
  63.     print_mask("in sig_usr1:");  
  64.   
  65.     alarm(1);  
  66.     while (!sigalrm_appear);  
  67.     canjmp = 0;  
  68.     siglongjmp(jmpbuf, 1);  
  69. }  
  70.   
  71. void sig_alrm(int signum)  
  72. {  
  73.     print_mask("in sig_alrm:");  
  74.     sigalrm_appear = 1;  
  75.   
  76.     return;  
  77. }  
  78.   
  79. void print_mask(const char *str)   
  80. {  
  81.     sigset_t sigset;  
  82.     int i, errno_save, flag = 0;  
  83.   
  84.     errno_save = errno;  
  85.   
  86.     if (sigprocmask(0, NULL, &sigset) < 0) {  
  87.         printf("sigprocmask error\n");  
  88.         exit(0);  
  89.     }  
  90.   
  91.     printf("%s\n", str);  
  92.     fflush(stdout);  
  93.   
  94.     for (i = 1; i < NSIG; i++) {  
  95.         if (sigismember(&sigset, i)) {  
  96.             flag = 1;  
  97.             psignal(i, "a blocked signal");  
  98.         }  
  99.     }  
  100.   
  101.     if (!flag) {  
  102.         printf("no blocked signal\n");  
  103.     }  
  104.   
  105.     printf("\n");  
  106.     errno = errno_save;  
  107. }  
 

Operation results:

 

  1. work> ./a.out &  
  2. [4] 28483  
  3. starting main:  
  4. no blocked signal  
  5.   
  6. sigsetjmp return directly  
  7.   
  8. kill -USR1 28483  
  9.   
  10. in sig_usr1:  
  11. a blocked signal: User defined signal 1  
  12.   
  13. in sig_alrm:  
  14. a blocked signal: User defined signal 1  
  15. a blocked signal: Alarm clock  
  16.   
  17. exiting main:  
  18. no blocked signal  
 

 

Life Cycle of Seven Signals

 

Complete execution of signal to signal processing function

For a complete signal life cycle (from the signal to the corresponding processing function to complete execution),

It can be divided into three important stages, which are characterized by four important events:

The signal is born; the signal is registered in the process; the signal is cancelled in the process; and the signal processing function is executed.


The following describes the practical significance of the four events:

The signal was born. The birth of a signal refers to the occurrence of an event triggering the signal.

(such as detecting hardware anomalies, timer timeouts, and calling signal sending functions)

kill() or sigqueue(), etc. Signals are "registered" in the target process;

Data members in the task_struct structure of a process that have pending signals in the process:

struct sigpending pending:

struct sigpending{

struct sigqueue *head, **tail;

sigset_t signal;

};


The third member is all the pending signal sets in the process, and the first and second members point to one respectively.

The beginning and end of a sigqueue-type structural chain (known as the "pending signal list"), in the list

Each sigqueue structure depicts the information carried by a particular signal and points to the next

sigqueue structure:

struct sigqueue{

struct sigqueue *next;

siginfo_t info;

}


Registration of signals

Signal registration in a process means that the signal value is added to the pending signal set of the process.

(sigset_t signal, the second member of the sigpending structure)

And add the end of the pending signal list. As long as the signal is concentrated in the pending signal of the process,

Indicates that the process already knows the existence of these signals, but has not yet had time to process them, or that the signal is blocked by the process.

When a real-time signal is sent to a process, regardless of whether the signal has been registered in the process,

It will be registered again, so the signal will not be lost. Therefore, the real-time signal is also called "reliable signal".

This means that the same real-time signal can be added multiple times to the pending signal list of the same process.

When a non-real-time signal is sent to a process, if the signal has been registered in the process,

Then the signal will be discarded, resulting in signal loss. Therefore, non-real-time signals are also called "unreliable signals".

This means that the same non-real-time signal occupies at most a sigqueue structure in the pending signal list of the process.

After the birth of a non-real-time signal,

(1) If the same signal is found to have been registered in the target structure, it is no longer registered. For a process,

It is equivalent to not knowing the occurrence of this signal and losing the signal.

(2) If there is no identical signal in the pending signal of the process, register yourself in the process.


Signal cancellation.

During process execution, it detects whether there is a signal waiting to be processed.

(Check this every time you return from system space to user space). If there is an open question

If the signal is waiting to be processed and the signal is not blocked by the process, then before the corresponding signal processing function is run,

The process will unload the structure of the signal in the pending signal chain. Whether to remove the signal from the process pending signal set

Deletion is different for real-time and non-real-time signals. For non-real-time signals, due to the pending letter

The sigqueue structure occupies only one sigqueue structure in the number information chain, so when the structure is released, the message should be sent

The number is deleted in the process pending signal set (the signal is cancelled); for real-time signals, it may be

The pending signal information chain occupies multiple sigqueue structures, so it should be aimed at occupying sigqueue structures.

Number discrimination: If only one sigqueue structure is occupied (the process receives the signal only once),

The signal should be deleted from the pending signal set of the process (the signal has been cancelled). Otherwise, it should not be in the process

The pending signal set deletes the signal (the signal has been cancelled).  

Before executing the corresponding signal processing function, the process first cancels the signal in the process.


Signal life termination.

After the process cancels the signal, the corresponding signal processing function is executed immediately, and after the execution is completed,

The impact of this signal transmission on the process is completely over.

 

8. On reentrant functions

 

Reentrant functions should be used in signal processing functions.

Reentrant functions should be used in signal processing programs

(Note: A reentrant function is a process that can be called by multiple tasks.

Tasks do not have to worry about data errors when they are invoked. Because the process is receiving a signal

After that, it will jump to the signal processing function to execute. If the signal processing function

Using non-reentrant functions, signal processing functions may modify the original process

The data should not be modified, so that the process returns from the signal processing function to the next execution time.

There may be unforeseen consequences. Non-reentrant functions are considered in signal processing functions

Insecure function. Most functions satisfying the following conditions are non-reentrant:

(1) Use static data structures such as getlogin(), gmtime(), getgrgid().

getgrnam(), getpwuid(), getpwnam(), etc.

(2) malloc () or free() function is called when the function is implemented.

(3) Standard I/O functions are used in the implementation. The Open Group regards the following functions as reentrant:

    _exit(),access(),alarm(),cfgetispeed(),cfgetospeed(),

    cfsetispeed(),cfsetospeed(),chdir(),chmod(),chown() ,

    close(),creat(),dup(),dup2(),execle(),execve(),

    fcntl(),fork(),fpathconf(),fstat(),fsync(),getegid(),

    geteuid(),getgid(),getgroups(),getpgrp(),getpid(),

    getppid(),getuid(),kill(),link(),lseek(),mkdir(),

    mkfifo(), open(),pathconf(),pause(),pipe(),raise(),

    read(),rename(),rmdir(),setgid(),setpgid(),setsid(),

    setuid(), sigaction(),sigaddset(),sigdelset(),sigemptyset(),

    sigfillset(),sigismember(),signal(),sigpending(),     

    sigprocmask(),sigsuspend(),sleep(),stat(),sysconf(),      

    tcdrain(),tcflow(),tcflush(),tcgetattr(),tcgetpgrp(),

    tcsendbreak(),tcsetattr(),tcsetpgrp(),time(),times(),

    umask(),uname(),unlink(),utime(),wait(),waitpid(),

    write().


Even if the signal processing functions are all "security functions", we should also pay attention to entering the processing functions.

First, the value of errno is saved, and then the original value is restored at the end. Because, in the process of signal processing,

Erno values can be changed at any time. In addition, longjmp() and siglongjmp() are not listed as reentrant functions.

Because there is no guarantee that other calls to the next two functions are secure.

Posted by Ohio Guy on Fri, 12 Apr 2019 21:03:33 -0700