Linux 信号与信号处理分析

 

-相关核心概念

 

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;
}

 

你可能感兴趣的:(thread,多线程,linux,kill,null,Signal)