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

o2o和o2b、o2c模式是什么意思啊百度seo搜索排名

o2o和o2b、o2c模式是什么意思啊,百度seo搜索排名,广东网站建设服务供应商,西安网站开发外包目录 一、了解信号 1、概念 2、前台进程和后台进程 3、信号的处理 4、硬件层面 5、信号与我们的代码是异步的 二、信号的产生 1、产生的方式 2、键盘组合键 3、kill命令 4、系统调用 man kill man raise man abort 5、异常软件条件 (1)异常产生信号 (…

目录

一、了解信号

1、概念

2、前台进程和后台进程

3、信号的处理

4、硬件层面

5、信号与我们的代码是异步的

 二、信号的产生

1、产生的方式

2、键盘组合键

3、kill命令

4、系统调用

man kill

 man raise

man abort

 5、异常软件条件

(1)异常产生信号

(2)软条件——闹钟

(3)core dump

三、信号保存

1、信号其他的相关概念

2、在内核中表示

3、sigset_t

4、信号集操作函数

(1)sigprocmask

(2)sigpending


我们进程通信讲的信号量和信号没有任何关系

一、了解信号

1、概念

我们举个例子了解一下信号

在我们的生活中处处可见信号,就比如我们网购了一件商品,这个快递在运输的过程,我们已经能够知道这个商品或被用来做什么,因为我们能识别快递

当快递送到了,而我们正在打游戏还有五分钟结束游戏,我们此时不会立即去取快递,而是打完这把游戏再去拿快递,也就是说拿快递这个过程不是立即执行的,我们可以理解成在合适的时间去取

在我们收到快递到了这个信息和拿到这个快递,中间会存在时间窗口。当我们收到这个快递,我们会有三种执行方式:1、默认动作(拆开快递直接使用),2、忽略动作(取到快递直接放一边) 3、自定义动作(我们把这个快递送给别人)。

快递运输到取快递这个过程对我们来说是异步的。

异步的理解:

异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
异步就相当于当客户端发送给服务端请求时,在等待服务端响应的时候,客户端可以做其他的事情,这样节约了时间,提高了效率。

 在我们的生活中也处处存在信号:红绿灯,下课铃声等等....

我们怎么认识这些信号呢?  从小有人教过我们。 我们不仅要识别信号,还要直到信号的处理方法,比如红灯停绿灯行。但信号产生了,我们可能并不立即处理这个信号,在合适的时候,因为我们可能正在做更重要的事情。 — 所以,信号产生后一直到信号处理时,中间一定有一个时间窗口。在这个时间窗口内,我们必须记住信号到来!

OS 中也会有很多信号围绕着信号去展开,所以进程要能够识别非常多的信号。这里只想说明进程能够认识信号,以及信号不管到没到来进程都知道该怎么做。

结论:

  1. 进程必须能识别并能够处理信号,信号没有产生,也要具备处理信号的能力。
  2. 进程收到一个具体信号的时候,进程可能并不会立即处理这个信号。
  3. 一个进程从信号产生到信号被处理,一定有时间窗口,进程会在合适的时候处理信号。

2、前台进程和后台进程

如下图 这就是前台进程,我们使用CTRL + C可以杀掉前台进程(该进程运行时,shell不会接收其他命令了)

我们在可执行程序后面加一个& 表示这个程序放在后台运行,这样 Shell 不必等待进程,结束就可以接受新的命令,启动新的进程,在他运行的时候还可以运行其他指令,并且Ctrl+c已经无法杀掉该进程。

我们要终止这个进程,就要用kill -9信号杀死这个进程。

Crtl+c为什么可以终止前台进程呢?

原理是用户按下 Ctrl+C,这个键盘输入产生一个硬件中断,被 OS 获取,解释成信号,发送给目标前台进程。前台进程因为收到信号,进而引起进程退出。

在Linux中,一次登录中,一个终端,一般会配上一个bash,只允许一个进程为前台进程,运行多个进程是后台进程。

那么既然一开始bash是前台进程,那么为什么使用CTRL+C时候,bash不退出呢?

这当然是因为bash在里面对这个信号做了特殊处理

Ctrl+c本质上被进程解释成收到信号,是2号信号,这个后续给大家验证。我们可以用kill -l来看到所有信号。

在我们系统中总共有62个信号(没有0、32、33信号)

1-31号信号被称为普通信号,34-64被称为实时信号。

不会被立即处理的是普通信号,要立即处理的是实时信号。

  • 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在 signal.h 中找到,例如其中有定义 #define SIGINT 2

由此我们可以知道,进程就是你,操作系统就是快递员,信号就是快递

3、信号的处理

信号有三种处理方式,我们在上面也提到过

1、默认动作    2、忽略   3、自定义动作(提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数)

我们在上面写的Ctrl+c实际是给该进程发2号信号。我们接下来写个代码测试一下。

我们先认识一个接口。

man signal

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

 这个函数的作用是设置对于signum信号的处理方法,处理方法为handler。handler是一个函数指针。

#include <iostream>
#include <unistd.h>
#include <signal.h>using namespace std;void handler(int sign)
{cout<<"process get a signo:"<<sign<<endl;}
int main()
{signal(2,handler);while(1){cout<<"i am a process:"<<getpid()<<endl;sleep(1);}return 0;
}

 运行结果如下:

这里我们Ctrl+c并不能终止该进程,因为Ctrl+c实际上是给进程发2号信号,默认执行方式是终止该进程,但我们用signal修改了该进程对信号的处理动作。

对于这个signal函数,只需要设置一次即可,往后都有效

只有收到了对应的信号,才会调用这个方法

注意:不是所有的信号都可以自定义的。有些信号不能自定义

4、硬件层面

我们会有几个问题思考:1、键盘数据是如何输入给内核的呢? 2、ctrl+c又是如何变成信号的呢?

首先键盘输出数据肯定操作系统先知道,那操作系统是怎么知道键盘上有数据的呢?

在冯诺依曼体系结构中,Linux下一切皆文件,键盘也有自己对应的文件,往键盘输入数据本质就是把输入的数据拷贝到缓冲区上。所以操作系统就知道了所以我们就可以用read,write通过文件的方式把数据读到进程当中。

操作系统怎么知道键盘有数据了?

其实在CPU上有很多的引脚,我们的CPU是直接插到主板上的。而键盘是可以间接的和CPU直接物理上连接到的。虽然CPU不从键盘读数据。但是键盘可以给CPU发送一个硬件中断。一旦键盘写完了数据就会给CPU发送硬件中断给CPU,通知CPU数据就绪,从而让操作系统去完成文件的拷贝。显示器和网卡与键盘的原理是一样的,我们用中断号来区分这些硬件, 假如键盘文件的中断号是10,把10存放到CPU寄存器中,告诉CPU这是键盘,让CPU去执行相应的硬件驱动程序。

在软件层面上,操作系统一启动,就会形成一张,中断向量表。里面放的是方法的地址。这些方法是直接访问外设的方法—主要是磁盘,显示器,键盘 然后最后这个读取键盘的方法,才是将键盘的数据放到缓冲区的方法

一句话总结一下这个过程:

当键盘输入数据,会通过硬件中断发送给CPU,CPU会利用这个中断号,让操作系统直接去通过中断向量表找到键盘的读取办法,通过这个办法让键盘上文件数据拷贝到操作系统的缓冲区

所以键盘这个外设是通过中断来工作的。这个就是硬件中断

而我们前面所说的信号,也是通过一堆数字来进行控制。这两者其实比较相似,但是没有关系。一个是软硬件结合的,一个是纯软件行为。

我们所用的信号,就是用软件的方式,对进程模拟的硬件中断

那ctrl+c又是如何变成信号的呢?

实际上把键盘文件数据拷贝到操作系统缓冲区的时候,操作系统会判断是数据还是控制,如果是控制,比如CTRL+ C会把这个转化为2号信号发送给进程。而不是放到缓冲区中。所以进程就收到了2号信号

5、信号与我们的代码是异步的

信号的产生的和我们自己的代码的运行是异步的

同步就是发生一件事后等这件事发生完了才继续做我们的事情

异步就是这件事发生后我们不管这个事情,继续做我们的事情

信号是进程之间事件异步通知的一种方式,属于软中断

 二、信号的产生

1、产生的方式

  1. 键盘组合键
  2. kill命令
  3. 系统调用
  4. 异常软件条件

产生的信号都是由操作系统给进程发送,因为操作系统是进程的管理者。

2、键盘组合键

我们刚刚讲了Ctrl+c是发送2号信号,再给大家讲两个组合键产生的信号

使用CTRL + \即可捕捉3号信号

#include <iostream>
#include <unistd.h>
#include <signal.h>using namespace std;void handler(int sign)
{cout<<"process get a signo:"<<sign<<endl;}
int main()
{signal(3,handler);while(1){cout<<"i am a process:"<<getpid()<<endl;sleep(1);}return 0;
}

CTRL + Z是19号信号

#include <iostream>
#include <unistd.h>
#include <signal.h>using namespace std;void handler(int sign)
{cout<<"process get a signo:"<<sign<<endl;}
int main()
{signal(19,handler);while(1){cout<<"i am a process:"<<getpid()<<endl;sleep(1);}return 0;
}

 

如下所示,我们似乎会发现,我们上面似乎并没有将19号信号用自定义的方法进行处理

其实这是因为不是所有的信号,都是可以被signal捕捉的,比如19,9号信号

我们可以用下面的代码进行测试。

我们能看到9号信号是无法被捕捉的,这里就不给大家全部演示了。

3、kill命令

我们在上面的实验也证明了kill命令也是可以给进程发信号的。

4、系统调用

man kill

 int kill(pid_t pid, int sig);

它的两个参数分别是pid和信号的编号。与命令行中的kill是很相似的

如果成功返回0,失败返回-1

我们可以简单的利用这个系统调用接口实现一个kill命令

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <signal.h>using namespace std;void Usage(string proc)
{cout<<"Usage:\n\t"<<proc<<"signum pid\n\n";
}
int main(int argc,char* argv[])
{if(argc!=3){Usage(argv[0]);return 1;}int signum=stoi(argv[1]);pid_t pid=stoi(argv[2]);int n = kill(pid,signum);if(n < 0){perror("kill");return 2;}return 0;
}

运行结果如下: 

 man raise

int raise(int sig);

 

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <signal.h>using namespace std;void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;exit(1);
}
int main()
{signal(2, myhandler);int cnt = 5;while(true){cout << "I am a process, pid: " << getpid() << endl;sleep(1);cnt--;if(cnt == 0) raise(2);}return 0;
}

