Linux编程细节6-内核-信号处理

1 信号概念

信号本质:信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。

信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。


2 信号来源

信号事件的发生有两个来源:
硬件来源, 如按下了键盘或者其它硬件故障;
软件来源, 最常用发送信号的系统函数是kill, raise, alarm和setitimer以及sigqueue函数,软件来源还包括一些非法运算等操作。

2 信号分类

Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,后来在实践中 暴露出一些问题(信号丢失),因此,把那些建立在早期机制上的信号叫做"不可靠信号"。

信号值区间可靠性实时性实现机制
x<SIGTMIN(32)不可靠非实时置位
SIGTMIN(32)<x<SIGTMAX(63)可靠实时排队


注意:

非实时信号不支持排队,都是不可靠信号
实时信号支持排队,都是可靠信号

2.1 信号列表

使用kill -l就会显示出linux支持的信号列表。

其中列表中,编号为1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。

xxx@xxx:~/xxx> kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
 5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
 9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
13) SIGPIPE     14) SIGALRM     15) SIGTERM     16) SIGSTKFLT
17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU
25) SIGXFSZ     26) SIGVTALRM   27) SIGPROF     28) SIGWINCH
29) SIGIO       30) SIGPWR      31) SIGSYS      34) SIGRTMIN
35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3  38) SIGRTMIN+4
39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7  58) SIGRTMAX-6
59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX



信号名称说明默认动作能否捕获
 1) SIGHUP  连接断开终止 
 2) SIGINT 终端中断符 【终端:Ctrl+C】终止 
 3) SIGQUIT  终端退出符【终端:Ctrl+\】终止+core 
 4) SIGILL非法硬件指令终止+core 
 5) SIGTRAP  硬件故障终止+core 
 6) SIGABRT异常终止终止+core 
 7) SIGBUS 硬件故障终止+core 
 8) SIGFPE算数异常终止+core 
 9) SIGKILL终止 终止
10) SIGUSR1 用户定义的信号终止 
11) SIGSEGV无效内存引用终止+core 
12) SIGUSR2用户定义的信号终止 
13) SIGPIPE写至无读进程的管道终止 
14) SIGALRM 超时终止 
15) SIGTERM终止【kill命令默认信号】终止 
16) SIGSTKFLT协处理器栈故障终止 
17) SIGCHLD子进程状态改变忽略 
18) SIGCONT使暂停进程继续继续/忽略 
19) SIGSTOP 停止暂停进程
20) SIGTSTP终端停止符【终端:Ctrl+Z】暂停进程 
21) SIGTTIN后台读控制tty暂停进程 
22) SIGTTOU后台写控制tty暂停进程 
23) SIGURG  紧急情况(套接字)忽略 
24) SIGXCPU超过CPU限制(setrlimit)终止+core/忽略 
25) SIGXFSZ 超过文件长度限制(setrlimit)终止+core/忽略 
26) SIGVTALRM 虚拟时间闹钟(setitimer)终止 
27) SIGPROF 梗概时间超时(setitimer)终止 
28) SIGWINCH终端窗口大小改变忽略 
29) SIGIO 异步IO终止/忽略 
30) SIGPWR电源失效/重启动终止/忽略 
31) SIGSYS无效系统调用终止+core 
34) SIGRTMIN   
35) SIGRTMIN+1    
36) SIGRTMIN+2   
37) SIGRTMIN+3    
38) SIGRTMIN+4   
39) SIGRTMIN+5   
40) SIGRTMIN+6    
41) SIGRTMIN+7   
42) SIGRTMIN+8   
43) SIGRTMIN+9   
44) SIGRTMIN+10   
45) SIGRTMIN+11   
46) SIGRTMIN+12   
47) SIGRTMIN+13    
48) SIGRTMIN+14   
49) SIGRTMIN+15   
50) SIGRTMAX-14   
51) SIGRTMAX-13   
52) SIGRTMAX-12   
53) SIGRTMAX-11   
54) SIGRTMAX-10   
55) SIGRTMAX-9   
56) SIGRTMAX-8   
57) SIGRTMAX-7   
58) SIGRTMAX-6   
59) SIGRTMAX-5    
60) SIGRTMAX-4    
61) SIGRTMAX-3    
62) SIGRTMAX-2   
63) SIGRTMAX-1   
64) SIGRTMAX   
    
    


