您的位置:首页 > 其它

信号

2017-01-30 20:39 267 查看
 信号
10.2 信号概念

    每个信号都有一个名字,这些名字都以三个字符SIG开头

    在头文件<signal.h>中,信号都被定义为正整数

    不存在编号为0的信号,kill函数对信号编号0有特殊的应用。POSIX.1将此种信处编号值为空信号。

 

    很多条件可以产生信号:

l  当用户按某些终端时,引发终端产生的信号

在终端按Ctrl+C键,通常产生中断信号(SIGINT)

l  硬件异常产生信号:除数为0、无效的内存引用等等

例“对执行一个无效内存引用的进程产生SIGSEGV信号

l  进程调用kill函数可将信号发送给另一个进程或进程组

l  用户可用kill命令将信号发送给其它进程

l  当检测到某种软件条件已经发生,并应用将其通知有关进程时也产生信号

如:SIGURG  在网络连接上传来带外数时产生

    SIGPIPE  在管道的读进程已终止后,一个进程写此管道时产生

    SIGALRM 进程所设置的闹钟时钟超时时产生

 

    可以要求内核在某个信号出现时按照下列三种方式之一进行处理,我们称之为信号的处理或者与信号相关的动作

1)、忽略此信号

    大多数信号都可使用这种方式处理,但有两种信号却决不能被忽略---SIGKILL和SIGSTOP

    这两种信号不能被忽略的原因是:它们向超级用户提供了使进程终止或停止的可靠方法

2)、捕捉信号

    为了做到这一点,要通知内核在某种信号发生时调用一个用户函数

    在用户函数中,可执行用户希望对这种事件进行的处理。

    如:捕捉到SIGCHLD信号,则表示一个子进程已经终止,所以此信号的捕捉函数可以调用waitpid以取得该子进程的进程ID以及它的终止状态

    又如,如果进程创建了临时文件,那么可能要为SIGTERM信号编写一个信号捕捉函数以清除临时文件

    注意:不能捕捉SIGKILL和SIGSTOP信号

3)、执行系统默认动作

    下表给出了针对每一种信号的系统默认动作,针对大多数信号的系统默认动作是终止进程

    注意:在“默认动用”列中,“终止+core”表示在进程当前工作目录的core文件中复制该进程的存储映像(该文件名为core),大多数UNIX调试程序都使用core文件以检查进程终止时的状态

 


10.3 signal函数

   Unix系统的信号机制最简单的接口是signal函数

#include <signal.h>

void (*signal(int signo, void(*func)(int)))) (int);

    返回值:若成功则返回信号以前的处理配置,若出错则返回SIG_ERR

参数

signo 是信号名

func的值是常量SIG_IGN、常量SIG_DFL或当接到此信号后要调用的函数的地址

   SIG_IGN  则向内核表示忽略此信号(SIGKILL,SIGSTOP不能忽略)

   SIG_DFL  则表示接到此信号后的动作是系统默认动作

    当指定函数地址时,则在信号发生时,调用该函数,我们称这种处理为“捕捉”该信号,称此函数为信号处理程序(signal handler)或信号捕捉函数(signal-catching function)

 

   signal函数原型说明此函数需要两个参数,返回一个函数指针,而该指针所指向的函数无返回值(void)。

 

    第一个参数signo是一个整数

    第二个参数是函数指针,它所指向的函数需要一个整型参数,无返回值

   

   signal的返回值是一个函数地址,该函数有一个整型参数(即最后的(int))

    用自然语言来描述也就是要向信号处理程序传送一个整型参数,而它却无返回值。

    当调用signal设置信号处理程序时,第二个参数指向该函数(也就是信号处理程序)的指针。signal的返回值则是指向之前的信号处理程序的指针。

 

    使用typedef定义则简单一些:

   typedef void  sigfunc(int);

    然后,可将signal函数原型写成:sigfunc*signal(int, sigfunc *)

 

    如果查看系统的头文件<signal.h>,则会看到下列形式的声明:

   #define  SIG_ERR  (void (*)()) -1

   #define  SIG_DFL  (void (*)()) 0

   #define  SIG_IGN  (void (*)()) 1

 

例:

#include <stdio.h>

#include <signal.h>

#include <stdlib.h>

#include <unistd.h>

 

static void sig_usr(int);

 

int

main(void)

 {

  if(signal(SIGUSR1, sig_usr)==SIG_ERR)

    perror("can't catch SIGUSR1");

  if(signal(SIGUSR2, sig_usr)==SIG_ERR)

    perror("can't catch SIGUSR2");

   for(;;)

    pause();

}

 

static void

