03.信号

1.信号的概念

信号和信号量是不一样的

信号量指的是系统中资源的个数

信号是我们linux操作系统中已经定义好的事件

信号并不传递实质性的有效数据,传输的只是一个标志

信号是多种多样的,并且一个信号对应一个事件,这样才能做到收到一个信号后,知道到底是一个什么事件,应该如何处理(但是要保证必须识别这个信号)

信号是一种软件层次的事件处理机制,模拟的是中断的处理机制

中断--硬件处理机制

特性:

1)信号是在软件层(中断是在硬件层次上的)次上对中断机制的一种模拟,是一种异步通信方式。

2)信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。

3)如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被 取消时才被传递给进程。(未决信号集和阻塞信号集)

linux内核中的信号:kill -l

不存在编号为 0 的信号。 其中 1-31 号信号称之为常规信号(也叫普通信号或标准信号) , 34-64 称之为实时信号, 驱动编程与硬件相关。 名字上区别不大。 而前32 个名字各不相同。

使用信号的时候,可以使用编号,也可以用信号的字符表示

常用的信号:

SIGINT: ctrl+c 回收资源,终止进程

SIGKILL:9号信号 立刻杀死一个进程,不回去回收资源

SIGALRM:定时器功能,设置定时时间

2.信号的产生和处理

中断是如何产生的(触发)----触发事件

中断如何处理的---中断服务函数---自定义

信号的产生:

1、当用户按某些终端键时,将产生信号。

例如:

终端上按“Ctrl+c”组合键通常产生中断信号 SIGINT、终端上按"Ctrl+"键通常产

生中断信号 SIGQUIT、终端上按"Ctrl+z"键通常产生中断信号 SIGSTOP。

2、硬件异常将产生信号。

此刻说的异常,指的是硬件发生故障

除数为 0,无效的内存访问等。这些情况通常由硬件检测到,并通知内核,然后内

核产生适当的信号发送给相应的进程。

3、软件异常将产生信号。--重要

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

4、调用 kill函数将发送信号。

注意:接收信号进程和发送信号进程的所有者必须相同,或发送信号进程的

所有者必须是超级用户。

5、运行 kill命令将发送信号。

此程序实际上是使用 kill函数来发送信号。也常用此命令终止一个失控的后

台进程。

信号的处理:

1、执行系统默认动作

对大多数信号来说,系统默认动作是用来终止该进程。

2、忽略此信号

接收到此信号后没有任何动作。

3、执行自定义信号处理函数

用用户定义的信号处理函数处理该信号。

使用指令关闭进程:

3.未决信号集和信号阻塞集

未处理的信号在未决信号集中由 “1”表示 0代表已处理

阻塞状态机:1--信号进入阻塞态 0---已经处理过了。

4.相关API函数:

kill 函数

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
功能: 给指定进程发送指定信号(不一定杀死)
参数:pid : 取值有 4 种情况 :
pid > 0: 将信号传送给进程 ID 为 pid 的进程。
pid = 0 : 将信号传送给当前进程所在进程组中的所有进程。
pid = -1 : 将信号传送给系统内所有的进程。
pid < -1 : 将信号传给指定进程组的所有进程。 
           这个进程组号等于 pid 的绝对值。
sig : 信号的编号, 这里可以填数字编号, 也可以填信号的宏定义, 可以通过命
令 kill - l("l" 为字母)进行相应查看。 不推荐直接使用数字, 应使用宏名, 
因为不同操作系统信号编号可能不同, 但名称一致。
返回值:
成功: 0
失败: -1
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>



int main(int argv,char *argc[])
{
//新建进程

    pid_t pd;

    pd=fork();
    if(pd<0)
    {
        perror("fork:");
        exit(1);
    }
  else  if(pd==0)//子进程
    {
        printf("子进程ID号:%d",getpid());
        while(1)
        {
            printf("子进程正在运行\n");
            sleep(1);
        }
    }
    else//父进程
    {
        printf("父进程正在运行,5秒之后关闭子进程\n");
         sleep(5);
        kill(pd,SIGINT);
    }
    return 0;
}

