linux-信号-信号产生时机-更改信号默认行为signal-sigaction-sa_mask-raise-alarm信号-时钟getitimer-setitimer

信号:

软件层面的异步事件机制;

异步:事件发生的时间顺序不固定;

中断:硬件层面的异步事件机制;


不同的信号用不同的整型标识;

kill -l:查看信号整型对应信号;

信号的默认行为:

Segmentation fault (core dumped) 是段错误(Segmentation Fault)的一个常见表现形式。段错误是程序在尝试访问其内存空间中未分配(或没有权限访问)的内存区域时发生的错误。这种错误通常发生在以下几种情况:

  1. 解引用空指针:尝试访问一个值为 NULL(或未初始化的指针,其值可能是任何随机地址)的指针所指向的内存位置。

  2. 数组越界:访问数组时超出了其分配的内存范围。

  3. 栈溢出:由于递归调用过深或局部变量使用过多导致的栈空间耗尽。

  4. 非法内存访问:如尝试修改只读内存区域(如字符串常量)或访问其他进程的内存。

当操作系统检测到程序试图进行上述非法内存访问时,它会终止该程序,并可能生成一个核心转储文件(core dump),该文件包含了程序终止时的内存、寄存器状态等信息,可用于调试程序以确定段错误的原因。

解决段错误通常需要:

  • 检查指针是否在使用前被正确初始化
  • 验证数组索引是否在有效范围内
  • 使用调试工具(如 gdb)来运行程序,并在段错误发生时检查程序的状态(如变量的值、堆栈跟踪等)。
  • 确保不会修改不应修改的内存区域

 core文件生成:收到信号;

 


信号产生的时机:

软件+异步: kill -9 pid

软件+同步:调用abort();;自己给自己发信号6,异常终止;

硬件+异步:ctrl +c  , ctrl \

硬件+同步:除0;除法计算器;

产生信号(来源)-------时间(可变)---------递送信号(目标进程)

当信号产生时:

信号产生会修改目标进程(认为所有的信号都来自内核)的task_struct;

mask:掩码;

pending:未决;

响应时机:可递送信号的时机;几乎所有状态(除了d状态,处于不可中断的睡眠状态);

更改默认的信号行为

不再执行默认操作,而是调用函数;

signal:注册信号处理行为(等到信号到来时才调用函数)

9号信号不能注册

是一种等到信号递送的被动调用行为;

typedef 给函数指针起了叫sighandler_t的别名;

注册2号信号:sigFunc是回调函数

printf(“”):不加换行,数据被留在缓冲区里;可能会丢失;

#include <43func.h>
void sigFun(int num){
    printf("num = %d\n",num);
}
int main(){
    void (*ret)(int);//返回值是一个以int为参数的函数指针
    ret = signal(SIGINT,sigFun);//(2(信号),函数(void(*fun)(int))
    ERROR_CHECK(ret,SIG_ERR,"signal");
    while (1)
    {
        /* code */
    }
    

}

进程上下文和中断上下文;

回调函数:自己写的函数交给其他进程来调用;

注册多个信号:

阻塞和未决实现的原理:

阻塞:

让产生的信号,不能马上递送,处于未决状态;

mask:阻塞信号集(位图);

未决:

已经产生但未递送的信号;

pending:未决信号集(位图);0/1

#include <43func.h>
void sigFun(int num){
    printf("before sleep\n");
    printf("num = %d\n",num);
    sleep(3);
    printf("after sleep\n");
}
int main(){
    void (*ret)(int);//返回值是一个以int为参数的函数指针
    ret = signal(SIGINT,sigFun);//(2(信号),函数(void(*fun)(int))
    ERROR_CHECK(ret,SIG_ERR,"signal");
    while (1)
    {
        /* code */
    }
    

}

在一个信号递送的过程中,有多个信号产生,只递送一个;

当进程递送信号A,把信号A本身加入mask,之后有A信号产生;将该信号加入pending集合;

信号递送完成后,A移除mask,递送pending的信号;

pending是位图结构,只能存储0或1(有或无);

