Unix™ Systems Programming [Electronic resources] : Communication, Concurrency, and Threads نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Unix™ Systems Programming [Electronic resources] : Communication, Concurrency, and Threads - نسخه متنی

Prentice Hall

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید










13.5 Signal Handling and Threads


All threads in a process share the process signal handlers, but each thread has its own signal mask. The interaction of threads with signals involves several complications because threads can operate asynchronously with signals. Table 13.2 summarizes the three types of signals and their corresponding methods of delivery.

Table 13.2. Signal delivery in threads.

type

delivery action

asynchronous

delivered to some thread that has it unblocked

synchronous

delivered to the thread that caused it

directed

delivered to the identified thread (pthread_kill)

Signals such as SIGFPE (floating-point exception) are synchronous to the thread that caused them (i.e., they are always generated at the same point in the thread's execution). Other signals are asynchronous because they are not generated at a predictable time nor are they associated with a particular thread. If several threads have an asynchronous signal unblocked, the thread runtime system selects one of them to handle the signal. Signals can also be directed to a particular thread with pthread_kill.


13.5.1 Directing a signal to a particular thread


The pthread_kill function requests that signal number sig be generated and delivered to the thread specified by thread.


SYNOPSIS
#include <signal.h>
#include <pthread.h>
int pthread_kill(pthread_t thread, int sig);
POSIX:THR

If successful, pthread_kill returns 0. If unsuccessful, pthread_kill returns a nonzero error code. In the latter case, no signal is sent. The following table lists the mandatory errors for pthread_kill.

error

cause

EINVAL

sig is an invalid or unsupported signal number

ESRCH

no thread corresponds to specified ID

Example 13.23

The following code segment causes a thread to kill itself and the entire process.


if (pthread_kill(pthread_self(), SIGKILL))
fprintf(stderr, "Failed to commit suicide\n");

Table 8.1 lists the POSIX signals with their symbolic names and default actions.


13.5.2 Masking signals for threads


While signal handlers are process-wide, each thread has its own signal mask. A thread can examine or set its signal mask with the pthread_sigmask function, which is a generalization of sigprocmask to threaded programs. The sigprocmask function should not be used when the process has multiple threads, but it can be called by the main thread before additional threads are created. Recall that the signal mask specifies which signals are to be blocked (not delivered). The how and set parameters specify the way the signal mask is to be modified, as discussed below. If the oset parameter is not NULL, the pthread_sigmask function sets *oset to the thread's previous signal mask.


SYNOPSIS
#include <pthread.h>
#include <signal.h>
int pthread_sigmask(int how, const sigset_t *restrict set,
sigset_t *restrict oset);
POSIX:THR

If successful, pthread_sigmask returns 0. If unsuccessful, pthread_sigmask returns a nonzero error code. The pthread_sigmask function returns EINVAL if how is not valid.

A how value of SIG_SETMASK causes the thread's signal mask to be replaced by set. That is, the thread now blocks all signals in set but does not block any others. A how value of SIG_BLOCK causes the additional signals in set to be blocked by the thread (added to the thread's current signal mask). A how value of SIG_UNBLOCK causes any of the signals in set that are currently being blocked to be removed from the thread's current signal mask (no longer be blocked).


13.5.3 Dedicating threads for signal handling


Signal handlers are process-wide and are installed with calls to sigaction as in single-threaded processes. The distinction between process-wide signal handlers and thread-specific signal masks is important in threaded programs.

Recall from Chapter 8 that when a signal is caught, the signal that caused the event is automatically blocked on entry to the signal handler. With a multithreaded application, nothing prevents another signal of the same type from being delivered to another thread that has the signal unblocked. It is possible to have multiple threads executing within the same signal handler.

A recommended strategy for dealing with signals in multithreaded processes is to dedicate particular threads to signal handling. The main thread blocks all signals before creating the threads. The signal mask is inherited from the creating thread, so all threads have the signal blocked. The thread dedicated to handling the signal then executes sigwait on that signal. (See Section 8.5.) Alternatively, the thread can use pthread_sigmask to unblock the signal. The advantage of using sigwait is that the thread is not restricted to async-signal-safe functions.