raise函数:

#include <signal.h>
int raise(int sig);
功能: 给当前进程发送指定信号(自己给自己发), 等价于 kill(getpid(), sig)
参数:sig: 信号编号
返回值:
成功: 0
失败: 非 0 值
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>





int main(int argv,char *argc[])
{
//新建进程

    pid_t pd;

    pd=fork();
    if(pd<0)
    {
        perror("fork:");
        exit(1);
    }
  else  if(pd==0)//子进程
    {
        int count=0;
        printf("子进程ID号:%d",getpid());
        while(1)
        {
            printf("子进程正在运行\n");
            sleep(1);
            count++;
            if(count>=5) raise(SIGINT);
        }
    }
    else//父进程
    {
        printf("父进程正在运行,5秒之后关闭子进程\n");
         sleep(5);
       // kill(pd,SIGINT);
       
    }
    return 0;
}

abort 函数

#include <stdlib.h>
void abort(void);
功能: 给自己发送异常终止信号 6) SIGABRT, 并产生 core 文件(日志信息), 
等价于 kill(getpid(), SIGABRT);
参数: 无
返回值: 无
abort是人为的让程序异常终止,因为有些时候,我们的设备在执行的时候,当不符合
我们的要求的时候,我们需要去查看当前设备的状态(硬件和软件),就需要人为终止
代码的运行。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>





int main(int argv,char *argc[])
{
//新建进程

    pid_t pd;

    pd=fork();
    if(pd<0)
    {
        perror("fork:");
        exit(1);
    }
  else  if(pd==0)//子进程
    {
        int count=0;
        printf("子进程ID号:%d",getpid());
        while(1)
        {
            printf("子进程正在运行\n");
            sleep(1);
            // count++;
            // if(count>=5) abort();
        }
    }
    else//父进程
    {
        printf("父进程正在运行,5秒之后关闭子进程\n");
         sleep(5);
       // kill(pd,SIGINT);
       abort();
    }
    return 0;
}

alarm 函数(闹钟)

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能:设置定时器(闹钟)。 在指定 seconds 后, 内核会给当前进程发送 14)
 SIGALRM 信号。 进程收到该信号, 默认动作终止。 每个进程都有且只有唯一的
 一个定时器。取消定时器 alarm(0), 返回旧闹钟余下秒数。
 参数:seconds: 指定的时间, 以秒为单位
 返回值:返回 0 或剩余的秒数
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>

int main(int argv,char *argc[])
{
//新建进程
int val=0;
val=alarm(6);//初始化
printf("1-->当前剩余时间%d s\n",val);
sleep(3);
val=alarm(6);
printf("2-->当前剩余时间%d s\n",val);
sleep(20);
    return 0;
}

setitimer 函数

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

下面是对各个参数的解释:
1.which:表示要设置的定时器类型,它可以是以下三个值之一:
- ITIMER_REAL:用于真实时间。当定时器时间到达时,会触发 SIGALRM 信号。
- ITIMER_VIRTUAL:用于进程在用户态运行的时间。当定时器时间到达时,会触发 SIGVTALRM 信号。
- ITIMER_PROF:用于进程在用户态和内核态运行的时间。当定时器时间到达时,会触发 SIGPROF 信号。
2.new_value:指向 struct itimerval 结构的指针,用于设置新的定时器值。struct itimerval 结构包括以下成员:
- it_interval:用于指定定时器的间隔时间,在定时器时间到达后的重复间隔时间。
- it_value:用于指定初始化定时器的延迟时间,在定时器第一次触发前的延迟时间。
3.old_value:指向 struct itimerval 结构的指针,用于获取之前的定时器值(可选)。如果不需要获取旧的定时器值,可以传入 NULL。
struct itimerval 结构的定义如下:
struct itimerval {
    struct timeval it_interval;  // 定时器的间隔时间
    struct timeval it_value;     // 初始化定时器的延迟时间
};

struct timeval {
    time_t      tv_sec;  // 秒
    suseconds_t tv_usec; // 微秒
};

