文章目录

  • 信号阻塞和未决信号
    • 信号集相关API函数
    • 操作流程
      • 代码示例
    • 未决信号
      • 代码示例
  • pause的使用
    • 代码示例
  • 信号传送处理过程
  • 可重入函数
    • 代码示例
  • 信号处理函数的继承
    • 代码示例
  • setitimer实现定时器
    • 代码示例

信号阻塞和未决信号

进程可以设置对某个信号的阻塞(屏蔽),需要用到sigset_t(信号集)数据类型。

信号集相关API函数

sigemptyset(3)

#include <signal.h>
int sigemptyset(sigset_t *set);
功能:将信号集清空
参数:
set:指定要清空的信号集
返回值:
0  成功
-1 错误

int sigfillset(sigset_t *set);

功能:将信号集设置为满
参数:
set:指定要置满的信号集
返回值:
0  成功
-1 错误

int sigaddset(sigset_t *set, int signum);

功能:给信号集添加信号
参数:
set:指定信号集
signum:指定要添加的信号
返回值:
0  成功
-1 错误

int sigdelset(sigset_t *set, int signum);

功能:删除信号集中的某个信号
参数:
set:指定信号集
signum:指定要删除的信号
返回值:
0  成功
-1 错误

int sigismember(const sigset_t *set,int signum);

功能:测试信号是否是信号集中的一个成员
参数:
set:指定的信号集
signum:指定信号
返回值:
-1  错误
0   信号不是信号集中的一个成员
1   信号集中有这个信号

操作流程

如果希望设置进程对2号信号阻塞,就需要设置阻塞信号集

  1. sigset_t block_set;
  2. 将block_set集合中的所有成员清空;
  3. 将2好信号添加到block_set集合中;
  4. 将block_set设置为这个进程的阻塞信号集。sigprocmask函数使用。

sigprocmask(2)

#include <signal.h>
int sigprocmask(int how,  const  sigset_t *set, \sigset_t *oldset);
功能:检测或改变阻塞信号
参数:
how:
SIG_BLOCK   当前信号集和set指定信号集的并集
SIG_UNBLOCK  将set集合里的成员从当前进程阻塞信号集中移除
SIG_SETMASK  将set指定为当前进程的阻塞信号集set:指定的新的信号集
oldset:之前的信号集保存到oldset中。
返回值:
0  成功
-1   错误

代码示例

  • blocked.c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void doit(int n){printf("recv %d signal...\n",n);return;
}int main(void){sigset_t block;printf("pid:%d\n",getpid());//为2号信号添加处理函数signal(64,doit);//将block清空sigemptyset(&block);//将2号信号添加到block集合中sigaddset(&block,64);//将block设置为进程的阻塞信号集sigprocmask(SIG_SETMASK,&block,NULL);//while(1){sleep(10);//}//解除信号阻塞sigprocmask(SIG_UNBLOCK,&block,NULL);return 0;
}
  • 执行结果

未决信号

在信号则是期间,可以查看未决信号。
如何查看未决信号。使用sigpending(2)查看

#include <signal.h>
int sigpending(sigset_t *set);
功能:检测未决信号
参数:
set:值-结果参数,用于返回进程的未决信号掩码
返回值:
0  成功
-1   错误

代码示例

  • pending.c
#include <stdio.h>
#include <signal.h>int main(void){//定义信号集变量sigset_t b,p;//清空b信号集sigemptyset(&b);//将2号信号添加到b信号集中sigaddset(&b,2);sigaddset(&b,3);//设置进程的信号屏蔽掩码为bsigprocmask(SIG_SETMASK,&b,NULL);while(1){sleep(1);//清空信号集psigemptyset(&p);//获取未决信号掩码sigpending(&p);//检测信号集p中有哪些未决信号int f=sigismember(&p,2);if(f==-1){perror("sigismember");return 1;}if(f==0){printf("not 2\n");}else{printf("2号信号未决\n");}int f3=sigismember(&p,3);if(f3==-1){perror("sigismember");return 1;}if(f3==0){printf("not 3\n");}else{printf("3号信号未决\n");}}return 0;
}
  • 执行结果

pause的使用

int pause(void);

#include <unistd.h>
int pause(void);
功能:等待一个信号
参数:
void
返回值:
-1   错误  errno被设置为EINTR

代码示例

  • pause.c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>void doit(int n){printf("recv %d signal\n",n);return;
}
int main(void){signal(2,doit);//等待信号到来int f=pause();/*if(f==-1){perror("pause");return 1;}*/printf("pause after...%d\n",f);return 0;
}
  • 执行结果

  • 使用alarm和pause完成sleep的功能,mysleep.c