当三个信号(非实时信号)要进入pending,第一个信号会被pending存储,pending标识1,当第二第三信号要进入pending,由于pending是位图,只能标识1个信号,所以后面的信号丢失;


信号从产生到递送之前有一段时间处于未决(pending)状态;未决

目标进程的能力可以不马上递送信号 让它处于未决状态;阻塞


不同信号的阻塞(mask):

2号递送时,不把3加入mask;

当2号信号递送一直到递送完成,2号信号一直在mask里面;

低速系统调用:

可能陷入永久等待的系统调用;

#include <43func.h>
void sigFun(int num){
    printf("before sleep\n");
    printf("num = %d\n",num);
    sleep(3);
   printf("after sleep\n");
}
int main(){
    void (*ret)(int);//返回值是一个以int为参数的函数指针
    ret = signal(SIGINT,sigFun);//(2(信号),函数(void(*fun)(int))
    ERROR_CHECK(ret,SIG_ERR,"signal");
    //ret = signal(SIGQUIT,sigFun);
   char buf[512] = {0};
   read(STDIN_FILENO,buf,sizeof(buf));
   printf("buf = %s\n",buf);
    

}

在注册信号后,如果信号递送完成之后,会自动重启低速系统调用;案例重启read;

当信号递送时进入回调函数,如果在函数运行过程中,也就是递送没有结束时,标准输入流的数据会被留在输入缓冲区,等到递送完成后重启的系统调用使用该数据;


signal的特点:

(1)一次注册,永久生效;

让注册只生效一次:在sigFun里调用signal函数:SIG_DFL(注册默认行为);

(2)递送A时,会将A加入mask,其他信号不会加入mask;

(3)会自动重启低速系统调用;


sigaction:更灵活的注册,精细控制信号

sigaction是一个用于改变进程接收特定信号后行为的函数,它定义在<signal.h>头文件中。以下是对sigaction函数的详细解析,包括其参数、结构体以及使用示例。

一、sigaction函数原型

 
 

c复制代码

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

二、参数解析

  1. signo
    • 类型:int
    • 描述:指定要处理的信号的编号。该参数可以是除SIGKILL和SIGSTOP外的任何有效信号。SIGKILL和SIGSTOP不能被捕获、阻塞或忽略,因此不能为它们设置信号处理函数。
  2. act
    • 类型:const struct sigaction *
    • 描述:指向sigaction结构体的指针,该结构体定义了与signo相关联的新信号处理动作。如果此参数非空,则根据act修改信号的处理动作。
  3. oact
    • 类型:struct sigaction *
    • 描述:指向sigaction结构体的指针,用于保存signo原来的处理动作。如果此参数非空,则通过oact传出原来的处理动作。如果不需要保存原处理动作,可以将其设置为NULL。

三、sigaction结构体

sigaction结构体用于指定信号的处理方式,其定义如下:

struct sigaction {
void (*sa_handler)(int); // 信号处理函数指针,与signal函数中的handler相同
void (*sa_sigaction)(int, siginfo_t *, void *); // 另一个信号处理函数指针,用于接收更多信号信息
sigset_t sa_mask; // 信号屏蔽字,在调用处理函数时,将sa_mask中的信号添加到进程的信号屏蔽字中
int sa_flags; // 信号处理标志,用于控制信号处理过程的各种选项
void (*sa_restorer)(void); // 废弃不用
};

四、sa_flags标志位

  • SA_SIGINFO:如果设置了此标志,则使用sa_sigaction成员作为信号处理函数,该函数可以接收更多关于信号的信息。
  • SA_NODEFER:默认情况下,信号处理函数执行时,会自动阻塞当前信号。如果设置了此标志,则在信号处理函数执行期间不会阻塞当前信号。
  • SA_RESTART:默认情况下,信号处理会中断系统调用并返回EINTR错误。如果设置了此标志,则系统调用在信号处理完成后会自动重启。
  • SA_RESETHAND:当信号处理函数返回时,自动将信号处理函数重置为SIG_DFL(默认处理)。
  • SA_ONSTACK:如果设置了此标志,并且已经通过sigaltstack()设置了备用信号栈,则信号处理函数会在备用栈上执行。

