linux中signal和sigaction信号函数学习

linux中signal函数详解

函数说明

       #include <signal.h>

       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);

函数参数说明

  • signum要捕捉的信号
  • handler要执行的捕捉函数指针,函数应用声明void func(int);

linux中sigaction函数详解

转自——linux中sigaction函数详解

函数原型:sigaction函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作)

int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oact);

signum参数指出要捕获的信号类型,act参数指定新的信号处理方式,oact参数输出先前信号的处理方式(如果不为NULL的话)。

struct sigaction结构体介绍

struct sigaction {
void (*sa_handler)(int); /* addr of signal handler, */
/* or SIG_IGN, or SIG_DFL */
sigset_t sa_mask; /* additional signals to block */
int sa_flags; /* signal options, Figure 10.16 */
/* alternate handler */
void (*sa_sigaction)(int, siginfo_t *, void *);
};
  • sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数
  • sa_mask 用来设置在处理该信号时,暂时将sa_mask 指定的信号集搁置
  • sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。 (一般填0,SA_SIGINFO会使用第二个函数指针)
    • SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
    • SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
    • SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 - SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号

例子1

通信sigaction捕捉setitimer产生的中断信号

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>

void catch_sig(int num){
    printf("catch %d sig\n",num);
}

int main(int argc, char const *argv[])
{
    //设置结构体
    struct sigaction act;
    act.sa_flags = 0;
    act.sa_handler = catch_sig;
    sigemptyset(&act.sa_mask);
    //注册捕捉
    sigaction(SIGALRM,&act,NULL);

    //setitimer
    struct itimerval myit = {{3,0},{5,0}};//间隔3秒,延迟5秒
    setitimer(ITIMER_REAL,&myit,NULL);
    while (1)
    {
        printf("who can kill me!\n");
        sleep(1);
    }
    

    return 0;
}

输出

who can kill me!
who can kill me!
who can kill me!
who can kill me!
who can kill me!
catch 14 sig
who can kill me!
who can kill me!
who can kill me!
catch 14 sig
who can kill me!
who can kill me!
who can kill me!
catch 14 sig
who can kill me!
who can kill me!
who can kill me!
catch 14 sig
who can kill me!
who can kill me!
who can kill me!
catch 14 sig
who can kill me!
who can kill me!
who can kill me!
catch 14 sig
who can kill me!
...

例子2——临时屏蔽信号

信号捕捉特性

1.进程正常运行时,默认PCB中有一个信号屏蔽字,假定为☆,它决定了进程自动屏
蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而
该函数有可能执行很长时间,在这期间所屏蔽的信号不由☆来指定。而是用 sa mask
来指定。调用完信号处理函数,再恢复为☆。
2.XX信号捕捉函数执行期间;XX信它动被屏蔽。
3.阻塞的常规号不支持排队,产生多次只记录一次。(后32个实时信号支持排队)

练习

  • 为某个信号设置捕捉函数
  • 验证在信号处理函数执行期间,该信号多次递送,那么只在处理函数之行结束后,处理一次。
  • 验证sa_mask在捕捉函数执行期间的屏蔽作用
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void catch_sig(int num){
    printf("begin call %d sig\n",num);
    sleep(5);//模拟捕获函数执行时间较长
    printf("end call ,catch %d sig\n",num);
}

int main(int argc, char const *argv[])
{
    struct sigaction act;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask,SIGQUIT);//临时屏蔽信号(ctrl + \)
    act.sa_handler = catch_sig;

    //注册捕捉
    sigaction(SIGINT,&act,NULL);

    while (1){
        printf("who can kill me?\n");
        sleep(1);
    }
    return 0;
}

输出

测试发送信号(短时间多次按下Ctrl + c)

$ ./a.out 
who can kill me?
who can kill me?
who can kill me?
who can kill me?
^Cbegin call 2 sig
end call ,catch 2 sig
who can kill me?
who can kill me?
who can kill me?
who can kill me?
^Cbegin call 2 sig
^C^C^C^Cend call ,catch 2 sig
begin call 2 sig
end call ,catch 2 sig
who can kill me?
who can kill me?
who can kill me?
^Cbegin call 2 sig
^\^\^\^\^C^C^Cend call ,catch 2 sig
begin call 2 sig
end call ,catch 2 sig
退出
$ 

注意,上面的输出中,^C是在终端按下Ctrl + c键,^\是按下Ctrl + \1,发出的信号2
下面的输出(第20行)说明在信号执行期间临时屏蔽(退出信号和中断信号,实际是屏蔽了两个信号),信号执行完毕,执行了退出操作。

^\^\^\^\^C^C^Cend call ,catch 2 sig

例子3——借助SIGCHLD信号回收子进程

子进程结束运行,其父进程会收到SIGCHLD信号。该信号的默认处理动作是忽略。可以捕捉该信号 ,在捕捉函数中完成子进程状态的回收。

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>

void catch_sig(int num){
    pid_t wpid;
    while ((wpid = waitpid(-1,NULL,WNOHANG))>0){
        printf("wait child %d ok \n",wpid);
    }
    
}

int main(int argc, char const *argv[])
{
    int i = 0;
    pid_t pid;

    //在创建子进程之前屏蔽SIGCHLD信号
    sigset_t myset,oldset;
    sigemptyset(&myset);
    sigaddset(&myset,SIGCHLD);
    //oldset 保留现场,设置了SIGCHLD到阻塞信号集
    sigprocmask(SIG_BLOCK,&myset,&oldset);

    for(i = 0;i<10;i++){
        pid = fork();
        if(pid ==0){
            break;
        }
    }

    if( i==10){
        //parent
        struct sigaction act;
        act.sa_flags = 0;
        sigemptyset(&act.sa_mask);
        act.sa_handler = catch_sig;

        sigaction(SIGCHLD,&act,NULL);

        //解除屏蔽现场
        sigprocmask(SIG_SETMASK,&oldset,NULL);

        while (1){
            sleep(1);
        }
    }else if(i<10){
        printf("I am %d child,pid = %d\n",i,getpid());
        sleep(1);
    }

    return 0;
}