#include <stdio.h>
#include <signal.h>
#include <unistd.h>void doit(int n){return;
}
unsigned int mysleep(unsigned int seconds){signal(SIGALRM,doit);//设置闹钟int l=alarm(seconds);//暂停pause();return l;
}
int main(void){while(1){mysleep(4);printf("jjjjjjj\n");}return 0;
}
  • 执行结果

信号传送处理过程

  1. 用户输入命令,在bash下启动一个前台作业。
  2. 用户按下ctrl+c键,这个键盘输入产生一个硬件中断。
  3. 如果CPU正在执行这个进程的代码,则该进程的用户空间代码暂停执行。CPU从用户态切换到内核态处理硬件中断。
  4. 终端驱动程序将ctrl+c解释成一个SIGINT信号,记录在该进程的PCB中。
  5. 当某个时刻,进程从内核态切换会用户态的时候,首先处理PCB中记录的信号,如果PCB中有未处理信号,找到信号对应的信号处理程序进行处理。
  6. 信号处理函数处理完毕,调用sigreturn(2),继续返回到进程的内核态。再次循环到第五步。

可重入函数

函数使用到的变量的空间全部分配在栈桢中,那这样的函数称为可重入函数。否则称为不可重入函数。
信号的处理函数尽量保证为可重入函数。

代码示例

  • reenterable.c
#include <stdio.h>
#include <signal.h>void doit(int n){int v;static int c=0;v=c;v++;usleep(5000);c=v;//usleep(5000);printf("c=%d\n",c);return;
}
int main(void){signal(2,doit);while(1)doit(333);return 0;
}
  • 执行结果

信号处理函数的继承

子进程会继承父进程的信号处理函数,信号属于进程资源,在进程的PCB中会有信号的记录。

代码示例

  • p_signal.c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>void doit(int n){printf("recv %d signal\n",n);return ;
}
int main(void){pid_t pid;//注册2号信号的处理函数为用户自定义signal(2,doit);//创建子进程pid=fork();if(pid ==-1){perror("fork");return 1;}if(pid==0){printf("child pid %d\n",\getpid());while(1);}else{wait(NULL);}return 0;
}
  • 执行结果

setitimer实现定时器

系统计时器,运行一个进程的时候,进程所消耗的时间包括三个部分:

  • 用户时间:进程消耗在用户态的时间
  • 内核时间:进程消耗在内核态的时间
  • 睡眠时间:进程消耗在等待I/O、睡眠等不被调度的时间。

内核为每个进程都维护了三个计时器:

  • 真实计时器:统计进程的执行时间。
  • 虚拟计时器:统计进程的用户时间。
  • 实用计时器:统计进程的用户时间和内核时间之和。

执行时间 = 用户时间 + 内核时间 + 睡眠时间。

这三个计时器除了统计进程的各种时间以外,还可以按照各自的计时规则,以定时器的方式工作,向进程周期性的发送不同的信号。

SIGALRM  真实定时器
SIGVTALRM   虚拟定时器
SIGPROF    实用定时器

通过使用setitimer(2)设置、启动、关闭定时器

#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);
功能:设置一个间隔时间的定时器
参数:
which:
ITIMER_REAL:SIGALRM
ITIMER_VIRTUAL:SIGVTALRM
ITIMER_PROF:SIGPROF
new_value:定时器新值
old_value:定时器原来的值
返回值:
0  成功
-1  错误  errno被设置
  • 补充
struct itimerval{/*间隔时间*/struct timeval it_interval; /* next value *//*初值时间*/struct timeval it_value;    /* current value */
};
struct timeval{long tv_sec;                /* seconds */long tv_usec;               /* microseconds */
};

代码示例

  • timer.c
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>void doit(int n){printf("recv signal...\n");return ;
}
int main(void){struct itimerval new;//设置计时器的初始时间new.it_value.tv_sec=5;new.it_value.tv_usec=0;//设置定时器的间隔时间new.it_interval.tv_sec=0;new.it_interval.tv_usec=500000;signal(SIGALRM,doit);//设置定时器setitimer(ITIMER_REAL,&new,NULL);while(1)pause();return 0;
}
  • 执行结果