sig_usr(int signo)    /*argument is signal number */

 {

  if(signo==SIGUSR1)

    printf("received SIGUSR1\n");

  else if(signo==SIGUSR2)

    printf("received SIGUSR2\n");

  else

    printf("received signal %d\n",signo);

 }

 

执行:

# ./signal &

[1] 2090

root@PF:~# kill -USR1 2090

root@PF:~# received SIGUSR1

kill -USR2 2090

root@PF:~# received SIGUSR2

kill -TERM 2090

    因为该进程不捕捉SIGTERM信号,而针对该信号的系统默认动作是终止,所以当向该进程发送SIGTERM信号后,该进程就会终止

 

1.程序启动

    当执行一个程序时,所有信号的状态都是系统默认或忽略

    通常所有信号都被设置为它们的默认动作,除非调用exec的进程忽略该信号。即,exec函数将原先设置为要捕捉的信号都更改为它们的默认动用,其它信号的状态则不变(对于一个进程原先要捕捉的信号,当其执行一个新程序后,就自然不能再捕捉了它了,因为信号捕捉函数的地址很可能在所执行的新程序文件中已无意义)

 

    例:cc main.c&

shell自动将后台进程对中断和退出信号的处理方式设置为忽略。于是,当按中断键时就不会影响到后进程

 

2. 进程创建

    当一个进程调用fork时,其子进程继承父进程的信息处理方式。因为子进程在开始时复制了父进程的存储映像,所以信号捕捉函数的地址在子进程中是有意义的。

 

 

10.9 kill和raise函数

   kill函数将信号发送给进程或进程组

   raise函数则允许进程向自身发送信号

 

#include <signal.h>

int kill(pid_t pid, int signo);

int raise(int signo);

     两个函数返回值:若成功则返回0,若出错则返回-1

kill的pid参数有4种不同的情况:

 pid>0 将该信号发送给进ID为pid的进程

 pid==0 将该信号发送给与发送进程属于同一进程组的所有进程(这些进程的进程组ID等于发送进程的进程组ID)

       ,而且发送进程具有向这些进程发送信号的权限

 pid<0 将该信号发送给其进程组ID等于pid的绝对值,而且发送进程具有向其发送信号的权限

 pid==-1 将该信号发送给发送进程有权限向它们发送信号的系统上的所有进程,但不包括系统进程

 

POSIX.1将编号为0的信号定义为空信号,如果signo参数是0,则kill仍将执行正常的错误检查,但不发送信号,这常被用来确定一个特定进程是否仍旧存在。

 

    如果向一个并不存在的进程发送空信号,则kill返回-1,并将errno设置为ESRCH

 

10.10 alarm和pause函数

    使用alarm函数可以设置一个计时器,在将来某个指定的时间该计时器会超时。

    当计时器超时时,产生SIGALRM信号。如果不忽略或不捕捉此信号,则其默认动作是终止调用该alarm函数的进程。

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

    返回值:0或以前设置的闹钟时间的余留秒数

参数:seconds的值是秒数,经过指定的seconds秒后会产生信号SIGALRM。

     经过指定的秒数后,信号由内核产生,由于进程调度的延迟,所以进程得到控制从而能够处理该信号还需一些时间

 

#include <unistd.h>

int pause(void);

    返回值:-1, 并将errno设置为EINTR

    只有律诗行了一个信号处理程序并从其返回时,pause才返回。

    在这种情况下,pause返回-1,并将errno设置为EINTR

 

例:使用longjmp,带超时限制调用read

     # cat -n longjmpread.c

     1  #include <setjmp.h>

     2  #include <unistd.h>

     3  #include <stdio.h>

     4  #include <stdlib.h>

     5  #include <signal.h>

     6

     7  #define MAXLINE 100

     8

     9  static void sig_alrm(int);

    10  static jmp_buf env_alrm;

    11

    12  int

    13  main(void)

    14   {

    15    int n;

    16    char line[MAXLINE];

    17

    18    if(signal(SIGALRM, sig_alrm)==SIG_ERR)

    19      perror("signal(SIGALRM)error");

    20    if(setjmp(env_alrm)!=0)

    21      {

    22        perror("read timeout");

    23        exit(1);

    24       }

    25

    26    alarm(10);

    27   if((n=read(STDIN_FILENO,line,MAXLINE))<0)

    28       perror("read error");

    29    alarm(0);

    30

    31    write(STDOUT_FILENO,line,n);

    32    exit(0);

    33    }

    34

    35    static void

    36    sig_alrm(int signo)

    37     {

    38      longjmp(env_alrm,1);

    39     }

# ./longjmpread

read timeout: Success

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: