您的位置:首页 > 编程语言

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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  unix 编程