运行结果如下:

这个raise相当于

kill(getpid(), 2);

man abort

void abort(void);

它的作用是引起一个正常的进程直接终止

它相当于给自己发送一个6号信号

我们先用下面代码进行测试

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <signal.h>using namespace std;void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;exit(1);
}
int main()
{signal(6,myhandler);while(true){cout << "I am a process, pid: " << getpid() << endl;sleep(1);}return 0;
}

 5、异常软件条件

(1)异常产生信号

我们先来看一段代码

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <signal.h>using namespace std;int main()
{cout<<"div before"<<endl;sleep(3);int a=10;a/=0;cout<<"div after"<<endl;return 0;
}

运行结果如下:

我们看到这个Floating point exception实际上就是信号,我们kill -l查看到是8号信号 我们也可以查看7号手册加以验证

 我们用signal捕捉8号信号看运行是什么情况

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <signal.h>using namespace std;void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;
}
int main()
{signal(8,myhandler);cout<<"div before"<<endl;sleep(3);int a=10;a/=0;cout<<"div after"<<endl;return 0;
}

运行结果如下:

这个我们发现是除0错误导致的,我们在看看野指针造成什么情况。

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <signal.h>using namespace std;void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;
}
int main()
{cout<<"point error before"<<endl;int *p=nullptr;*p=10;cout<<"point error after"<<endl;
}

