首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Linux笔记(15)| Linux的信号

Linux笔记(15)| Linux的信号

作者头像
飞哥
发布于 2020-11-13 06:42:38
发布于 2020-11-13 06:42:38
2.8K00
代码可运行
举报
运行总次数:0
代码可运行

今天要分享的是Linux中的信号机制,信号是一种软件中断,是一种处理异步事件的方法,可以很好地在多个进程之间进行同步和简单的数据交换。

一、发送信号

发送信号通常有三种方式,分别是使用kill、raise、sigqueue函数

1、kill函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int kill(pid_t pid,int sig);

第一个参数代表向谁发送,第二个参数代表发送什么信号。调用成功返回0,调用失败返回-1.

2、raise函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int raise(int sig);

这个是向自身发送一个信号,等价于

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
kill(getpid(),sig);

3、sigqueue函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int sigqueue(pid_t pid,int sig,const union sigval value);

其中第三个参数的形式为

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef union sigval
{
int sival_int;
void *sival_ptr;
}sigval_t;

这个函数除了能发送信号之外,还能携带一些参数,这些参数就保存在共用体里面。

下面写一个简单的代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<signal.h>
#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char* argv[])
{
    pid_t pid;
    pid=fork();
    if(pid==0){
        printf("This is child process\n");
        sleep(10);
        printf("does't receive abort signal\n");
    }
    else{
        printf("This is parent process\n");
        sleep(5);
        if(kill(pid,SIGABRT)==-1){
            printf("kill function failed\n");
        }
    }
    return 0;
}

在这里使用fork创建了一个子进程,子进程本来调用sleep函数要睡眠10秒,但是父进程在睡眠5秒后向子进程发送一个中止的信号,导致子进程在5s时中止了。从下面的图中也可以看到原来两个进程都是处于睡眠态,后面就结束了进程。

二、信号的注册和响应

前面讲了三种发送信号的方式,但是光发送信号还不够,对于接收方来说,还得对信号进行处理。

一般可以使用signal函数和sigaction函数来注册信号。

1、signal函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void (*signal(int signum,void(*handler)(int)))(int);

这个函数原型看起来很复杂,可以难倒很多人,我们可以对他进行简化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);

这样就清楚很多了。首先看sighandler_t他是一个函数指针,输入参数是int类型,无返回值。

再看signal这个函数,他有两个输入参数,第一个是int类型,第二个是一个函数指针,另外,他返回的也是一个函数指针。

只有对函数指针非常熟悉,才能看懂上面这个表达式。如果还是不懂,可以对比下面这个表达式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
char* function(int a,char* b);

这个应该很容易看出是一个函数了吧,两个输入参数,返回一个char*。

如果这个可以看懂,那么上面那个其实是类似的,只不过他不是char* ,而是一个 函数指针类型:void(*)(int).

signal函数的第一个参数是信号类型,第二个参数是函数指针,也就是跳转到哪里去执行。也就是说,当收到第一个参数表示的信号之后,就会跳转到第二个参数指向的代码段去执行。

2、sigaction函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int sigaction(int signum, const struct sigaction *act,
                  struct sigaction *oldact);

这个函数不比上面的简单,其中第二个参数里的结构体类型展开是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
}

在这个结构体中,成员 sa_handler 是一个函数指针,其含义与 signal 函数中的信号处理函数类似。成员sa_sigaction 则是另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息。当 sa_flags 成员的值包含了 SA_SIGINFO 标志时,系统将使用 sa_sigaction 函数作为信号处理函数,否则使用 sa_handler 作为信号处理函数。

接下来写一个简单的代码,来应用一下上面的几个函数。实现的需求就是创建一个子进程,父进程每隔一秒钟向子进程发送一个信号,子进程收到信号之后往一个txt文档中写入一句话。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<signal.h>
#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
void signalDeal(int sig,siginfo_t *info,void*t);

int fg=0;
int main(int argc,char* argv[])
{
    if(argc!=2){
        printf("please input param\n");
        return -1;
    }
    char writebuff[]="This is a test\n";
    int count=0,seektemp=0,j=0;
   int fd=open(argv[1],O_RDWR | O_CREAT,S_IRWXU);       //open a file
   pid_t pid=fork();
   if(pid==0)                            //child process
   {
        struct sigaction act;
        act.sa_sigaction=signalDeal;
        sigemptyset(&act.sa_mask);
        act.sa_flags=SA_SIGINFO;
        sigaction(SIGUSR1,&act,NULL);    
       while(1)
       {
        while(!fg);
        fg=0;
        if(count==0){                         //first write
            int ret=write(fd,writebuff,strlen(writebuff));
            if(ret==-1){
                printf("write failed\n");
            }
            seektemp=lseek(fd,0,SEEK_CUR);
            count++;
        }
        else{                                 //not first write
            j=strlen(writebuff)*count;             //offset
            seektemp=lseek(fd,j,SEEK_SET);
            int ret=write(fd,writebuff,strlen(writebuff));
            if(ret==-1){
                printf("write failed\n");
            }
            count++;
        }
       }
       
   }
   else {                               //parent process
        
        while(1)
        {
            sleep(1);
            int ret=kill(pid,SIGUSR1);
            if(ret==-1){
                printf("send signal failed\n");
            }
            else{
                printf("send signal success\n");
            }
        }
        
   }
}

void signalDeal(int sig,siginfo_t *info,void*t)
{
    if(sig==SIGUSR1)
    {
        fg=1;
    }
}

在命令行输入

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gcc test.c
./a.out a.txt

之后可以看到生成了一个a.txt文档,并且文档里有我们写入的内容

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