2.2 常用信号解释

2.2.1 SIGHUP

SIGHUP会在以下3种情况下被发送给相应的进程:
1、当终端检测到网络断开时,会将挂断信号发送到session首进程以及作为job提交的进程(即用 & 符号提交的进程)
2、session首进程退出时,该信号被发送到该session中的前台进程组中的每一个进程
3、若父进程退出导致进程组成为孤儿进程组,且该进程组中有进程处于停止状态(收到SIGTSTP信号),SIGHUP会被发送到该进程组中的每一个进程。系统对SIGHUP信号的默认处理是终止收到该信号的进程。所以若程序中没有捕捉该信号,当收到该信号时,进程就会退出。

2.2.2 SIGINT

程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出,用于通知 前台进程组终止进程。

SIGQUIT-信号3-终端推出符-终止core

xxx

SIGILL-信号4-终止


2.2.3 SIGTERM

SIGTERM比较友好,进程能捕捉这个信号,根据您的需要来关闭程序。在关闭程序之前,您可以结束打开的记录文件和完成正在做的任务。在某些情况下,假 如进程正在进行作业而且不能中断,那么进程可以忽略这个SIGTERM信号。

SIGTERM可以被捕获:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include <signal.h>

#define MAXFILE 65535
void sigterm_handler(int arg);
volatile sig_atomic_t _running = 1;