运行结果如下:

我们查看这是11号信号

 我们捕捉一下11号信号看看

void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;
}
int main()
{signal(11,myhandler);cout<<"point error before"<<endl;int *p=nullptr;*p=10;cout<<"point error after"<<endl;
}

 运行结果显示也是一直打印

这里为什么除0和野指针问题会导致进程崩溃呢?

这里我们曾经讲过在CPU上有一个eip/pc寄存器来记录当前执行的是哪一行代码

这里我们再讲一个,其实在CPU内部还一个状态寄存器,在它的签名有一个溢出标志位,当我们的代码除0,可以理解成在除一个极限小的数字,所以溢出了,在CPU状态寄存器的溢出标志位设置为1,意味着CPU在执行当前进程出异常了,又因为操作系统是硬件的管理者,得知CPU在执行当前进程出异常,所以操作系统给当前进程发送信号,但这个信号被我们捕捉了,它本来默认是终止,但这个异常没有解决,操作系统就一直发信号,所以才会出现一直打印的情况。 这里这个行为并不影响其他进程,我们之前讲过在CPU里的寄存器是进程的上下文,修改CPU内部的状态只影响了自己。这也更好的验证了进程的独立性。

野指针的问题

如下图所示,在CPU里面有一个内存管理单元,因为直接查页表太慢了,所以有一个MMU硬件来进行查表。一旦异常,也就是地址转化失败了。虚拟到物理转化失败了。在CPU内还有一个寄存器,一旦转化失败了。它会把转化失败的虚拟地址放在这里,CPU异常操作系统就知道了,给进程发信号终止该进程。后面的原理是一样的。