Program 13.3 and returns. You can replace the setdone with any thread-safe function. Program 13.7 by using the SIGUSR1 signal to set the done flag for the computethread object of Program 13.6. The main program no longer sleeps a specified number of seconds before calling setdone. Instead, the delivery of a SIGUSR1 signal causes signalthread to call setdone.

Program 13.14 signalthread.c

A dedicated thread that sets a flag when a signal is received.


#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include "doneflag.h"
#include "globalerror.h"
static int signalnum = 0;
/* ARGSUSED */
static void *signalthread(void *arg) { /* dedicated to handling signalnum */
int error;
sigset_t intmask;
struct sched_param param;
int policy;
int sig;
if (error = pthread_getschedparam(pthread_self(), &policy, &param)) {
seterror(error);
return NULL;
}
fprintf(stderr, "Signal thread entered with policy %d and priority %d\n",
policy, param.sched_priority);
if ((sigemptyset(&intmask) == -1) ||
(sigaddset(&intmask, signalnum) == -1) ||
(sigwait(&intmask, &sig) == -1))
seterror(errno);
else
seterror(setdone());
return NULL;
}
int signalthreadinit(int signo) {
int error;
pthread_attr_t highprio;
struct sched_param param;
int policy;
sigset_t set;
pthread_t sighandid;
signalnum = signo; /* block the signal */
if ((sigemptyset(&set) == -1) || (sigaddset(&set, signalnum) == -1) ||
(sigprocmask(SIG_BLOCK, &set, NULL) == -1))
return errno;
if ( (error = pthread_attr_init(&highprio)) || /* with higher priority */
(error = pthread_attr_getschedparam(&highprio, &param)) ||
(error = pthread_attr_getschedpolicy(&highprio, &policy)) )
return error;
if (param.sched_priority < sched_get_priority_max(policy)) {
param.sched_priority++;
if (error = pthread_attr_setschedparam(&highprio, &param))
return error;
} else
fprintf(stderr, "Warning, cannot increase priority of signal thread.\n");
if (error = pthread_create(&sighandid, &highprio, signalthread, NULL))
return error;
return 0;
}

Program 13.15 computethreadsig.c

A main program that uses signalthread with the SIGUSR1 signal to terminate the computethread computation of Program 13.6.


#include <math.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "computethread.h"
#include "globalerror.h"
#include "sharedsum.h"
#include "signalthread.h"
int showresults(void);
int main(int argc, char *argv[]) {
int error;
int i;
int numthreads;
pthread_t *tids;
if (argc != 2) { /* pass number threads on command line */
fprintf(stderr, "Usage: %s numthreads\n", argv[0]);
return 1;
}
if (error = signalthreadinit(SIGUSR1)) { /* set up signal thread */
fprintf(stderr, "Failed to set up signal thread: %s\n", strerror(error));
return 1;
}
numthreads = atoi(argv[1]);
if ((tids = (pthread_t *)calloc(numthreads, sizeof(pthread_t))) == NULL) {
perror("Failed to allocate space for thread IDs");
return 1;
}
for (i = 0; i < numthreads; i++) /* create numthreads computethreads */
if (error = pthread_create(tids+ i, NULL, computethread, NULL)) {
fprintf(stderr, "Failed to start thread %d: %s\n", i,
strerror(error));
return 1;
}
fprintf(stderr, "Send SIGUSR1(%d) signal to proc %ld to stop calculation\n",
SIGUSR1, (long)getpid());
for (i = 0; i < numthreads; i++) /* wait for computethreads to be done */
if (error = pthread_join(tids[i], NULL)) {
fprintf(stderr, "Failed to join thread %d: %s\n", i, strerror(error));
return 1;
}
if (showresults())
return 1;
return 0;
}

The modular design of the signalthread object makes the object easy to modify. Chapter 16 uses signalthread for some implementations of a bounded buffer.

Exercise 13.24

Run computethreadsig of Program 13.15 from one command window. Send the SIGUSR1 signal from another command window, using the kill shell command. What is its effect?

Answer:

The dedicated signal thread calls setdone when the signal is pending, and the threads terminate normally.


    / 276