Unix高级编程:信号处理函数的注册、信号的产生、阻塞、未决
2017-01-15 22:54
423 查看
一、使用signal(2)向进程注册信号处理函数
"kill -l" 显示所有可用的系统信号的编号
/*举例向进程注册2号信号的用户自定义的处理程序,signal.c*/
#include <stdio.h>
#include <signal.h>
void handle(int signum) {
printf("recv signal num %d..\n", signum);
return;
}
int main(void) {
#if 0 /* 想放开的时候,把 0 变成 1 */
handle是signal回调函数的参数,在signal函数的实现中调用了handle函数,handle函数参数是信号的编号
#endif
signal(SIGINT, &handle);
while(1);
return 0;
}
补充:
1)注释补充
// C++的注释
/**/ C语言的注释,不能嵌套
#if 0
#endif 条件编译注释,0 换成 1 则让注释内容再次生效
2)信号场景的理解
1. 用户输入命令,在shell下启动一个前台进程
2. 用户按下Ctrl+C,这个键盘的输入产生一个硬件中断
3. 如果CPU当前正在执行这个进程的代码,则该进程的用户空间代码暂停执行,CPU"从用户态切换到内核态处理硬件中断"
4. 终端驱动程序将Ctrl+C解释成一个 SIGINT 信号,记录在该进程的PCB中
5. 当某个时刻,进程需要从内核态返回到用户态的时候,首先处理PCB中记录的信号,发现有一个SIGINT信号待处理,而这个信号的默认处理动作是终止该进程
6. 现场的情况:
————信号的处理函数是用户自定义的,调用用户自定义的信号处理函数,调用完毕,继续返回到内核态,该信号的记录清空,执行第 5 步。
二、信号的产生、闹钟与睡眠
第一种:"硬件产生",Ctrl+C 或者 Ctrl+\
第二种:"使用命令产生" kill -信号编号 pid
第三种:"使用函数产生" kill/raise/alarm等
"kill"(2)
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
功能:给一个进程发送信号(给进程号为pid的进程发送sig信号)
参数:
"pid" 进程pid号(>0 的情况)
"sig" 信号的编号
返回值:
成功 - 返回 0
失败 - 返回-1,errno被设置
/*举例验证,使用kill给某个进程发送信号,kill.c*/
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
pid_t pid;
int signo;
pid = atoi(argv[1]); //从命令行获取pid和signo
signo = atoi(argv[2]);
kill(pid, signo); //给指定的进程发送signo信号
return 0;
}
"raise"(3)
#include <signal.h>
int raise(int sig);
功能:发送一个信号给自己的进程
参数:"sig" 指定要发送给自己的信号编号
返回值:
成功 - 返回 0
失败 - 返回非 0
/*举例验证,给自己发送2号信号,signal3.c*/
#include <stdio.h>
#include <signal.h>
void handle(int signum) {
printf("signal num %d..\n", signum);
return;
}
int main(void) {
int i = 0;
signal(SIGINT, &handle);
while(i++ < 5) {
printf("i = %d\n", i);
if(i == 3) {
raise(2); //i=1 i=2 i=3 signal num 2.. i=4 i=5
}
}
return 0;
}
"alarm"(2) "闹钟"
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能:整理一个信号在seconds指定的时间到达自己的进程
参数:"seconds" 指定的时间,默认单位:秒
0 没有设置新闹钟
返回值:
成功 - 返回剩余的秒数,在闹钟时间到达的时候产生 14)SIGALRM 信号,发送给自己的进程
失败 - 返回 0 取消闹钟
/*举例验证alarm函数的使用,代码参见 alarm.c*/
#include <stdio.h>
#include <unistd.h>
int main(void) {
int i = 0;
alarm(1); //设置闹钟为1秒
for(;; i++) {
printf("i = %d\n", i);//可用来测试CPU的速度
}
return 0;
}
"sleep"(3) "睡眠"
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
功能:给程序指定seconds时间的睡眠
参数:"seconds" 指定的时间,默认单位:秒
返回值:
成功 - 返回 0
失败 - 返回剩余的秒数,在进程被信号中断的时候,返回剩余秒数
三、信号阻塞和未决信号
信号阻塞:所谓的信号阻塞是进程对某一个或一些信号进行阻塞。
进程调用"sigprocmask"函数设置对信号的阻塞
"sigprocmask"(2)
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
功能:检测或改变为阻塞的信号
参数:
类型sigset_t /* 集合类型,用于标识信号的阻塞和未决状态 */
"how"
SIG_BLOCK 将进程当前的信号组合集合和set指定的集合/*合并*/
SIG_UNBLOCK 将set集合里指定的信号从进程当前阻塞的信号集里/*移除*/解除set信号集里指定的信号的阻塞
SIG_SETMASK 将进程阻塞的信号集/*设置为参数set的集合*/
"set" 要操作的/*新的信号集*/
"oldset" 如果oldset非空,将进程的原先的阻塞信号集/*存储*/到oldset指定的集合里;如果oldset是 NULL,则不会存储原来的信号阻塞集
返回值:
成功 - 返回 0
失败 - 返回 -1
/*举例验证,阻塞进程中的2号信号,sig2.c*/
#include <stdio.h>
#include <signal.h>
void handle(int signum) {
printf(" recv signum: %d\n", signum);
return ;
}
int main(void) {
sigset_t set;
//初始化信号集set
sigemptyset(&set);
//向set信号集里添加2号信号
sigaddset(&set, SIGINT);
//注册2号信号的处理函数为handle
signal(2, handle);
//将信号集合set设置为当前进程的阻塞集合
sigprocmask(SIG_SETMASK, &set, NULL);
while(1);
return 0;
}
"未决信号"
信号产生了,但是信号还没有被递达,这时候信号的状态称为未决状态,这个信号称为未决信号。
对"sigset_t类型的操作函数(3)"使用下列:
"sigemptyset" //初始化,清空信号集
#include <signal.h>
int sigemptyset(sigset_t *set);
功能:将集合里的成员全部清空
参数:"set" 指定的信号集合
返回值:
成功 - 返回 0
失败 - 返回 -1
"sigfillset" //填充信号集
#include <signal.h>
int sigfillset(sigset_t *set);
功能:将集合的所有成员全部填充
参数:"set" 指定的信号集合
返回值:
成功 - 返回 0
失败 - 返回 -1
"sigaddset" //添加信号到信号集
#include <signal.h>
int sigaddset(sigset_t *set, int signum);
功能:将signum信号添加到set指定的集合里
参数:
"set" 指定的信号集合
"signum" 指定的信号编号
返回值:
成功 - 返回 0
失败 - 返回 -1
"sigdelset" //删除信号集里指定的信号
#include <signal.h>
int sigdelset(sigset_t *set, int signum);
功能:从set指定的集合里将signum指定的信号编号删除
参数:
"set" 指定的信号集合
"signum" 指定的信号编号
返回值:
成功 - 返回 0
失败 - 返回 -1
"sigismember" //测试信号是否是信号集里的成员
#include <signal.h>
int sigismember(const sigset_t *set, int signum);
功能:测试signum是否是set集合里的一个成员
参数:
"set" 指定的信号集合
"signum" 指定的信号编号
返回值:
成功 - 返回 1 代表signum是set集合的成员,0 代表不是。
失败 - 返回 -1
"检测信号是否处于未决状态",需要使用sigpending函数:
"sigpending"(2)
#include <signal.h>
int sigpending(sigset_t *set);
功能:检测未决信号
参数:"set" 未决信号集被返回到set指定的集合里
返回值:
成功 - 返回 0
失败 - 返回 -1
/*举例验证未决信号,对2号信号阻塞时进行检测 sigpending.c*/
#include <stdio.h>
#include <signal.h>
void handle(int signum) {
printf(" recv signum: %d\n", signum);
return ;
}
int main(void) {
sigset_t set, pset;//pset用于检测
//向进程注册2号信号的处理函数为handle
signal(2, handle);
//初始化set集合为空
sigemptyset(&set);
//将2号信号添加到集合set里
sigaddset(&set, 2);
//设置进程对信号的阻塞的集合
sigprocmask(SIG_BLOCK, &set, NULL);
while(1) {
//检测进程的未决信号,将未决信号添加到pset的集合里
sigpending(&pset);
//检测2号信号是否处于未决状态
int r = sigismember(&pset, 2);
if(r > 0) {
printf("检测到2号信号为未决信号!\n");
break;
}
}
return 0;
}
"可靠信号":
当在进程中设置了对某个信号阻塞,当有同一个信号多次到达的时候,解除信号阻塞的时候,"会有1个信号的多次捕获"。
此为可靠信号(34-64 号)。
"不可靠信号":
当在进程中设置了对某个信号阻塞,当有同一个信号多次到达的时候,解除信号阻塞的时候,"对这个信号只有1次捕获,造成了信号的丢失"。
此为不可靠信号(1-31 号)。
/*举例验证,可靠信号与不可靠信号,kekao.c*/
#include <stdio.h>
#include <signal.h>
void handle(int signum) {
printf("recv signum: %d\n", signum);
return ;
}
int main(void) {
sigset_t set, oset;
signal(35, handle);//1-31信号,发送多次,捕获1次
sigemptyset(&set);
sigaddset(&set, 35);
sigemptyset(&oset);
sigprocmask(SIG_SETMASK, &set, &oset);
sleep(20);
sigprocmask(SIG_SETMASK, &oset, NULL);//恢复原来的信号
return 0;
}
"kill -l" 显示所有可用的系统信号的编号
/*举例向进程注册2号信号的用户自定义的处理程序,signal.c*/
#include <stdio.h>
#include <signal.h>
void handle(int signum) {
printf("recv signal num %d..\n", signum);
return;
}
int main(void) {
#if 0 /* 想放开的时候,把 0 变成 1 */
handle是signal回调函数的参数,在signal函数的实现中调用了handle函数,handle函数参数是信号的编号
#endif
signal(SIGINT, &handle);
while(1);
return 0;
}
补充:
1)注释补充
// C++的注释
/**/ C语言的注释,不能嵌套
#if 0
#endif 条件编译注释,0 换成 1 则让注释内容再次生效
2)信号场景的理解
1. 用户输入命令,在shell下启动一个前台进程
2. 用户按下Ctrl+C,这个键盘的输入产生一个硬件中断
3. 如果CPU当前正在执行这个进程的代码,则该进程的用户空间代码暂停执行,CPU"从用户态切换到内核态处理硬件中断"
4. 终端驱动程序将Ctrl+C解释成一个 SIGINT 信号,记录在该进程的PCB中
5. 当某个时刻,进程需要从内核态返回到用户态的时候,首先处理PCB中记录的信号,发现有一个SIGINT信号待处理,而这个信号的默认处理动作是终止该进程
6. 现场的情况:
————信号的处理函数是用户自定义的,调用用户自定义的信号处理函数,调用完毕,继续返回到内核态,该信号的记录清空,执行第 5 步。
二、信号的产生、闹钟与睡眠
第一种:"硬件产生",Ctrl+C 或者 Ctrl+\
第二种:"使用命令产生" kill -信号编号 pid
第三种:"使用函数产生" kill/raise/alarm等
"kill"(2)
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
功能:给一个进程发送信号(给进程号为pid的进程发送sig信号)
参数:
"pid" 进程pid号(>0 的情况)
"sig" 信号的编号
返回值:
成功 - 返回 0
失败 - 返回-1,errno被设置
/*举例验证,使用kill给某个进程发送信号,kill.c*/
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
pid_t pid;
int signo;
pid = atoi(argv[1]); //从命令行获取pid和signo
signo = atoi(argv[2]);
kill(pid, signo); //给指定的进程发送signo信号
return 0;
}
"raise"(3)
#include <signal.h>
int raise(int sig);
功能:发送一个信号给自己的进程
参数:"sig" 指定要发送给自己的信号编号
返回值:
成功 - 返回 0
失败 - 返回非 0
/*举例验证,给自己发送2号信号,signal3.c*/
#include <stdio.h>
#include <signal.h>
void handle(int signum) {
printf("signal num %d..\n", signum);
return;
}
int main(void) {
int i = 0;
signal(SIGINT, &handle);
while(i++ < 5) {
printf("i = %d\n", i);
if(i == 3) {
raise(2); //i=1 i=2 i=3 signal num 2.. i=4 i=5
}
}
return 0;
}
"alarm"(2) "闹钟"
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能:整理一个信号在seconds指定的时间到达自己的进程
参数:"seconds" 指定的时间,默认单位:秒
0 没有设置新闹钟
返回值:
成功 - 返回剩余的秒数,在闹钟时间到达的时候产生 14)SIGALRM 信号,发送给自己的进程
失败 - 返回 0 取消闹钟
/*举例验证alarm函数的使用,代码参见 alarm.c*/
#include <stdio.h>
#include <unistd.h>
int main(void) {
int i = 0;
alarm(1); //设置闹钟为1秒
for(;; i++) {
printf("i = %d\n", i);//可用来测试CPU的速度
}
return 0;
}
"sleep"(3) "睡眠"
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
功能:给程序指定seconds时间的睡眠
参数:"seconds" 指定的时间,默认单位:秒
返回值:
成功 - 返回 0
失败 - 返回剩余的秒数,在进程被信号中断的时候,返回剩余秒数
三、信号阻塞和未决信号
信号阻塞:所谓的信号阻塞是进程对某一个或一些信号进行阻塞。
进程调用"sigprocmask"函数设置对信号的阻塞
"sigprocmask"(2)
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
功能:检测或改变为阻塞的信号
参数:
类型sigset_t /* 集合类型,用于标识信号的阻塞和未决状态 */
"how"
SIG_BLOCK 将进程当前的信号组合集合和set指定的集合/*合并*/
SIG_UNBLOCK 将set集合里指定的信号从进程当前阻塞的信号集里/*移除*/解除set信号集里指定的信号的阻塞
SIG_SETMASK 将进程阻塞的信号集/*设置为参数set的集合*/
"set" 要操作的/*新的信号集*/
"oldset" 如果oldset非空,将进程的原先的阻塞信号集/*存储*/到oldset指定的集合里;如果oldset是 NULL,则不会存储原来的信号阻塞集
返回值:
成功 - 返回 0
失败 - 返回 -1
/*举例验证,阻塞进程中的2号信号,sig2.c*/
#include <stdio.h>
#include <signal.h>
void handle(int signum) {
printf(" recv signum: %d\n", signum);
return ;
}
int main(void) {
sigset_t set;
//初始化信号集set
sigemptyset(&set);
//向set信号集里添加2号信号
sigaddset(&set, SIGINT);
//注册2号信号的处理函数为handle
signal(2, handle);
//将信号集合set设置为当前进程的阻塞集合
sigprocmask(SIG_SETMASK, &set, NULL);
while(1);
return 0;
}
"未决信号"
信号产生了,但是信号还没有被递达,这时候信号的状态称为未决状态,这个信号称为未决信号。
对"sigset_t类型的操作函数(3)"使用下列:
"sigemptyset" //初始化,清空信号集
#include <signal.h>
int sigemptyset(sigset_t *set);
功能:将集合里的成员全部清空
参数:"set" 指定的信号集合
返回值:
成功 - 返回 0
失败 - 返回 -1
"sigfillset" //填充信号集
#include <signal.h>
int sigfillset(sigset_t *set);
功能:将集合的所有成员全部填充
参数:"set" 指定的信号集合
返回值:
成功 - 返回 0
失败 - 返回 -1
"sigaddset" //添加信号到信号集
#include <signal.h>
int sigaddset(sigset_t *set, int signum);
功能:将signum信号添加到set指定的集合里
参数:
"set" 指定的信号集合
"signum" 指定的信号编号
返回值:
成功 - 返回 0
失败 - 返回 -1
"sigdelset" //删除信号集里指定的信号
#include <signal.h>
int sigdelset(sigset_t *set, int signum);
功能:从set指定的集合里将signum指定的信号编号删除
参数:
"set" 指定的信号集合
"signum" 指定的信号编号
返回值:
成功 - 返回 0
失败 - 返回 -1
"sigismember" //测试信号是否是信号集里的成员
#include <signal.h>
int sigismember(const sigset_t *set, int signum);
功能:测试signum是否是set集合里的一个成员
参数:
"set" 指定的信号集合
"signum" 指定的信号编号
返回值:
成功 - 返回 1 代表signum是set集合的成员,0 代表不是。
失败 - 返回 -1
"检测信号是否处于未决状态",需要使用sigpending函数:
"sigpending"(2)
#include <signal.h>
int sigpending(sigset_t *set);
功能:检测未决信号
参数:"set" 未决信号集被返回到set指定的集合里
返回值:
成功 - 返回 0
失败 - 返回 -1
/*举例验证未决信号,对2号信号阻塞时进行检测 sigpending.c*/
#include <stdio.h>
#include <signal.h>
void handle(int signum) {
printf(" recv signum: %d\n", signum);
return ;
}
int main(void) {
sigset_t set, pset;//pset用于检测
//向进程注册2号信号的处理函数为handle
signal(2, handle);
//初始化set集合为空
sigemptyset(&set);
//将2号信号添加到集合set里
sigaddset(&set, 2);
//设置进程对信号的阻塞的集合
sigprocmask(SIG_BLOCK, &set, NULL);
while(1) {
//检测进程的未决信号,将未决信号添加到pset的集合里
sigpending(&pset);
//检测2号信号是否处于未决状态
int r = sigismember(&pset, 2);
if(r > 0) {
printf("检测到2号信号为未决信号!\n");
break;
}
}
return 0;
}
"可靠信号":
当在进程中设置了对某个信号阻塞,当有同一个信号多次到达的时候,解除信号阻塞的时候,"会有1个信号的多次捕获"。
此为可靠信号(34-64 号)。
"不可靠信号":
当在进程中设置了对某个信号阻塞,当有同一个信号多次到达的时候,解除信号阻塞的时候,"对这个信号只有1次捕获,造成了信号的丢失"。
此为不可靠信号(1-31 号)。
/*举例验证,可靠信号与不可靠信号,kekao.c*/
#include <stdio.h>
#include <signal.h>
void handle(int signum) {
printf("recv signum: %d\n", signum);
return ;
}
int main(void) {
sigset_t set, oset;
signal(35, handle);//1-31信号,发送多次,捕获1次
sigemptyset(&set);
sigaddset(&set, 35);
sigemptyset(&oset);
sigprocmask(SIG_SETMASK, &set, &oset);
sleep(20);
sigprocmask(SIG_SETMASK, &oset, NULL);//恢复原来的信号
return 0;
}
相关文章推荐
- unix环境高级编程-alarm、pause()与信号处理函数
- UNIX环境编程学习笔记(25)——信号处理进阶学习之 sigaction 函数
- unix 环境高级编程 信号函数杂记一
- UNIX环境高级编程学习之第十章信号-用信号和非局部转移函数写非阻塞的IO函数
- UNIX环境高级编程学习之第十章信号-信号集的操作,让进程阻塞SIGQUIT信号
- UNIX环境编程学习笔记(25)——信号处理进阶学习之 sigaction 函数
- unix 环境高级编程 信号二 可重入函数与不可重入函数
- Unix环境高级编程(阅读笔记)----信号集、信号屏蔽函数sigprocmask
- unix环境高级编程一书中部分错误处理函数
- UNIX环境高级编程第10章信号10.3singal函数
- unix环境高级编程_信号函数定义
- UNIX环境高级编程----函数索引(部分)
- signal---高级信号注册函数
- UNIX环境高级编程学习之第十章信号-用信号实现父子进程同步
- unix环境高级编程-1.7-出错处理
- Linux下C语言编程--信号处理函数
- unix环境高级编程-4.9-chmod,fchmod函数和粘住位
- unix 高级环境编程-1.9-信号
- Unix环境高级编程 写dup2功能相同的函数
- Unix环境高级编程_线程安全函数和可重入函数