文章目录
- 信号的概念
- 实践信号
- 关于前台和后台进程的操作
- 操作系统与外设
- 信号的产生
- signal系统调用
前面的篇章结束了信号量的话题,那么接下来引入的是信号的话题,信号和信号量之间没有任何关系,只是名字比较像
信号的概念
在生活中存在各种各样的信号,例如有红绿灯这样的存在,而进程信号和生活中的信号其实是比较像的,为什么有红灯停绿灯行这样的概念,其实就是因为作为人从小就被指导有这样的观念,因此对应到进程信号中,有下面的几个结论:
- 当信号还没有产生的时候,其实进程就已经知道如何应对这个信号了,这是操作系统在设计的时候就已经被设计好的理念,所以在内核中存在这样的设计:
上述的这些信号就是在操作系统内部已经设计好的内容,当某个进程得到了这样某种信号,就会对对应的信号做出反应,就如同当人遇到绿灯会行驶,遇到红灯会停止一样的道理
- 信号的到来,进程并不清楚具体什么时候来,信号的到来和当前进程的逻辑是异步产生的
- 信号产生后,进程并不一定要立即处理它,而是在一个合适的时机进行处理,这也就意味着进程必须要对于即将来临的信号有一定的存储能力,以便于在外来需要对该信号处理的时候可以找到这个信号并进行处理
异步的概念
在第二条结论中提及到了异步的概念,因此这里对于异步进行一些解释
同步和异步是一组概念,同步概念的引出是在管道中引出的,管道的执行逻辑是具有一定的顺序性,产生顺序性的本质是让两个进程相互知道彼此的两个进程,感受到对应进程的存在,这也就是为什么管道在写端写满后,如果读端不读,写端就不会写的原因,这就是因为两个进程之间建立了一定的相关性,两个进程之间是会相互影响的,而对于异步来说,就是同步的相反点,当进程a向某个位置输出信息时,进程a没有因为其他外界的原因导致停止对于某个位置信息的输出,而是在一直输出,那么这个过程就称作是异步
信号的本质是一种向目标进程发送通知消息的一种机制
实践信号
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
while (true)
{
cout << "it is a process, pid:" << getpid() << endl;
sleep(1);
}
return 0;
}
运行上述结果
此时会发现的现象是,运行的任何命令都没有效果,此时运行kill命令就可以对其进行终止了:
此时进程终止,并且以前的命令也都显示出来了
下面对上述的现象做出解释
进程在运行的过程中,通常有两种模式,一种是前台模式,运行方式是
./process
一种是后台模式,运行方式是
./process &
这是对于一个进程的两种运行方式,其中后台程序一般是执行一些耗时比较长的任务,就可以使用后台进行启动,并且不会占用前台的操作
对于Linux系统来说,前台程序执行后再进行输入命令就无效了,为什么?因为shell本身也是一个进程,如果把一个进程放到了前台,那么就相当于把shell放到了后台,shell放到了后台,自然就不能对用户的命令进行解释了,因此区分一个进程是用前台启动的还是后台启动的,就看能不能从键盘上接受用户的输入,这也是最直接的方法
关于前台和后台进程的操作
[test@VM-16-11-centos signal]$ ./process >> log1.txt &
[1] 11861
[test@VM-16-11-centos signal]$ ll
total 24
-rw-rw-r-- 1 test test 351 Jan 30 16:13 log1.txt
-rw-rw-r-- 1 test test 79 Jan 30 12:58 Makefile
-rwxrwxr-x 1 test test 9176 Jan 30 12:58 process
-rw-rw-r-- 1 test test 198 Jan 30 12:55 Process.cc
[test@VM-16-11-centos signal]$ jobs
[1]+ Running ./process >> log1.txt &
[test@VM-16-11-centos signal]$ fg 1
./process >> log1.txt
ll
pwd
^Z
[1]+ Stopped ./process >> log1.txt
[test@VM-16-11-centos signal]$ bg 1
[1]+ ./process >> log1.txt &
[test@VM-16-11-centos signal]$ jobs
[1]+ Running ./process >> log1.txt &
将一个进程从后台提到前台
fg number
将一个进程从前台放到后台,自动沉睡
ctrl+z
将一个后台程序唤醒
bg number
查看当前运行的后台进程
jobs
操作系统与外设
如果谈到进程信号,就需要引入中断的概念,但是对于中断的概念来说,更需要提及的是这样的问题
操作系统是硬件的管理者,所以硬件上的变化操作系统应该知道并且接收,例如鼠标点击了哪里,键盘按下了哪个键,这些操作系统都要知道,只有知道了键盘按下的数据,才能根据冯诺依曼体系,利用键盘驱动把键盘上的数据拷贝到操作系统中对应键盘的文件缓冲区中,上层就可以通过文件描述符把对应的数据读取上来,所以scanf就能读取到对应的数据了
问题是:操作系统怎么知道键盘被按下了?
由冯诺依曼体系可以知道,计算机结构中有输入单元,也有输出单元,有内存CPU外设这些信息,那么操作系统是如何知道外设中的数据就绪了呢?难道是让操作系统隔一段时间就去检测一下外设中的输入信息吗?结合实际生活的经验以及操作系统对于效率的控制就能知道,这是绝对不可能的,所以一定是让硬件来告诉操作系统
操作系统最初在进行设定的时候,就要求CPU和外设要取得同步,能够让CPU知道关于外设上的各种信息,所以引入了一门新的技术叫做中断技术,关于中断技术,就要先清楚CPU本身的结构,CPU内部有运算器做数据处理,也有控制器进行设备的控制,那么也就意味着CPU必定要和设备直接或间接的相连,所以CPU就会提供一个一个的针脚,这个针脚是有编号的,针脚会与电脑中的主板相连,而主板上的硬件电路是和外设相连,所以在冯诺依曼体系中,CPU之和内存进行数据交互,就是因为外设太慢了,但是实际上CPU一定是会和外设有关联的,只不过不是进行直接的数据交互,那在计算机中,主板就担任了关联的角色,在主板上有各种各样的硬件电路,这些硬件电路就和CPU上的针脚相连,换句话说,当电脑上插入一个新的外设设备后,实际上就和电脑连接完毕了,此时键盘上发送的消息,CPU就能知道,发送的信息就叫做中断信息
对于中断信息这个概念来说,中断体现在把光电信号就叫做中断,换句话说,对应在电脑中的每一个外设与CPU本身都可以进行高低电平的信息交互,外设和CPU硬件是可以间接相连的,未来也能够进行数据的拷贝,不光如此,键盘在触发的时候会产生出很多的光电信号,这些光电信号的强弱就会被CPU识别到,CPU中的各种各样的寄存器就能对这些光电信号进行识别,识别的结果其实就是说某一种数据被放到了计算机中,这样就被操作系统读取到了,这样也就把硬件的行为信息转换成了软件的信息
经过上面的这一系列过程,就把数据从硬件转换到软件了,软硬件结合的本质就是通过寄存器或者内存中的数据来进行软硬件的数据结合
关于8259板
电脑上可以插入很多的电子设备,可能是键盘网卡这样对于操作系统来说有用的硬件设备,也可能是一个普通的风扇这样,仅仅是需要供电的设备,那么操作系统对于每个这样的设备都要进行链接吗?答案是否定的,在CPU和硬件之间会有一个电路板,叫做8259板,和电脑相连的所有设备,都会通过这个电路板再传递给CPU,而这个电路板的作用就是可以把有效的信息进行转换,传递给CPU中
所以得出的一个结论是,外设可以间接的向CPU特定的针脚中发送信息,表示自己的数据已经就绪了,而CPU中每一个针脚都会有一个对应的编号,这个编号就叫做中断号,因此在计算机中,每一个设备都会分配不同的中断号,当这个设备发生中断后,就能识别到这个设备的信息已经就绪了,那CPU就能通过这些信息拿到键盘上对应的中断号,拿到中断号之后,为了能够更快的对这些外设做出响应,在操作系统的内部会提供一张表,这个表中是一个函数指针数组,存储的是一个一个的指针,而这些指针指向的内容就是特定硬件的读取方法,当外设启动的时候,就会通过这张表形成对应硬件就绪后的读取方法,当中断号传递来之后,就借助这个数组索引到需要的内容,进而读取到这个设备想要传递来的数据信息。这张表叫做中断向量表,这张表在操作系统启动的时候,就要创建出的一张表。这样就保持了操作系统高效的特点,同时也能读取到各个设备上的消息
当用户摁键盘的时候,键盘就会基于中断来进行驱动,所以就会通过硬件告知CPU,进而通过操作系统把中断号提取出来,再从中断向量表中找到对应的方法,进而就能完成执行的操作
谈了这么多问题,其实就是想引出关于中断的概念,而键盘的行为和操作系统的行为其实和信号十分类似,它们都遵循异步的逻辑,键盘怎么摁,操作系统不会专门去等着它,而是在摁完之后操作系统会在合适的时候对这个行为做出响应,那信号是不是也是类似呢?一个信号传递给进程,进程可能并不会立即做出响应,它会在完成了当前最重要的任务后,再对这个信号做出反应,这其实就是一个异步的逻辑原理,那信号中的这些各种各样的信号,是不是就如同对应的中断号,而中断向量表,不就是和kill列表中的各种信号是一样的吗?
因此得出的结论是,信号本身就是用软件来模拟中断的行为,只不过这两个操作在设计上是两套机制,一个是纯硬件,一个是软件上的操作,但是在响应和异步的方面,它们之间是有很多的相似处的
信号的产生
Ctrl+c的命令可以让前台的进程终止,那为什么呢?借助信号来解决这个问题
键盘中的数据是有很多种的,其中一种是1234这样的普通数据,一种是例如Ctrl+c这样的组合键,这样的数据被叫做是控制数据,它传递的是一种控制的思想观念,操作系统如果收到了普通数据,就把数据传递给用户进程,如果是组合键,就把这个数据转换成一种动作
因此从头理思路,在键盘上按下了Ctrl+c后,此时光电信号会由键盘控制器解析成中断信号,中断信号再传递到8259板进行解析,8259板再传递到对应的CPU上的针脚,CPU的针脚再进行解析,最终读取到数据,就会向进程发送2号信号,最终使得进程结束
所以,引出的结论是,Ctrl+c命令本质上就是一种组合键,它要传递的控制数据就是向进程发送2号信号,也就是说kill -2和Ctrl+c是等价的,具体可以通过看返回上一个命令的状态来看
signal系统调用
这里介绍一个系统调用:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
该函数最终形成的效果是,如果有一个信号被发送,那么此时就会被替换为自定义的函数行为,因此这个函数的第二个参数实际上是一个函数指针
#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;
void handler(int singno)
{
cout << "这是" << singno << "号信号" << endl;
exit(1);
}
int main()
{
signal(2, handler);
signal(3, handler);
while(true)
{
cout << "我的进程pid是" << getpid() << endl;
sleep(1);
}
return 0;
}
从这个程序运行结果可以看出,Ctrl+C确实是传递的2号信号
在系统中信号主要分为有普通信号,下标从1到31,也有实时信号,那么这里只进行普通信号的研究
对于普通信号来说,它是没有0号信号的,为什么呢?
在进程等待的学习中就有这样的概念,一个进程的退出有两种信息,一个是退出时收到的信号编号,一个是退出时收到的退出码,所以对于进程退出来说是有两个数字可以参考的,退出信号和退出码,也是根据这两个数字来判断进程的健康状态的,加入进程的退出信号是零,表示的是这个进程在运行期间没有收到任何信号,它是很正常的跑完的,至于结果是否正确,则是可以根据进程的退出码来表示它到底退出的是否正确,由此可以看出,退出信号不应该有0号新号,如果有0号信号,那还能表示没有收到任何信号吗?因此在设计的时候大概率就没有进行对应的设计,就是为了更好的识别进程的正常退出这样的情况