【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, 合理.

相关推荐
LiQiang331 小时前
Ubuntu2404修改国内镜像
linux
杰哥技术分享1 小时前
Ubuntu 22.04安装SQL Server指南
linux·运维·ubuntu·sqlserver
遇见火星1 小时前
ubuntu18.04 升级Ubuntu 20.04
linux·运维·ubuntu·系统升级
x县豆瓣酱1 小时前
【第四节】ubuntu server安装docker
linux·ubuntu·docker
Gene_20222 小时前
【TOOL】ubuntu升级cmake版本
linux·运维·ubuntu
思序 LogicFlow2 小时前
关于在Linux上部署 SecretFlow --- P2P部署模式
linux·服务器
YC运维3 小时前
网络配置综合实验全攻略(对之前学习的总结)
linux·服务器·网络
平凡灵感码头4 小时前
什么是 Bootloader?怎么把它移植到 STM32 上?
linux·soc
无敌的牛4 小时前
Linux基础开发工具
linux·运维·服务器
Edingbrugh.南空4 小时前
实战指南:用pmap+gdb排查Linux进程内存问题
linux·运维·服务器