Linux进程信号

目录

互斥的四个概念

​编辑

 查看当前的信号量

认识信号量接口

semget

semctl

理解IPC资源的管理

信号入门

生活角度的信号

技术应用角度的信号

注意 

信号概念

 用kill -l命令可以察看系统定义的信号列表

​编辑

信号处理常见方式概览 

信号产生 

认识常用接口

signal

通过系统接口向进程发送信号

kill

raise

abort

由软件条件产生的信号

alarm

由硬件条件产生的信号


互斥的四个概念

我们把大家都能看到的资源:公共资源

a.互斥:任何一个时刻,都只允许一个执行流在进行公共资源的访问——加锁

b.我们把任何一个时刻都只允许一个执行流在进行访问的共享资源,叫做临界资源

c.临界资源是要通过代码访问的,凡是访问临界资源的代码,叫做临界区

d.原子性

 查看当前的信号量

ipcs -s

认识信号量接口

信号量的获取

semget

int semget(key_t key,int nsems,int semflg)

第一个参数:   键值,用于唯一标识一个信号量集。通常可以通过 ftok 函数生成。

第二个参数:信号量集,代表信号量的个数 

第三个参数: 标志位,用于指定信号量的权限和行为选项。

semctl

得到一个信号量集标识符或创建一个信号量集对象

第一个参数:   信号量集标识符

第二个参数:是要操作的信号量在集合中的索引,通常为0,表示第一个信号量。

第三个参数:

cmd参数列表: 

cmd    解释
IPC_STAT    从信号量集上检索semid_ds结构,并存到semun联合体参数的成员buf的地址中
IPC_SET    设置一个信号量集合的semid_ds结构中ipc_perm域的值,并从semun的buf中取出值
IPC_RMID    从内核中删除信号量集合
GETALL    从信号量集合中获得所有信号量的值,并把其整数值存到semun联合体成员的一个指针数组中
GETNCNT    返回当前等待资源的进程个数
GETPID    返回最后一个执行系统调用semop()进程的PID
GETVAL    返回信号量集合内单个信号量的值
GETZCNT    返回当前等待100%资源利用的进程个数
SETALL    与GETALL正好相反
SETVAL    用联合体中val成员的值设置信号量集合中单个信号量的值
 

理解IPC资源的管理

在shmctl,semctl,msqctl这些接口的描述里,都有对其数据结构的解释

 操作系统用一个指针数组来统一管理,这也是一种多态的表现

信号入门

生活角度的信号

--你在网上买了很多件商品,在等待不同商品快递的到来。但即便快递还没有到来,你也知道快递到了的时候应该怎么处理快递,也就是你能“识别快递”。
--当快递到达目的地了,你收到了快递到来的通知,但是你不一定要马上下楼取快递,也就是说取快递的行为并不是一定要立即执行,可以理解成在“在合适的时候去取”。
--在你收到快递到达的通知,再到你拿到快递期间,是有一个时间窗口的,在这段时间内你并没有拿到快递,但是你知道快递已经到了,本质上是你“记住了有一个快递要去取”。
--当你时间合适,顺利拿到快递之后,就要开始处理快递了,而处理快递的方式有三种:1、执行默认动作(打开快递,使用商品)2、执行自定义动作(快递是帮别人买的,你要将快递交给他)3、忽略(拿到快递后,放在一边继续做自己的事)。
--快递到来的整个过程,对你来讲是异步的,你不能确定你的快递什么时候到。

技术应用角度的信号

1. 用户输入命令,在Shell下启动一个前台进程。 . 用户按下Ctrl-C ,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程 . 前台进程因为收到信号,进而引起进程退出

注意 

1. Ctrl-C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程 结束就可以接受新的命令,启动新的进程。

用Ctrl+c只能杀死前台进程,后台进程用kill -9命令可以杀死

2. Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl-C 这种控制键产生 的信号

3. 前台进程在运行过程中用户随时可能按下 Ctrl-C 而产生一个信号,也就是说该进程的用户空间代码执行 到任何地方都有可能收到 SIGINT 信号而终止,所以信号相对于进程的控制流程来说是异步 (Asynchronous)的

