今天继续探讨信号相关的东东,话不多说,正入正题:

信号在内核中的表示:
下面用图来进一步描述这种信号从产生到递达之间的状态(信号阻塞与未诀):
 
那是怎么来决定的呢?下面慢慢来举例分解:
所以,通过这些图,可以描述信号从产生到递达的一个过程,上面的理解起来可能有点难,下面会用代码来进一步阐述,在进行实验之前,还需了解一些函数的使用,这些函数在实验中都会被用到,也就是信号集操作函数。
信号集操作函数:
其中解释一下sigset_t,百度百科解释为:
而这个函数的意义就是将这64位清0
这个函数的意义是将这屏蔽字的64位都变为1
将这个信号所对应的位置为1
将这个信号所对应的位置为0
检测这一个信号所对应的位当前是0还是1
 
以上是操作信号集的五个相关的函数,但是注意:这五个函数仅仅是改变这个信号集变量,如set,并非真正改变进程信号当中的屏蔽字,所以接下来介绍的函数就是改变信号当中的屏蔽字的
sigprocmask:
好了,理论说了很多,下面正式开始实验,来体会一下一个信号从产生到递达的一个状态转换过程:
首先,我们打印出系统的未诀信号,目的是为了观察之后的状态:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() void handler(int sig);
void printsigset(sigset_t *set)//打印出信号集的状态,其中参数set为未诀状态的信号集
{
int i;
for (i=1; i<NSIG; ++i)//NSIG表示信号的最大值,也就是等于64
{
if (sigismember(set, i))//说明是未诀状态的信号
putchar('1');
else
putchar('0');//说明不是未诀状态的信号
}
printf("\n");
} int main(int argc, char *argv[])
{
sigset_t pset; for (;;)
{
sigpending(&pset);//该函数是获取进程当中未诀状态的信号集 ,保存在pset当中
printsigset(&pset);//打印信号集的状态,看有没有未诀状态的信号产生
sleep();
}
return ;
}

【说明】:sigpending是用来获取进程中所有的未诀信号集:

这时看一下运行效果:

可以发现,当前状态没有未诀的信号,因为还没有被阻塞的信号过,信号也没有产生过,所以不可能有未诀的状态。
这时,我们来安装一个SIGINT信号:

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() void handler(int sig);
void printsigset(sigset_t *set)
{
int i;
for (i=; i<NSIG; ++i)
{
if (sigismember(set, i))
putchar('');
else
putchar('');
}
printf("\n");
} int main(int argc, char *argv[])
{
sigset_t pset;
if (signal(SIGINT, handler) == SIG_ERR)//安装一个SIGINT信号
ERR_EXIT("signal error"); for (;;)
{
sigpending(&pset);
printsigset(&pset);
sleep();
}
return ;
} void handler(int sig)
{
printf("recv a sig=%d\n", sig);
}

这时再看下效果:

从结果来看,信号被直接递达了,所以这次也没有看到有1的未诀状态的信号,因为信号必须被阻塞才会出现未诀状态,所以接下来将SIGINT信号利用上面介绍到的函数来将其阻塞掉:

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() void handler(int sig);
void printsigset(sigset_t *set)
{
int i;
for (i=; i<NSIG; ++i)
{
if (sigismember(set, i))
putchar('');
else
putchar('');
}
printf("\n");
} int main(int argc, char *argv[])
{
sigset_t pset; sigset_t bset;
sigemptyset(&bset);//将信号集清0
sigaddset(&bset, SIGINT);//将SIGINT所对应的位置1 if (signal(SIGINT, handler) == SIG_ERR)
ERR_EXIT("signal error"); sigprocmask(SIG_BLOCK, &bset, NULL);//更改进程中的信号屏蔽字,其中第三个参数传NULL,因为不关心它原来的信号屏蔽字
for (;;)
{
sigpending(&pset);
printsigset(&pset);
sleep();
}
return ;
} void handler(int sig)
{
printf("recv a sig=%d\n", sig);
}

编译运行:

从结果来看,将SIGINT信号来了,由于添加到了信号屏蔽字为1,所以会被阻塞掉,并且可以看到SIGINT对应的位也打印为1了。

【说明】:SIGINT对应的位是指:

下面,我们做一件事情,就是当我们按下ctrl+\解除阻塞,这样处于未诀状态的信号就会被递达,则对应的未诀状态位也会还原成0,具体代码如下:

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() void handler(int sig);
void printsigset(sigset_t *set)
{
int i;
for (i=; i<NSIG; ++i)
{
if (sigismember(set, i))
putchar('');
else
putchar('');
}
printf("\n");
} int main(int argc, char *argv[])
{
sigset_t pset;
sigset_t bset;
sigemptyset(&bset);
sigaddset(&bset, SIGINT);
if (signal(SIGINT, handler) == SIG_ERR)
ERR_EXIT("signal error");
if (signal(SIGQUIT, handler) == SIG_ERR)//注册一个ctrl+c信号
ERR_EXIT("signal error"); sigprocmask(SIG_BLOCK, &bset, NULL);
for (;;)
{
sigpending(&pset);
printsigset(&pset);
sleep();
}
return ;
} void handler(int sig)
{
if (sig == SIGINT)
printf("recv a sig=%d\n", sig);
else if (sig == SIGQUIT)
{
sigset_t uset;//当按下ctrl+\时,则对SIGINT信号解除阻塞
sigemptyset(&uset);
sigaddset(&uset, SIGINT);
sigprocmask(SIG_UNBLOCK, &uset, NULL);
}
}

