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

相关推荐
打鱼又晒网6 分钟前
linux网络套接字 | 深度解析守护进程 | 实现tcp服务守护进程化
linux·网络协议·计算机网络·tcp
良许Linux20 分钟前
0.96寸OLED显示屏详解
linux·服务器·后端·互联网
蜜獾云30 分钟前
docker 安装雷池WAF防火墙 守护Web服务器
linux·运维·服务器·网络·网络安全·docker·容器
小屁不止是运维31 分钟前
麒麟操作系统服务架构保姆级教程(五)NGINX中间件详解
linux·运维·服务器·nginx·中间件·架构
bitcsljl1 小时前
Linux 命令行快捷键
linux·运维·服务器
ac.char1 小时前
在 Ubuntu 下使用 Tauri 打包 EXE 应用
linux·运维·ubuntu
Cachel wood1 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Youkiup1 小时前
【linux 常用命令】
linux·运维·服务器
qq_297504611 小时前
【解决】Linux更新系统内核后Nvidia-smi has failed...
linux·运维·服务器
weixin_437398212 小时前
Linux扩展——shell编程
linux·运维·服务器·bash