其他

内核捕捉信号过程

内核捕捉信号过程

setitimer函数

释意

set value of an interval timer

代码

       #include <sys/time.h>

       int getitimer(int which, struct itimerval *curr_value);
       int setitimer(int which, const struct itimerval *new_value,
                     struct itimerval *old_value);

参数1,整型which

       ITIMER_REAL    This timer counts down in real (i.e., wall clock) time.  At each expiration, a SIGALRM signal is  gener‐
                      ated.

       ITIMER_VIRTUAL This  timer  counts  down  against the user-mode CPU time consumed by the process.  (The measurement in‐
                      cludes CPU time consumed by all threads in the process.)  At each expiration, a SIGVTALRM signal is gen‐
                      erated.

       ITIMER_PROF    This  timer counts down against the total (i.e., both user and system) CPU time consumed by the process.
                      (The measurement includes CPU time consumed by all threads in the process.)  At each expiration, a  SIG‐
                      PROF signal is generated.

                      In  conjunction with ITIMER_VIRTUAL, this timer can be used to profile user and system CPU time consumed
                      by the process.

参数2,参数3,结构体 itimerval

           struct itimerval {
               struct timeval it_interval; /* Interval for periodic timer */
               struct timeval it_value;    /* Time until next expiration */
           };

           struct timeval {
               time_t      tv_sec;         /* seconds */
               suseconds_t tv_usec;        /* microseconds */
           };

sigemptyset函数

       sigemptyset() 
       initializes the signal set given by set to empty, with all signals excluded from the set.

将set给定的信号集初始化为空,并将所有信号排除在集合之外

sigaddset和sigdelset函数

       sigaddset() and sigdelset() 
       add and delete respectively signal signum from set.

分别从集合添加删除信号

sigprocmask函数

设置阻塞信号集

       #include <signal.h>

       /* Prototype for the glibc wrapper function */
       int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

参数how

       SIG_BLOCK
              The set of blocked signals is the union of the current set and the set argument.

       SIG_UNBLOCK
              The signals in set are removed from the current set of blocked signals.  It is permissible to attempt to unblock a signal which is not blocked.

       SIG_SETMASK
              The set of blocked signals is set to the argument set.

waitpid函数

转自——waitpid()函数详解
函数原型

       #include <sys/types.h>
       #include <sys/wait.h>

       pid_t wait(int *wstatus);

       pid_t waitpid(pid_t pid, int *wstatus, int options);

       int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

参数——pid

从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义。

  • pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
  • pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
  • pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
  • pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

参数——options

options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用,比如:

ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);

如果我们不想使用它们,也可以把options设为0,如:

ret=waitpid(-1,NULL,0);

如果使用了WNOHANG(wait no hung)参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。

而WUNTRACED参数,由于涉及到一些跟踪调试方面的知识,加之极少用到,这里就不多费笔墨了,有兴趣的读者可以自行查阅相关材料。

wait不就是经过包装的waitpid吗?没错,察看<内核源码目录>/include/unistd.h文件349-352行就会发现以下程序段:

static inline pid_t wait(int * wait_stat) { return waitpid(-1,wait_stat,0); }

返回值和错误

返回值和错误

waitpid的返回值比wait稍微复杂一些,一共有3种情况:

  1. 当正常返回的时候,waitpid返回收集到的子进程的进程ID;
  2. 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
  3. 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD;

信号表

man 7 signal查看信号

       SIGHUP        1       Term    Hangup detected on controlling terminal
                                     or death of controlling process
       SIGINT        2       Term    Interrupt from keyboard (中断)
       SIGQUIT       3       Core    Quit from keyboard (退出)
       SIGILL        4       Core    Illegal Instruction
       SIGABRT       6       Core    Abort signal from abort(3)
       SIGFPE        8       Core    Floating-point exception
       SIGKILL       9       Term    Kill signal
       SIGSEGV      11       Core    Invalid memory reference
       SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                     readers; see pipe(7)
       SIGALRM      14       Term    Timer signal from alarm(2)
       SIGTERM      15       Term    Termination signal
       SIGUSR1   30,10,16    Term    User-defined signal 1
       SIGUSR2   31,12,17    Term    User-defined signal 2
       SIGCHLD   20,17,18    Ign     Child stopped or terminated
       SIGCONT   19,18,25    Cont    Continue if stopped
       SIGSTOP   17,19,23    Stop    Stop process
       SIGTSTP   18,20,24    Stop    Stop typed at terminal
       SIGTTIN   21,21,26    Stop    Terminal input for background process
       SIGTTOU   22,22,27    Stop    Terminal output for background process

SIGCHLD信号

SIGCHLD信号的产生条件

  • 子进程终止时
  • 子进程接收到SIGSTOP信号停止时
  • 子进程处在停止态,接受到SIGCONT后唤醒时

注释


  1. UNIX环境高级编程第3版 15页,英文版18页 ↩︎

  2. 终端键盘上有两种产生信号的方法,分别称为中断键(interrupt key通常是Delete键或 Ctrl+C )和退出键(quit keCtrl+\ ),它们被用于中断当前运行的进程。另一种产生信号的方法是调用kill函数。在一个进程中调用此函数就可以向另一个进程发送一个信号。 ↩︎

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值