Linux进程通信之信号

2024-01-09 14:38:37

目录

1、Linux中的信号编号及其名字

2、信号的处理:

3、信号的使用

1.入门版

1.信号发送函数kill

示例:

2.信号处理函数的注册signal

示例:

2.高级版

1. 信号处理发送函数sigqueue

示例:

2.信号处理函数的注册sigaction

?示例:

Linux 中信号是一种进程间通信的机制,用于在异步事件发生时通知进程。实际信号是软中断,许多重要的程序都需要处理信号。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。

1、Linux中的信号编号及其名字

具体的信号名称可以使用kill -l来查看信号的名字以及序号,信号是从1开始编号的,不存在0号信号。kill对于信号0有特殊的应用。

这里介绍一些常用的信号

  1. SIGHUP (1): 挂起信号。通常在与终端连接的会话结束时发送给进程。

  2. SIGINT (2): 中断信号。通常由用户在终端按下 Ctrl+C 时生成,用于中断正在运行的程序。

  3. SIGQUIT (3): 退出信号。类似于 SIGINT,但当用户在终端按下 Ctrl+\ 时生成,通常用于生成核心转储文件。

  4. SIGILL (4): 非法指令信号。表示进程尝试执行一个非法的指令,通常是因为程序错误导致的。

  5. SIGTRAP (5): 跟踪/断点陷阱信号。通常由调试器使用,用于实现断点等调试功能。

  6. SIGABRT (6): 中止信号。通常由 abort 函数生成,表示进程发生了严重错误。

  7. SIGFPE (8): 浮点异常信号。表示进程执行了一个无效的浮点操作,例如除以零。

  8. SIGKILL (9): 杀死信号。用于强制终止进程。该信号不能被捕获或忽略。

  9. SIGSEGV (11): 段错误信号。表示进程访问了无效的内存地址,通常是因为编程错误导致的。

  10. SIGPIPE (13): 管道破裂信号。通常在进程尝试向已关闭的管道写入数据时生成。

  11. SIGALRM (14): 定时器超时信号。通常由 alarm 函数生成,表示定时器已过期。

  12. SIGTERM (15): 终止信号。用于请求进程正常终止。进程可以捕获此信号,并执行清理工作后终止。

  13. SIGUSR1 (10): 用户定义信号 1。可以由用户定义为特定用途。

  14. SIGUSR2 (12): 用户定义信号 2。可以由用户定义为特定用途。

  15. SIGCHLD (17): 子进程状态改变信号。通常在子进程退出或停止时生成。

  16. SIGCONT (18): 继续执行信号。通常用于从停止状态继续进程的执行。

  17. SIGSTOP (19): 停止信号。用于暂停进程的执行。与 SIGKILL 不同,SIGSTOP 可以被捕获并处理。

  18. SIGTSTP (20): 终端停止信号。通常由用户在终端按下 Ctrl+Z 时生成,用于暂停正在运行的进程。

  19. SIGTTIN (21): 后台进程尝试读取标准输入时的信号。

  20. SIGTTOU (22): 后台进程尝试写入标准输出时的信号。

2、信号的处理:

信号的处理有三种方法,分别是:忽略、捕捉和默认动作

忽略信号:大多数信号都可以通过将其处理动作设置为 SIG_IGN(表示忽略)来屏蔽,例如

#include <signal.h>
int main() {
    // 忽略 SIGUSR1 信号
    signal(SIGUSR1, SIG_IGN);
    // 进程执行其他任务 
    return 0;
}

但是,对于 SIGKILLSIGSTOP 两个信号,是不能被忽略的。这是因为它们是系统用于可靠终止和停止进程的方式

捕捉信号:用户可以通过注册一个信号处理函数来捕捉信号。当信号发生时,内核将调用用户定义的函数。

系统默认动作:每个信号都有一个默认的处理动作,通常比较粗暴,例如终止进程。可以通过 man 7 signal 命令查看系统对每个信号的默认处理动作。用户可以选择采用默认动作,也可以通过设置信号处理函数来改变默认行为。

3、信号的使用

信号处理函数的注册

信号处理函数的注册不只一种方法,分为入门版和高级版

  1. 入门版:函数signal
  2. 高级版:函数sigaction

信号处理发送函数

信号发送函数也不止一个,同样分为入门版和高级版
1.入门版:kill
2.高级版:sigqueue

1.入门版

1.信号发送函数kill

kill 函数用于向指定进程发送信号

函数原型

int kill(pid_t pid, int sig);

  • pid 参数是目标进程的进程 ID(Process ID)。可以使用 getpid 函数获取当前进程的进程 ID,或者使用其他手段获取要发送信号的目标进程的进程 ID。
  • sig 参数是要发送的信号编号。可以使用预定义的宏(如 SIGTERMSIGKILL 等)来表示信号,也可以使用具体的信号编号。
示例:

通过命令行参数向指定进程发送信号

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>

int main(int argc, char **argv) {
    // 检查命令行参数数量
    if (argc < 3) {
        printf("用法:%s <信号编号> <进程ID>\n", argv[0]);
        exit(-1);
    }

    // 打印命令行参数信息
    printf("Command: %s, Signal: %s, Process ID: %s\n", argv[0], argv[1], argv[2]);

    // 将命令行参数转换为整数,得到信号编号和目标进程的进程 ID
    int sig = atoi(argv[1]);
    int pid = atoi(argv[2]);

    // 打印目标进程的进程 ID 和要发送的信号编号
    printf("目标进程ID:%d,信号编号:%d\n", pid, sig);

    // 使用 kill 函数向指定进程发送信号
    if (kill(pid, sig) == 0) {
        printf("信号发送成功 %d\n", pid);
    } else {
        perror("kill");
        exit(-1);
    }

    return 0;
}
2.信号处理函数的注册signal