这里是对CPU的硬件不同的报错,操作系统能检测并分辨出是除0错误还是野指针问题。

那么异常只会由硬件产生吗?

比如我们之前的管道,如果一开始读写端都打开,但是我们突然关闭了读端。那么写端进程就会被杀掉。会收到一个SIGPIPE(13)号信号。这就是一种软件异常。

也有的异常,操作系统只是会返回值出错的形式进行处理

(2)软条件——闹钟

man alarm

unsigned int alarm(unsigned int seconds);

alarm 系统调用用于设置一个定时器,当定时器计时器达到指定的时间时,内核会发送一个 SIGALRM 信号(14号信号)给调用进程。这可以用于实现定时器功能,例如在一定时间间隔内执行某个特定的操作或执行定时任务

seconds 参数表示定时器的秒数。如果 seconds 参数为非零值,表示设置定时器,在指定秒数后会发送 SIGALRM 信号给进程。如果 seconds 参数为零,则表示取消之前设置的定时器。

返回值是剩余的未完成的定时器秒数。如果之前有一个定时器已经设置,调用 alarm 会取消之前的定时器,并返回剩余的秒数。如果没有之前的定时器,或者之前的定时器已经到期,返回值为 0。

我们来看这段代码

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <signal.h>using namespace std;int main()
{int n=alarm(5);while(1){cout<<"proc is running"<<endl;sleep(1);}return 0;
}

运行结果如下:

 我们尝试捕捉一下14号信号

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <signal.h>using namespace std;void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;
}
int main()
{signal(14,myhandler);int n=alarm(5);while(1){cout<<"proc is running"<<endl;sleep(1);}return 0;
}

运行结果如下

因为闹钟只会响一次,所以我们只捕捉了一次。

如果我们想让它每隔3秒响一次

我们再来看代码

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <signal.h>using namespace std;void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;int n=alarm(3);
}
int main()
{signal(14,myhandler);int n=alarm(3);while(1){cout<<"proc is running"<<endl;sleep(1);}return 0;
}

关于它的返回值,我们做一下试验

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <signal.h>using namespace std;void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;int n=alarm(3);cout<<"剩余时间:"<<n<<endl;
}
int main()
{signal(14,myhandler);int n=alarm(50);while(1){cout<<"proc is running,pid:"<<getpid()<<endl;sleep(1);}return 0;
}

返回值返回的是上一个闹钟的剩余时间。

(3)core dump

先解释一下什么是 Core Dump,当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部保存到磁盘上,文件名通常是 core,这叫做 Core Dump。

进程异常终止通常是因为有 Bug,比如非法内存访问导致段错误,事后可以用调试器检查 core 文件以查清错误原因,这叫做 Post-mortem Debug(事后调试)。一个进程允许产生多大的 core 文件取决于进程的 Resource Limit(这个信息保存在 PCB 中)。默认是不允许产生 core 文件的,因为 core 文件中可能包含用户密码等敏感信息,不安全。
在开发调试阶段可以用 ulimit 命令改变这个限制,允许产生 core 文件。 首先用 ulimit 命令改变 Shell 进程的 Resource Limit,
允许 core 文件最大为 1024K: $ ulimit -c 1024

我们可以先用ulimit -a查看

我们用上面提到的命令使其产生core文件。大小为1024

