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

做网站大公司有哪些竞价推广返点开户

做网站大公司有哪些,竞价推广返点开户,网站广告下悬浮代码怎么做,营销相关网站目录 一、信号 1.1、生活中的信号 1.2、Linux中的信号 二、信号处理常见方式 三、信号的产生 3.1、简单理解信号的保存和发送 3.2、键盘产生信号 3.3、核心转储 3.4、系统调用接口产生信号 3.4.1、kill 3.4.2、raise 3.4.3、abort 3.5、软件条件产生信号 3.6、硬…

目录

一、信号

1.1、生活中的信号

1.2、Linux中的信号

二、信号处理常见方式

三、信号的产生

3.1、简单理解信号的保存和发送

3.2、键盘产生信号

3.3、核心转储

3.4、系统调用接口产生信号

3.4.1、kill

3.4.2、raise

3.4.3、abort 

3.5、软件条件产生信号

3.6、硬件异常产生信号

四、信号的保存

4.1、相关概念

4.2、信号保存——三个数据结构

4.3、信号集——sigset_t

4.3、信号操作函数


一、信号

1.1、生活中的信号

        在生活中,我们很容易能够想到常见的一些信号。比如,红绿灯,手机闹钟,上下课铃声,转向灯等等。我们人不仅能够识别它们,还能够知道不同的信号对应的下一步动作应该怎么做。比如,红灯停绿灯行;上课铃响就上课,下课铃响就下课;转向灯告诉别人我要转的方向。

        那么,我们是怎么识别并知道这些信号,并且知道信号发出后,接下来的动作应该是怎么样的呢?首先,这当然是规定过的,交通部门规定了红灯停绿灯行,而如果交通部门规定红灯行,绿灯停,那么我们也就只能照做。其次,我们从出生开始,大人们就不断告诉我们,要红灯停,绿灯行,久而久之,我们就记住了特定场景下的信号,以及后续我们需要做到动作,并且终身不忘。

        而且,即使我们没有在过马路,而是在吃饭,我们也能够知道应该如何处理红绿灯信号。

        再比如,如果,我的9点的闹钟响了,但是我没有立即起床,而是30分钟后再起床。这就说明,当信号产生的时候,我们不一定会立即执行后续动作,但是我记住了闹钟响过了,我该起床了,后面我再执行起床的动作。

        上面就是一些生活中的信号,以及我们对待信号的方式。下面我们就来看看Linux中的信号。

1.2、Linux中的信号

什么是Linux信号?

        Linux信号本质是一种通知机制,是用户或者操作系统,通过发送一定的信号,来通知进程某件事已经发生,你可以后续对其进行处理。

Linux信号的特点

       结合上面生活中的信号的特点,Linux信号有如下特点:

        a. 进程能够识别信号,即能够看到信号发送给了自己,并且知道后续的处理动作。

        b. 进程能够识别信号,已经由Linux设计者提前设计好了,并且规定了各种信号的后续处理动作。

        c. 信号的产生是随机的,信号产生时,进程可能正在做自己的事,所以,进程不一定会立即对信号进行处理。

        d. 因为进程不一定立即处理信号,所以进程一定能够将信号记住,后续再进行处理。

        e. 进程会在合适的时候处理信号(什么时候合适?后面会讲)。

        g. 一般而言,信号的产生相对于进程是异步的。

信号查看:我们可以通过 kill -l 命令查看Linux中有哪些信号

        其中,1~31号信号,是普通信号,34~64是实时信号。我们在平时使用中使用的最多的是普通信号。

二、信号处理常见方式

为了方便后面的讲解,我们首先了解一下信号处理的常见方式:

1、执行该信号的默认处理动作(进程自带的,Linux设计者写好的逻辑)。

2、用户自己提供一个信号处理函数,要求在进行信号处理时,使用用户自己定义的方式处理信号,这种方式称为捕捉(Catch)一个信号。

3、忽略该信号。

我们可以通过 man 7 signal 查看信号的默认处理动作:

value:信号编号  action:默认处理动作。 

三、信号的产生

