26信号处理一:从闹钟到进程控制的奥秘

🔥个人主页: Milestone-里程碑

❄️个人专栏: <<力扣hot100>> <<C++>><<Linux>>

<<Git>><<MySQL>>

🌟心向往之行必能至

目录

一.前言

[1.1 什么是信号](#1.1 什么是信号)

1.2基本结论(非常重要)

1.3查看信号

二.产生信号

2.1键盘产生信号:发送给前台进程

我们在程序运行时,如果使用ctrl+c,发现程序会终止

[2.1.1 前后进程](#2.1.1 前后进程)

[2.1.2 前后台进程的部分命令](#2.1.2 前后台进程的部分命令)

[2.1.3 记录信号](#2.1.3 记录信号)

[2.1.4 捕捉信号](#2.1.4 捕捉信号)

[2.2 系统调用产生信号](#2.2 系统调用产生信号)

[2-2-1 kill](#2-2-1 kill)

[2.2.2 raise](#2.2.2 raise)

[2.2.3 abort](#2.2.3 abort)

[2.3 调用系统命令产生信号](#2.3 调用系统命令产生信号)

三总结:


一.前言

信号与前面的信号量的关系就是老婆:老婆饼,两者毫无关系

1.1 什么是信号

生活中的闹钟,信号灯,肚子叫,铃声等等都是信号

信号是一种发送给进程,用来异步通知的机制

何为同步 异步

老师上课时,张三肚子疼,去上厕所.老师继续讲,这为异步,等张三回来叫,这叫同步

1.2基本结论(非常重要)

  • 信号处理,进程在信号没有产生的时候,早就知道信号该如何处理了(在红灯亮之前,你就知道该如何处理)
  • 信号的处理,不是立即处理,而是可以等一会进行处理,合适的时候(必须记录,后续会讲),再进行处理(外卖到了,你可以打完你这局游戏再去拿)
  • 人能识别信号,是被提前"教育"过的,进程也是,OS被设计时,进程早已经内置了对信号的识别和处理方式了
  • 信号源非常多->给进程产生信号的,信号源,非常多(要进行管理)

1.3查看信号

bash 复制代码
lcb@hcss-ecs-1cde:~$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

一共有61个信号.我们重点关注前31个(延时信号),后面的实时信号不关注

二.产生信号

收到信号,处理信号有三种方式

1.默认处理信号

2.自定义处理信号

3.忽略处理

产生信号的方式非常多

2.1键盘产生信号:发送给前台进程

我们在程序运行时,如果使用ctrl+c,发现程序会终止

证明发送的就是信号,我们可以通过自定义处理信号证明

需要使用到下面这个接口进行自定义

bash 复制代码
  #include <signal.h>

       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);

代码

bash 复制代码
#include<iostream>
#include<signal.h>
using namespace std;
void hendlerdo(int srg)
{
    cout<<"接到一个信号:"<<srg<<endl;
}
int main()
{
    signal(SIGINT,hendlerdo);
    while(true)
    {
        sleep(2);
        cout<<"1"<<endl;
    }
       return 0;
}

SIGINT对应的就是我们的ctrl+c,可结果自定义后,不再终止了,我们可以使用ctrl+\进行终止(未被自定义)


2.1.1 前后进程

遇到的奇怪现象

在上面的代码运行中,此处如果你使用ll pwd等等命令符,会发现无法使用

而如果我们在程序运行时,后面多加个&,却又可以使用,但ctrl+这些却又无法使用

这其实与键盘的输入对象和前后进程有关

知识

#/$./XXX ->前台进程

#/$ ./YYY& ->后台进程

命令行shell进程 ->前台进程
后台进程无法从标准输入获取信息(键盘),前台进程可以从标准输入获取信息

但两种进程都可以在标准输出上打印

原因:

键盘只有一个,输入数据,一定要发送给1个确定的进程,而前台进程只有一个,后台进程可以有多种

OS在启动时,自动会打开bash进程作为前台进程.而我们不加上&打开./test,那么./test这个子进程就成为了前台进程,而bash进程就自动变为了后台进程,无法获取信息,执行命令了


回归前述

前面我们提过fork会产生一个子进程,但此处的子进程会继承父进程,父子进程都是前台进程,但是如果父进程杀死,子进程就成为了孤儿进程,此时就会被领养,成为后台进程

2.1.2 前后台进程的部分命令

jobs---查看所有后台进程

fg任务号----将特定的后台进程,变为前台进程

ctrl+z---将前台进程暂停,并将其转入后台进程

bg任务号----:让后台进程回复运行

bash 复制代码
lcb@hcss-ecs-1cde:~/code/linux/first/12$ ./test
jobgs
1
1
^Z
[3]+  Stopped                 ./test
lcb@hcss-ecs-1cde:~/code/linux/first/12$ bg 3
[3]+ ./test &
1
lcb@hcss-ecs-1cde:~/code/linux/first/12$ fd 1
f1
fd 3
1
Command 'fd' not found, but can be installed with:
apt install fdclone
Please ask your administrator.
lcb@hcss-ecs-1cde:~/code/linux/first/12$ 1
fg 3
./test
1
1
^C接到一个信号:2
1
^\Quit (core dumped)

2.1.3 记录信号

我们前面提过,发送了信号,会进行记录,合适的时候再执行,那么记录在哪呢?

bash 复制代码
struct task_struct{
    unsigned int sigs;
};

使用sigs进行记录,即位图

sigs的位置用来记录1到31号命令编号

位置的内容用来记录是否收到

根据前面所学知识,知道,task_struct属于OS内部的操作系统,用户无法修改,而修改sigs,就是修改内核资源,因此操作系统要提供接口进行系统调用

不管信号怎么产生,发送,在底层,都是由OS发送

维度 信号 通信 IPC
用途 事件通知、进程控制 数据 / 消息传递
数据量 仅传递信号类型(无数据) 可传递大量结构化数

2.1.4 捕捉信号

bash 复制代码
#include<iostream>
#include<signal.h>
using namespace std;
void hendlerdo(int srg)
{
    cout<<"接到一个信号:"<<srg<<endl;
}
int main()
{
    for(int i=0;i<32;++i)
    signal(i,hendlerdo);
    while(true)
    {
        sleep(2);
        cout<<"1"<<endl;
    }
       return 0;
}

实验发现,除了9号信号和19号信号(杀死,终止),其他都被捕获成功,特殊处理:就是为了防止病毒

2.2 系统调用产生信号

2-2-1 kill

kill 命令是调⽤ kill 函数实现的。 kill 函数可以给⼀个指定的进程发送指定的信号。

bash 复制代码
NAME
kill - send signal to a process
SYNOPSIS
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
RETURN VALUE
On success (at least one signal was sent), zero is returned. On error,
-1 is returned, and errno is set appropriately.

使用:

mykill.cc

bash 复制代码
#include <iostream>
#include <sys/types.h>
#include <signal.h>
#include<string>
using namespace std;
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        cout << "./mykill signumber pid" << endl;
        return 1;
    }
    int signum = stoi(argv[1]);
    pid_t pid = stoi(argv[2]);
    int n = kill(signum, pid);
    if (n == 0)
    {
        cout << "pid:" << pid << "成功接受一个信号" << endl;
    }
    return 0;
}

test.cc

bash 复制代码
#include<iostream>
#include<signal.h>
#include<sys/types.h>
using namespace std;
void hendlerdo(int srg)
{
    cout<<"接到一个信号:"<<srg<<endl;
}
int main()
{
    for(int i=0;i<32;++i)
        signal(i,hendlerdo);
    while(true)
    {
        sleep(1);
        cout<<"pid:"<<getpid()<<endl;
        cout<<"1"<<endl;
    }
       return 0;
}

2.2.2 raise

raise 函数可以给当前进程发送指定的信号(⾃⼰给⾃⼰发信号)。

bash 复制代码
NAME
raise - send a signal to the caller
SYNOPSIS
#include <signal.h>
int raise(int sig);
RETURN VALUE
raise() returns 0 on success, and nonzero for failure

每隔一秒给自己发送一个信号2

bash 复制代码
#include<iostream>
#include<signal.h>
#include<sys/types.h>
using namespace std;
void hendlerdo(int srg)
{
    cout<<"接到一个信号:"<<srg<<endl;
}
int main()
{
    for(int i=0;i<32;++i)
        signal(i,hendlerdo);
    while(true)
    {
        sleep(1);
        // cout<<"pid:"<<getpid()<<endl;
        // cout<<"1"<<endl;
        raise(2);
    }
       return 0;
}

2.2.3 abort

abort 函数使当前进程接收到信号⽽异常终⽌。

bash 复制代码
NAME
abort - cause abnormal process termination
SYNOPSIS
#include <stdlib.h>
void abort(void);
RETURN VALUE
The abort() function never returns.
// 就像exit函数⼀样,abort函数总是会成功的,所以没有返回值。
bash 复制代码
#include<iostream>
#include<signal.h>
#include<sys/types.h>
using namespace std;
#include<cstdlib>
void hendlerdo(int srg)
{
    cout<<"接到一个信号:"<<srg<<endl;
}
int main()
{
    for(int i=0;i<32;++i)
        signal(i,hendlerdo);
    while(true)
    {
        sleep(1);
        // cout<<"pid:"<<getpid()<<endl;
        // cout<<"1"<<endl;
        // raise(2);
        abort();
    }
       return 0;
}

2.3 调用系统命令产生信号

即使用kill -signum pid

三总结:

上面产生信号的方式,都是让OS发送信号来修改比特位

相关推荐
We་ct1 小时前
LeetCode 102. 二叉树的层序遍历:图文拆解+代码详解
前端·算法·leetcode·typescript
jghhh011 小时前
基于C# WinForm实现自动在线升级的方案
开发语言·c#
wanhengidc1 小时前
云手机 打造云端算力
运维·服务器·网络·游戏·智能手机
Gofarlic_OMS1 小时前
LS-DYNA许可证全局状态及集群计算资源使用可视化监控大屏
运维·开发语言·算法·matlab·自动化
阿里嘎多学长1 小时前
2026-02-25 GitHub 热点项目精选
开发语言·程序员·github·代码托管
载数而行5201 小时前
算法系列4之插入排序
数据结构·c++·算法·排序算法
会员果汁1 小时前
二分搜索-C
c语言·算法
智者知已应修善业1 小时前
【查找指定字符串首位置与数量不区分大小写完整匹配】2025-5-3
c语言·c++·经验分享·笔记·算法
fengfuyao9851 小时前
基于局部均值分解(LMD)的MATLAB信号分解程序实现
算法·matlab·均值算法