int main()
{
    int i,fd;
    char *buf="this is a Dameon\n";
    int len = strlen(buf);

    pid_t pc = fork(); //父生子 留子
    if(pc<0)
    {
        printf("error fork\n");
        exit(1);
    }
    else if(pc>0)
        exit(0);
    setsid();    //1,在子进程中创建新会话

    pid_t pid = fork();//子生孙 留孙
    if (pid < 0)
        perror("fork error");
    else if (pid > 0)
        exit(0);
    chdir("/");  //2,改变当前目录为根目录
    umask(0);    //3,重设文件权限掩码
    for(i=0; i<MAXFILE; i++) //4,关闭文件描述符
        close(i);
    signal(SIGTERM, sigterm_handler);//5,设置kill信号
    while( _running )
    {
        if((fd=open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0)
        {
            perror("open");
            exit(1);
        }
        write(fd,buf,len);
        close(fd);
        //usleep(10*1000); //10毫秒
        sleep(10); //10秒
    }
}
void sigterm_handler(int arg)
{
    _running = 0;
}


2.2.4 SIGKILL

SIGKILL进程是不能忽略的。这是一个 “我不管您在做什么,立刻停止”的信号。假如您发送SIGKILL信号给进程,Linux就将进程停止在那里。Linux进程中的kill命令是通过向进程发送指定的信号来结束进程的。如果没有指定发送信号,那么默认值为TERM信号。TERM信号将终止所有不能捕获该信号的进程。至于那些可以捕获该信号的进程可能就需要使用kill(9)信号了,该信号是不能被捕捉的。


一个案例:

http://bbs.chinaunix.net/thread-2197309-1-1.html

描述:我写了一个后台程序,在系统重启时会做一些保存工作。因此用signal()对SIGTERM进行了处理。测试时让后台程序运行,用命令kill -15  PID,保存工作运行良好。但是实际测试,我用命令 reboot,后台进程却不能接收到SIGTERM信号,未进行保存工作。同样都是发送SIGTERM信号给进程(kill和reboot),为什么处理不一样?请指点。

原因:如果这个程序脱离任何终端,在后台运行(非守护进程),那么它能接收到SIGTERM信号,并按要求执行。但是我做测试的时候,它是在一个终端上运行程序的,属于此终端的一个子进程。在reboot命令的后,终端先关闭了,此进程随即结束,并未收到SIGTERM信号。未脱离终端的后台进程(&),那么同属于一个会话组,会话组组长是终端bash,因此reboot时,父进程(bash)先被杀死,即session的组长进程被杀死了,因此其会话组其他进程,包括该后台进程也被杀死。


2.2.5 SIGCHLD

子进程退出时,会给父进程发送SIGCHLD信号,父进程默认忽略之。

APUE上SIGCLD语义写的有点不清楚,到底我们的系统是如何来处理SIGCLD信号呢?
1.SIG_DFL :

默认的处理方式是不理会这个信号,但是也不会丢弃子进行状态,所以如果不用wait,waitpid对其子进行进行状态信息回收,会产生僵尸进程。
2.SIG_IGN :

忽略的处理方式,在这种方式下,子进程状态信息会被丢弃,也就是自动回收了,所以不会产生僵尸进程,但是问题也就来了,wait,waitpid却无法捕捉到子进程状态信息了,如果你随后调用了wait,那么会阻塞到所有的子进程结束,并返回错误ECHILD,也就是没有子进程等待。
3.自定义处理方式:

SIGCLD会立即检查是否有子进程准好被等待,这便是SIGCLD最大漏洞了,一旦在信号处理函数中加入了信号处理方式重建的步骤,那么每次设置SIGCLD处理方式时,都会去检查是否有信号到来,如果此时信号的确到来了,先是调用自定义信号处理函数,然后是调用信号处理方式重建函数,在重建配置的时候,会去检查信号是否到来,此时信号未被处理,会再次触发自定义信号处理函数,一直循环。所以在处理SIGCLD时,应该先wait处理掉了信号信息后,再进行信号处理方式重建。
   SIGCHLD在配置信号处理方式时,是不会立即检查是否有子进程准备好被等待,也不会在此时调用信号处理函数。




2.2.6 SIGQUIT

和SIGINT类似, 但由QUIT字符(通常是Ctrl-/)来控制.,用于通知 前台进程组终止进程 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。





2.2.7 SIGCONT

让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符


2.2.8 SIGSTOP

停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.


2.2.9 SIGTSTP

停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号 ,用于通知 前台进程组终止进程


2.2.10 SIGTTIN

当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.


2.2.11 SIGTTOU

类似于SIGTTIN, 但在写终端(或修改终端模式)时收到.


3 信号处理方法

3.1 进程对信号的响应

进程可以通过三种方式来响应一个信号:

(1) 忽略信号,
    即对信号不做任何处理,
    其中,有两个信号不能忽略:SIGKILL及 SIGSTOP;
(2) 捕捉信号。
    定义信号处理函数,当信号发生时,执行相应的处理函数;
(3) 执行缺省操作,
    Linux对每种信号都规定了默认操作,
    详细情况请参考相关资料。

注意,进程对实时信号的缺省反应是进程终止。

3.2 进程对信号的继承

我们知道程序的创建无非两种情况,一种是调用exec函数一种是调用fork函数

一般情况下,当一个程序执行时,所有信号的状态都是系统默认或者忽略。通常所有信号的动作都被设置成系统默认。

1,exec函数将原先设置为要捕捉的信号都更改为系统默认动作,其他信号的状态不变。因为在新进程中,原进程的信号处理程序的地址在新进程中可能已无意义,所以原来要捕捉的信号在新进程中自然就不能捕捉了。
2,fork函数中子进程继承其父进程的信号处理方式。因为子进程开始时复制了父进程的存储映像,所以信号处理程序是有意义的。

4 信号的安装

4.1 信号安装函数signal(不推荐)

4.1.1 函数定义

头文件 
#include<signal.h>
函数原型:
void (*signal (int signo, void (*func)(int))) (int);


4.1.2 参数说明 

参数signo

表示准备捕获或忽略的信号值

参数func

参数值值含义
func信号调用函数
SIG_IGN#define SIG_IGN (void (*) ()) 1    :忽略此信号
SIG_DFL#define SIG_DFL (void (*) ()) 0    :恢复默认行为

返回值

返回值值含义
func成功:返回返回最后一次为安装信号signo而调用signal()时的handler值
SIG_ERR失败:#define SIG_ERR (void (*) ()) -1    


4.1.3 函数理解

可以将该函数理解为如下定义:

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);