设置 core file size,kill -8/11 后,发现报错信息中多了一个(core dumped),且 ll 还发现多了一个 core 文件

ulimit 命令改变了 Shell 进程的 Resource Limit,test 进程的 PCB 由 Shell 进程复制而来,所以也具有和 Shell 进程相同的 Resource Limit 值,这样就可以产生 Core Dump 了。 
前面讲进程等待的时候说过一个概念,父进程中 waitpid 可以获取子进程的退出信息,其中 status 中,低 7 位表示进程退出时的终止信号,次低 8 位表示进程退出时的退出码,而低 8 位中的最后 1 位还没有讲
它表示进程是否 core dump,core dump 是一个标志位。

当一个进程被异常退出时,退出码没有意义,我们不仅想知道它的退出信号,更想知道的是它在代码的哪一行触发的信号。因为云服务器默认看不到现象,如果是虚拟机的话就可以看到。所以为了让云服务器能够看到,我们就需要设置一下,ulimit -a 查看系统资源,其中 ulimit -c 1024 就设置好了 core file size。

在上面运行报错后,有一个(core dumped),它叫做核心转储。当一个进程崩溃时,OS 会将进程运行时的核心数据 dump 到磁盘上,方便用户进行调试,一旦发生核心转储,core dump 标志位就会被设置 1,否则就是 0。

一般而言,线上环境的核心转储是被关闭的。因为程序每崩溃一次就会 dump 一次,而这一个 core 文件有 56 万多个字节,还不说这个文件不大。如果线上环境的核心转储是打开的,那么在公司项目中有几千台机器,那肯定是自动运行的,此时如果存在大量错误,一运行就 dump,一 dump 就运行,那么过了一晚,服务器肯定都登不上了,原因就是磁盘已经被大量的 core 文件占用了。

三、信号保存

1、信号其他的相关概念

在操作系统给进程发送信号的时候,实际是给进程的PCB发送。

在task_struct中维护一个int signal,我们不把它当整数,我们可以理解成一个32位的数组,或者是位图,把他当成二进制来看,用0,1来描述信号,用位图来管理信号

所谓的发信号,本质上是操作系统去修改task_struct的信号位图对应的比特位。

那为什么是操作系统发送信号呢?

因为操作系统是进程的管理者,只有操作系统有资格去修改tast_struct内部的属性。

为什么要信号保存呢?

进程收到信号之后,可能不会立即处理这个信号,会有一个不被处理的时间窗口,所以我们要把信号保存起来。

  • 实际执行信号的处理动作(忽略、默认、自定义捕捉)称为信号递达(Delivery)。
  • 信号从产生到递达之间的状态,称为信号未决(Pending)。
  • 进程可以选择阻塞(Block)某个信号。
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。

注意 :阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。我们可以把阻塞理解成已读不回,忽略理解成未读。

2、在内核中表示

实际在 Linux kernel 的 task_struct 中还包含了一些信号相关字段,如下面这个信号在内核中的示意图:这个图应该横着来看:

SIGHUP(1),没有收到 pending,也没有收到 block,所以默认处理是 SIG_DFL。

SIG_INT(2),收到 pending,因为也收到了 block,所以不会处理 SIG_IGN。

SIGQUIT(3),没有收到 pending,收到了 block,如果没有收到对应的信号,照样可以阻塞信号,所以阻塞更准备的理解是它是一种状态;

信号的自定义捕捉方法是用户提供的,是在用户权限下对应的方法。下面学习信号的操作都是围绕着这三个表来展开。

  1. pending(未决):它是一个无符号整型的位图,比特位的位置代表信号的编号,比特位的内容 0 1 代表是否收到信号,OS 发送信号本质是修改 task_struct ➡ pending 位图的内容。
  2. handler(递达):它是一个函数指针数组,它是用信号的编号,作为 handler 数组的索引,找到该信号编号对应的信号处理方式,然后执行对应的方法。
  3. block(阻塞):它是一个无符号整型的位图,比特位的位置代表信号的编号,比特位的内容 0 1 代表是否阻塞该信号。
  • 每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP 信号未阻塞也未产生过,当它递达时执行默认处理动作。
  • SIGINT 信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
  • SIGQUIT 信号未产生过,一旦产生 SIGQUIT 信号将被阻塞,它的处理动作是用户自定义函数 sighandler。如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?。Linux是这样实现的:常规信号在递达之前产生多次只计一次,如果存在一个信号多次的情况,在递达的时候再把block对应的信号设置为1,也就是阻塞该信号,当递达结束后,再解除该信号的阻塞。而实时信号在递达之前产生多次可以依次放在一个队列里。本章不讨论实时信号

