linux进程间通信--信号

2021-03-08

概念:

  1. 信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式
  2. 信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。
  3. 如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被 取消时才被传递给进程。

用户进程对信号的响应方式:

  1. 忽略信号:对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。
  2. 捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。
  3. 执行缺省操作:Linux对每种信号都规定了默认操作

signal

设置信号处理方式

1
2
3
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

参数说明:

返回值:

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

void signalHandler(int signo)
{
    switch (signo){
        case SIGALRM:
            printf("Caught the SIGALRM signal!\n");
            break;
   }
}

int main(int argc, char *argv[])
{
    signal(SIGALRM, signalHandler);

    struct itimerval new_value, old_value;
    new_value.it_value.tv_sec = 0;
    new_value.it_value.tv_usec = 1;
    new_value.it_interval.tv_sec = 0;
    new_value.it_interval.tv_usec = 200000;
    setitimer(ITIMER_REAL, &new_value, &old_value);
    
    for(;;);
     
    return 0;
}

常用信号列表

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
SIGABRT     由调用abort函数产生,进程非正常退出
SIGALRM     用alarm函数设置的timer超时或setitimer函数设置的interval timer超时
SIGBUS      某种特定的硬件异常,通常由内存访问引起
SIGCANCEL   由Solaris Thread Library内部使用,通常不会使用
SIGCHLD     进程Terminate或Stop时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略
SIGCONT     当被stop的进程恢复运行的时候,自动发送
SIGEMT      和实现相关的硬件异常
SIGFPE      数学相关的异常,如被0除,浮点溢出,等等
SIGFREEZE   Solaris专用,Hiberate或者Suspended时候发送
SIGHUP      发送给具有Terminal的Controlling Process,当terminal被disconnect时候发送
SIGILL      非法指令异常
SIGINFO     BSD signal。由Status Key产生,通常是CTRL+T。发送给所有Foreground Group的进程
SIGINT      由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group进程
SIGIO       异步IO事件
SIGIOT      实现相关的硬件异常,一般对应SIGABRT
SIGKILL     无法处理和忽略。中止某个进程
SIGLWP      由Solaris Thread Libray内部使用
SIGPIPE     在reader中止之后写Pipe的时候发送
SIGPOLL     当某个事件发送给Pollable Device的时候发送
SIGPROF     Setitimer指定的Profiling Interval Timer所产生
SIGPWR      和系统相关。和UPS相关。
SIGQUIT     输入Quit Key的时候(CTRL+\)发送给所有Foreground Group的进程
SIGSEGV     非法内存访问
SIGSTKFLT   Linux专用,数学协处理器的栈异常
SIGSTOP     中止进程。无法处理和忽略。
SIGSYS      非法系统调用
SIGTERM     请求中止进程,kill命令缺省发送
SIGTHAW     Solaris专用,从Suspend恢复时候发送
SIGTRAP     实现相关的硬件异常。一般是调试异常
SIGTSTP     Suspend Key,一般是Ctrl+Z。发送给所有Foreground Group的进程
SIGTTIN     当Background Group的进程尝试读取Terminal的时候发送
SIGTTOU     当Background Group的进程尝试写Terminal的时候发送
SIGURG      当out-of-band data接收的时候可能发送
SIGUSR1     用户自定义signal 1
SIGUSR2     用户自定义signal 2
SIGVTALRM   setitimer函数设置的Virtual Interval Timer超时的时候
SIGWAITING  Solaris Thread Library内部实现专用
SIGWINCH    当Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程
SIGXCPU     当CPU时间限制超时的时候
SIGXFSZ     进程超过文件大小限制
SIGXRES     Solaris专用,进程超过资源限制的时候发送

sigemptyset

将信号集初始化为空。