该函数本身是个set函数,但其返回值实际上是个get操作,返回上一次该信号的设置函数,因此有如下特别用法:

void int sig_int(),sig_quit();
//仅当信号当前未被忽略时,进程才会捕获他们
if(signal(SIGINT,SIG_IGN) !=SIG_IGN)
	signal(SIGINT,sig_int);
if(signal(SIGQUIT,SIG_IGN)!=SIG_IGN)
	signal(SIGQUIT,sig_quit);
因此,该signal函数的缺陷之一是:不改变信号的处理方式就不能确定信号的当前处理方式。


4.1.4 代码示例

#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void ouch(int sig)
{
  printf("OUCH! -I got signal %d\n", sig);
  void signal(SIGINT, SIG_DFL);
}

int main()
{
  (void) signal(SIGINT, ouch);
   
  while(1)
  {
    printf("Hello World!\n");
    sleep(1); 
  }
}
说明:
第一次按Ctrl+c让程序作出响应,然后继续执行。再按一次Ctrl+c程序结束, 因为SIGINT信号的处理方式已恢复成默认



4.2 信号安装函数sigaction(推荐)

4.2.1 函数定义

头文件 
#include<signal.h>
函数原型:
int sigaction(int signo,const struct sigaction *restrict act, struct sigaction *restrict oact);

结构sigaction定义如下:
struct sigaction{
  void (*sa_handler)(int); /* function, SIG_DFL or SIG_IGN */
  sigset_t sa_mask;        /* signals to block in sa_handler */
  int sa_flag;             /* signal action modifiers */
  void (*sa_sigaction)(int,siginfo_t *,void *);
};


4.2.2 参数说明

参数signo

为信号的值,可以为除SIGKILL及SIGSTOP 外的任何一个特定有效的信号,为这两个信号定义自己的处理函数,将导致信号安装错误。

参数act

该结构的作用是定义在接收到信号后应该采取的行动。可以为空,进程会以缺省方式对信号处理;

参数oact

指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。
如果oldact不是空指针, sigaction将把原先对该信号的动作写到它指向的位置。
如果oldact  是空指针, 则sigaction函数就不需要再做其它设置了。


如果把参数act, oact都设为NULL,那么该函数可用于检查信号的有效性。

参数sa_handler

参数值值含义
sa_handler信号调用函数
SIG_IGN#define SIG_IGN (void (*) ()) 1    :忽略此信号
SIG_DFL#define SIG_DFL (void (*) ()) 0    :恢复默认行为

参数sa_mask

字段说明了一个信号集,在调用该信号捕捉函数之前,这一信号集要加进进程的信号屏蔽字中。仅当从信号捕捉函数返回时再将进程的信号屏蔽字复位为原先值。头文件 signal.h 中有一组函数用来操作信号集sigset_t,它们分别是 sigaddset 、 sigemptyset 、 sigfillset 和 sigdelset 等。

参数sa_flag

SA_INTERRUPT 由此信号中断的系统调用不会自动重启
SA_RESTART   由此信号中断的系统调用会自动重启
SA_SIGINFO   提供附加信息,一个指向siginfo结构的指针以及一个指向进程上下文标识符的指针
SA_RESETHAND 当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL(默认是不重置的)
SA_NODEFER   捕获到信号时不将它添加到信号屏蔽字中 
SA_NOCLDSTOP 子进程停止时不产生SIGCHLD信号 

SA_NOCLDWAIT 避免子进程僵死

返回值

成功0
失败-1

如果给出的信号无效或者试图对一个不允许被捕获或忽略的信号进行捕获或忽略,错误变量errno将被置为EINVAL。

4.2.3 函数理解

xxx

4.2.4 代码示例

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
 
void ouch(int sig){
        printf("OUCH! - I got signal %d/n", sig);
}
 
int main()
{
        struct sigaction act;
 
        act.sa_handler = ouch;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
 
        sigaction(SIGINT, &act, 0);
 
        while(1){
                printf("hello. /n");
                sleep(1);
        }
 
         return 0;
} 