3、sigset_t

可以理解为了能让我们更好的对上面的三张表操作,OS 给我们提供了一种系统级别 sigset_t  类型这个类型 OS 内部的当然也有定义,我们可以使用这个数据类型在用户空间和内核交互,此时就一定需要系统接口。

未决和阻塞标志可以用相同的数据类型 sigset_t 来存储,sigset_t 称为信号集

sigset_t 定义的变量 set 当然是在栈上开辟空间,那么这个栈就是用户栈,实际上我们在进程地址空间中谈的代码段、数据段、堆区、内存映射段、栈区、命令行参数、环境变量都是在用户空间,而将来要把用户空间中的进程信号属性设置到内核,所以除了 sigset_t,一定还需要系统接口。

4、信号集操作函数

当然光有 sigset_t 这个类型还不够,这个类型本身就是一个位图。实际我们不支持或者不建议直接操作 sigset_t,因为不同平台,甚至不同位数的 OS,sigset_t 位图的底层组织结构实现可能是不一样的,所以 OS 提供了一些专门针对 sigset_t 的系统接口,这些接口会先在用户层把信号相关的位图数据处理好。

这些函数是以位图为单位,将位图全部清理或者全部置1等。

​
#include <signal.h>
int sigemptyset(sigset_t* set);//全部置0
int sigfillset(sigset_t* set);//全部置1
int sigaddset(sigset_t* set, int signo);//指定位置置为1 信号集添加一个信号   
int sigdelset(sigset_t* set, int signo);//指定位置置为0 信号集指定信号删除
int sigismember(const sigset_t* set, int signo);//判断特定信号是否已经被设置​

  • 函数 sigemptyset 初始化 set 所指向的信号集,使其中所有信号的对应 bit 清零,表示该信号集不包含任何有效信号。
  • 函数 sigfillset 初始化 set 所指向的信号集,使其中所有信号的对应 bit 置位,表示该信号集的有效信号包括系统支持的所有信号。

注意:在使用 sigset_ t 类型的变量之前,一定要调用 sigemptyset 或 sigfillset 做初始化,使信号集处于确定的状态。初始化 sigset_t 变量之后就可以在调用 sigaddset 和 sigdelset 在该信号集中添加或删除某种有效信号。

这四个函数都是成功返回  0, 出错返回  -1 。 sigismember  是一个布尔函数, 用于判断一个信号集的有效信号中是否包含某种信号, 若包含则返回  1, 不包含则返回  0, 出错返回  -1 。

(1)sigprocmask

调用函数 sigprocmask 可以读取或更改进程的信号屏蔽字(阻塞信号集)。传入一个 set 信号集,设置进程的 block 位图,一般把用户空间定义的信号集变量或对象设置成进程 block 位图,这样的信号集叫做信号屏蔽字(Signal Mask),阻塞信号集也叫做当前进程的信号屏蔽字,这里的屏蔽应该理解为阻塞而不是忽略。

set:输入型参数,由用户层把信号屏蔽字拷贝到内核。
oset:输出型参数,把老的信号屏蔽字返回,方便恢复,不想保存可设置 NULL。

如果 oset 是非空指针,则读取进程的当前信号屏蔽字通过 oset 参数传出。如果 set 是非空指针,则更改进程的信号屏蔽字,参数 how 指示如何更改。如果 oset 和 set 都是非空指针,则先将原来的信号屏蔽字备份到 oset 里,然后根据 set 和 how 参数更改信号屏蔽字。假设当前的信号屏蔽字为 mask,下表说明了 how 参数的可选值。

如果调用 sigprocmask 解除了对当前若干个未决信号的阻塞,则在 sigprocmask 返回前,至少将其中一个信号递达。 

返回值

成功返回0,失败返回-1

(2)sigpending

获取当前调用进程的 pending 信号集, 通过  set  参数传出。调用成功则返回  0, 出错则返回  -1 。

