当前位置: 首页 > wzjs >正文

做响应式网站价格seo诊断

做响应式网站价格,seo诊断,三星网上商城下载,做网站发现是传销文章目录 捕捉信号信号是什么时候被处理的?内核如何实现信号的捕捉sigaction代码应用可重入函数volatileSIGCHLD信号 捕捉信号 信号是什么时候被处理的? 内核态:允许进程访问操作系统的代码和数据! 用户态:只能访问&…

文章目录

    • 捕捉信号
      • 信号是什么时候被处理的?
      • 内核如何实现信号的捕捉
      • sigaction
      • 代码应用
      • 可重入函数
      • volatile
      • SIGCHLD信号

捕捉信号

信号是什么时候被处理的?

内核态:允许进程访问操作系统的代码和数据!
用户态:只能访问(进程)自己的代码和数据!

当进程从内核态返回到用户态的时候,进行信号的检测和处理!

返回之前,还处于内核态,而且这时候更重要的事已经做完了!

CPU在跑代码时,不仅仅跑用户自己写的代码,还跑库函数的代码和操作系统的代码!

调用系统调用,除了调用相应的函数,还需要切换身份:用户身份变成内核身份(或者反过来)。

内核态-用户态

image-20250417145521767

不允许以内核态的身份来访问用户代码,

防止用户用这种方式用操作系统的权限做非法操作。

身份切换就是将ECS寄存器的低两位比特位改变0->3/3->0

image-20250423233150572

内核如何实现信号的捕捉

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。

由于信号处理函数的代码是在用户空间的,处理过程比较复杂,

举例如下:

用户程序注册了SIGQUIT信号的处理函数sighandler

当前正在执行main函数,这时发生中断或异常切换到内核态。

在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。

内核决定返回用户态后不是恢复main函数的上下文继续执行,

而是执行sighandler函数,sighandlermain函数使用不同的堆栈空间,

它们之间不存在调用和被调用的关系,是 两个独立的控制流程。

sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。

如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。

sigaction

image-20250424230021961

#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact); 
sigaction函数可以读取和修改与指定信号相关联的处理动作。
调用成功则返回0,出错则返回-1。
signo是指定信号的编号。
若act指针非空,则根据act修改该信号的处理动作。(输入型参数)
若oact指针非空,则通过oact传出该信号原来的处理动作。(输出型参数)

actoact指向sigaction结构体:

sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,

赋值为常数SIG_DFL表示执行系统默认动作,

赋值为一个函数指针表示用自定义函数捕捉信号,

或者说向内核注册了一个信号处理函数,

该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,

这样就可以用同一个函数处理多种信号。

显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。

image-20250424230120772

代码应用

使用sa_handler

#include<iostream>
#include<unistd.h>
#include<cstdlib>
#include<signal.h>
#include<cstring>using namespace std;void handler(int signo)
{cout<<"catch a signo: "<<signo<<endl;
}int main()
{struct sigaction act,oact;memset(&act,0,sizeof(act));memset(&oact,0,sizeof(oact));act.sa_handler=handler;sigaction(2,&act,&oact);while(1){cout<<"I am a process: "<<getpid()<<endl;sleep(1);}return 0;
}

image-20250424231454700


问题1:什么时候将捕捉的信号的pending位图由1->0的呢?

思路:在handler信号捕捉执行完之前打印pending位图。

如果pending位图是 0000 0000 0000 0000 0000 0000 0000 0000

那么说明位图的改变是在捕捉信号之前

否则位图的改变就是在捕捉信号之后

#include<iostream>
#include<unistd.h>
#include<cstdlib>
#include<signal.h>
#include<cstring>using namespace std;//问题1:pending位图,什么时候由1->0
//思路:在handler信号捕捉执行完之前打印pending位图。
//如果pending位图是 0000 0000 0000 0000 0000 0000 0000 0000
//那么说明位图的改变是在捕捉信号之前
//否则位图的改变就是在捕捉信号之后
void PrintPending()
{sigset_t set;sigemptyset(&set);sigpending(&set);for(int i=31;i>=1;i--){if(sigismember(&set,i)){cout<<"1";}else{cout<<"0";}}cout<<endl;
}void handler(int signo)
{PrintPending();cout<<"catch a signo: "<<signo<<endl;
}int main()
{struct sigaction act,oact;memset(&act,0,sizeof(act));memset(&oact,0,sizeof(oact));act.sa_handler=handler;sigaction(2,&act,&oact);while(1){cout<<"I am a process: "<<getpid()<<endl;sleep(1);}return 0;
}