按下Ctrl+C组合键,就可以看到一条消息。因为sigaction函数 连续处理到来的SIGINT信号。要想终止这个程序,按下Ctrl+\组合键,它默认情况下产生SIGQUIT信号。


4.3 信号集sigset_t类型

创建信号集sigset_t函数有如下几个:

#include <signal.h>
int sigemptyset(sigset_t *set);  
int sigfillset(sigset_t *set); 
int sigaddset(sigset_t *set, int signum); 
int sigdelset(sigset_t *set, int signum); 
int sigismember(const sigset_t *set, int signum);
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
int sigpending(sigset_t *set);
ing sigsuspend(const sigset_t *sigmask);

1,int sigemptyset(sigset_t *set);   
初始化由set指定的信号集,信号集里面的所有信号被清空;

2,int sigfillset(sigset_t *set);    
调用该函数后,set指向的信号集中将包含linux支持的64种信号;,

3,int sigaddset(sigset_t *set, int signum);         
在set指向的信号集中加入signum信号;
,
4,int sigdelset(sigset_t *set, int signum);         
在set指向的信号集中删除signum信号;

5,int sigismember(const sigset_t *set, int signum); 
判定信号signum是否在set指向的信号集中;

成功,返回0,如果参数how值无效,它将返回-1并设置errno为EINVAL.


6,int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
根据参数how指定的方法修改进程的信号屏蔽字。新的屏蔽字由参数set(如果它不为空)指定,而原先的信号屏蔽字将保存到信号集oset中。
how的取值为:
SIG_BLOCK   把参数set中的信号添加到信号屏蔽字中
SIG_SETMASK 把信号屏蔽字设置为参数set中的信号
SIG_UNBLOCK 从信号屏蔽字中删除参数set中的信号
如果参数set为空指针,how的值就没有意义,此时调用的唯一目的就是把当前屏蔽字的值保存到oset中。

成功,返回0,如果参数how值无效,它将返回-1并设置errno为EINVAL.


7,int sigpending(sigset_t *set);
如果一个信号被进程阻塞,它就不会传递给进程,但会停留在待处理状态。程序可以通过调用sigpending来查看它阻塞的信号中有哪些正停留在待处理状态。
该函数的作用是:将被阻塞的信号中停留在待处理状态的一组信号写到参数set指向的信号集中,
成功时,返回0,否则,  返回-1并设置errno以表明错误的原因。

8,ing sigsuspend(const sigset_t *sigmask);
进程可以通过调用它来挂起自己的执行,,直到信号集中的一个信号到达为止。它是pause函数更通用的表现形式。
该函数将进程的屏蔽辽替换为由参数sigmask给出的信号集,然后挂起程序的执行,程序将在信号处理函数执行完毕后继续执行。
如果接收到的信号终止了程序,sigsuspend就不会返回;
如果接收到的信号没有终止程序,sigsuspend就返回-1并将errno设置为EINTR.


4.4 可重入函数

信号处理程序中的可重入函数

accept

fchmod

lseek

sendto

stat

access

fchown

lstat

setgid

symlink

aio_error

fcntl

mkdir

setpgid

sysconf

aio_return

fdatasync

mkfifo

setsid

tcdrain

aio_suspend

fork

open

setsockopt

tcflow

alarm

fpathconf

pathconf

setuid

tcflush

bind

fstat

pause

shutdown

tcgetattr

cfgetispeed

fsync

pipe

sigaction

tcgetpgrp

cfgetospeed

ftruncate

poll

sigaddset

tcsendbreak

cfsetispeed

getegid

posix_trace_event

sigdelset

tcsetattr

cfsetospeed

geteuid

pselect

sigemptyset

tcsetpgrp

chdir

getgid

raise

sigfillset

time

chmod

getgroups

read

sigismember

timer_getoverrun

chown

getpeername

readlink

signal

timer_gettime

clock_gettime

getpgrp

recv

sigpause

timer_settime

close

getpid

recvfrom