3.1、简单理解信号的保存和发送

        为了下面我们讲解信号的产生,这里我们先简单地理解一下信号的保存。

        前面讲到过,信号产生后,进程不一定会立即处理信号,而是在之后的某个合适的时间对信号进行处理。所以在这中间的一段时间里,我们必须对信号进行保存。

        对于保存,进程只需要知道是否有这个信号,就可以对信号进行处理,所以我们可以使用位图来对信号进行保存。0就代表该比特位对应的信号没有产生,1就代表产生了该信号。这样,在之后进程只需要遍历一遍位图,就可以知道产生了哪些信号,然后进行处理。

        该位图在进程的PCB中,属于内核数据,只有操作系统能够修改,所以信号的发送就是os把信号对应的比特位的数字由0改成1。

        当然,关于信号的保存和发送我们会在下面的内容中,进行详细的讲解,这里只是有一个概念。

3.2、键盘产生信号

        在之前讲进程等待时,我们知道使用 Ctrl + c 的组合键能够终止一个进程,而且我们也讲了,其本质就是通过向进程发送2号信号,来终止进程的。下面我们就来证明一下:

        我们使用自定义函数,将信号进行捕捉:signal

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);RETURN VALUE
signal() returns the previous value of the signal handler, or SIG_ERR on error.  In the event of an error, errno is set to indicate the cause.
#include <iostream>
#include <signal.h>
#include <unistd.h>using namespace std;void catchsig(int signum)
{cout << "进程捕捉到了一个信号:" << signum << " "<< "pid"<< " " << getpid() << endl;
}int main()
{signal(SIGINT, catchsig);while (true){cout << "我是一个进程,我正在运行"<< " "<< "pid"<< " " << getpid() << endl;sleep(1);}return 0;
}

        通过对比上面两张图,我们发现Ctrl + c 和发送2号命令,都调用了我们自定义的处理动作。所以 Ctrl + c的本质就是发送2号命令。

3.3、核心转储

        上面的一张图,在信号的默认动作action中,term表示只终止进程,而还有的信号的动作是core,这个动作不仅会终止进程,还可以发生核心转储。这个与我们前面的进程等待的内容又有些关联了。

        上图是进程等待中,父进程获取子进程信息的status位图结构。低7位保存信号,之前有一个core dump标志,该比特位表示是否发生了核心转储。

        核心转储:当进程出现某种异常时,是否由os将当前进程在内存中的相关核心数据,转存到磁盘中。

        一般来说,云服务器上的核心转储功能是被关闭了的。而我们可以使用ulimit -a 命令查看core文件,ulimit -c 大小 命令打开云服务器的核心转储功能。

那么核心转储有什么作用呢?我们使用下面的代码来看看:

#include <iostream>
#include <signal.h>
#include <unistd.h>using namespace std;void catchsig(int signum)
{cout << "进程捕捉到了一个信号:" << signum << " "<< "pid"<< " " << getpid() << endl;
}int main()
{signal(SIGQUIT, catchsig);while (true){cout << "我是一个进程,我正在运行"<< " "<< "pid"<< " " << getpid() << endl;sleep(1);int a = 100;a /= 0;}return 0;
}

运行代码后生成了core文件,且以进程pid为后缀。

        我们知道程序出错了,而有了core文件后,我们不用去一行一行找出错位置,使用core文件在gdb下可以直接定位出错位置,如下:

3.4、系统调用接口产生信号

3.4.1、kill

NAMEkill - send signal to a processSYNOPSIS#include <sys/types.h>#include <signal.h>int kill(pid_t pid, int sig);Feature Test Macro Requirements for glibc (see feature_test_macros(7)):kill(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

        其实,我们常常使用的kill命令的底层所调用的就是该函数,下面我们可以模拟实现一下 kill命令的实现。

#include <iostream>
#include <cassert>
#include <sys/types.h>
#include <signal.h>using namespace std;static void Usage(const string &proc)
{cout << "\nUsage:" << proc << " pid signo\n"<< endl;
}//  ./mykill 2 pid
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}int signo = atoi(argv[1]);int sigpid = atoi(argv[2]);int n = kill(sigpid, signo);assert(n == 0);return 0;
}

3.4.2、raise

作用:进程让os给自己发送某一个信号。

