LINUX系统编程:信号(1)

目录

什么是信号?

为什要有信号呢?

进程接受信号的过程

1.信号的产生

1.1kill命令产生信号

1.2键盘产生信号

1.3系统调用接口

1.3.1killl()

1.3.2raise()

1.3.3abort()

1.4软件条件

1.5异常

1.6对各种情况产生信号的理解

1.6.1kill命令

1.6.2键盘产生信号

1.6.3异常

除零错误

野指针


信号和信号量是两个完全不相干的技术,不要混淆。

什么是信号?

Linux让系统提供让用户给其他进程异步发送消息的一种方式。

站在进程的角度看待信号

1.信号在没被发送之前,进程是认识这个信号。

2.在接受到信号之后,进程是被设定好去怎么处理这个信号的。

3.信号到来的时候,如果进程有更重要的事情要做,这个信号会被临时保存。

4.收到信号之后可以不立即处理,等到机会合适在处理信号。

5.信号产生是不定时,随时都可能接受到信号,所以信号是异步发送的,是别的用户和进程。

为什要有信号呢?

操作系统要求进程有对外部信号响应的能力。(例:该进程发生除0错误,这个时候操作系统就会给进程发信号,让其停止)。

进程接受信号的过程

1.信号的产生

信号是如何产生的呢?

1.1kill命令产生信号

1-34都是标准信号,34之后的是实时信号。

其中每个数字都是一个宏,使用kill命令可以 kill -9 也可以 kill -SIGKILL

1.2键盘产生信号

ctrl + c 和 ctrl + \

信号捕捉:当我们向进程发送一个信号,进程接受信号后,会做出系统设定好的动作,

捕捉信号就是将这个信号的默认动作,改为自定义的动作。

signal(2,handler);

第一个参数就是要捕捉的信号,第二个参数为int,返回之为void的函数指针,捕捉到信号之后,就执行handler函数。

demo代码

大概意思就是进程收到2号信号时,会把2号信号的默认动作替换为handler函数。

#include <iostream>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <signal.h>

using namespace std;

void handler(int sig)
{
    cout<<endl;
    cout<<"get a signal ,number is:"<< sig <<endl;//打印捕捉到的信号
    exit(0);
}

int main()
{
    signal(2,handler);//捕捉2号信号
    while(true)
    {
        ;
    }
    return 0;
}

把程序跑起来,然后ctrl + c,进程果然接受到了2号信号。

ctrl + \ 会对进程发送3号信号 SIGQUIT

采用同样的方法,只不过把捕捉信号2,换成捕捉信号3。

1.3系统调用接口

1.3.1killl()

这个不是命令,是系统调用接口

kill()系统调用接口,能被用于发送任意信号,给任意的进程。

参数pid就是要发送信号进程的pid,sig就是发送几号信号。

成功返回0,失败-1被返回,适当的错误码被设置。

1.3.2raise()

对当前进程发送信号

成功返回0,失败返回非0.

1.3.3abort()

终止进程。

1.4软件条件

我的理解就是,软件触发了某种条件,导致需要操作系统介入发送信号。

例:管道写端关闭时,读端就会收到13号信号

现打开服务端,再打开客户端,客户端向管道写入,服务端从管道中读取。

关闭读端,看写端收到的信号。

果然收到了13号信号。

alarm(警报)

参数seconds:在多少秒之后警报会响起。

发送SIGALRM信号给调用进程,在参数seconds秒。

如果参数seconds是0,任何还未响应的警报会被删除。

在任何情况下,之前设置的警报都会被取消。

返回之前设定好警报的剩余时间,如果之前没设置返回0.

demo代码:设置一个5秒的警报

void handler(int sig)
{
    cout<<"get a signal, number:"<< sig <<endl;
    exit(0);
}

int main()
{
    alarm(5);
    signal(14,handler);
    int cnt = 5;
    while(cnt)
    {   
        cout<<cnt<<endl;
        sleep(1);
        cnt--;
    }
    return 0;
}

5秒之后收到14号信号。

1.5异常

这里就不验证了

例:除0错误 8)SIGFPE

        野指针 11) SIGSEGV

1.6对各种情况产生信号的理解

1.6.1kill命令

命令本身就是一个可执行的程序,本质就是使用了系统调用接口kill,让操作系统发送信号。

1.6.2键盘产生信号

操作系统怎么知道键盘输入了什么数据呢?

难道轮训的去查看键盘输入吗?这样效率也太低下了。

首先计算机启动时,会创建一个中断向量表,这是一个函数指针数组。

cpu是有很多针脚的,每个针脚都有自己的编号,其中键盘是直接与cpu2号针脚相连。

当键盘输入的时候,就会发生硬件中断,2号针脚就会产生高电平,操作系统拿着这针脚号,去中断向量表中,执行对应函数就可以读取到键盘的数据了。

读取键盘数据之后,要判断一下,如果是组合键,就执行对应的命令,如果是字符,就向对应文件的缓冲区写入。

1.6.3异常
除零错误

demo代码

void handler(int sig)
{
    cout<<"/0 error"<<endl;
}

int main()
{
    signal(8,handler);
    int a = 10;
    int b = a/0;
    return 0;
}

这个代码有个奇怪的现象,运行起来发现cout<<"/0 error"<<endl;会一直打印,这是为什么呢?

一个原因是我捕捉之后没有退出,即使不退出也不应该循环打印。

进程将10写入eax中,0写入ebx中,eax和ebx相除,发生除零错误(硬件上的错误),发生错误之后,溢出标志位会被设置为1,这时候os看见cpu出问题了,就给进程发送了 SIGFPE 让其停止。

cpu中是有很多组数据的,liunx是分时操作系统,cpu会在不同的进程来回切换,每当切换到除零错误这个进程,os一看出现错误,就会发送一次信号,切换到一次就会发送一次信号。

野指针

demo代码

void handler(int sig)
{
    cout<<"pointer error"<<endl;
    exit(0);
}

int main()
{
    signal(11,handler);
    int * p = nullptr;
    *p = 100;
    return 0;
}

首先eax拿到0号地址,发现要对0号地址进行写入,mmu就拿着0号地址,和cr3中的页表起始地址,进行转化,发现根本转化不了,因为0号地址的区域是只读的,或者访问的区域压根没到0号地址,就会将出错的地址储存在cr3中,os一看不对劲,赶紧给进程发SIGSEGV信号,让进程停止。

  • 29
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值