前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux进程间通信 信号

Linux进程间通信 信号

作者头像
开源519
发布2021-03-15 15:06:33
2.5K0
发布2021-03-15 15:06:33
举报
文章被收录于专栏:开源519

信号是提供处理异步事件机制的软件中断。这些事情可以来自系统外部--例如系统产生中断符(通常Ctrl-C),或者来自程序或内核内部的活动,例如进程执行除以零的代码。作为一种进程间通信的基本形式,进程也可以给另一个进程发送信号。 --《linux系统编程》 ”

如上所述,信号可以实现进程间的通信。本章主要记录信号的使用方法。

概念

  • 信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
  • 信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。
  • 如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被 取消时才被传递给进程。
  • 信号有明确生命周期,首先产生信号,然后内核存储信号直到可以发送它,最后内核一旦有空闲,会适当处理信号。

产生

产生信号的方式有很多种,比如终端驱动程序,进程,系统。

  • 与进程终止相关的信号。当进程退出,或者子进程终止时,发出这类信号。
  • 与进程例外事件相关的信号。如进程越界,或企图写一个只读的内存区域(如程序正文区),或执行一个特权指令及其他各种硬件错误。
  • 与在系统调用期间遇到不可恢复条件相关的信号。如执行系统调用exec时,原有资源已经释放,而目前系统资源又已经耗尽。
  • 与执行系统调用时遇到非预测错误条件相关的信号。如执行一个并不存在的系统调用。
  • 在用户态下的进程发出的信号。如进程调用系统调用kill向其他进程发送信号。
  • 与终端交互相关的信号。如用户关闭一个终端,或按下break键等情况。
  • 跟踪进程执行的信号。

处理

进程对于处理信号的方式,可概括为如下三种方式:

  • 忽略信号 不采取任何操作。但是有两种信号不可忽略:SIGKILL和SIGSTOP。如此做的原因是系统管理员需要杀死和停止进程,如果进程能够忽略SIGKILL(使进程不能被杀死)SIGSTOP(使进程不能被停止),将破坏这一权利。
  • 捕获并处理信号 类似于硬件中断处理,内核会暂停正在执行的程序,并跳转到注册的信号响应处理函数中去。一旦处理完毕,会继续执行中断前的操作。(重要的不允许被打断的程序,必须先禁止处理信号)
  • 执行默认操作 该操作取决于被发送的信号,默认操作通常是终止进程。

过去,当一个信号被发送后,除了知道发生了一个信号之外,处理函数对于发生了什么一无所知。现在内核可以给处理函数提供大量的上下文,甚至信号能传递用户定义的数据,跟后来更高级的IPC通信机制一样。

接口使用

以上大概的记录,大概能明白信号是怎么一回事。重点是在理解信号的基础上,会使用信号实现进程间的通信,在软件设计交互时,多一个设计思路。

发送函数

  • kill(): 向其他进程发送信号

通常用法,kill给进程号为pid的进程发送信号sig。

头文件

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

原型

int kill(pid_t pid, int sig)

pid

0 sig被发送到当前进程所在进程组中每一个进程 -1 sig被发送到每个有权限发送信号的进程(除init进程外) <-1 sig被发送到进程组为-pid的每一个进程

sig

发送的信号量

返回值

0: 成功 -1:失败

  • raise(): 向自己所在进程发送信号

头文件

#include <signal.h>

原型

int raise(int sig)

sig

发送的信号量

返回值

0: 成功 -1:失败

接收函数

  • signal(): 注册信号响应函数

头文件

#include <signal.h>

原型

typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);

signum

监听的信号量

handler

SIG_DFL: 默认的信号处理程序 SIG_IGN: 忽视该信号 自定义响应函数

返回值

该函数返回信号处理程序之前的值,当发生错误时返回 SIG_ERR。

实例演示

功能: 设计两个C/S进程,其中一个client进程负责向server进程发送信号;server进程负责响应处理client信号。 演示:

代码如下

client.cpp

代码语言:javascript
复制
#include <stdio.h>
#include <signal.h>
#include <iostream>
#include <stdlib.h>
#include "common.h"

int main(int argc, char *argv[])
{
    int signo = 0;
    char mode[1] = {0};

    fprintf(stdout, "1: SIGUSR1\n");
    fprintf(stdout, "2: SIG_TEST1\n");
    fprintf(stdout, "3: default\n");
    while (1) {
        signo = 0;
        fprintf(stdout, "Select signal to send %s: ", argv[1]);
        scanf("%s", mode);
        switch(mode[0]) {
          case '1':
            signo = SIGUSR1;
            break; 
          case '2':
            signo = SIG_TEST1;
            break;
          default:
            signo = SIG_DEFAULT;
            break;
        }
        //fprintf(stdout, "mode: %d %d\n", signo, SIG_DEFAULT);
        kill(atoi(argv[1]), signo);
    }
    return 0;
}

server.cpp

代码语言:javascript
复制
#include <stdio.h>
#include <signal.h>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include "common.h"

void SignalHandler(int sig)
{
    fprintf(stdout, "Receive signal: ");
    switch(sig) {
      case SIGUSR1:
        fprintf(stdout, "SIGUSR1\n");
        break;
      case SIG_TEST1:
        fprintf(stdout, "SIG_TEST1\n");
        break;
      default:
        fprintf(stdout, "default\n");
        break;
    }
}

int main(int argc, char *argv[])
{
    fprintf(stdout, "Pid: %d\n", getpid());
    signal(SIGUSR1, SignalHandler);
    signal(SIG_TEST1, SignalHandler);
    signal(SIG_DEFAULT, SignalHandler);
    while(1);

    return 0;
}

参考

《linux系统编程》

https://www.cnblogs.com/electronic/p/10939769.html https://blog.csdn.net/dxpqxb/article/details/78251551

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源519 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概念
  • 产生
  • 处理
  • 接口使用
  • 实例演示
  • 代码如下
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档