【Linux】进程信号及相关函数/系统调用的简单认识与使用

文章目录

  • 前言
  • 一、相关函数/系统调用
    • [1. signal](#1. signal)
    • [2. kill](#2. kill)
    • [3. abort (库函数)](#3. abort (库函数))
    • [4. raise (库函数)](#4. raise (库函数))
    • [5. alarm](#5. alarm)

前言

现实生活中, 存在着诸多信号, 比如红绿灯, 上下课铃声...我们在接收到信号时, 就会做出相应的动作. 对于进程也是如此的, 进程也会收到来自 OS 发出的信号, 根据信号的不同也会做出不同的动作, 进程在收到信号时也并不一定会立即执行, 也可以在适当的时候在执行该信号对应的动作, 一般信号常见处理方式有如下三种:

  1. 忽略此信号.
  2. 执行该信号的默认处理动作.
  3. 提供一个信号处理函数, 要求内核在处理该信号时切换到用户态执行这个处理函数, 这种方式称为捕捉一个信号.

而在进程中用以保存信号的容器可以是一个位图, 通过 0, 1 来表示是否收到某信号.

在 Linux 中, 可以通过指令:

bash 复制代码
kill -l

来查看系统定义的信号列表:

通过指令:

bash 复制代码
man 7 signal

可以查看关于信号的详细说明:

在 Linux 中可以通过指令:

bash 复制代码
kill -信号编号 进程pid

来对指定进程发送指定信号.

一、相关函数/系统调用

1. signal

头文件: #include <signal.h>

函数声明: sighandler_t signal(int signum, sighandler_t handler);

  • signum: 被设置的信号编号.
  • handler: 被设置的信号的新的处理函数, 是一个回调函数, 通过用户传递.
  • sighandler_t: 是一个函数指针, 可以指向一个返回值为 void, 参数为 int 的函数, 以下是系统中的 typedef:
    typedef void (*sighandler_t)(int);

功能: 将指定的信号的处理函数覆盖为 handler.

示例代码:

cpp 复制代码
#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;

void sighandler(int signo)
{
    cout << "void sighandler(int signo): " << signo << endl;
}

int main()
{
    signal(2, sighandler);
    while(1)
    {
        cout << "Hello" << endl;
        sleep(1);
    }
    return 0;
}

运行结果:

实际 Ctrl + C 就是编号为 2 的信号, 平常通过 Ctrl + C 向进程发送 SIGINT(2) 号信号, 可以终止进程, 但是把信号 2 的处理函数换成了自定义的, 所以在终端按下 Ctrl + C 时执行我们自定义的函数.

PS: 9, 18, 19 号信号即时被重定向了新的处理函数也没用, 该信号仍然会执行原本的处理函数.

2. kill

头文件:

#include <sys/types.h>

#include <signal.h>

函数声明: int kill(pid_t pid, int sig);

  • pid: 目标进程pid.
  • sig: 向目标发送的信号编号.

功能: 向指定进程发送指定信号.

示例代码:

cpp 复制代码
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
using namespace std;

int main()
{
    for(int i = 0; i < 10; ++i)
    {
    	if(i == 5)
        {
            kill(getpid(), 9);
        }
        cout << i << ":Hello" << endl;
        sleep(1);
    }
    return 0;
}

运行结果:

在输出 5 条语句后, 向自己发送 9 号信号, 直接终止自己了.

3. abort (库函数)

头文件: #include <stdlib.h>

函数声明: void abort(void);

功能: 向调用进程发送终止信号.

示例代码:

cpp 复制代码
#include <iostream>
#include <cstdlib>
#include <unistd.h>
using namespace std;

int main()
{
    for(int i = 0; i < 10; ++i)
    {
        if(i == 5)
        {
            abort();
        }
        cout << i << ":Hello" << endl;
        sleep(1);
    }
    return 0;
}

运行结果:

4. raise (库函数)

头文件: #include <signal.h>

函数声明: int raise(int sig);

  • 返回值: 成功调用返回 0, 失败返回非零整数.
  • sig: 信号编号.

功能: 向调用进程发送指定信号.

示例代码:

cpp 复制代码
#include <iostream>
#include <csignal>
#include <unistd.h>
using namespace std;

int main()
{
    for(int i = 0; i < 10; ++i)
    {
        if(i == 5)
        {
            raise(9);
        }
        cout << i << ":Hello" << endl;
        sleep(1);
    }
    return 0;
}

运行结果:

5. alarm

头文件: #include <unistd.h>

函数声明: unsigned int alarm(unsigned int seconds);

  • 返回值: 返回一个无符号整数, 表示前一个闹钟剩于的秒数, 打个比方, 闹钟设置为 30s 后响, 但是在 20s 的时候就收到了 SIGALRM(14) 信号, 此时闹钟会提前响, 返回值就为 30 - 20 = 10.
  • seconds: 多少秒后响铃.

功能: 在过了 seconds 秒以后终止调用进程.

示例代码:

cpp 复制代码
#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
    alarm(1);
    for(int i = 0; ; i++)
    {
        cout << i << ":Hello" << endl;
    }
    return 0;
}

运行结果:

可以看到, 在 1 秒钟后闹钟响了, 进程也就被终止了.

接下来通过另一段代码查看返回值:

cpp 复制代码
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;

void sighandler(int signo)
{
    cout << "void sighandler(int signo): " << signo << endl;
    int n = alarm(10);
    cout << "n: " << n << endl;
}

int main()
{
    cout << "pid:" << getpid() << endl;
    signal(SIGALRM, sighandler);
    alarm(10);
    while(1);
    return 0;
}

运行结果:

可以看到, 在闹钟设定后, 以我最快的速度给调用闹钟的进程发送 14 号信号之后, 返回的剩于秒数为 8s, 也就是说闹钟只跑了 2s, 而后又设置了一个 10s 后响的闹钟, 这次没有提前发送 14 号信号, 它正常跑完, 返回的剩于秒数为 0s, 合理.

相关推荐
fangjianj2 小时前
LAMP 项目部署
linux
mengge.cloud3 小时前
0909华为云计算类综合服务小白实验教程
linux·运维·服务器·网络·华为云·云计算
fangjianj3 小时前
自有服务及软件包
linux
风哥2号5 小时前
数据库教程FGMT03‑生产环境Linux+Oracle19c+ASM安装配置与项目实战
linux·数据库
一木 之林5 小时前
七、一-AI 工程实践、插件化调试与软件交付
java·linux·c++
zhengqweasd6 小时前
Linux网络(一):服务器数据包从网卡到应用程序,完整收包流程详解
linux·服务器·网络
脚踏实地,坚持不懈!6 小时前
Linux 6.18.7 内存分配与回收 —— 代码梳理
linux·运维·网络
byte轻骑兵6 小时前
【BlueZ 】hci 模块:用户态 HCI 层的核心封装与消息处理
linux·人工智能·bluez·电脑蓝牙·嵌入式蓝牙
Android系统攻城狮7 小时前
Linux Gstreamer深度解析之gst_audio_format_build_integer调用流程与实战(十二)
linux·运维·服务器·音视频·车载音视频·gstreamer进阶
吴声子夜歌8 小时前
Shell脚本——流程控制:for循环
linux·运维·shell