linux基础——信号阻塞及未决信号相关推荐

  1. 信号集操作函数,信号阻塞与未决

    一,信号集及相关操作函数 信号集被定义为一种数据类型: typedef struct { unsigned long sig[_NSIG_WORDS]: } sigset_t 信号集用来描述信号的集合 ...

  2. Qt信号阻塞和断开信号槽

    Qt信号阻塞和断开信号槽 Qt程序中有时候不希望信号槽的触发,在某段流程结束之后,又需要继续回复信号槽状态,这时候可以用阻塞或者断开信号槽的方法来处理. 1. 阻塞方法 bool QObject::b ...

  3. Linux学习之系统编程篇: 阻塞信号集、未决信号集、自定义信号集

    阻塞信号集和未决信号集在内核 PCB 中,因此我们无法操作,但是可以操作自定义信号集,然后将其通过函数映射给阻塞信号集来间接操作. 信号集本质:位图(1-31 号)

  4. Linux环境进程间通信(二): 信号--转载

    http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html http://www.ibm.com/developerworks ...

  5. Linux系统编程---6(信号的机制,信号4要素,Linu常规信号表,定时器)

    信号的概念 信号在我们的生活中随处可见, 如:古代战争中摔杯为号:现代战争中的信号弹:体育比赛中使用的信号枪- 他们都有共性: 简单 不能携带大量信息,只能带一个标志. 满足某个特设条件才发送. Un ...

  6. Linux系统编程(四)信号

    Linux系统编程(四)信号 一.什么是信号? 1.信号的本质 2.信号来源 硬件来源 软件来源 二.常见信号 1.可靠信号和不可靠信号 2.不可靠信号主要有以下问题: 3.可靠信号与不可靠信号注册机 ...

  7. linux系统编程(九) 信号

    文章目录 1.信号 1.1 信号的概念 1.1.1 信号的机制 1.1.2 与信号相关的事件和状态 1.1.3 信号的编号 1.1.4 信号4要素 1.1.5 Linux常规信号一览表 1.2 信号的 ...

  8. Linux系统编程32:进程信号之详解信号集操作函数(sigset_t ,sigpending,sigprocmask)

    文章目录 (1)sigset_t (2)信号集操作函数 (1)sigset_t 前面说过,未决和阻塞分别用位图来表示,于是我们把保存位图这样的数据类型称为sigset_t,sigset_t称为信号集, ...

  9. Linux 信号详解五(信号阻塞,信号未决)

    信号在内核中的表示 执行信号的处理动作成为信号递达(Delivery),信号从产生到递达之间的状态称为信号未决(Pending).进程可以选择阻塞(Block)某个信号. 被阻塞的信号产生时将保持在未 ...

  10. Linux系统编程31:进程信号之什么是信号的阻塞及相关术语(递达,未决,pending位图,handler位图)

    文章目录 (1)信号相关术语 (2)信号在内核中的表示 前面说过,操作系统发出信号之后,对于进程有可能不是立马就处理的,所以如果不是立即处理,那么在这个空档期间进程究竟对信号做了怎样的处理呢? (1) ...

最新文章

  1. 为什么建议大家使用Linux开发?
  2. DeeplyTough | 学习蛋白质结合位点的结构比较
  3. IOS8的新特性:简洁易用的毛玻璃效果
  4. 设计模式(2): 观察者模式-1
  5. 推荐系统笔记(开源工具)
  6. Dapr 已在塔架就位 将发射新一代微服务
  7. 当初互联网大佬给的几块钱“羊毛”,现在又要我们加倍还回去!
  8. Android笔记树
  9. 【性能优化】如何让APK瘦成一道闪电
  10. thinkphp无法加载控制器:Admin
  11. Java Jad 反编译class文件
  12. RoadRunner安装与使用教程
  13. 第七章 Git操作 7.1利用gitee提交代码
  14. Web:6 大主流 Web 框架优缺点对比之Ember
  15. matlab 字符串 倒序,MATLAB tip
  16. thinkadmin 各种回调的使用
  17. ARM 汇编指令 DCD
  18. On the contrary...
  19. 1T文件夹 - 微云
  20. 你是我的四月天[转自天涯]

热门文章

  1. Preact 在TSX中优雅使用className
  2. list数组遍历时能不能使用remove()方法,要注意什么
  3. 见过世面的程序员,到底有多厉害
  4. UTF-8编码转中文解码
  5. 从零基础开始学习(一) esp32 micro python编程软件环境Thonny的安装
  6. QQ邮箱的POP3与SMTP服务器是什么?
  7. 软件系统产品线特征及构建过程
  8. 后棱镜时代的个人信息黑洞,电影《绝对控制》在现实中存在吗?
  9. 浅谈work_mem
  10. 第二十四天 小丁三战链表