image-20250424232724787

结论:在处理信号之前,pending位图就已经由1->0了。


问题二:信号被处理的时候,对应的信号也会被添加到block表中,防止信号被嵌套捕捉!

思路:

在信号捕捉(处理)方法里面写一个死循环,

那么就一直是在捕捉信号的过程,

再来一个2号信号,因为2号信号的block位图是1,所以只能阻塞(不能递达),

所以2号信号的pending0->1

image-20250424234244352

结论:

当某个信号的处理函数被调用时,

内核自动将当前信号加入进程的信号屏蔽字,

防止该信号被嵌套捕捉,

当信号处理函数返回时自动恢复原来的信号屏蔽字,

这样就保证了在处理某个信号时,

如果这种信号再次产生,

那么它会被阻塞到当前处理结束为止。

在阻塞期间,该信号发送了n次,但是最终也只会被记录一次。


使用sa_mask

如果在调用信号处理函数时,

除了当前信号被自动屏蔽之外,

还希望自动屏蔽另外一些信号,

则用sa_mask字段说明这些需要额外屏蔽的信号,

当信号处理函数返回时自动恢复原来的信号屏蔽字。

对比实验:

没有手动屏蔽任何信号

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <signal.h>
#include <cstring>using namespace std;void PrintPending()
{sigset_t set;sigemptyset(&set);sigpending(&set);for (int i = 31; i >= 1; i--){if (sigismember(&set, i)){cout << "1";}else{cout << "0";}}cout << endl;
}void handler(int signo)
{cout << "catch a signo: " << signo << endl;while (1){PrintPending();sleep(1);}//该循环就是在处理信号
}int main()
{struct sigaction act, oact;memset(&act, 0, sizeof(act));memset(&oact, 0, sizeof(oact));// sigemptyset(&act.sa_mask);// sigaddset(&act.sa_mask,1);// sigaddset(&act.sa_mask,3);// sigaddset(&act.sa_mask,4);act.sa_handler = handler;//SIG_DFL SIG_IGNsigaction(2, &act, &oact);while (1){cout << "I am a process: " << getpid() << endl;sleep(1);}return 0;
}

image-20250424235334636

手动屏蔽1,3,4号信号

sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,1);
sigaddset(&act.sa_mask,3);
sigaddset(&act.sa_mask,4);

image-20250424235546122

sa_flags字段包含一些选项,本章的代码都把sa_flags设为0,

sa_sigaction是实时信号的处理函数。

可重入函数

可重入函数

main函数调用insert函数向一个链表head中插入节点node1,

插入操作分为两步,刚做完第一步的 时候,因为硬件中断使进程切换到内核,

再次回用户态之前检查到有信号待处理,于是切换到sighandler函数,

sighandler也调用insert函数向同一个链表head中插入节点node2,

插入操作的两步都做完之后从sighandler返回内核态,

再次回到用户态就从main函数调用的insert函数中继续往下执行,

先前做第一步之后被打断,现在继续做完第二步。

结果是,main函数和sighandler先后向链表中插入两个节点,

而最后只有一个节点真正插入链表中了。

image-20250417145534585

像上例这样,insert函数被不同的控制流程调用,、

有可能在第一次调用还没返回时就再次进入该函数,这称为:重入,

insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为:不可重入函数,

反之,如果一个函数只访问自己的局部变量或参数,则称为可重入(Reentrant) 函数。

如果一个函数符合以下条件之一则是不可重入的:

调用了mallocfree,因为malloc也是用全局链表来管理堆的。

调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

volatile

保存内存可见性!!

该关键字在C当中我们已经有所涉猎,今天我们站在信号的角度重新理解一下

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <signal.h>
#include <cstring>using namespace std;
int flag=0;void handler(int signo)
{cout<<"catch a sig: "<<signo<<endl;flag=1;
}int main()
{signal(2,handler);while(!flag);cout<<"process normal quit!"<<endl;return 0;
}

image-20250425200844784