struct itimerval 结构用于存储定时器的时间值,包括间隔时间和初始化延迟时间。it_interval 成员用于指定定时器的间隔时间,即定时器时间到达后的重复间隔时间;it_value 成员用于指定初始化定时器的延迟时间,即定时器第一次触发的延迟时间。

在调用 setitimer() 函数时,将 which 参数设置为所需的定时器类型,将 new_value 参数传递给一个指向 struct itimerval 结构的指针,该结构包含所需的定时器值,从而设置间隔定时器的时间参数。如果需要获取之前的定时器值,可以将 old_value 参数传递给一个指向 struct itimerval 结构的指针,用于获取旧的定时器值;如果不需要获取旧的定时器值,可以将 old_value 参数设置为 NULL。

sigemptyset函数(信号集)

 是一个 POSIX 标准的函数,用于初始化一个空的信号集。它的原型如下:

int sigemptyset(sigset_t *set);

下面是对参数的解释:

set:指向 sigset_t 类型的指针,用于指定要初始化为空的信号集。

sigset_t 是一个用于表示信号集的数据类型,用于存储一组信号。对信号集进行操作时,需要先将其初始化为空集,然后通过其他函数添加或移除特定的信号。

sigemptyset 函数的作用是将指定的信号集 set 初始化为空集,即清空其中的所有信号。

sigfillset函数(信号集)

作用是将所有信号都添加到指定的阻塞信号集中

int sigfillset(sigset_t *set);

  • set 一个指向 sigset_t 类型的指针,表示要进行操作的目标信号集。

sigfillset 函数用于将指定的信号集中的所有信号都设置为被阻塞的状态。这意味着在使用该信号集进行信号屏蔽操作时,所有信号都会被阻塞,不会传递给进程的信号处理函数。

sigaction函数

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

struct sigaction {
    void (*sa_handler)(int);           // 处理信号的函数指针
    void (*sa_sigaction)(int, siginfo_t *, void *);  // 扩展的信号处理函数(可选)
    sigset_t sa_mask;                  // 在信号处理函数执行期间要被阻塞的信号集
    int sa_flags;                      // 用于指定信号处理的一些标志
    void (*sa_restorer)(void);          // 已经废弃,无需使用
};

下面是对各个参数的解释:

  1. signum:表示要设置或获取的信号编号。
  2. act:指向 struct sigaction 结构的指针,用于设置新的信号处理函数以及信号的行为标志。struct sigaction 结构包括以下成员:
  3. void (*sa_handler)(int):表示用于处理信号的函数指针,也可以设置为 SIG_IGN(忽略)或 SIG_DFL(默认)。
  4. void (*sa_sigaction)(int, siginfo_t *, void *):用于处理信号的另一种处理函数,与 sa_handler 二选一。该函数比较复杂,可以接收更多的信号和附加信息。
  5. sigset_t sa_mask:用于设置在信号处理函数执行期间需要阻塞的其他信号集。
  6. int sa_flags:用于设置信号的行为标志。
  7. oldact:指向 struct sigaction 结构的指针,用于获取之前的信号处理函数和信号的行为标志(可选)。如果不需要获取之前的值,可以传入 NULL。

struct sigaction 结构用于定义信号处理函数及信号的行为。在调用 sigaction 函数时,通过 signum 参数指定要设置或获取的信号编号,通过 act 参数传递一个指向 struct sigaction 结构的指针,该结构包含了新的信号处理函数以及信号的行为标志。如果需要获取之前的信号处理函数和行为标志,可以将 oldact 参数传递给一个指向 struct sigaction 结构的指针来接收。

函数返回值为 0 表示成功,-1 表示失败。

使用信号集设置需要执行的工作,再延时N秒之后运行此信号

sigemptyset--清空信号集中的信号

sigaction--设置需要执行的动作

setitimer--设置延时,延时N秒之后运行

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

