Linux 进程与线程、线程与信号
进程与线程
多线程程序调用fork后,子进程只是调用fork线程的完整复制。子进程自动继承父进程种互斥锁的状态。这引起一个问题:子进程不知道从父进程继承来的互斥锁的具体状态。这个互斥锁可能被加锁了,但不是调用fork函数的那个线程锁住的,而是由其他线程锁住的。
/*
prepare 在fork调用后创建子进程前被执行,用于锁住父进程种互斥锁
parent 在fork创建出子线程后,fork返回前在父进程中被执行,所用是释放被锁住的互斥锁
child 在fork返回前在子进程中执行,也是用于释放在prepare中被锁住的互斥锁
成功时返回0,失败返回错误码
*/
extern int pthread_atfork (void (*__prepare) (void),
void (*__parent) (void),
void (*__child) (void)) __THROW;
void prepare() {
pthread_mutex_lock(&mutex);
}
void infork() {
pthread_mutex_unlock(&mutex);
}
pthread_atfork(prepare, infork, infork);
线程与信号
每个线程可独立设置信号掩码
// 设置进程线程信号掩码
extern int sigprocmask (int __how, const sigset_t *__restrict __set,
sigset_t *__restrict __oset) __THROW;
// 设置线程线程信号掩码
extern int pthread_sigmask (int __how,
const __sigset_t *__restrict __newmask,
__sigset_t *__restrict __oldmask)__THROW;
// 向进程发信号
extern int kill (__pid_t __pid, int __sig) __THROW;
// 向线程发信号
// 当sig为0时步发送信号,但仍会执行错误检查,可用于检测目标线程是否存在
extern int pthread_kill (pthread_t __threadid, int __signo) __THROW;
// 用于进程
extern int sigqueue (__pid_t __pid, int __sig, const union sigval __val) __THROW;
// 用于线程
extern int pthread_sigqueue (pthread_t __threadid, int __signo,
const union sigval __value) __THROW;
进程中所有线程共享该进程信号,线程库根据线程掩码决定将信号发给哪个线程。因此在每个子线程单独设置线程掩码会导致逻辑错误。此外,所有线程共享信号处理函数,也就是在一个线程中设置某个信号处理函数后会覆盖其他线程的同一信号处理函数。这2点说明我们应该定义一个专门的线程用于处理所有信号
- 在创建子线程前调用pthread_sigmask设置信号掩码,所有新子线程都会继承这个掩码。时候所有线程都不会相应被屏蔽信号
- 在某个线程中调用以下函数等待信号并处理
extern int sigwait ( const sigset_t *__restrict __set, int *__restrict __sig ) __nonnull ((1, 2));
可以简单的将set设置为第1步指定的被屏蔽的信号,sig参数用于存储收到的信号值。成功返回0,失败返回错误码。如果使用了sigwait,就不应该再使用信号处理函数。
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <errno.h> /* Simple error handling functions */ #define handle_error_en(en, msg) \ do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0) static void * sig_thread(void *arg) { sigset_t *set = arg; int s, sig; for (;;) { s = sigwait(set, &sig); if (s != 0) handle_error_en(s, "sigwait"); printf("Signal handling thread got signal %d\n", sig); } } int main(int argc, char *argv[]) { pthread_t thread; sigset_t set; int s; /* Block SIGQUIT and SIGUSR1; other threads created by main() will inherit a copy of the signal mask. */ sigemptyset(&set); sigaddset(&set, SIGQUIT); sigaddset(&set, SIGUSR1); s = pthread_sigmask(SIG_BLOCK, &set, NULL); if (s != 0) handle_error_en(s, "pthread_sigmask"); s = pthread_create(&thread, NULL, &sig_thread, (void *) &set); if (s != 0) handle_error_en(s, "pthread_create"); /* Main thread carries on to create other threads and/or do other work */ pause(); /* Dummy pause so we can test program */ }