sigpending

times

connect

getppid

recvmsg

sigprocmask

umask

creat

getsockname

rename

sigqueue

uname

dup

getsockopt

rmdir

sigset

unlink

dup2

getuid

select

sigsuspend

utime

execle

kill

sem_post

sleep

wait

execve

link

send

socket

waitpid

_Exit & _exit

listen

sendmsg

socketpair

write


所以在信号处理函数中不要使用printf等函数。像printf()和malloc()之类的标准库函数,它们会使用全局的数据(比如malloc()需要使用进程的堆数据结构)是不可重入的,即使加锁也无法解决,加锁可以使得库函数Tread-safe,但是无法解决可重入。

当仍需要使用printf等函数时,可以使用标记位,在之后进行这些操作:

int sig_int_flag;

main(){
	int sig_int();
	...
	signal(SIGINT, sig_int);
	...
	while(sig_int_flag == 0)
		pause();
	//printf
	...
}

sig_int(){
	signal(SIGINT, sig_int);//重置该信号
	sig_int_flag = 1; //设置标记
}


5 信号的发送

发送信号的主要函数有:
kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()。


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

kill函数把参数sig给定的信号发送结由参数pid给出的进程号所指定的进程或进程组。
头文件:
#include <signal.h>
函数原型:
int kill(pid_t pid, int signo);
参数说明:
pid:可能选择有以下四种
1. pid>0,信号将送往进程ID为pid的进程。
2. pid=0,信号将送往所有与调用kill()的那个进程属同一个使用组的进程。
3. pid=-1,信号将送往所有调用进程有权给其发送信号的进程,除了进程1(init)。
4. pid<-1,信号将送往以-pid为组标识的进程。
int:返回值,成功执行时,返回0。失败返回-1

特别的, 当signo值为0时(即空信号),实际不发送任何信号, 但照常进行错误检查。 因此,可用于检查目标进程是否存在, 以及当前进程是否具有向目标发送信号的权限。如果向一个并不存在的进程发送空信号,则kill返回-1,并将errno设置为ESRCH。

5.2 函数raise-信号发送给自身

raise向进程本身发送信号,参数为即将发送的信号值。

#include <signal.h>
int raise(int signo);
调用成功 返回 0;否则,返回 -1。
raise(signo); 相当于  kill(getpid(), signo);

5.3 函数alarm-设置一个定时器信号

alarm函数用来设置一个定时器,到时间后,向自身发送SIGALRM信号。
头文件:
#include<unistd.h>
函数原型:
unsigned int alarm(unsigned int seconds);
参数:
seconds:设定的秒数,若为0则表示取消之前的闹钟设定。
返回值:
int:上次闹钟设置的余留值。

几点说明:
1,进程调用alarm后,任何以前的alarm()调用都将无效,被新设定所替代。
2,如果参数seconds为0, 则取消以前的闹钟时钟,其余留值仍作为alarm函数的返回值。
3, 经过指定的秒数后,信号由内核产生,由于进程调度的延迟,所以进程得到控制从而能够处理该信号还需要一些时间。



模拟一个闹钟:


#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
static int alarm_fired = 0;
void ding(int sig)
{
  alarm_fired = 1;
}
子进程在等待5秒后发送一个SIGALRM信号给它的父进程。


int main()
{
  pid_t pid;
  printf(“alarm application starting\n”);
  
  pid = fork();
  switch(pid) {
  case -1:
    /* Failure */
    perror(“fork failed”);
    exit(1);
  case 0:
    /* child */
    sleep(5);
    kill(getppid(), SIGALRM);
    exit(0);
  }
父进程通过一个signal调用安排好捕获SIGALRM信号的工作,然后等待它的到来。




  /* if we get here we are the parent process */
  printf(“waiting for alarm to go off\n”);
  (void) signal(SIGALRM, ding);


  /*程序把执行挂起,直到出现一个信号为止*/
  pause();
  if (alarm_fired)
    printf(“Ding!\n”);


  printf(“done\n”);
  exit(0);
}
$ ./alarm
alarm application starting
waiting for alarm to go off
<5 second pause>