标准情况下,键入 CTRL-C ,2号信号被捕捉,执行自定义动作,

修改 flag=1while 条件不满足,退出循环,进程退出。

因为main函数和handler函数是两个不同的执行流,

flag的值在main函数中没有修改flagmain函数只对flag进行了逻辑判断(检测),

算数运算/逻辑运算都是在cpu内部进行的,

所以在优化条件下,flag 变量可能直接被优化到寄存器中。

优化

image-20250425201818311

mysignal:mysignal.ccg++ -o $@ $^ -O3 -std=c++11
.PHONY:clean
clean:rm -rf mysignal

image-20250425201949053

优化情况下,键入 CTRL-C ,2号信号被捕捉,执行自定义动作,

修改 flag=1 ,但是 while 条件依旧满足,进程继续运行!

但是很明显flag肯定已经被修改了,但是为何循环依旧执行?

很明显, while 循环检查的flag,

并不是内存中最新的flag,这就存在了数据二异性的问题。

while 检测的flag其实已经因为优化,被放在了CPU寄存器当中。

image-20250425203237625

如何解决呢?很明显需要 volatile

volatile int flag=0;

image-20250425203332549

volatile 作用:防止过度优化,保持内存的可见性,

告知编译器,被该关键字修饰的变量,不允许被优化,

对该变量的任何操作,都必须在真实的内存中进行操作。

SIGCHLD信号

之前讲过用waitwaitpid函数清理僵尸进程,

父进程可以阻塞等待子进程结束,

也可以非阻塞地查询是否有子进程结束等待清理(也就是轮询的方式)。

采用第一种方式,父进程阻塞了就不能处理自己的工作了;

采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一 下,程序实现复杂。

image-20250425203751546

其实,子进程在终止时会给父进程发SIGCHLD信号,

该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,

这样父进程只需专心处理自己的工作,不必关心子进程了,

子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。

验证:在子进程退出时,父进程会收到17号信号

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <signal.h>
#include <cstring>using namespace std;void handler(int signo)
{cout<<"I am "<<getpid()<<" catch a signal: "<<signo<<endl;
}int main()
{signal(17,handler);pid_t id=fork();if(id==0){while(1){cout<<"I am child , pid: "<<getpid()<<" , ppid: "<<getppid()<<endl;sleep(1);break;}cout<<"child quit!!!!!!!!!!"<<endl;exit(1);}while(1){cout<<"I am father , pid: "<<getpid()<<endl;sleep(1);}return 0;
}

image-20250425204758043

父进程在进行等待的时候,我们可以采用基于信号的方式异步等待。

等待的好处:

1.获取子进程的退出状态,释放子进程的僵尸。

虽然我们不知道父子进程谁先运行,但是我们知道父进程一定最后退出!

还是得调用 wait/waitpid 这样的接口!

并且要保证父进程是一直在运行的。(防止子进程变孤儿)

请编写一个程序完成以下功能:父进程fork出子进程,

子进程调用exit终止,父进程自定义SIGCHLD信号的处理函数,

在其中调用wait/waitpid等待子进程退出。

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <signal.h>
#include <cstring>
#include<sys/wait.h>
#include<sys/types.h>using namespace std;void handler(int signo)
{sleep(3);pid_t rid=waitpid(-1,nullptr,0);cout<<"I am "<<getpid()<<" catch a signal: "<<signo<<" ,child quit: "<<rid<<endl;
}int main()
{signal(17,handler);pid_t id=fork();if(id==0){while(1){cout<<"I am child , pid: "<<getpid()<<" , ppid: "<<getppid()<<endl;sleep(5);break;}cout<<"child quit!!!!!!!!!!"<<endl;exit(1);}while(1){cout<<"I am father , pid: "<<getpid()<<endl;sleep(1);}return 0;
}

image-20250425205944924

如果我们有5个子进程,

同时退出?