本文分享自 电子技术研习社 微信公众号,前往查看

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
linux系统编程之信号(四):信号的捕捉与sigaction函数
本文介绍了Linux信号处理的基础知识,包括信号的来源、信号的发送与接收、信号的默认处理、信号的捕捉和处理、信号的屏蔽与解除、以及多线程环境中信号的处理方法。
s1mba
2017/12/28
4.1K0
linux系统编程之信号(四):信号的捕捉与sigaction函数
OpenHarmony 内核源码分析(信号生产篇) | 注意结构体的名字和作用.
关于信号篇,本只想写一篇,但发现把它想简单了,内容不多,难度极大.整理了好长时间,理解了为何<<深入理解linux内核>>要单独为它开一章,原因有二
小帅聊鸿蒙
2025/03/21
680
使用信号实现进程同步(踢皮球游戏)
学习了信号机制,我们就可以利用信号机制实现进程间同步了,比如我们希望一个进程处理完某件事情后再通知另外一个进程继续处理某件事情,这种需求实现的方法有很多,但是用信号实现是最方便的,这里我们举例用了一个踢皮球的小游戏充分展示了利用信号实现进程间同步的功能。程序执行后效果如下:
我与梦想有个约会
2023/10/20
2490
使用信号实现进程同步(踢皮球游戏)
Linux进程通信——信号
版权声明:本文为博主原创文章,转载请注明博客地址: https://blog.csdn.net/zy010101/article/details/83931740
zy010101
2019/05/25
2.6K0
linux中sigaction函数详解
一、函数原型:sigaction函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作)
全栈程序员站长
2022/09/01
1.7K0
linux中sigaction函数详解
异步通信之 信号
在软件层次上对中断机制的一种模拟,是一种异步通信的方式 。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。
看、未来
2021/10/09
1.3K0
异步通信之 信号
41-新的信号注册函数 sigaction
在这之前,我们一直使用 signal 来注册信号处理函数,而且一开始我甚至都没有提起过 signal 还有一个兄弟——sigaction.
全栈程序员站长
2022/09/01
1.5K0
linux系统编程之信号(五):实时信号与sigqueue函数
一、sigqueue函数 功能:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用。 原型:int sigqueue(pid_t pid, int s
s1mba
2017/12/28
2.4K0
sigaction信号处理
sa_mask:设置在处理该信号时暂时将sa_mask 指定的信号集搁置 sa_flags:设置信号处理相关操作
全栈程序员站长
2022/09/01
6010
sigaction信号处理
【Linux信号】三:信号的捕捉
信号捕捉主要是为了防止进程意外结束,并得到异常信息,捕捉信号后可以执行我们想要的动作。
mindtechnist
2024/08/08
6000
【Linux信号】三:信号的捕捉
sigprocmask sigaction
sigaction : signal增强版本, 当处理信号时, 可以随意添加信号屏蔽字
全栈程序员站长
2022/09/01
2840
Linux信号处理
目前 Linux 支持64种信号。信号分为非实时信号(不可靠信号)和实时信号(可靠信号)两种类型,对应于 Linux 的信号值为 1-31 和 34-64。
用户7686797
2020/08/25
6.1K0
【Linux】进程信号
Linux进程信号是一种进程间通信的机制,它允许一个进程通知另一个进程某个事件已经发生。以下是关于Linux进程信号的详细介绍:
大耳朵土土垚
2024/12/24
2800
【Linux】进程信号
Linux内核编程--进程通信信号
信号是 Linux 进程间通信的最古老的方式。信号是软件中断,它是在软件层次上对中断机制的一种模拟。
Coder-ZZ
2022/05/09
3.2K0
Linux内核编程--进程通信信号
【Linux】:进程信号(再谈信号保存和信号捕捉)
🌈 当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止
IsLand1314
2024/11/26
5690
【Linux】:进程信号(再谈信号保存和信号捕捉)
【Linux】信号>信号产生&&信号处理&&信号保存&&信号详解
SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump,我们来验证一下
用户10925563
2024/06/04
7570
【Linux】信号>信号产生&&信号处理&&信号保存&&信号详解
【Linux进程#7】:进程信号(再谈信号保存和信号捕捉)
✨ 不懂就问,问了不懂,你懂问他 🌏
IsLand1314
2025/06/02
1480
【Linux进程#7】:进程信号(再谈信号保存和信号捕捉)
信号
好,看完上面这些处理函数,其实这几个函数真的就是对信号集进行操作而已,而不会对具体信号有什么动作。 别急
看、未来
2020/08/26
1.3K0
信号
【Linux篇章】Linux 进程信号1:解锁系统高效运作的 “隐藏指令”,开启性能飞跃新征程(精讲信号产生和保存)
本篇文章将以一个小白视角带你了解什么是Linux中的信号;如何查看Linux中常见常用的信号;如何通过五种方式产生不同的信号;以及产生后是如何保存在进程的三种信号表中的;通过相关代码示例;带你通俗易懂的了解底层原理以及信号相关函数接口调用等。
羑悻的小杀马特.
2025/05/06
1980
【Linux篇章】Linux 进程信号1:解锁系统高效运作的 “隐藏指令”,开启性能飞跃新征程(精讲信号产生和保存)
【Linux篇】信号背后的故事:保存与处理的科学与技巧
当进程收到信号时,内核会将该信号标记为"待处理"(pending),而非立即触发处理。这种机制称为信号保存,具体表现为:
熬夜学编程的小王
2025/04/27
1580
【Linux篇】信号背后的故事:保存与处理的科学与技巧
推荐阅读
相关推荐
linux系统编程之信号(四):信号的捕捉与sigaction函数
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档