done
$

5.4 函数pause-挂起直到捕获一个信号

pause()会令当前的进程暂停(进入睡眠状态),直到被信号(signal)所中断。
头文件:
#include<unistd.h>
函数原型:
int pause(void);
返回值:
int:只返回-1。并将errno设置为EINTR


5.5 函数setitimer

#include <sys/time.h>
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
setitimer()比alarm功能强大,支持3种类型的定时器:
ITIMER_REAL   : 设定绝对时间;
                经过指定的时间后,内核将发送SIGALRM信号给本进程;
ITIMER_VIRTUAL: 设定程序执行时间;
                经过指定的时间后,内核将发送SIGVTALRM信号给本进程;
ITIMER_PROF   : 设定进程执行以及内核因本进程而消耗的时间和,
                经过指定的时间后,内核将发送ITIMER_VIRTUAL信号给本进程;


which : 指定定时器类型(上面三种之一);
value : 是结构体itimerval的一个实例,结构itimerval形式见附录1。
ovalue: 可不做处理。


调用成功, 返回 0,
    否则, 返回-1。


5.6 函数abort

#include <stdlib.h>
void abort(void);
向进程发送SIGABORT信号,默认情况下进程会异常退出,
当然可定义自己的信号处理函数。
即使SIGABORT被进程设置为阻塞信号,调用abort()后,SIGABORT仍然能被进程接收。
该函数无返回值。



5.3 函数sigqueue



#include <sys/types.h>
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval val);
调用成功, 返回 0;
    否则, 返回 -1。


sigqueue()是比较新的发送信号系统调用,
主要是针对实时信号提出的(当然也支持前32种),
支持信号带有参数,
与函数sigaction()配合使用。


pid : 指定接收信号的进程ID; 
sig : 确定即将发送的信号;
val : 是一个联合数据结构union sigval,
      指定了信号传递的参数,即通常所说的4字节值。


typedef union sigval {
        int sival_int;
        void *sival_ptr;
}sigval_t;
sigqueue()比kill()传递了更多的附加信息,
但sigqueue()只能向一个进程发送信号,
而不能发送信号给一个进程组。


如果signo=0,将会执行错误检查,但实际上不发送任何信号,
0值信号可用于检查pid的有效性以及当前进程是否有权限向目标进程发送信号。


在调用sigqueue时,
sigval_t指定的信息会拷贝到3参数信号处理函数
(3参数信号处理函数指的是信号处理函数由 sigaction安装,
并设定了sa_sigaction指针,稍后将阐述)
的siginfo_t结构中,
这样信号处理函数就可以处理这些信息了。
由于 sigqueue系统调用支持发送带参数信号,
所以比kill()系统调用的功能要灵活和强大得多。

5.3 函数sigqueue



#include <sys/types.h>
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval val);
调用成功, 返回 0;
    否则, 返回 -1。


sigqueue()是比较新的发送信号系统调用,
主要是针对实时信号提出的(当然也支持前32种),
支持信号带有参数,
与函数sigaction()配合使用。


pid : 指定接收信号的进程ID; 
sig : 确定即将发送的信号;
val : 是一个联合数据结构union sigval,
      指定了信号传递的参数,即通常所说的4字节值。


typedef union sigval {
        int sival_int;
        void *sival_ptr;
}sigval_t;
sigqueue()比kill()传递了更多的附加信息,
但sigqueue()只能向一个进程发送信号,
而不能发送信号给一个进程组。


如果signo=0,将会执行错误检查,但实际上不发送任何信号,
0值信号可用于检查pid的有效性以及当前进程是否有权限向目标进程发送信号。


在调用sigqueue时,
sigval_t指定的信息会拷贝到3参数信号处理函数
(3参数信号处理函数指的是信号处理函数由 sigaction安装,
并设定了sa_sigaction指针,稍后将阐述)
的siginfo_t结构中,
这样信号处理函数就可以处理这些信息了。
由于 sigqueue系统调用支持发送带参数信号,
所以比kill()系统调用的功能要灵活和强大得多。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值