threads and signals

    Each thread has its own signal mask, but the handling of the signal is shared by all threads in the process. This means that a single thread can block certain signals, but when a thread modifies the handling behavior associated with a given signal, all threads must share the change in handling behavior. This way, if one thread chooses to ignore a given signal, another thread can revoke that thread's signal selection in two ways: restore the default handling behavior for the signal, or set a new signal handler for the signal.
    In-process signals are delivered to a single thread. If a signal is related to a hardware failure, the signal is generally sent to the thread that caused the event, while other signals are sent to either thread.
    How a process uses the sigprocmask function to block signaling is discussed in the Signal Basic Functions section, but the behavior of this function is not defined in a multithreaded process, where pthread_sigmask must be used. A thread can also wait for one or more signals to occur through sigwait, and call pthread_kill to send a signal to the thread.
#include <signal.h>
int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
int sigwait(const sigset_t *restrict set, int *restrict signop);
int pthread_kill(pthread_t thread, int signo);
                         /* All functions return value: if successful, return 0; otherwise, return error number */

    The parameters of pthread_sigmask are the same as sigprocmask, which will not be repeated here, but the function works in a thread and returns an error code when it fails.
    The set parameter of sigwait specifies the set of signals the thread is waiting for. On return, the integer pointed to by signop will contain the number of the sent signal. If a signal in the signal set is pending when sigwait is called, then sigwait returns without blocking. Before returning, sigwait will remove the pending signals from the process. If the implementation supports queued signals, and multiple instances of the signal are suspended, then sigwait will remove one instance of the signal, and the other instances will continue to be queued.
    To avoid erroneous behavior, a thread must block those signals it is waiting for before calling sigwait. sigwait atomically unblocks the signal set until a new signal is delivered. Before returning, sigwait will restore the thread's signal mask. If the signal is not blocked when sigwait is called, then there is a time window before the thread completes the call to sigwait in which the signal can be sent to the thread.
    The advantage of using sigwait is that it simplifies signal processing, allowing asynchronously generated signals to be handled synchronously. To prevent a signal from interrupting a thread, the signal can be added to each thread's signal mask, and then a dedicated thread can be assigned to handle the signal. These dedicated threads can call functions without worrying about which functions are safe to call in signal handlers, because these function calls come from the normal thread context, not traditional signal handlers that interrupt the normal execution of the thread.
    If multiple threads are blocked waiting for the same signal in a sigwait call, only one thread can return from sigwait when the signal is delivered. If a signal is caught and a thread is waiting for the same signal in a sigwait call, then it is up to the operating system implementation to decide how to deliver the signal: the operating system can let sigwait return, or it can activate the signal handler, but both The two situations do not occur at the same time.
    The kill function can send a signal to a process, and to send a signal to a thread, call pthread_kill. You can pass a signo value of 0 to check if the thread exists. Note that passing a signal to a thread will still kill the entire process if the default action of a signal is to terminate the process. In addition, since the alarm timer is a process resource, and all threads share the same alarm. So it is impossible for multiple threads in the process to use the alarm timer without interfering with each other.
    When the sigsuspend function was introduced in the previous sigsuspend function and abort function , an example was given in which a flag was set in the signal handler to indicate that the main program should exit. The only runnable control thread in that example was the main thread and the signal handler. So blocking the signal is enough to avoid missing flag modifications. In multithreading, a mutex needs to be used to protect the flag, as shown in the following example program.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>

static int quitflag;		// set nonzero by thread
sigset_t	mask;

pthread_mutex_t	lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t	waitloc = PTHREAD_COND_INITIALIZER;

void *thr_fn(void *arg){
	int sign, err;
	for(;;){
		if((err=sigwait(&mask, &signo)) != 0){
			printf("sigwait error\n");
			exit(1);
		}
		switch(signo){
			case SIGINT:
				printf("interrupt\n");
				break;
			case SIGQUIT:
				pthread_mutex_lock(&lock);
				quitflag = 1;
				pthread_mutex_unlock(&lock);
				pthread_cond_signal(&waitloc);
				return (void *)0;
			default:
				printf("unexpected signal: %d\n", signo);
				exit(1);
		}
	}
}

int main(void){
	sigset_t oldmask;
	sigemptyset(&mask);
	sigaddset(&mask, SIGINT);
	sigaddset(&mask, SIGQUIT);
	pthread_sigmask(SIG_BLOCK, &mask, &oldmask);

	pthread_t	tid;
	if(pthread_create(&tid, NULL, thr_fn, NULL) != 0){
		printf("failed to create thread\n");
		exit(1);
	}

	pthread_mutex_lock(&lock);
	while(quitflag == 0)
		pthread_cond_wait(&waitloc, &lock);
	pthread_mutex_unlock(&lock);

	quitflag = 0;	// SIGQUIT has been caught and is blocked

	sigprocmask (SIG_SETMASK, & oldmask, NULL);
	exit(0);
}

    The result of running the program is the same as the previous one. In this program, there is no need to rely on the signal handler to interrupt the main thread, and there is a dedicated independent thread for signal processing. Change the value of quitflag under the protection of the mutex, so that the main thread will not miss the wakeup call during pthread_cond_signal.
    The thing to note here is that SIGINT and SIGQUIT are blocked at the start of the main thread. When a thread is created for a signal handler, the new thread inherits the existing signal mask. Because sigwait unblocks the signal, only one thread can be used for signal reception. This allows coding the main thread without worrying about interruptions from these signals.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326066203&siteId=291194637