NAMEraise - send a signal to the callerSYNOPSIS#include <signal.h>int raise(int sig);DESCRIPTIONThe raise() function sends a signal to the calling process or thread.  In a single-threaded program it is equivalent tokill(getpid(), sig);
#include <iostream>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>int main()
{cout << "我开始运行了" << endl;sleep(2);raise(2);return 0;
}

3.4.3、abort 

作用:让os给自己发一个6号信号。其实abort的底层也是去调用 raise(6)去实现的。

NAMEabort - cause abnormal process terminationSYNOPSIS#include <stdlib.h>void abort(void);
#include <iostream>
#include <cassert>
#include <unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <signal.h>using namespace std;int main()
{cout << "我开始运行了" << endl;sleep(2);abort();return 0;
}

        所以,总的来说,系统调用接口产生信号的具体过程就是: 用户调用系统接口——os执行对应的代码——os向目标进程写入信号——修改信号对应的比特位——进程后续对信号进行处理。

3.5、软件条件产生信号

~ 管道

        在前面的进程间通信的管道中,我们讨论了一个问题:对于正在通信的两个进程,当管道的读端不读了,而且读端关闭了,但是写端一直在写。这时,写就没有任何意义了。我们验证了,在这个时候,os会通过发送13号信号的方式终止进程。因为管道是一个通过文件在内存级的实现,所以管道是一个软件,所以这种情况就是软件条件不满足而产生信号的一种情况。

~ 设置闹钟 alarm

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

        调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号,该信号的默认处理动作是终止当前进程。这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。闹钟一旦触发了,将会自动移除。

我们可以使用该函数写一个能够测试自己的电脑CPU的计算能力的代码:

#include <iostream>
#include <cassert>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>using namespace std;int count = 0;void sigcath(int sig)
{cout << "final count: "<< " " << count << endl;
}int main()
{alarm(1);signal(SIGALRM, sigcath);while (true)count++;return 0;
}

我们也可以写一个代码来让os帮助我们每隔1秒就可以显示cout最新的计算结果

#include <iostream>
#include <cassert>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>using namespace std;uint64_t count = 0;void sigcath(int sig)
{cout << "final count: "<< " " << count << endl;alarm(1);
}int main()
{alarm(1);signal(SIGALRM, sigcath);while (true)count++;return 0;
}

3.6、硬件异常产生信号

~ 除0错误

我们来看一看下面的代码:

#include <iostream>
#include <cassert>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>using namespace std;void hander(int sig)
{cout << "我捕捉了一个信号:"<< " " << sig << endl;sleep(1);
}int main()
{signal(SIGFPE, hander);int a = 100;a /= 0;return 0;
}

运行结果如下:

        我们知道了如果代码中出现了除0错误,os会给进程发送8号信号,那么是怎么产生并发送的呢?

        首先,计算以及各种信息的处理是由CPU这个硬件进行的。CPU中有一个寄存器,叫做状态寄存器,它含有一个位图,该位图上有溢出标记位。 CPU在进行计算时,发现代码中出现了除0错误,因此将溢出标记位由0改为1,进程异常,CPU将该进程切出。os会自动进行计算完成后,检测状态寄存器,当检查到溢出标记位为1时,os就会提取当前正在运行的进程的PID,给其发送8号信号。

        那么为什么会是死循环打印呢?

        上面讲到,溢出标记位由0改为1后,CPU就会将该进程切出,因为寄存器里面的数据是该进程的上下文,所以位图也会跟随进程一起切出。但是,我们虽然将信号进行了捕捉,但是并没有让进程退出,所以这个进程只是被切出去了,当CPU正常进行调度时,再次调度该进程,上下文恢复上去,os立马识别到了溢出标记位还是1,再次打印,如此反复。

所以,为了解决这个问题,我们要在捕捉函数最后加上 exit,让进程退出。

      ~ 野指针和越界访问

        我们知道,指针变量必须通过地址才能找到目标位置。而我们语言上的地址是虚拟地址,所以我们前面讲了通过页表将物理地址和虚拟地址建立联系。但是事实上,我们是通过页表+MMU(memory manger unit,一个硬件)的方式将物理地址和虚拟地址建立联系的,所以当代码中出现了野指针或者越界访问时,因为这是一个非法地址,那么MMU一定会报错,它会将自己内部的寄存器进行标识,os就能够检测到,且知道是哪个进程的地址转化出错了。