信号概念

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

 用kill -l命令可以察看系统定义的信号列表
信号处理常见方式概览 

可选的处理动作有以下三种:

1. 忽略此信号。

2. 执行该信号的默认处理动作。

3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉 (Catch)一个信号。

信号产生 

a.信号的产生对于进程来说是异步的

b.进程该如何处理对应产生的信号?记录在哪里?
--> 先描述,再组织——>怎么描述一个个信号,用什么数据结构管理这个信号

——> 01来描述信号,用位图这种数据结构管理这个信号

如0000 0000 0000 0000 0000 0001 0000 0000

c.所谓的发送信号,本质就是写入信号,直接修改特定进程的信号位图中的特定比特位0->1

d.数据内核结构,只能由OS进行修改,无论后面我们有多少种信号产生的方式,最终必须有OS来完成最后的发送过程

认识常用接口
signal

这里的第一个参数表示信号的编号,第二个参数则是一个函数指针,可以指向要执行的函数 (信号处理的方法)

这里2号信号的默认执行方法是杀死进程,用signal函数可以改变信号的处理方式,这里直接用了自定义的信号处理动作

这里提出一个小问题,在调用signal方法的时候,handler方法被调用了吗?

答案是并没有,他知识在操作系统内部更改了2号信号的处理动作,并没有调用handler

那么handler方法在什么时候才被调用呢?当2号信号产生的时候!

用signal(2,handler)来执行用户动作的自定义捕捉

这里两个信号都做捕捉

注:ctrl+c 2号信号 ctrl+\ 3号信号

以此类推,是不是可以对所有的信号都走自定义动作的设置

所有信号都自定义是不行的

这里9号信号之所以能够杀死进程,是因为9号信号是管理员信号,是不能被自定义动作的

通过系统接口向进程发送信号
kill

写一个我们自己的kill命令

loop.cc

#include <iostream>
#include <unistd.h>

int main()
{
    while (true)
    {
        std::cout << "我是一个进程,我正在运行 ..., pid: " << getpid() << std::endl;
        sleep(1);
    }
}

mykill.cc

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

int count = 0;

void Usage(std::string proc)
{
    std::cout << "\tUsage: \n\t";
    std::cout << proc << " 信号编号 目标进程" << std::endl;
}

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    int signo = atoi(argv[1]);
    int target_id = atoi(argv[2]);
    int n = kill(target_id,signo);
    if(n!=0)
    {
        std::cerr<<errno<<":"<<strerror(errno)<<std::endl;
        exit(2);
    }

    }
raise
int raise(int sig)

 参数代表信号的编号

用法:“谁调用我,我就给谁发这个信号” 

abort

用来直接终止进程,谁调用就终止谁,在信号里是6号信号,这个接口信号在被自定义捕捉后依然会使进程退出,作用雷同与exit

由软件条件产生的信号
alarm

这里的信号是14号信号SIGALRM

这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。打个比方,某人要小睡一觉,设定闹钟为30分钟之后 响,20分钟后被人吵醒了,还想多睡一会儿,于是重新设定闹钟为15分钟之后响,“以前设定的闹钟时间还余下的时间”就 是10分钟。如果seconds值为0,表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数

由硬件条件产生的信号

我们平时在输入的时候,计算机怎么知道我们从键盘输入了数据呢?键盘是通过硬件中断的方式,通知系统,我们的键盘已经按下了

硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除 以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非 法内存地址,,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程 

如除0这个运算操作(8号信号),还有野指针问题等(11号信号)

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-01-10 02:40:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-10 02:40:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-10 02:40:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-10 02:40:04       18 阅读

热门阅读

  1. infer。。。。

    2024-01-10 02:40:04       37 阅读
  2. 第二百五十五回

    2024-01-10 02:40:04       36 阅读
  3. 联合union

    2024-01-10 02:40:04       36 阅读
  4. Unity3D 如何实现多玩家语音聊天详解

    2024-01-10 02:40:04       42 阅读
  5. linux 使用多版本 go goenv.sh

    2024-01-10 02:40:04       39 阅读
  6. Python程序中的异常处理解决方法

    2024-01-10 02:40:04       35 阅读