网站接入地查询,襄阳网站开发,内江做网站多少钱,建设网站需要学什么引入
我们之前学习了信号量#xff0c;信号量和信号可不是一个东西#xff0c;不能混淆。
信号是什么以及一些基础概念
信号是一种让进程给其他进程发送异步消息的方式
信号是随时产生的#xff0c;无法预测信号可以临时保存下来#xff0c;之后再处理信号是异步发送的…引入
我们之前学习了信号量信号量和信号可不是一个东西不能混淆。
信号是什么以及一些基础概念
信号是一种让进程给其他进程发送异步消息的方式
信号是随时产生的无法预测信号可以临时保存下来之后再处理信号是异步发送的。因为这两个进程发送信号的进程和接收信号的进程互不相干
kill -l :查看信号
我们可以使用kill -l查看所有信号
他的输出大概是这样子
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
...可以发现没有0、32、33号信号。1-31分别对应一个bit位34到64号信号是实时信号当开始执行实时信号必须执行完才能执行其他的信号
信号处理
面对信号我们有多种处理方式 默认动作 自定义处理–捕捉 忽略信号
我们就是通过signal系统调用来更改处理信号的方式
信号的产生
有三种方式kill命令、键盘输入、系统调用
kill命令
使用kill命令
kill -num pid常见的就是
kill -9 pid
#终止进程键盘输入
像是输入ctrl c也可以停止当前进程
使用系统调用函数
使用kill函数给指定的进程发送指定的信号
#include sys/types.h
#include signal.hint kill(pid_t pid, int sig);成功返回0失败返回-1并设置errno
raise函数对调用raise的进程发送信号
#include signal.hint raise(int sig);这个函数功能相当于调用
kill(getpid(), sig);abort函数 调用该函数的进程直接退出 使用signal修改信号
使用typedef简化写法
typedef void (*signal_handler_t)(int);
signal_handler_t signal(int sig, signal_handler_t func);
//func是回调函数
//底层调用func的时候func的参数就是sig我们先传入要对哪个信号进行修改再传入我们自定义的修改方法func 并且修改一次后一直生效
SIG_IGN 是ignore忽略信号的意思
signal(num, SIG_IGN)
//接收num信号后不执行任何操作
//9号19号信号无法被忽略可以区了解一下这两个信号的作用就能理解为什么了异常
最常见的就是代码出现问题爆出了异常
比如出现num/0的情况产生SIGFPE信号 访问野指针产生11号信号SIGSEGV
Core Dump 是什么 当一个进程要异常终止时,可以选择**把进程的用户空间内存数据全部保存到磁盘上,文件名通常是core,**这叫做Core Dump。 为什么 进程异常终止通常是因为有Bug,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因,这叫做Post-mortem Debug事后调试。一个进程允许产生多大的core文件取决于进程的Resource Limit(这个信息保存 在PCB中)。默认是不允许产生core文件的, 因为core文件中可能包含用户密码等敏感信息,不安全以及防止频繁崩溃导致生成大量core dump文件。 在开发调试阶段可以用ulimit命令改变这个限制,允许产生core文件。 首先用ulimit命令改变Shell进程的Resource Limit,允许core文件最大为1024K:
ulimit -c 1024信号保存
信号其他相关常见概念
实际执行信号的处理动作称为信号递达(Delivery) (默认、忽略、自定义信号从产生到递达之间的状态,称为信号未决(Pending)信号被临时保存进程可以选择阻塞 (Block )某个信号。即无法执行该信号具体原理是位图被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作9和19号进程无法屏蔽
内核中的结构三张表重要
这张图显示了block位图记录信号是否阻塞、pending位图表示未决、handler表示函数指针数组记录信号执行方法
对三张表的操作
从上图来看,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。 因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。 阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。
基本接口了解即可
#include signal.h// 初始化信号集为空不包含任何信号。
// 参数
// set - 指向要初始化的信号集。
// 返回值
// 成功返回 0失败返回 -1。
int sigemptyset(sigset_t *set);// 将信号集中的所有信号置为有效包含所有信号。
// 参数
// set - 指向要填充的信号集。
// 返回值
// 成功返回 0失败返回 -1。
int sigfillset(sigset_t *set);// 向信号集中添加一个指定的信号。
// 参数
// set - 指向信号集。
// signo - 要添加的信号编号。
// 返回值
// 成功返回 0失败返回 -1。
int sigaddset(sigset_t *set, int signo);// 从信号集中删除一个指定的信号。
// 参数
// set - 指向信号集。
// signo - 要删除的信号编号。
// 返回值
// 成功返回 0失败返回 -1。
int sigdelset(sigset_t *set, int signo);// 检查某个信号是否在信号集中。
// 参数
// set - 指向信号集。
// signo - 要检查的信号编号。
// 返回值
// 如果信号在信号集中返回 1否则返回 0失败返回 -1。
int sigismember(const sigset_t *set, int signo);在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号。
注意 上面的接口都没有写入内核中需要
sigprocmask修改block位图
#include signal.h
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功则为0,若出错则为-1sigpending查看未决信号集
#include signal.hint sigpending(sigset_t *set);可以用于 查看哪些信号被屏蔽且处于等待状态。
补充 pending位图先清零对应信号再递达
信号的处理
什么时候处理
进程从内核态切换到用户态时OS检测并处理信号
如何处理
可以先看一下下面这个图
能看到有4次状态切换这里的原理是 如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。 由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信号的处理函数sighandler。 当前正在执行main函数,这时发生中断或异常切换到内核态。 在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函 数,sighandler比和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是 两个独立的控制流程。 sighandler函数返 回后自动执行特殊的系统调用sigreturn再次进入内核态。 如果没有新的信号要递达,这次再返回用户态就是恢复
调用与屏蔽
当某个信号的处理函数被调用时,**内核自动将该信号加入进程的信号屏蔽字,**即处理期间不允许再次调用。 当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。
结语
进程间通信到这里就暂时结束了虽然内存池还差代码实现、共享内存还完全没写但我准备之后用到了再写希望对大家有帮助