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

模块建站工具5元域名免备案

模块建站工具,5元域名免备案,查权重工具,免费招代理的平台有哪些一、信号的概念 信号是linux系统提供的一种#xff0c;向指定进程发送特定事件的方式。收到信号的进程#xff0c;要对信号做识别和处理。信号的产生是异步的#xff0c;进程在工作过程中随时可能收到信号。 信号的种类分为以下这么多种#xff08;用指令kill -l查看向指定进程发送特定事件的方式。收到信号的进程要对信号做识别和处理。信号的产生是异步的进程在工作过程中随时可能收到信号。 信号的种类分为以下这么多种用指令kill -l查看 其中1-31号信号是普通信号32-64号信号是实时信号暂且不关注。 二、信号的产生 1、通过kill命令向指定的进程发送指定信号 通过代码验证利用 man 2 signal查看接受信号的系统调用 其中handler是一个函数指针返回值为void,参数为intint表示的是接收的是几号信号该系统调用是将信号进行自定义处理也就是捕捉信号捕捉之后的信号一般都不会进行自己的默认处理有例外。下面用代码验证kill命令向进程发送的信号 #includeiostream #includesignal.h using namespace std;void handler(int signal) {coutreceive signal:signalendl;exit(1);//异常退出}int main() {sighandler_t nsignal(4,handler);while(1){coutI am working! pid:getpid()endl;sleep(1);}return 0; } 可以看到接受4号信号后实现了自定义处理。 2、键盘产生信号 Ctrl c 产生2号信号Ctrl \ 产生3号信号Ctrl z 产生20号信号 只需修改sighandler_t nsignal(2,handler);中的2为3 、20即可自定义处理不同的信号。 3、系统调用产生信号 ①kill kill(pid,signal) pid表示给指定的进程发送。signal表示发送几号信号。 #includeiostream #includesignal.h using namespace std;void handler(int signal) {coutreceive signal:signalendl;//exit(1);//表示异常退出}int main() {sighandler_t nsignal(2,handler);int cnt10;while(cnt--){coutI am working! pid:getpid()endl;sleep(1);}kill(getpid(),2);//等价于 raise(2)//abort();//发送6号信号return 0; } ②raise raise(int signal) 表示向本进程发送signal号信号代码如上 ③abort abort发送6号信号。 注意 ①为了防止有程序对所有信号都自定义捕捉而恶意不退出有9号信号不允许自定义捕捉。 ②如何理解信号的发送 真正发送信号的是OS 信号在进程的task_struct结构体中其实是一个32位int位图的形式这就是为什么普通信号有31个且从1开始而不是从0开始。发送信号的过程其实是OS在修改指定进程pcb中的信号的指定位图。因为只有OS有这个权限去修改进程的内核结构对象所以只有OS能发送信号。 4、软件条件 ①在管道学习中已知当管道读端关闭之后写端如果还在写则会发送13号信号SIGPIPE让写端进程退出。这是一种软件条件。写端写入操作的条件不满足 ②闹钟设定几秒后收到一个14号SIGALRM信号 #includeiostream #includesignal.h #includecstdlib #includeunistd.h using namespace std;void handler(int signal) {coutreceive signal:signalendl;exit(1);//表示异常退出}int main() {sighandler_t nsignal(14,handler);unsigned int retalarm(5);while(1){coutI am working! pid:getpid()endl;sleep(1);}/*int cnt5;while(cnt--){coutI am working! pid:getpid()endl;sleep(1);}*///kill(getpid(),9);//raise(2);//abort();//发送6号信号return 0; } OS在对一些超时的程序会设定一个闹钟超出这个时间之后就会向指定进程发送14信号终止。 时间通过时间戳整数来比较。 操作系统要对闹钟做管理先描述再组织 闹钟结构体struct alarm { time_t expired;//未来的超时时间secondsNow(); pid_t pid; } 对于闹钟结构体采用最大堆最小堆的组织方式高效组织。这样以未来的超时时间作为基准进行堆排序只要最近的超时的闹钟未超时后面的闹钟就都不会超时。 闹钟的返回值表示的是上一个闹钟的剩余时间。 alarm(0):表示的含义是取消闹钟返回上一个闹钟的剩余时间。 5、异常 ①除0异常 当系统有除0异常时OS会发送8号信号SIGFPE使进程退出当运行以下代码时系统会一直打印 #includeiostream #includesignal.h #includecstdlib #includeunistd.h using namespace std;void handler(int signal) {coutreceive signal:signalendl;//exit(1);//表示异常退出} int main() {sighandler_t nsignal(8,handler);int a4;int ba/0;return 0; } ②野指针异常 当系统有野指针异常时OS会发送11号信号SIGSEGV使进程退出当运行以下代码时系统会一直打印 #includeiostream #includesignal.h #includecstdlib #includeunistd.h using namespace std;void handler(int signal) {coutreceive signal:signalendl;//exit(1);//表示异常退出}int main() {sighandler_t nsignal(11,handler);int * pnullptr;*p100;return 0; } ③产生异常的原理 a、对于除0操作 计算都要通过CPU计算CPU内部有一个寄存器eflag,内部有一个溢出标记位当有除0操作时CPU是将其转换为加法而除0操作会一直加该硬件就会出错然后将错误传递给OSOS接受到这种错误之后就会处理处理方式就是向目标进程发送信号。 当我们自定义处理信号之后为什么会一直打印寄存器只有一套但是寄存器里面的数据是属于每一个进程的当我们自定义信号处理后进程不退出后随着进程的切换调度寄存器先是被其他进程使用而当本错误进程又切换到运行队列中时寄存器会恢复上下文此时发现又出错又交给OS然后就会再次发送一次信号所以就又会打印。 所以推荐接受信号后退出进程否则就会一直卡在错误代码这里是除0的代码。退出进程后寄存器的错误数据就会清空进程自己的异常自己承受不用让OS承担。 b、对于野指针操作 在CPU内部有一个硬件MMUMMU将页表的虚拟地址转换为物理地址CR3寄存器存储的是页表的地址MMUCR3得到物理地址当给定指针p指向空的时候又对其进行赋值MMUCR3转换的时候接收到的是一个错误的虚拟地址会将错误的虚拟地址放进CR2;此时OS发现了CR2内有错误的地址就向进程发信号了。 三、信号的处理基础 1、默认处理与忽略 使用 man signal 7指令查看所有信号的默认处理方式 默认处理方式为终止进程的有Core Term两种这两种的区别 Term异常终止 Core异常终止但会形成一个debug文件。 对于云服务器来说默认形成debug文件的功能是关闭的 ulimit -a查看被关闭的功能ulimit -c 10240打开core file功能当系统错误时会形成debug文件core文件这里面存储的是进程退出前运行的信息是进程退出的时候的镜像数据。 centos系统的core dump形成的文件文件名是以进程id为结尾的这可能会导致无数的core文件因为每次运行的进程id可能不同而ubuntu系统对该bug做了修正每次都会覆盖。 为什么云服务器要默认关闭core file核心转储功能当云服务器挂了之后有一些监控程序会让该服务器自动重启而如果一个程序每次重启就报错就会形成大量core 文件会将磁盘占满。 core文件如何使用当程序出错退出时在makefile 文件中如果带有一个 -g选项那么后续gdb的时候输入core-file core 就可以定位哪一行出错。core是协助我们进行debug的文件进行事后调试。 代码验证core dump标志位 core dump标志位为1说明形成了core文件。 代码 #includeiostream #includesignal.h #includecstdlib #include sys/types.h #includesys/wait.h #includeunistd.h using namespace std;void handler(int signal) {coutreceive signal:signalendl;//exit(1);//表示异常退出} int Sum(int start,int end) {int sum0;for(int istart;iend;i){sum /0;sumi;}return sum; }int main() {pid_t idfork();if(id0){//child processint totalSum(0,100);exit(0);}//father processint status;pid_t ridwaitpid(id,status,0);if(ridid){//等待成功printf(exit code: %d,exit sig: %d,core dump:%d\n,(status8)0xff,(status0x7f),(status7)0x1);}return 0;}/*int main() {sighandler_t nsignal(11,handler);int * pnullptr;*p100;return 0; }*//*int main() {sighandler_t nsignal(8,handler);int a4;int ba/0;return 0; }*//*int main() {sighandler_t nsignal(14,handler);unsigned int retalarm(5);while(1){coutI am working! pid:getpid()endl;sleep(1);}/*int cnt5;while(cnt--){coutI am working! pid:getpid()endl;sleep(1);}//kill(getpid(),9);//raise(2);//abort();//发送6号信号return 0; }*/ 结果 2、自定义 利用signal系统调用实现自定义处理具体代码在前面。 四、信号的保存 1、一些新概念  实际执行信号的处理动作称为信号递达。 信号从产生到递达之间的状态叫做信号未决。临时在PCB中保存而未被处理 有的信号会被进程阻塞。阻塞一个信号那么对应的信号一旦产生将永不递达一直未决直到主动接触阻塞。阻塞描述的是信号要不要被递达的特点。 注①一个信号是否处于阻塞状态和它是否未决并没有关系。②阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。 2、信号保存的深入理解 1pending表未决信号集 一张位图32位整数位图但只用了31位其中比特位的位置代表信号编号比特位的内容代表信号是否收到0 -未收到1-收到。 2handler表 是一个函数指针数组。普通信号是1-31号数字编号这些编号对应的handler数组的下标OS为每一个进程都维护了这样一个handler表例如收到2号信号就那种2号信号去索引信号处理方法。 在系统调用signal2handler中我们能够实现对2号信号的自定义捕捉的原理其实是拿着2号信号的编号将自己写的自定义函数指针填进2号信号对应的handler数组中的处理方法而原默认处理方法就被覆盖了。 3block表 一张位图和pending类型完全一样其中比特位的位置代表信号编号比特位的内容代表信号是否阻塞0 -未阻塞1-阻塞 两张位图函数指针数组实现了让进程识别信号。 五、信号集操作 sigset_t linux系给用户提供的一个用户级的数据类型但禁止用户手动设置该位图的值而是提供了一系列信号机操作函数。 1、sigprocmask:对block位图进行修改的系统调用。 其中how: 其中old_set表示返回修改之前的位图。 返回值0成功返回值-1失败 2、sigpending:获取当前进程的pending位图 只用来获取改变是通过产生信号的那5种方式。其中set是输出型参数。 3、代码验证 屏蔽2号信号向2号信号不断发送再获取pending信号集就能看到由0变1的变化。再解除对2号信号的屏蔽看到由1到0的变化。 #includeiostream #includesignal.h #includecstdlib #include sys/types.h #includesys/wait.h #includeunistd.h using namespace std;void handler(int signal) {coutreceive signal:signalendl;//exit(1);//表示异常退出//验证pending位图清0的操作是在handler处理前还是处理后cout-------------------endl;sigset_t pending_bit;sigpending(pending_bit);PrintPending(pending_bit);cout-------------------endl;} void PrintPending(sigset_t pending_bit) {for(int i31;i1;i--){if(sigismember(pending_bit,i)){cout1;}else cout0;}coutendl; }int main() {sighandler_t nsignal(2,handler);sigset_t new_bit;sigset_t old_bit;sigemptyset(new_bit);sigemptyset(old_bit);sigaddset(new_bit,2);//屏蔽2号信号sigprocmask(SIG_BLOCK,new_bit,old_bit);int cnt20;while(1){sigset_t pending_bit;sigpending(pending_bit);coutpid:getpid()pending:;PrintPending(pending_bit);cnt--;if(cnt0){//解除对2号信号的屏蔽cout解除对2号信号的屏蔽: ;sigprocmask(SIG_SETMASK,old_bit,new_bit);}sleep(1);}return 0; } 解除屏蔽一般会立即处理当前被解除屏蔽且被pending的信号。 pending位图对应的信号在被递达之前清0。 六、信号的处理底层理解 三种处理方式 signal(2,SIG_IGN);//忽略处理signal(2,SIG_DFL);//默认处理signal(2,handler);//自定义捕捉 1、信号处理的时机和信号捕捉 信号可能不会被立即处理而是在合适的时候处理合适的时候指的是进程从内核态返回到用户态的时候。 当OS从内核态准备切换到用户态之前先处理当前进程中可以递达的信号查看信号的handler指针数组如果是SIG_DFL大部分就直接退出进程了如果是SIG_IGN那么就直接忽略而如果是自定义捕捉就要回到用户态执行信号处理函数而不是回到主控制流程。 OS不能在内核态直接执行信号处理函数因为该函数内可能会对OS内核做修改这会影响到OS的安全。所以该信号处理函数只能在用户态下执行。由于信号处理函数和main函数是两个执行流不能切换所以OS要继续回到内核态然后再返回到用户态往下执行。 信号捕捉的过程要经历4次状态的切换。 2、内核态和用户态 1深入理解地址空间——内核空间 对于每个进程其【3,4】GB的地址空间是内核空间其实就是OS除了用户级页表之外还有一个内核级页表因为OS在电脑启动时就被加载到内存中了是第一个软件内核级页表指向的就是内存中的OS。这就是为什么无论进程如何切换我们都能找到OS。 我们进程访问OS就是在本身的虚拟地址空间中访问的和访问库函数在共享区和本身写的函数在正文代码区一样区别是因为OS要保证自己的安全用户在地址空间内访问OS时只能通过系统调用。 而内核级页表只需维护一份就够了也就是说所有的进程的内核空间都是一样的内核级页表在系统中只存在一份。 2键盘输入数据的过程 当键盘上有按键被按则会通过硬件中断的方式触发CPU中断每种设备包括磁盘、网卡等在事前已经被分配了中断号当CPU触发中断时其实是该设备对应的中断号被写入CPU寄存器中OS在启动时已经构建了一张函数指针表CPU则会根据中断号索引到函数指针表中的函数键盘的中断号对应的函数就是将键盘中的数据读到内存中。 这套机制类似信号其实是先有硬件中断然后信号是根据这种机制用软件模仿出来的。 3如何理解OS正常运行 OS是如何运行的 操作系统的本质是一个死循环时钟中断不断调度系统任务 如何理解系统调用 系统提供了一张函数指针数组只要找到特定数组下标的方法就能执行系统调用了。具体的如何实现系统调用如下图 4内核态和用户态 也就是说CPU保证了什么时候可以访问内核空间的地址。 注意OS由用户态进入内核态并不一定是进行了系统调用系统在调度时当某个进程的时间片到时会从调度队列中剥离下来从而从用户态进入内核态该过程是可能发生在代码的任意位置的所以在代码执行的任一位置都可能发生而该进程再次被调度时则是从内核态回到用户态的过程该过程会检测信号。 3、信号在处理时默认屏蔽本信号 另一种处理信号的方式sigaction 其中与该函数同名的结构体结构 代码如下 #includeiostream #includesignal.h #includecstdlib #include sys/types.h #includesys/wait.h #includeunistd.h using namespace std;//sigaction的使用 signal mask的用处 //当前如果正在处理2号信号默认2号信号会被屏蔽 //对2号信号处理完成的时候会自动解除对2号信号的屏蔽 避免递归处理void PrintPending(sigset_t pending_bit) {for(int i31;i1;i--){if(sigismember(pending_bit,i)){cout1;}else cout0;}coutendl; } void handler(int signal) {coutreceive signal:signalendl;//sleep(100);while(1){sigset_t pending;sigpending(pending);PrintPending(pending);sleep(1);}exit(1); }int main() {struct sigaction act,oact;sigemptyset(act.sa_mask);//设置处理2号信号时对2号屏蔽的同时对其他信号也进行屏蔽sigaddset(act.sa_mask,3);//有很多信号无法被屏蔽 act.sa_handlerhandler;sigaction(2,act,oact);while(1){coutI am a process,pid:getpid()endl;sleep(1);}return 0; } 代码结果 从结果中可以看出在sa_mask中加入3时处理2号信号的过程中3号信号也被屏蔽了。 当前如果正在处理2号信号默认2号信号会被屏蔽对2号信号处理完成的时候会自动解除对2号信号的屏蔽这是为了避免递归处理。 七、可重入函数与volatile 1、可重入函数 在执行头插时有两行代码在执行第一行代码时发生信号捕捉OS从用户态进入内核态的方式不止系统调用一种而信号捕捉内部也有头插这样最后得到的结果中信号捕捉内部头插的节点则会丢失这种函数称为不可重入函数。 STL的绝大多数函数都是不可重入函数。函数是否可重入是函数的特点对于不可重入函数在多线程时就需要注意可重入函数一般只会使用内部的局部变量而不会使用全局变量。 2、volatile 代码 #includeiostream #includesignal.h #includecstdlib #include sys/types.h #includesys/wait.h #includeunistd.h using namespace std;int gflag0; //volatile int gflag0;void changedata(int signo) {coutreceive signo:signoendl;gflag1;}int main() {signal(2,changedata);while(!gflag);//while不要其他代码coutprocess exit normalendl;return 0; } 正常编译运行后结果 但是编译器知道在main函数执行流中没有对 gflag做修改 CPU有两种运算算数运算和逻辑运算 对于while(!gflag)语句其实是CPU不断对gflag做检测而gflag是在内存中的数据那么CPU进行判断的话就需要不断的将gflag加载到CPU中 但编译器是可以优化编译的 -O查看所有优化级别 将优化级别改为-O1就会出现下列问题即发送2号信号修改gflag之后进程仍然无法退出。 这是因为优化之后编译器发现main函数执行流没有对gflag做修改那么直接将gflag的值放到寄存器中修改gflag的值只是修改内存中的gflag而对寄存器中的值没有影响。这是寄存器隐藏了内存中的真实值 这是编译器的优化导致的问题。如何保持内存的可见性利用volatile关键字。改完之后运行结果 八、SIGCHILD信号 子进程退出时会给父进程发送sigchild信号。sigchild的默认处理是ignore。 验证sigchild的存在 #includeiostream #includesignal.h #includecstdlib #include sys/types.h #includesys/wait.h #includeunistd.h using namespace std;//子进程退出时会给父进程发sigchild信号void DoOtherthing() {coutI am doing other thingendl; } void notice(int signo) {coutget signal: signo pid: getpid()endl;pid_t ridwaitpid(-1,nullptr,0);//waitpid的第一个参数为-1表示等待任意一个子进程if(rid0){coutwait child success,pid:ridendl;} }int main() {signal(SIGCHLD,notice);pid_t idfork();if(id0){//child processcoutI am child process,pid:getpid()endl;sleep(5);exit(1);}//父进程while(1){DoOtherthing();sleep(1);}return 0; } 代码结果 但以上在信号捕捉中进程等待的问题是 如果有多个子进程这些子进程同时退出那么以上代码只会等待一次只会回收一个子进程所以要将代码改成while循环的形式不断回收 如果有多个子进程有些进程退出有些进程永远不退出。此时waitpid就会阻塞但由于现在是在信号捕捉的逻辑中那么就会一直停在这而不会去执行main函数中的主逻辑。此时应该选择非阻塞方式等待。代码改成下面的形式 #include iostream #include signal.h #include cstdlib #include sys/types.h #include sys/wait.h #include unistd.h using namespace std;// 子进程退出时会给父进程发sigchild信号void DoOtherthing() {cout I am doing other thing endl; } void notice(int signo) {cout get signal: signo pid: getpid() endl;while (1){pid_t rid waitpid(-1, nullptr, WNOHANG); // waitpid的第一个参数为-1表示等待任意一个子进程if (rid 0){cout wait child success,pid: rid endl;}else if(rid0){cout wait child success done endl;break; // rid0说明等待失败父进程已经没有子进程等待了}else{cout wait child success done endl;break; // 非阻塞等待方式}} }int main() {signal(SIGCHLD, notice);for (int i 0; i 10; i){pid_t id fork();if (id 0){// child processcout I am child process,pid: getpid() endl;sleep(1);exit(1);}}// 父进程while (1){DoOtherthing();sleep(1);}return 0; } 由于UNIX 的历史原因,要想不产生僵尸进程还有另外一种办法:父进程调用sigaction将SIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不会产生僵尸进程,也不会通知父进程。 等待的目的①获取子进程的退出信息②使子进程不再是僵尸进程 如果不关心子进程的退出信息则可以直接设置对SIGCHILD信号的处理动作为SIG_IGN这是最简单的方式。系统默认的IGN和用户设置的SIG_IGN是不一样的
http://www.pierceye.com/news/840948/