glibc中的实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* A `sigset_t' has a bit for each signal.  */
typedef unsigned long int __sigset_t;         
/* A set of signals to be blocked, unblocked, or waited for.  */
typedef __sigset_t sigset_t;                                    

/* Clear all signals from SET.  */
int                                  
sigemptyset (sigset_t *set)          
{                                    
  if (set == NULL)                   
    {                                
      __set_errno (EINVAL);          
      return -1;                     
    }                                
                                     
  memset (set, 0, sizeof (sigset_t));
                                     
  return 0;                          
}

sigaddset

将指定信号加入到信号集合中去

1
2
#include <signal.h>
int sigaddset(sigset_t *set, int signum);

sigaction

sigaction函数的功能是检查或修改与指定信号相关联的处理动作

1
2
int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

参数:

act与oldact为NULL时,用于检测信号的有效性

相关结构体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
struct sigaction {
    void     (*sa_handler)(int);
    void     (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t   sa_mask;
    int        sa_flags;
    void     (*sa_restorer)(void);
};

siginfo_t {
    int      si_signo;    /* Signal number */
    int      si_errno;    /* An errno value */
    int      si_code;     /* Signal code */
    int      si_trapno;   /* Trap number that caused
                             hardware-generated signal
                             (unused on most architectures) */
    pid_t    si_pid;      /* Sending process ID */
    uid_t    si_uid;      /* Real user ID of sending process */
    int      si_status;   /* Exit value or signal */
    clock_t  si_utime;    /* User time consumed */
    clock_t  si_stime;    /* System time consumed */
    sigval_t si_value;    /* Signal value */
    int      si_int;      /* POSIX.1b signal */
    void    *si_ptr;      /* POSIX.1b signal */
    int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
    int      si_timerid;  /* Timer ID; POSIX.1b timers */
    void    *si_addr;     /* Memory location which caused fault */
    long     si_band;     /* Band event (was int in
                             glibc 2.3.2 and earlier) */
    int      si_fd;       /* File descriptor */
    short    si_addr_lsb; /* Least significant bit of address
                             (since Linux 2.6.32) */
}

#ifdef __USE_POSIX199309
union sigval
{
  int sival_int;
  void *sival_ptr;
};

typedef union sigval __sigval_t;
#else
union __sigval
{
  int __sival_int;
  void *__sival_ptr;
};

typedef union __sigval __sigval_t;
#endif

示例(发送信号函数在sigqueue函数的例子中,需要两个配合测试):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <signal.h>

void handle_sigaction(int signum, siginfo_t *info, void *context)
{
    printf("signal:%d\n", signum);
    printf("info:%x\n", info->si_int);
}


int main()
{
    int i = 0;
    struct sigaction act;

    sigemptyset(&act.sa_mask);
    act.sa_sigaction = handle_sigaction;
    act.sa_flags = SA_SIGINFO|SA_RESTART;
    sigaction(SIGRTMIN+9, &act, NULL);

    while(1)
    {
        sleep(1);
        printf("sleeping %d\n", i);
        i++;
    }

    return 0;
}

sigqueue

用于给指定进程发送信号,并附带一些参数

1
int sigqueue(pid_t pid, int sig, const union sigval val)

参数:

示例(对应sigaction函数的信号处理示例):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>                                                                      
#include <signal.h>                                                                     
#include <stdlib.h>                                                                     
#include <string.h>                                                                     
                                                                                        
static void send_signal(int flag)                                           
{                                                                                       
    FILE * fp;                                                                          
    char buffer[80];                                                                    
    pid_t pid;                                                                          
    union sigval sival_data;                                                            
    int ret;                                                                            
                                                                                        
    fp = popen("pidof signal_process","r");                                             
    if(fp != NULL){                                                                     
        memset(buffer,0x00,sizeof(buffer));                                             
        if ((fgets(buffer,sizeof(buffer),fp) == NULL) || (buffer[0] == '\0')){          
            pclose(fp);                                                                 
            return;                                                                     
        }                                                                               
    }else{                                                                              
        return;                                                                         
    }                                                                                   
    pclose(fp);                                                                         
                                                                                        
    pid = atoi(buffer);                                                                 
    sival_data.sival_int = flag;                                                        
    ret = sigqueue(pid, SIGRTMIN+9, sival_data);                                        
    return;                                                                             
}                                                                                       
                                                                                        
int main()                                                                              
{                                                                                       
    send_signal(8);                                                         
    return 0;                                                                           
}                                                                                       
                                                                                        
words: 2436 tags: linux c