四、信号的保存

4.1、相关概念

a. 信号递达:进程对信号的处理动作称为信号递达。

b. 信号未决:信号从产生到递达之间的这个状态称为信号未决。

c. 信号阻塞:被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。

阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

4.2、信号保存——三个数据结构

        前面我们讲到,在进程的PCB中,存在一种位图是用来保存信号的,但是事实上有3种数据结构与信号是相关的。他们分别是pending位图,block位图,typedef void(*handler_t)(int signo),handler_t handler[32]={0}结构。

        pending位图:该位图就是我们常说的用来保存信号的位图。

        block位图:该位图比特位的位置与信号标号一一对应,比特位的内容代表该信号是否阻塞。

        typedef void(*handler_t)(int signo),handler_t handler[32]={0}:这个是一个函数指针数组,这个数组在内核中有指针指向它,这个数组称为当前进程所匹配的信号递达的所有方法,数组下标代表信号的编号,数组的每一个元素都是一个函数指针(存函数地址),指向信号对应的处理方法。

4.3、信号集——sigset_t

        上面讲到的三个结构都是属于进程PCB,是内核数据结构。所以os必定不会让用户直接访问这三个结构,更不能够让用户直接进行位移操作。那么如果用户想要得到pending和block位图该怎么办呢?于是,Linux就提供了一种数据类型信号集——sigset_t,用户可以直接使用。

4.3、信号操作函数

        既然Linux提供了信号集,那么必定也通过了与之相关的各种方法,让用户能够去操作,这样用户根本就不需要关系在内核中这些结构到底是怎么样的。下面的5个函数就是对信号集进行操作的函数。

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);

sigpending:获取当前进程的 pending 信号集。信号发送的本质就是对pending位图进行修改。

NAMEsigpending - examine pending signalsSYNOPSIS#include <signal.h>int sigpending(sigset_t *set);读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。

sigprocmask :读取或更改进程的信号屏蔽字(阻塞信号集) (block)

NAMEsigprocmask - examine and change blocked signalsSYNOPSIS#include <signal.h>int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

下表说明了how参数的可选值及其作用:

选项作用
SIG_BLOCKset包含了我们希望添加到当前信号屏蔽字的信号
SIG_UNBLOCKset包含了我们希望从当前信号屏蔽字中解除阻塞的信号
SIG_SETMASK设置当前信号屏蔽字为set所指向的信号

注:9号信号是不能被捕捉或阻塞的。 

http://www.mmbaike.com/news/49992.html

相关文章:

  • 网站怎么做网站收录北京网站推广公司
  • wordpress页面怎么编辑盐城seo营销
  • 香港服务器网站班级优化大师app
  • 重庆网站制作托管网店推广平台
  • 广西模板十大名牌排名榜seo诊断分析
  • 设计公司网站要包含什么信息china东莞seo
  • 上海网站备案流程sem搜索
  • 罗湖附近公司做网站建设多少钱怎么推广自己的店铺
  • 手机网站的作用网络营销模式包括哪些
  • 素材模板大全网络优化初学者难吗
  • 哈尔滨网站建设可信赖湖南优化推广
  • 做封面的免费网站网站广告调词软件
  • wordpress怎么连接数据库电脑优化大师
  • 网站策划书萌新seo
  • html5 企业 网站公司网站怎么弄
  • 进一步加大网站集约化建设力度百度推广页面投放
  • 中国排建设银行悦生活网站加强服务保障 满足群众急需需求
  • 阿里云服务器搭网站同时做网盘免费b站推广网站下载
  • 网络网站是多少钱优化推广什么意思
  • 个人做网站seo余姚关键词优化公司
  • 如何给网站死链接做404西安疫情最新通知
  • 租网站服务器价格英文外链代发
  • wordpress 添加登录界面seo网站推广方式
  • 搜狗搜索网页版网站性能优化的方法有哪些
  • 搜索引擎优化面对哪些困境seo研究
  • 电商网站开发步骤宁波seo外包推广公司
  • 网站开发历史sem竞价托管费用
  • html网页制作个人网站网站建设制作
  • 澳门私人做网站seo的工作内容主要包括
  • 做动态网站用什么语言百度权重3的网站值多少