用于设置信号处理函数,捕捉到相应的信号后调用处理函数

函数原型

typedef void (*sighandler_t)(int);(信号处理函数)typedef将这个类型的指针函数取了一个别名sighandler_t

sighandler_t signal(int signum, sighandler_t handler);

signal函数用于注册信号处理函数,接受两个参数:

  • signum:要处理的信号的编号。
  • handler:指向用户定义的信号处理函数的指针。设置成SIG_IGN表示忽略对于的信号

函数返回一个先前与该信号相关联的处理函数的指针,如果调用失败返回SIG_ERR,返回值的作用为了方便后续的操作,比如在程序结束时回复原来的信号处理函数。

示例:

使用注册信号处理函数捕捉(Ctrl+C) 键

#include <signal.h>
#include <stdio.h>

// 定义信号处理函数的类型
typedef void (*sighandler_t)(int);

// 函数原型:注册信号处理函数
// sighandler_t signal(int signum, sighandler_t handler);

// 信号处理函数,处理接收到的信号
void handle(int signum)
{
    printf("接收到信号 %d\n", signum);
}

int main()
{
    // 定义保存信号处理函数返回值的变量
    sighandler_t handler_t;

    // 使用 signal 函数注册信号处理函数
    handler_t = signal(SIGINT, handle);

    // 检查 signal 函数的返回值是否出错
    if (handler_t == SIG_ERR) {
        printf("signal 函数执行出错!\n");
    }

    // 进入无限循环,等待信号的到来
    while (1);

    return 0;
}

2.高级版

1. 信号处理发送函数sigqueue

函数用于向指定进程发送信号,与 kill 函数相比,sigqueue 提供了更多的灵活性。它可以携带一个整数值或指针作为辅助数据,用于传递更多信息给接收信号的进程

函数原型

int sigqueue(pid_t pid, int sig, const union sigval value);

  • pid:目标进程的进程 ID,指定信号要发送到的进程。
  • sig:要发送的信号的编号。
  • value:一个 union sigval 类型的结构,用于携带辅助数据

union sigval 结构定义如下

union sigval {
? ? int sival_int; ? ? ?// 整数值
? ? void *sival_ptr; ? ?// 指针值
};

?函数返回值为 0 表示成功,-1 表示失败,错误原因存储在 errno 中。

示例:
#include <signal.h>
#include <stdio.h>

// int sigqueue(pid_t pid, int sig, const union sigval value);
// union sigval {
//     int   sival_int;
//     void *sival_ptr;
// };

int main(int argc, char **argv) {
    // 定义 sigval 结构体用于携带辅助数据
    union sigval value;
    value.sival_int = 100;  // 替换成要传递的整数值

    // 从命令行参数获取目标进程的进程 ID 和要发送的信号编号
    int pid = atoi(argv[2]);
    int sig = atoi(argv[1]);

    // 使用 sigqueue 函数向指定的进程发送信号,并携带辅助数据
    if (sigqueue(pid, sig, value) == 0) {
        // 信号发送成功
        printf("信号成功发送至进程 %d,携带的整数值为 %d\n", pid, value.sival_int);
    } else {
        perror("sigqueue");
    }

    return 0;
}
2.信号处理函数的注册sigaction

sigaction 函数用于检查或修改指定信号的处理方式。这个函数允许程序员指定一个信号的处理函数,并提供了一些额外的控制选项。

函数原型

int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

  • signum:要操作的信号的编号。
  • act:一个指向 struct sigaction 结构体的指针,用于指定新的信号处理方式。
  • oldact:一个指向 struct sigaction 结构体的指针,用于保存原来的信号处理方式

struct sigaction 结构体定义如下

struct sigaction {
    void (*sa_handler)(int);          // 函数指针,指定信号处理函数
    void (*sa_sigaction)(int, siginfo_t *, void *);  // 备用的信号处理函数
    sigset_t sa_mask;                 // 在信号处理函数执行期间阻塞的信号集
    int sa_flags;                     // 用于指定一些额外的标志
    void (*sa_restorer)(void);        // 废弃,无需关心
};

?sigaction 函数返回 0 表示成功,-1 表示出错,错误信息存储在 errno

sigaction函数的详细介绍有小红旗的是我们重点需要注意的?

?

?示例:
#include <signal.h>
#include <stdio.h>

// 信号处理函数
void handler(int signum, siginfo_t *data, void *estimate)
{
    printf("Received signal: %d\n", signum);

    // 检查是否有传递的附加信息
    if (estimate != NULL)
    {
        printf("Sender PID: %d, Data: %d, Value: %d\n", data->si_pid, data->si_int, data->si_value.sival_int);
    }
}

int main()
{
    // 定义 struct sigaction 结构体
    struct sigaction act;
    act.sa_sigaction = handler;  // 设置信号处理函数为 handler
    act.sa_flags = SA_SIGINFO;   // 设置 sa_flags,表示使用 sa_sigaction 作为处理函数

    // 注册信号处理函数
    sigaction(10, &act, NULL);

    printf("Process PID: %d\n", getpid());

    // 让程序保持运行,等待信号
    while (1);

    return 0;
}

文章来源:https://blog.csdn.net/weixin_58198422/article/details/135442934
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。