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

信号在内核中的表示:
下面用图来进一步描述这种信号从产生到递达之间的状态(信号阻塞与未诀):
 
那是怎么来决定的呢?下面慢慢来举例分解:
所以,通过这些图,可以描述信号从产生到递达的一个过程,上面的理解起来可能有点难,下面会用代码来进一步阐述,在进行实验之前,还需了解一些函数的使用,这些函数在实验中都会被用到,也就是信号集操作函数。
信号集操作函数:
其中解释一下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. Jenkins - 插件管理

    1 - Jenkins插件 Jenkins通过插件来增强功能,可以集成不同的构建工具.云平台.分析和发布工具等,从而满足不同组织或用户的需求. Jenkins 提供了不同的的方法来安装插件(需要不同级 ...

  2. [转帖]IOC Security: Indicators of Attack vs. Indicators of Compromise

    IOC Security: Indicators of Attack vs. Indicators of Compromise https://www.crowdstrike.com/blog/ind ...

  3. djngo未整理

    Django Django基础命令 - runserver 本地建议运行Django - shell 进入Django项目得python shell环境 - test 执行Django 用列测试 数据 ...

  4. php位运算及其高级应用

    我们之前学过逻辑与(&&)      条件1 && 条件2 当两边条件同时成立时候返回1 逻辑或(||)         条件1 || 条件2    当两边条件只要有一 ...

  5. 3. Spark SQL解析

    3.1 新的起始点SparkSession 在老的版本中,SparkSQL提供两种SQL查询起始点,一个叫SQLContext,用于Spark自己提供的SQL查询,一个叫HiveContext,用于连 ...

  6. 【scratch3.0教程】2.1 涂鸦花朵

    第4课    涂鸦花朵 1.编程前的准备 在设计一个作品之前,必须先策划一个脚本,然后再根据脚本,收集或制作素材(图案,声音等)接着就可以启动Scratch,汇入角色,舞台,利用搭程序积木的方式编辑程 ...

  7. 第14章 Salesforce标准对象

    14.1 Sales Cloud基本信息 Sales Cloud 会为您提供管理业务的一切功能.生成最佳潜在客户.通过销售漏斗管理业务机会,并使用现有客户培养关系.以及,预测收入.设置销售区域,并将代 ...

  8. k8s部署traefik

    基础知识 同nginx相比,traefik能够自动感知后端容器变化,从而实现自动服务发现.  traefik部署在k8s上分为daemonset和deployment两种方式各有优缺点: daemon ...

  9. mysql_重置密码

    # 修改编码 ```pythonshow variables like '%char%'; #查看当前使用的编码 1.打开配置文件: vim /etc/mysql/my.cnf 2.在[client] ...

  10. gitlab-runner 的 executors 之 docker

    gitlab-runner 的 executors 之 docker GitLab Runner 实现了许多执行程序,可用于在不同的场景中运行构建.所有执行程序分别为: SSH Shell Paral ...