可以完全取代signal;

sigaction:

SA_SIGINFO:有这个属性,就选sigaction的第二个参数;

默认:

使用1参数的回调函数,

(不自动重启低速系统调用),

递送过程中会把自己加入mask;


使用无参数的sigaction:

#include <43func.h>
void sigFunc(int num){
    printf("num = %d\n",num);
}

int main(){
    struct sigaction act;
    memset(&act,0,sizeof(act));
    act.sa_handler = sigFunc;
    int ret = sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    while (1)
    {
        /* code */
    }
    
    
}

sigaction默认不会重启系统调用直接退出:

#include <43func.h>
void sigFunc(int num){
    printf("num = %d\n",num);
}

int main(){
    struct sigaction act;
    memset(&act,0,sizeof(act));
    act.sa_handler = sigFunc;
    int ret = sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    char buf[100] = {0};
    read(STDIN_FILENO,buf,sizeof(buf));
    printf("%s",buf);  
    
}

sigaction默认和signal一样:会把自己的信号加入mask

#include <43func.h>
void sigFunc(int num){
    printf("before,num = %d\n",num);
    printf("num = %d\n",num);
    sleep(3);
     printf("after,num = %d\n",num);
}

int main(){
    struct sigaction act;
    memset(&act,0,sizeof(act));
    act.sa_handler = sigFunc;
    int ret = sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    //char buf[100] = {0};
    // read(STDIN_FILENO,buf,sizeof(buf));
    // printf("%s",buf);  
    while (1)
    {
        /* code */
    }
    
    
}

让sigaction重启系统调用:

#include <43func.h>
void sigFunc(int num){
    printf("before,num = %d\n",num);
    printf("num = %d\n",num);
    sleep(3);
     printf("after,num = %d\n",num);
}
int main(){
    struct sigaction act;//
    memset(&act,0,sizeof(act));//
    act.sa_handler = sigFunc;//
    act.sa_flags = SA_RESTART;//设置sigaction重启系统调用
    //(2,struct sigaction *act,struct sigaction *oldact,)
    int ret = sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    char buf[100] = {0};
    read(STDIN_FILENO,buf,sizeof(buf));
    printf("%s",buf);  
    // while (1)
    // {
    //     /* code */
    // }
    
    
}


三参数版本回调:

#include <43func.h>
void sigFunc(int num){
    printf("before,num = %d\n",num);
    printf("num = %d\n",num);
    sleep(3);
     printf("after,num = %d\n",num);
}
void sigFun3(int num,siginfo_t *siginfo,void *p){
    printf("num =%d\n",num);
    printf("sender pid = %d\n",siginfo->si_pid);
}
int main(){
    struct sigaction act;//
    memset(&act,0,sizeof(act));//
    act.sa_sigaction = sigFun3;//
    act.sa_flags = SA_RESTART|SA_SIGINFO;//设置sigaction重启系统调用
    //(2,struct sigaction *act,struct sigaction *oldact,)
    int ret = sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    char buf[100] = {0};
    read(STDIN_FILENO,buf,sizeof(buf));
    printf("%s",buf);  
    // while (1)
    // {
    //     /* code */
    // }
    
    
}

已进入函数就把递送行为改为默认递送行为;

SA_RESETHAND:只注册一次

#include <43func.h>
void sigFunc(int num){
    printf("before,num = %d\n",num);
    printf("num = %d\n",num);
    sleep(3);
     printf("after,num = %d\n",num);
}
void sigFun3(int num,siginfo_t *siginfo,void *p){
    printf("num =%d\n",num);
    printf("sender pid = %d\n",siginfo->si_pid);
}
int main(){
    struct sigaction act;//
    memset(&act,0,sizeof(act));//
    act.sa_sigaction = sigFun3;//
    act.sa_flags = SA_RESTART|SA_SIGINFO|SA_RESETHAND;//设置sigaction重启系统调用
    //(2,struct sigaction *act,struct sigaction *oldact,)
    int ret = sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    char buf[100] = {0};
    read(STDIN_FILENO,buf,sizeof(buf));
    printf("%s",buf);  
    // while (1)
    // {
    //     /* code */
    // }
    
    
}

 