编译运行:

从中可以看到,当我们按下ctrl+\时,并没有退出,而是解除了阻塞,所以对应的SIGINT位也变为0了。

另外,看下这种情况:

多次按了ctrl+c,可在按ctrl+\解除阻塞时,只响应了一次信号处理函数,这也由于SIGINT是不可靠信号,不支持排队。

另外,由于我们捕获了ctrl+\信号,所以没办法退出这个进程了,那怎么办呢,可以利用shell命令将其强制杀掉如下:

好了,今天学的东西可能有些生涩,需好好消化,下节再见!

 

linux系统编程之信号(四)的更多相关文章

  1. linux系统编程之信号(七)

    今天继续学习信号,主要是学习关于时间和定时器相关的函数的使用,关于这个实际上有很多内容,这里先简要进行说明,等之后再慢慢进行相关深入,也主要是为接下来要做的一个综合linux系统编程的例子做准备,好了 ...

  2. linux系统编程之信号(一):中断与信号

    一,什么是中断? 1.中断的基本概念 中断是指计算机在执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的事件处理程序,待处理完毕后又返回原来被 ...

  3. linux系统编程之信号(二)

    经过了漫长的间歇,对于c语言的学习也被中断了很久,现实确实有很多的无耐,计划中的事情总会被打乱,但不管怎样,学习的道路是不能休止的,所以经过了一断温习后现在继续学习C语言,话不多说,进入正题: 信号分 ...

  4. linux系统编程之信号(四):alarm和可重入函数

    一,alarm() 在将可重入函数之前我们先来了解下alarm()函数使用: #include <unistd.h> unsigned int alarm(unsigned int sec ...

  5. linux系统编程之信号(三):信号安装、signal、kill,arise讲解

    一,信号安装 如果进程要处理某一信号,那么就要在进程中安装该信号.安装信号主要用来确定信号值及进程针对该信号值的动作之间的映射关系,即进程将要处理哪个信号:该信号被传递给进程时,将执行何种操作. li ...

  6. linux系统编程之信号(七):被信号中断的系统调用和库函数处理方式

        一些IO系统调用执行时, 如 read 等待输入期间, 如果收到一个信号,系统将中断read, 转而执行信号处理函数. 当信号处理返回后, 系统遇到了一个问题: 是重新开始这个系统调用, 还是 ...

  7. linux系统编程之信号(六):信号发送函数sigqueue和信号安装函数sigaction

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

  8. linux系统编程之信号(二):信号处理流程(产生、注册、注销、执行)

        对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说,可以分为三个阶段: 信号诞生 信号在进程中注册 信号在进程中的注销 信号处理函数执行 1    信号诞生     信号事件 ...

  9. linux系统编程之信号:信号发送函数sigqueue和信号安装函数sigaction

    信号发送函数sigqueue和信号安装函数sigaction sigaction函数用于改变进程接收到特定信号后的行为. sigqueue()是比较新的发送信号系统调用,主要是针对实时信号提出的(当然 ...

随机推荐

  1. [LeetCode] 496. Next Greater Element I 下一个较大的元素 I

    You are given two arrays (without duplicates) nums1 and nums2 where nums1’s elements are subset of n ...

  2. 函数vs方法

    举例 python代码示例: class A(): def method_demo(): print("我是一个方法,在类内") def function_demo(): prin ...

  3. hihocoder 1566 皇室成员的名字

    #1566 : 皇室成员的名字 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho正在学习世界历史.他发现历史上很多西方国家的皇室成员的名字都是由英文名字加罗马数字组 ...

  4. Postgresql中的large object

    1.初识postgresql large object 一位同事在对使用pg_dump备份出来的文件(使用plain格式)进行恢复时,觉得速度非常慢,让我分析一下是什么原因. 我拿到他的.bak文件, ...

  5. python time模块(13)

    python time模块主要包含各种提供日期.时间功能的相关函数.time模块既提供了把日期.时间格式化为字符串的功能,也提供了从字符串恢复日期.时间的功能. 一.前言 在 time 模块内提供了很 ...

  6. hashMap的源码实现

    1.初步认识hashMap public static void main(String[] args) { HashMap<String, Integer> map = new Hash ...

  7. MySQL8.0.16 单机 Linux安装以及使用

    安装 先去下载 https://dev.mysql.com/downloads/mysql/ 然后上传到Linux 进入存放目录,解压到指定目录[我这里是/soft/mysql8] [root@loc ...

  8. adb常用命令总结

    针对移动端 Android 的测试, adb 命令是很重要的一个点,必须将常用的 adb 命令熟记于心, 将会为 Android 测试带来很大的方便,其中很多命令将会用于自动化测试的脚本当中. And ...

  9. LeetCode | 142. 环形链表 II

    原题(Medium): 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始) ...

  10. 链表习题(8)-寻找单链表中数据域大小为k的结点,并与前一结点交换,如果前一结点存在的情况下

    /*寻找单链表中数据域大小为k的结点,并与前一结点交换,如果前一结点存在的情况下*/ /* 算法思想:定义两个指针,pre指向前驱结点,p指向当前结点,当p->data == k的时候,交换 p ...