struct itimerval tim;
struct sigaction my_act;
//执行动作
void my_Function(int val)//类似中断服务函数
{
    printf("已经进入动作执行函数中\n");


}
int main(int argv,char *argc[])
{
/*
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

int sigemptyset(sigset_t *set);

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
*/
//清空信号集

//设置执行动作
my_act.sa_handler=my_Function;
// my_act.sa_mask=NULL;//设置阻塞的信号
my_act.sa_flags=NULL;
sigemptyset(&my_act.sa_mask);
//当闹钟信号触发之后,执行my_act
sigaction(SIGALRM,&my_act,NULL);
//间隔时间
tim.it_interval.tv_sec=1;
tim.it_interval.tv_usec=0;

//初始化的延时时间
tim.it_value.tv_sec=5;
tim.it_value.tv_usec=0;
setitimer(ITIMER_REAL,&tim,NULL);

sleep(20);//时间一定要比定时器延时时间长
    return 0;
}

5.给信号 注册 自定义函数

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

功能:

注册信号处理函数(不可用于 SIGKILL、SIGSTOP 信号),即确定收到信号后处理函数的入口地

址。此函数不会阻塞。

参数:

signum:信号的编号,这里可以填数字编号,也可以填信号的宏定义,可以通过命令 kill - l("l"

为字母)进行相应查看。

handler : 取值有 3 种情况:

SIG_IGN:忽略该信号

SIG_DFL:执行系统默认动作

信号处理函数名:自定义信号处理函数,如:func

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

void My_func(int val)
{
    printf("My_func is running.....\n");
}

int main(int argv,char *argc[])
{
    int count=0;
//新建进程
while(1)
{
    signal(SIGINT,My_func);
    sleep(1);
    printf("count-->%d\n",count++);
}
  



    return 0;
}

信号集

内核中的位置:

/usr/include/i386-linux-gnu/bits/sigset.h

作用:

#include <signal.h>
 int sigemptyset(sigset_t *set); //将 set 集合置空--初始化
 int sigfillset(sigset_t *set); //将所有信号加入 set 集合
 int sigaddset(sigset_t *set, int signo); //将 signo 信号加入到 set 集合
 int sigdelset(sigset_t *set, int signo); //从 set 集合中移除 signo 信号
 int sigismember(const sigset_t *set, int signo); //判断信号是否存在于集合中
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>



int main(int argv,char *argc[])
{
    sigset_t sig_gather;
    int ret;
    //创建一个空的信号集合
    //sigset_t *set
    sigemptyset(&sig_gather);
    //添加一个信号到信号集合
   // int sigaddset(sigset_t *set, int signum);
   ret=sigaddset(&sig_gather,SIGINT);
   if(ret==0)
   {
        printf("SIGINT 已经存在集合中\n");
   }
    sleep(1);

    //int sigdelset(sigset_t *set, int signum);
    sigdelset(&sig_gather,SIGINT);
    ret=sigismember(&sig_gather,SIGINT);
    if(ret==0)
    {
        printf("当前信号SIGINT已经删除\n");
    }

    return 0;
}

信号阻塞集

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

功能:

检查或修改信号阻塞集,根据 how 指定的方法对进程的阻塞集合进行修改,新的信 号阻塞集由set 指定,而原先的信号阻塞集合由 oldset 保存。

参数:

how : 信号阻塞集合的修改方法,有 3 种情况:

  • SIG_BLOCK:向信号阻塞集合中添加 set 信号集,新的信号掩码是 set 和旧信号掩码的并集。 相当于

mask = mask | set

  • SIG_UNBLOCK:从信号阻塞集合中删除 set 信号集,从当前信号掩码中去除 set 中的信号。相当于

mask = mask & ~ set

  • SIG_SETMASK:将信号阻塞集合设为 set 信号集,相当于原来信号阻塞集的内 容清空,然后按照 set 中的信号重新设置信号阻塞集。相当于 mask =set。

set : 要操作的信号集地址。

  • 若 set 为 NULL,则不改变信号阻塞集合,函数只把当前信号阻塞集合保存到 oldset 中。

oldset : 保存原先信号阻塞集地址

返回值:

  • 成功:0,
  • 失败:-1,失败时错误代码只可能是 EINVAL,表示参how不合法

思考:

如何往阻塞信号集中添加信号,以及如何从信号阻塞集中删除信号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值