那么在捕捉方法里设置一个循环,不断等待子进程退出即可。

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <signal.h>
#include <cstring>
#include <sys/wait.h>
#include <sys/types.h>using namespace std;void handler(int signo)
{sleep(3);pid_t rid;while ((rid = waitpid(-1, nullptr, WNOHANG)) > 0){cout << "I am " << getpid() << " catch a signal: " << signo << " ,child quit: " << rid << endl;}
}int main()
{signal(17, handler);for (int i = 0; i < 5; i++){pid_t id = fork();if (id == 0){while (1){cout << "I am child , pid: " << getpid() << " , ppid: " << getppid() << endl;sleep(5);break;}cout << "child quit!!!!!!!!!!" << endl;exit(1);}}while (1){cout << "I am father , pid: " << getpid() << endl;sleep(1);}return 0;
}

image-20250425211328656

退出一半?

在等待的时候,使用非阻塞等待,就不会一直卡在没有退出的子进程上了。

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <signal.h>
#include <cstring>
#include<ctime>
#include <sys/wait.h>
#include <sys/types.h>using namespace std;void handler(int signo)
{sleep(3);pid_t rid;while ((rid = waitpid(-1, nullptr, WNOHANG)) > 0){cout << "I am " << getpid() << " catch a signal: " << signo << " ,child quit: " << rid << endl;}
}int main()
{srand(time(nullptr));signal(17, handler);for (int i = 0; i < 5; i++){pid_t id = fork();if (id == 0){while (1){cout << "I am child , pid: " << getpid() << " , ppid: " << getppid() << endl;sleep(5);break;}cout << "child quit!!!!!!!!!!" << endl;exit(1);}sleep(rand()%5+3);}while (1){cout << "I am father , pid: " << getpid() << endl;sleep(1);}return 0;
}

image-20250425212208515

事实上,由于UNIX 的历史原因,要想不产生僵尸进程还有另外一种办法:

父进程调 用sigaction将SIGCHLD的处理动作置为SIG_IGN,

这样fork出来的子进程在终止时会自动清理掉,

不会产生僵尸进程,也不会通知父进程。

系统默认的忽略动作和用户用sigaction函数自定义的忽略

通常是没有区别的,但这是一个特例。

此方法对于Linux可用,但不保证在其它UNIX系统上都可用。

请编写程序验证这样做不会产生僵尸进程。

测试代码

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <signal.h>
#include <cstring>
#include <ctime>
#include <sys/wait.h>
#include <sys/types.h>using namespace std;int main()
{signal(17, SIG_IGN);// SIG_DFL -> IGNfor (int i = 0; i < 5; i++){pid_t id = fork();if (id == 0){while (1){cout << "I am child , pid: " << getpid() << " , ppid: " << getppid() << endl;sleep(3);break;}cout << "child quit!!!!!!!!!!" << endl;exit(1);}sleep(1);}while (1){cout << "I am father , pid: " << getpid() << endl;sleep(1);}return 0;
}

image-20250425213317158

http://www.dtcms.com/wzjs/297759.html

相关文章:

  • ui设计师面试必问问题温州seo网站建设
  • 电子商务b2c网站建设网络推广公司电话
  • 做外贸如何建立网站大数据平台
  • 化工类网站模板阿里云云服务平台
  • 云相册网站怎么做的seo资讯推推蛙
  • 招聘网站建设维护百度自助建站官网
  • 网站怎么做会被收录西安百度爱采购推广
  • wordpress线上聊天插件爱站网seo
  • 做网站的语言有哪些深圳网站建设公司
  • 找人做网站网站搭建的流程
  • 响应式做的好的网站google官网入口注册
  • 做字幕网站关键词排名优化软件价格
  • 聊城做网站的公司案例广州网站制作公司
  • cssoseo点石论坛
  • 玉石电商网站建设方案推广网站制作
  • 网站维护和推广足球世界排名国家最新
  • 网站建设的三要素百度关键词优化企业
  • 建设项目一次公示网站信阳网站推广公司
  • 网站如何建设二级域名代理免费网页制作成品
  • 苏州新区网站制作建设推建站系统哪个好
  • 网站建设价格需要多少钱制作网站教程
  • 如何做微信网站防封优化软件
  • 网站后台组成手机网页制作app
  • 亚马逊雨林十大恐怖生物seo服务 文库
  • 怎么提交网站google浏览器官网入口
  • 网站建设要经历哪些步骤?数据分析软件工具有哪些
  • 苏州网站推广排名网站站点
  • 有没有做相册的网站磁力王
  • 做娱乐性手机网站seo网站优化平台
  • 帝国cms模板网2023网站seo