SA_NODEFER:在递送A时不屏蔽A;

#include <43func.h>
void sigFunc(int num)
{
    printf("before,num = %d\n", num);
    printf("num = %d\n", num);
    sleep(3);
    printf("after,num = %d\n", num);
}
void sigFun3(int num, siginfo_t *siginfo, void *p)
{
    printf("before,num = %d\n", num);
   // printf("num =%d\n", num);
    //printf("sender pid = %d\n", siginfo->si_pid);
    sleep(3);
    printf("after,num = %d\n", num);
}
int main()
{
    struct sigaction act;                                //
    memset(&act, 0, sizeof(act));                        //
    act.sa_sigaction = sigFun3;                          //
    act.sa_flags = SA_RESTART | SA_SIGINFO | SA_NODEFER; // 设置sigaction重启系统调用
    //(2,struct sigaction *act,struct sigaction *oldact,)
    int ret = sigaction(SIGINT, &act, NULL);
    ERROR_CHECK(ret, -1, "sigaction");
    char buf[100] = {0};
    read(STDIN_FILENO, buf, sizeof(buf));
    printf("%s", buf);
    // while (1)
    // {
    //     /* code */
    // }
}


sa_mask:指定递送过程中的额外屏蔽信号;

本质是位图

操作位图的函数:

mask:阻塞集合;//平时没有信号,在递送过程中将自己加入(SA_NODEFER就不加入);

pending:未决集合;

sigaddset:

在SIGINT的递送过程中,把SIGINT加入阻塞;

sa_mask是一种临时的额外阻塞

#include <43func.h>
void sigFun(int num){
    printf("before\n");
    printf("num = %d\n",num);
    sleep(3);
    printf("after\n");
}

int main(){
    
    struct sigaction act;//创建结构体,用于sigaction的参数
    memset(&act,0,sizeof(act));
    act.sa_handler = sigFun;//无参数版本,
    act.sa_flags = SA_RESTART;
    sigaddset(&act.sa_mask,SIGQUIT);//act结构体中的阻塞集合
    int ret = sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    while (1)
    {
        /* code */
    }
    
}

等到2号信号完成才终止;


sigprocmask:实现全程阻塞:

#include <43func.h>
int main(){
    sigset_t set;//创建位图,mask
    sigemptyset(&set);//清空set
    sigaddset(&set,SIGQUIT);//把SIGQUIT加入set;
    sigset_t oldset;//存旧的mask集合
    //把现在的mask集合换成set,并把原mask存入oldset
    sigprocmask(SIG_SETMASK,&set,&oldset);
    printf("sleep\n");
    sleep(5);
    printf("wakeup\n");
    sigprocmask(SIG_SETMASK,&oldset,NULL);
    printf("bye\n");
    return 0;
}


sigpending:获取pending集合

#include <43func.h>
void sigFun(int num){
    printf("num = %d\n",num);
}
int main(){
    //signal(SIGQUIT,sigFun);
    sigset_t set;
    sigemptyset(&set);
    sigset_t oldset;
    sigaddset(&set,SIGQUIT);
    sigprocmask(SIG_SETMASK,&set,&oldset);
    printf(" sleep\n");
    sleep(5);
    printf("wakeup!\n");
    sigset_t pending;
    sigpending(&pending);//获取pending集合
    if(sigismember(&pending,SIGQUIT)){
        printf("SIGQUIT is pending!\n");
    }else{
        printf("SIGQUIT is not pending!\n");
    }
      signal(SIGQUIT,sigFun);
    sigprocmask(SIG_SETMASK,&oldset,NULL);
   // signal(SIGQUIT,sigFun);
    printf("bye");
    return 0;
}


pause:暂停,等待信号;


kill:发送信号给进程

#include <43func.h>
int main(int argc,char *argv[]){
    ARGS_CHECK(argc,3);
    int sig = atoi(argv[1] + 1);
    pid_t pid = atoi(argv[2]);
    printf("%d %d\n",sig,pid);
    int ret = kill(pid,sig);
    ERROR_CHECK(ret,-1,"kill");
}

