Linux进程之-信号

1、信号的基本概念

信号特点: 简单,不能携带大量信息,满足特定条件发生。信号也叫软件产生的中断,有可能会有延迟。
信号的机制: 进程B发送给进程A,内核产生信号,内核处理。
信号的产生:

  • 按键产生,例如ctrl+c,ctrl+z,ctrl+\
  • 调用函数 kill、raise、abort

kill函数:
原型:

       #include <signal.h>
       int kill(pid_t pid, int sig);

参数:
(1)pid>0,要发送的进程ID;pid=0,代表当前调用进程组内所有进程;pid=-1,代表有权限发送的所有进程;pid<0,代表-pid对应的组内所有进程。
(2)sig 对应的信号。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<signal.h>

int main()
{
    int i;
    for(i=0;i<5;i++)
    {
        pid_t pid = fork();
        if(pid == 0)
        {
            break;
        }
    }
    if(i == 2)
    {
        printf("I will kill father after 5s\n");
        sleep(5);
        kill(getppid(),SIGKILL);  //kill函数使用
        while (1)
        {
            sleep(1);
        } 
    }
    else if(i == 5)
    {
        //parent
        while (1)
        {
            printf("I am parent,I am happy\n");
            sleep(1);
        }
            
    }
    return 0;
}

raise函数
作用:给当前进程发送指定信号,给自己发送信号。raise(signo) == kill(getpid(),signo)

       #include <signal.h>
       int raise(int sig);

返回值:成功,0;失败,非0。
abort函数
作用:给自己发送异常终止信号( 6)SIGABRT 信号),终止并产生core文件。

       #include <stdlib.h>
       void abort(void);

返回值:无返回值。
例子:

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

int main()
{
    printf("I will die\n");
    sleep(2);
    raise(SIGKILL); //kill(getppid(),sig);
    //abort();
    return 0;
}
  • 定时器 alarm、setitimer —时钟产生信号
    alarm函数
    作用:设置定时器(闹钟)。在指定seconds后,内核会给当前进程发送 ( 14)SIGALRM信号)。进程收到该信号,默认动作终止。

每个进程都有且只有唯一个定时器。

原型:

       #include <unistd.h>
       unsigned int alarm(unsigned int seconds);
  1. 参数:几秒后发送信号。特别的,如果传入参数秒为0,代表取消闹钟。
  2. 返回值:返回0或剩余的秒数,无返回值表示失败。
    常用:取消定时器 alarm(0) ,返回旧闹钟余下秒数。
#include<stdio.h>
#include<unistd.h>

int main()
{
    int ret;
    ret = alarm(6);  //设置6s定时器
    printf("ret = %d\n",ret);
    sleep(2);        //sleep 2s后,消耗2s时间
    ret = alarm(5);  //再重新设定5s定时器
    printf("ret = %d\n",ret);
    while (1)
    {
        printf("I will be kill by alarm!\n");
        sleep(1);
    }
    
    return 0;
}

输出:
~/Linux_C$ ./alarm_
ret = 0
ret = 4
I will be kill by alarm!
I will be kill by alarm!
I will be kill by alarm!
I will be kill by alarm!
I will be kill by alarm!
Alarm clock
**

##NOTE:重新设定定时器,返回上一个定时器剩余时间,继续执行新定时器的设定。

**

  • 命令产生 kill
  • 硬件异常、段错误、浮点型错误、总线错误、SIGPIPE
    信号的状态:
  • 产生
  • 递达,信号达到并且处理完
  • 未决,信号被阻塞了。产生和递达之间的状态,主要由于阻塞(屏蔽)导致该状态。
    信号的默认处理方式:
  • 忽略(丢弃)
  • 执行默认动作
  • 捕获。9、19号信号不能捕获,不能忽略,甚至不能阻塞。
    信号的4要素: (可通过 man 7 signal 查看帮助文档获取,也可以查看/usr/src/linux-headers-4.15.0-142/arch/s390/include/uapi/asm/signal.h )
  • 编号
  • 事件
  • 名称
  • 默认处理动作
    忽略、终止、终止+core、暂停、继续

2、信号相关函数/参考文档使用信号集操作相关函数

(1) setitimer函数

作用: 周期性地发送信号。

       #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);

参数:

  • which
    (1)ITIMER_REAL 自然定时法,SIGALRM
    (2)ITIMER_VIRTUAL 计算进程执行时间,SIGVTALRM
    (3)ITIMER_PROF 进程执行时间+调度时间,ITIMER_VIRTUAL
  • 第2、 3个参数为结构体,new_value要设置的闹钟时间,old_value原闹钟时间(可以用于恢复)
           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 */
           };
  • 返回值:成功0,失败-1。On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

3、信号捕捉函数signal、sigaction

(1)信号捕捉基本概念

捕捉:调用户处理函数。
Linux内核的进程控制块PCB是一个结构体,task_struct,除了包含进程id、状态、工作目录、用户id、组id、文件描述符表,还包含了信号相关的信息,主要指阻塞信号集和未决信号集。
在这里插入图片描述阻塞信号集(信号屏蔽字): 将某些信号加入集合,对他们设置屏蔽,当屏蔽x信号后,再收到该信号,该信号的处理将推后(解除屏蔽后再处理)。

