Unix高级编程:信号处理函数的注册、信号的产生、阻塞、未决

来源:互联网 发布:淘宝贷款怎么申请 编辑:程序博客网 时间:2024/05/11 18:36
一、使用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;
}

0 0
原创粉丝点击