信号1.0,信号概念、signal()处理、前后台进程、闹钟设置、初识信号三张表。

@bit::Shadow
✧(≖ ◡ ≖✿

目录

信号引入

信号的基本理解

基本结论

☆☆用户层、C库层、内核层

信号认识

信号总类型和处理模式

分布准则

信号处理动作

signal()

sigaction()

忽略信号动作

前台进程、后台进程

进程定义与特点

后台进程

实例

*前后台进程切换

后台--->前台

前台--->后台

部分信号接口

*程序异常的信号处理

闹钟alarm

循环闹钟

进程调度与时间片

信号的保存

管理模式

位图表的基本单位sigset_t

🔗博客typeid().name()的使用"C++进阶知识3.0"


信号引入

生活中的信号:红绿灯🚥、闹钟、敲门声等等。

信号的基本理解

中断正在做的事,信号是一种进程异步通知机制。

信号的对象是"进程",进程处理信号有两种模式:阻塞等待模式(一般模式)和非阻塞模式。

信号的产生,相对于进程是异步的。

基本结论

1.信号处理前,进程在信号还没有产生时就已经准备好了信号的处理机制。信号注册表(kill -l)

2.信号处理不一定是产生就立即处理,可延迟处理 。block 1

3.信号有很多,信号源更多。

☆☆用户层、C库层、内核层

由浅到深:用户层---C库层---内核层

用户使用的系统调用实际上是C库封装的Linux内核代码,这也就解释了为什么有时候在man下的查询在内核中找不到。

层次 组件 角色 代码在哪里?
用户应用层 你的C程序 (read(fd, buf, count)) 调用者 你自己写的 .c 文件
C库层 glibc (GNU C Library) 包装器 glibc 源码库 (独立于内核)
内核层 Linux Kernel (sys_read()) 真实干活的人 Linux 内核源码

所以在man 2中查询的调用可能在Linux内核中找不到。像 handler_t 内核中根本没有,内核中是__handler_t 。

信号认识

信号可被键盘、程序产生

信号总类型和处理模式

kill -l #查询信号宏集

使用man 7 signal 查询 Core Term pause() SIGCHILD等信息。

分布准则

【1,31】:普通信号

【32,33】:为线程库预留,不对程序开放。

【34,64】:实时信号。(对实时信号不作探讨)

信号处理动作

1.默认处理动作。像:

2.自定义处理动作。使用接口自定义的行为。

cpp 复制代码
//函数指针的重定义,返回类型是void
typedef void (*handler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
//更合适的sigaction()
int sigaction(int signum, struct sigaction* act, struct sigaction* oact);
signal()

拦截Ctrl+c的进程终止信号

cpp 复制代码
void handler_2(int) { 
    printf("Ctrl+C被执行了,已成功拦截\n"); 
}

void test2()
{
    //signal重定义SIGINT2号信号
    signal(SIGINT, handler_2);
}
sigaction()
cpp 复制代码
#include <signal.h>

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
忽略信号动作

指收到信号

例子

前台进程、后台进程

进程定义与特点

  • 当运行可执行程序./pc后默认将运行为前台进程,其余进程均为"后台进程"。
  • 只有前台进程方可从键盘读入数据,前台进程任何时候只能有一个。
  • 后台进程可以有多个,前后台进程均可以向显示器上打印。

后台进程

运行及监视

bash 复制代码
//运行
./a.out &
//监视
jobs -l #带pid  '-'代表上一次被替换的进程 '+'代表替换方
jobs  #不带pid

实例

后台运行并查看

*前后台进程切换

后台--->前台

fg 【任务号】:将后台进程提到前台运行

前台--->后台

1.Ctrl + z :暂停(由于前台任务无法接受输入,所以必须先暂时性的暂停处理)。

2.bg 【任务号】:对指定任务号的暂停前台处理,将前台任务正式提到后台。

部分信号接口

kill() raise()

cpp 复制代码
//kill -[signum] [pid]的系统调用格式
int kill(pid_t pid, int signum);

//send sig to caller
int raise(int signum); 

特殊的void abort(void)

cpp 复制代码
void abort(void); //特殊的恢复执行 (6)SIGART 信号

功能:

1.没有进行6号信号的捕捉重定义,正常执行终止进程。

2.设置了自定义信号处理函数。在执行自定义处理函数返回后,系统会恢复默认的 SIGABRT 行为,再次发送信号,最终终止进程。

*程序异常的信号处理

此处程序异常指在执行流中遇到的异常,像:除0 空指针解引用。

库的制作与2.0原理文章中,MMU将寄存器CR3(存储物理地址)的地址转换为物理地址。此处MMU的作用是接受程序异常信号并返回给CPU,执行接下来的进程调度或其他工作,MMU是中间硬件单元。

闹钟alarm

cpp 复制代码
unsigned int alarm (unsigned int seconds);

为当前设置定一计时闹钟,在seconds秒后OS为当前进程发送 (14)SIGALRM 信号执行此信号的动作,默认情况下是终止当前进程。

返回值

循环闹钟

利用闹钟结束后会产生信号调用动作催生循环。

cpp 复制代码
void handler_2(int) 
{ 
    printf("14:ALRM信号\n");
    sleep(1);
    
    // 恢复默认动作 SIG_DFL
    //typedef void (*handler)(int)
    signal(SIGALRM, SIG_DFL);
    printf("默认动作\n");
}

int main() 
{
    alarm(1);
    signal(SIGALRM, handler_2); 
    sleep(1);
    return 0;
}

进程调度与时间片

这是Linux2.6.18版本的时间片控制

使用计时器决断进程调度工作

cpp 复制代码
struct timer_list {
	struct list_head entry;
	unsigned long expires;

	void (*function)(unsigned long);//超时后执行的回调函数
	unsigned long data;//传递给回调函数的参数

	//指向该定时器所属的时间轮基座,水桶由小变大由上到下接水
	struct tvec_t_base_s *base;
};

其中base时间轮算法:

使用槽位的方法来计时

cpp 复制代码
struct tvec_t_base_s {
    spinlock_t lock;           // 保护该时间轮的锁 
    unsigned long timer_jiffies;  // 该CPU上次处理定时器的时间 
    struct timer_list *running_timer;  /* 当前正在执行的定时器 */
    tvec_root_t tv1;    秒针       /* 第一级时间轮:256个槽位 */
    tvec_t tv2;         分针       /* 第二级:64个槽位 */
    tvec_t tv3;         时针       /* 第三级:64个槽位 */
    tvec_t tv4;                /* 第四级:64个槽位 */
    tvec_t tv5;                /* 第五级:64个槽位 */
};

信号的保存

信号的执行流程图

即在信号递达之前存在一"未决"状态,表示信号未被递达。

管理模式

2张位图+1张函数指针表管理信号。 Linux内核有所不同此处仅做参考

usigned int位图表【1,31】第32位未使用

1.位图的下标位置 对应信号宏。

2.位内0/1代表是否作用。

block表:表示是否对该信号发送了阻塞。

pending表:表示信号是否产生。 "信号产生"后在进入动作函数前pending位已被置零。

位图表的基本单位sigset_t

🔗博客typeid().name()的使用"C++进阶知识3.0"

cpp 复制代码
std::cout << typeid(sigset_t).name() << std::endl;
// 输出10__sigset_t
部分 含义
10 后面紧跟的标识符(__sigset_t)的长度是 10 个字符
__sigset_t 实际的类型名称(系统层--linux内核层)

内核:

|------------------------------------------|--------------------------|
| typedef unsigned long __kernel_sigset_t; | /* at least 32 bits */ |

感谢支持,持续更新

欢迎关注

相关推荐
nazisami1 小时前
深入学习C++11
c++·c++11
HackTwoHub1 小时前
免费FOFA高级会员、DayDaymap、360Quake、Hunter测绘搜索引擎高级会员免费使用最大1W条查询工具
运维·安全·web安全·搜索引擎·网络安全·系统安全·安全架构
(Charon)1 小时前
【C++ 面试高频:STL 容器 vector、map、unordered_map 总结】
开发语言·c++·面试
我是一颗柠檬1 小时前
【Java项目技术亮点】滑动窗口限流算法
java·开发语言·算法
++==1 小时前
git的安装以及基本命令使用、远程仓库的操作、vscode连接远程仓库进行项目的上传、gitee的使用
linux·git·gitee
鹤落晴春1 小时前
RH124问答4:创建、查看和编辑文本文件
linux·运维
Irissgwe1 小时前
二叉树进阶
数据结构·c++·算法·c·二叉搜索树
放下华子我只抽RuiKe51 小时前
FastAPI 全栈后端(七):测试与自动化
运维·前端·人工智能·react.js·前端框架·自动化·fastapi
hairenwangmiao1 小时前
c++排序(第一章----桶排序与sort排序)
数据结构·c++·排序