未决信号集:

  • 信号产生,未决信号集中描述该信号的位,立刻翻转为1,表示信号处于未决状态。当信号被处理对应位翻转回为0。这一刻往往非常短暂。
  • 信号产生后由于某些原因(主要是阻塞)不能抵达,这类信号的集合称之为未决信号集。在屏蔽解除前,信号一直处于未决状态。
    在这里插入图片描述信号捕捉特性:
    1、进程正常运行时,默认PCB中有一个信号屏蔽字,假定为 * ,他决定了进程自动屏蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而该函数有可能执行很长时间,在这期间所屏蔽的信号不由 * 来指定,而是用sa_mask来指定。调用完信号处理函数,再恢复为 * 。
    2、xxx 信号捕捉函数执行期间,xxx信号自动被屏蔽。
    3、阻塞的常规信号不支持排队,产生多次只记录一次。(后32个实时信号支持排队)

(2)信号捕捉函数

       #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);

sigemptyset 函数

清空信号集

sigfillset 函数

填充信号集

sigaddset 函数

添加某个信号到信号集

sigdelset 函数

从信号集中删除某个信号

sigismember 函数

判断信号是否为集合里的成员,sigismember () 函数返回1代表signum在集合中。

sigprocmask() 函数

       #include <signal.h>

       int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

作用:设置阻塞或解除阻塞信号集,用来屏蔽信号、解除屏蔽也使用该函数,其本质为读取或修改进程的信号屏蔽字(PCB中)。

严格注意,屏蔽信号,只是将信号处理延后执行(延至解除屏蔽);而忽略表示将信号丢弃处理。

参数:
set 传入参数,是一个位图,set中哪位置为 1,就表示当前进程屏蔽哪个信号。
oldset 传出参数,保存旧的信号屏蔽集。
how 参数取值,假设当前的信号屏蔽字为mask

  1. SIG_BLOCK: 设置阻塞,当how设置为此值,set 表示需要屏蔽的信号,相当于mask = mask|set。
  2. SIG_UNBLOCK:解除阻塞,当how设置为此值,set 表示需要解除屏蔽的信号,相当于 mask = mask & set。
  3. SIG_SETMASK:设置set为新的阻塞信号集,当how设置为此值,set 表示用于代替原始屏蔽集的新屏蔽集。相当于mask = set,调用sigprocmask解除了对当前若干个信号的阻塞。

返回值:成功 0,失败 -1,设置errno。

sigpending() 函数

       #include <signal.h>

       int sigpending(sigset_t *set);

作用:读取当前进程的未决信号集。参数:set 传出参数。返回值:成功 0,失败 -1,设置errno。
练习:编写程序,把所有常规信号的未决状态打印至屏幕。

//获取常规信号的未决信号集
#include<stdio.h>
#include<unistd.h>
#include<signal.h>

int main()
{
    sigset_t pend;
    sigpending(&pend);

    int i;
    for(i=1; i<32; i++)
    {
        if(sigismember(&pend,i) == 1)
        {
            printf("1");
        }
        else
        {
            printf("0");
        }
    }
    printf("\n");
    return 0;
}
//获取常规信号的未决信号集
#include<stdio.h>
#include<unistd.h>
#include<signal.h>

int main()
{
    sigset_t pend,sigproc;
    //设置阻塞信号,等待按键产生信号
    sigemptyset(&sigproc); //先清空
    sigaddset(&sigproc,SIGINT);
    sigaddset(&sigproc,SIGQUIT);
    sigaddset(&sigproc,SIGKILL); //9号信号不可被阻塞
    //设置阻塞信号集
    sigprocmask(SIG_BLOCK,&sigproc,NULL);
    //循环取未决信号集,打印
    while (1)
    {
        sigpending(&pend);

        int i;
        for(i=1; i<32; i++)
        {
            if(sigismember(&pend,i) == 1)
            {
                printf("1");
            }
            else
            {
                printf("0");
            }
        }
        printf("\n");
    }

    return 0;
}

运行程序,会一直打印未决信号集,ctrl+c 会发现第二位信号变为1。
0100000000000000000000000000000


signal() --信号捕捉函数

       #include <signal.h>

       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);

作用:注册一个信号捕捉函数。
该函数由ANSI定义,由于历史原因在不同版本的UNIX和不同版本的linux中可能有不同的行为,因此应尽量避免使用,使用sigaction() 代替。

sigaction() --信号捕捉函数

       #include <signal.h>

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

参数:

  • signum 捕捉的信号
  • act 传入的动作,是一个结构体类型指针
           struct sigaction {
               void     (*sa_handler)(int);  //函数指针
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;  //执行捕捉函数期间,临时屏蔽的信号集
               int        sa_flags;  //一般填0,SA_SIGINFO 会使用第二个函数指针
               void     (*sa_restorer)(void);  //无效
           };
  • oldact 原动作,恢复现场。

5、使用信号完成子进程的回收

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值