-相关核心概念
1.信号(signal)是向进程发送的软件通知,通知进程有事件发生。
2.引发信号的事件发生时,信号就被创建了(generate)了。
3.进程根据信号采取行动时,信号就被传递(deliver)了。
4.信号的生命期(lifetime)就是信号的创建和传递之间的时间间隔。
5.已经生成但还未被传递的信号被称为挂起(pending)的信号。
如果在传递信号时,进程执行了信号处理程序(signal handler),
那么进程就捕捉(catch)到了这个信号。程序可以使用用户编写的函数
名作为参数调用sigaction函数,SIG_DFL表示采取默认动作,SIG_IGN
表示忽略某个信号,那么在传递时那个信号就会被抛弃,不会对程序
产生影响。
信号生成时所采取的动作取决于那个信号当前使用的信号处理程序
和进程信号掩码(process signal mask)。信号掩码中包含一个当前被
阻塞的信号(blocked signal)的列表。被阻塞的信号不会像被忽略的信
号一样被丢弃。如果一个挂起信号被阻塞了,那么当进程接触了对那个
信号的阻塞时,信号就会被传递出去。程序通过调用sigprocmask改变
它的进程信号掩码来阻塞一个信号,通过调用sigaction将信号处理程序
设置为SIG_IGN来忽略一个信号。
-产生信号
调用kill函数可以向指定进程发送指定的信号,调用raise函数可以
使进程向自己发送指定的信号,调用alarm函数可以使进程向自己在经过
指定的秒数之后发送SIGALRM信号。
-信号集
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t
信号集用来描述信号的集合,linux所支持的所有信号可以全部或部分
的出现在信号集中,主要与信号阻塞相关函数配合使用。
-信号掩码操作
下面是为信号集操作定义的相关函数:
头文件 #include <signal.h>
sigemptyset(sigset_t *set)
//初始化由set指定的信号集,信号集里面的所有信号被清空;
sigfillset(sigset_t *set)
//调用该函数后,set指向的信号集中将包含linux支持的64种信号;
sigaddset(sigset_t *set, int signum)
//在set指向的信号集中加入signum信号;
sigdelset(sigset_t *set, int signum)
//在set指向的信号集中删除signum信号;
sigismember(const sigset_t *set, int signum)
//判定信号signum是否在set指向的信号集中。
-信号的捕捉、忽略和等待
sigaction函数允许调用程序检查或指定与特定信号相关的动作,即处理
信号的行为,或忽略信号。
等待信号处理机制(非忙等),忙等是指连续使用CPU来检测事件的发生,更
有效的方式是将进程或线程挂起知道所等待的事件发生为止。POSIX中的pause,
sigsuspend和sigwait函数提供了三种等待机制。
pause函数将调用线程挂起,知道传递了一个信号为止,这个信号的动作
或者是执行用户定义的处理程序,或者是终止进程。如果信号的动作是终止进
程,pause就不返回。如果信号被进程捕捉,pause就会在信号处理程序返回后返
回。调用形式为:
int pause(void);//总是返回-1。如果被信号中断,pause就将errno设置为EINTR。
sigsuspend函数用参数sigmask指向的信号掩码来阻塞相应的信号,并将进
程或线程挂起,知道进程或线程捕捉到相应的信号。sigsuspend函数在信号处理
程序返回之后返回,并将sigmask指向的信号掩码设置为调用sigsuspend之前的
信号阻塞状态。调用形式为:
int sigsuspend(const sigset_t *sigmask);//总是返回-1。
sigwait函数的形式为:
int sigwait(const sigset_t *restrict sigmask, int *restrict signo);
函数会将调用的线程挂起,直到sigmask信号集中的信号被挂起,然后从挂起的
信号集合中删除那个信号。当sigwait返回时,会把从挂起的信号集合中删除的
那个信号的信号码存储在signo指向的地址中。如果成功,sigwait返回0,否则返
回-1并设置errno。
注意:sigmask指定的信号集中的信号必须在sigwait调用之前被阻塞,并且不能被
忽略,如果为这些信号指定处理函数,这些处理函数也就不会被调用。
-多线程程序中的信号
pthread_kill 函数将指定的信号发送到指定的线程。它的形式为:
int pthread_kill(pthread_t thread, int sig); //成功返回0,否则返回错误码
线程可以用pthread_sigmask函数来检查或设置它的信号掩码。形式为:
int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t
*restrict oset);
参数how:如果为 SIG_SETMASK,线程信号掩码被set取代。
如果为 SIG_BLOCK,会使线程阻塞set中的信号
如果为 SIG_UNBLOCK,会使线程接触对set中的信号的阻塞。
set:为指定的信号集。
oset:保存pthread_sigmask调用前的信号掩码。
-多线程信号处理策略
为信号处理使用特定的线程。主线程在创建线程之前阻塞所有的信号,这样,
所有的线程都将信号阻塞了。然后,专门用来处理信号的线程对那些需要处理的信
号执行sigwait,这样指定的信号都将被这个信号处理线程处理。
-多线程实例
功能描述:主线程在创建子线程之后,等待子线程运行结束,然后继续运行。不同
之处在于,主线程没有调用pthread_join来等待子线程运行结束,而是等待子线程
发送到信号。子线程则在运行结束前向主线程发送信号,来通知主线程。利用信号
机制,实现了线程的同步。
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
void *threadfunc(void *pvoid)
{
pthread_t *main_tid = (pthread_t*) pvoid;
printf("Child thread says:Hello!World!/n");
pthread_kill(*main_tid,SIGUSR1);
}
int main()
{
sigset_t sigs;
int sig;
pthread_t tid;
pthread_t main_tid;
//阻塞SIGUSR1信号,当线程传递信号时,信号会被挂起
sigemptyset(&sigs);
sigaddset(&sigs,SIGUSR1);
sigprocmask(SIG_BLOCK,&siigs,NULL);
main_tid = pthread_self();
pthread_create(&tid, NULL, &threadfunc, &main_tid);
//等待其它线程发送信号
sigwait(&sigs, &sig);
printf("Main thread says:Hello!World!/n");
return 0;
}