set为输出型参数 

我们写一下代码来熟悉一下这些接口

#include <iostream>
#include <signal.h>
#include <unistd.h>using namespace std;void PrintPending(sigset_t &pending)
{for(int signo=31;signo>=1;signo--){if(sigismember(&pending,signo)){cout<<"1";}else{cout<<"0";}}cout<<endl;
}
int main()
{//定义信号集变量sigset_t bset,oset;sigset_t pending;//初始化sigemptyset(&bset);sigemptyset(&oset);sigemptyset(&pending);//添加想要的屏蔽的信号sigaddset(&bset,2);//将该屏蔽的信号设置到block中sigprocmask(SIG_BLOCK,&bset,&oset);//重复打印pending信号集while(1){int n=sigpending(&pending);if(n<0) continue;PrintPending(pending);sleep(1);}}

运行结果如下:我们屏蔽了2号信号,当我们按下Ctrl+c,发现pending位图接收到了2号信号,由0->1。 

我们想让这个信号过10秒解除,并能递达 

我们能看到我们发送2号信号的时候,pending位图上显示,接收到了2号信号,可此时2号信号被屏蔽了,一共循环了10次 解除了屏蔽,2信号递达了并执行默认动作。

当我们捕捉2号信号看看,使其执行自定义动作

#include <iostream>
#include <signal.h>
#include <unistd.h>using namespace std;void handler(int signo)
{cout<<"hello linux"<<endl;
}void PrintPending(sigset_t &pending)
{for(int signo=31;signo>=1;signo--){if(sigismember(&pending,signo)){cout<<"1";}else{cout<<"0";}}cout<<endl;
}
int main()
{signal(2,handler);//定义信号集变量sigset_t bset,oset;sigset_t pending;//初始化sigemptyset(&bset);sigemptyset(&oset);sigemptyset(&pending);//添加想要的屏蔽的信号for(int i=1;i<31;i++){sigaddset(&bset,i);}//将该屏蔽的信号设置到block中sigprocmask(SIG_BLOCK,&bset,&oset);int count=0;//重复打印pending信号集while(1){int n=sigpending(&pending);if(n<0) continue;PrintPending(pending);cout<<"proc id:"<<getpid()<<endl;if(count++==10){//解除2号信号的屏蔽sigprocmask(SIG_SETMASK,&oset,nullptr);cout<<"2号信号解除"<<endl;}sleep(1);}}

运行结果如下:

信号捕捉执行了自定义动作。

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

相关文章:

  • 找网络公司做网站需要注意seo的形式有哪些
  • 想搭网站做软件首先要学设么浙江网络推广
  • 化妆品网站设计系统需求的策划书外汇交易平台
  • 网站维护推广表徐州百度运营中心
  • 制作化妆品网站百度竞价排名是哪种方式
  • 建设网站需要什么软件下载制作一个app软件需要多少钱
  • 深圳做网站收费百度seo报价
  • 智能写作网站公司网站的推广
  • 甘肃兰州疫情最新情况最新消息河南网站seo费用
  • 网站建设 郑州国内疫情最新情况
  • 嘉陵 建设 摩托车官方网站百度seo推广工具
  • 用ps做网站还是wd北京度seo排名
  • 网站商城建设6整合营销的概念
  • 自己建设网站要多久网站seo谷歌
  • 如何做网站来做淘宝客合肥关键词优化平台
  • 临沂的网站建设怎么在百度做免费推广
  • 微信网站如何制作搜索引擎优化论文
  • c 怎么做网站开发如何做网站营销推广
  • 政府网站建设情况企业推广文案范文
  • 网站地图 html广州网站推广排名
  • 广东省著名商标在什么网站做石家庄seo排名公司
  • 怎么做类似淘宝网站吗百度收录申请入口
  • 网站建设竞标需要怎么做网站指数查询
  • 中英西班牙网站建设在线网站建设平台
  • 如何做 旅游网站内容网站设计公司官网
  • 做公司网站价格谷歌浏览器下载安装2022最新版
  • 石柱网站开发免费发布广告信息网
  • 陕西住房建设厅官方网站武威网站seo
  • 利用html做博客网站企业网站的在线推广方法有
  • 衡阳北京网站建设建站流程主要有哪些