raise:给自己发信号;(实现有序退出时使用)

1. sigemptyset

功能:初始化信号集,将所有信号从信号集中清除。

原型

#include <signal.h>
int sigemptyset(sigset_t *set);

参数

  • set:指向信号集的指针。

返回值:成功时返回0,失败时返回-1。

用途:在设置sa_mask之前,通常使用sigemptyset来初始化信号集,确保它开始时是空的。

2. sigaddset

功能:向信号集中添加一个信号。

原型

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

参数

  • set:指向信号集的指针。
  • signum:要添加到信号集中的信号编号。

返回值:成功时返回0,失败时返回-1。

用途:将特定的信号添加到sa_mask中,以便在信号处理函数执行期间屏蔽这些信号。

3. sigdelset

功能:从信号集中删除一个信号。

原型

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

参数

  • set:指向信号集的指针。
  • signum:要从信号集中删除的信号编号。

返回值:成功时返回0,失败时返回-1。

用途:虽然sa_mask的主要用途是在信号处理函数执行期间屏蔽信号,但在某些情况下,可能需要从信号集中删除信号,尽管这在sa_mask的上下文中可能不太常见。

4. sigaction

功能:查询或设置与信号相关联的处理动作。

原型

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oact);

参数

  • signum:要操作的信号编号。
  • act:指向包含新信号处理的sigaction结构的指针。如果此参数非空,则使用它来设置新的信号处理。
  • oact:如果非空,则指向一个sigaction结构,该结构用于存储之前的信号处理设置。

返回值:成功时返回0,失败时返回-1。

用途:虽然sigaction本身不是直接操作sa_mask的函数,但它是设置和查询信号处理动作的主要接口,包括sa_mask的设置。通过修改sigaction结构中的sa_mask成员,可以指定在信号处理函数执行期间哪些信号应该被屏蔽。


 alarm:

默认行为:终止进程;所以要注册SIIGALRM;

#include <43func.h>

int main(){
    time_t now = time(NULL);
    printf("%s\n",ctime(&now));
    alarm(5);
    pause();
    now = time(NULL);
    printf("%s\n",ctime(&now));
}

#include <43func.h>

void sigFun(int num){
    time_t now = time(NULL);
    printf("%s\n",ctime(&now));
}
int main(){
  signal(SIGALRM,sigFun);
  sigFun(0);
  alarm(5);
  pause();
}

自己实现sleep():

sleep可能是用alarm实现的,所以不能混用;


时钟:

间隔定时器:

ITIMER_REAL:真实时间,SIGALARM(信号)

ITIMER_VIRTUAL:虚拟时间 占用用户态CPU的时间 SIGVTALARM

ITIMER_PROF:实用时间 占用用户态+内核态CPU的时间 SIGPROF;

用实用时间比较程序性能;

getitimer():获取当前定时器

setitimer():设置定时器:

#include <43func.h>
void sigFunc(int signum){
    time_t now = time(NULL);
    puts(ctime(&now));
}
int main(){
    struct itimerval itimer;
    itimer.it_value.tv_sec = 5;//计时器的当前值秒,5秒后第一次触发
    itimer.it_value.tv_usec = 0;//微秒
    itimer.it_interval.tv_sec = 2;//间隔时间重置值 秒每隔两秒触发一次
    itimer.it_interval.tv_usec = 0;//微秒
    //真实时间
    // signal(SIGALRM,sigFunc);//注册信号
    // setitimer(ITIMER_REAL,&itimer,NULL);//用真实时间参数,给出SIGALARM信号;
    //用户态
    // signal(SIGVTALRM,sigFunc);//注册SIGVTALRM信号(虚拟时间);
    // //用虚拟时间做参数返回SIGVTALRM信号;
    // setitimer(ITIMER_VIRTUAL,&itimer,NULL);
    //实用时间,内核态和用户态;
    signal(SIGPROF,sigFunc);
    setitimer(ITIMER_PROF,&itimer,NULL);
    sigFunc(0);
    while(1);
}

真实时间:

虚拟时间

实用时间


四窗口聊天:

代码实现:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值