前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux笔记(15)| Linux的信号

Linux笔记(15)| Linux的信号

作者头像
飞哥
发布2020-11-13 14:42:38
2.6K0
发布2020-11-13 14:42:38
举报
文章被收录于专栏:电子技术研习社

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

一、发送信号

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

1、kill函数

代码语言:javascript
复制
int kill(pid_t pid,int sig);

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

2、raise函数

代码语言:javascript
复制
int raise(int sig);

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

代码语言:javascript
复制
kill(getpid(),sig);

3、sigqueue函数

代码语言:javascript
复制
int sigqueue(pid_t pid,int sig,const union sigval value);

其中第三个参数的形式为

代码语言:javascript
复制
typedef union sigval
{
int sival_int;
void *sival_ptr;
}sigval_t;

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

下面写一个简单的代码

代码语言:javascript
复制
#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
复制
void (*signal(int signum,void(*handler)(int)))(int);

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

代码语言:javascript
复制
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);

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

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

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

代码语言:javascript
复制
char* function(int a,char* b);

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

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

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

2、sigaction函数

代码语言:javascript
复制
int sigaction(int signum, const struct sigaction *act,
                  struct sigaction *oldact);

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

代码语言:javascript
复制
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
复制
#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
复制
gcc test.c
./a.out a.txt

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

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档