相关文章:

  • 外贸 静态网站 怎么做微信红包建设网站
  • 南京网站优化哪家好卖文具的网站建设
  • 黔西南州住房和城乡建设局网站wordpress导航浮动
  • 建设建材网站的目的免费网站建设的
  • 沈阳模板建站公司推荐wordpress 增加导航
  • 一般网站系统并发量建立网站的技术
  • 乐清网站推广公司佛山行业网站设计公司
  • 如何将优酷视频放到自己的网站傻瓜式建个人网站
  • 网站搭建与生成技术教材做网站用什么免费字体
  • flash 制作网站福田蒙派克图片
  • 使用cdn的网站赤水市建设局官方网站
  • 免费做个人网站产品推广方案策划书
  • 水利工程建设监理网站做餐厅logo什么网站素材多
  • 影楼行业网站做网站怎么宣传
  • 云做网站南宁网站建设索q.479185700
  • 运城做网站要多少钱天山网站
  • php网站端口企业网站建设及运营现状分析
  • 北京做网站价格如何知道一个网站是用什么做的
  • 海外域名网站国外做多媒体展览的网站
  • 阿里 网站建设方案书 模板wordpress影视模版
  • 广西网站建设工具网站推广方法主要有哪些
  • 源码购买网站郑州新一网站建设
  • 大学生网站设计论文范文某集团网站建设规划书
  • 温州哪里有网站建设深圳关键词首页排名
  • 做网站用什么面板好网站建设网站公司
  • 寻求网站建设技术网页升级访问永久你懂的
  • 做网站的公司有多少家无后台基础怎么建设网站
  • 在公司做网站是什么职位有链接的网站怎么做
  • 手机网站开发前台架构专业群建设网站
  • 做网站设计怎么样网站ui怎么做的