前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >linux 异步通知《Rice linux 学习笔记》

linux 异步通知《Rice linux 学习笔记》

作者头像
Rice加饭
发布2022-05-09 21:11:04
1.4K0
发布2022-05-09 21:11:04
举报
文章被收录于专栏:Rice嵌入式Rice嵌入式

这篇文章来讲讲linux中的异步通信的原理,相关函数的说明,以及驱动程序的实现。

信号的概念:

信号,是一种软中断(软件层上对中断机制的一种模拟)。为 Linux 提供了一种处理异步事件的方式。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。

信号,可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。

信号是有生命周期:

用户进程对信号的响应方式:

1、忽略信号:对信号不做任何处理,注意:SIGKILL和SIGSTOP不可忽略。

2、捕捉信号:定义对应的处理函数,当信号发生时,执行相应的处理函数

3、执行缺省操作:Linux对每种信号都规定了默认操作

信号处理的流程:

当系统进程产生系统调用、中断或异常时,系统将进入内核空间处理相应事件,通过信号通知用户进程,执行信号处理函数,信号处理函数执行完,返回到内核空间,然后再返回用户空间继续执行应用程序。

Linux提供的信号类型:

信号名

含义

默认操作

SIGHUP

该信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,通知同一会话内的各个作业与控制终端不再关联。

终止

SIGINT

该信号在用户键入INTR字符(通常是Ctrl-C)时发出,终端驱动程序发送此信号并送到前台进程中的每一个进程。

终止

SIGQUIT

该信号和SIGINT类似,但由QUIT字符(通常是Ctrl-\)来控制。

终止

SIGILL

该信号在一个进程企图执行一条非法指令时(可执行文件本身出现错误,或者试图执行数据段、堆栈溢出时)发出。

终止

SIGFPE

该信号在发生致命的算术运算错误时发出。这里不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误。

终止

SIGKILL

该信号用来立即结束程序的运行,并且不能被阻塞、处理和忽略。

终止

SIGALRM

该信号当一个定时器到时的时候发出。

终止

SIGSTOP

该信号用于暂停一个进程,且不能被阻塞、处理或忽略。

暂停进程

SIGTSTP

该信号用于暂停交互进程,用户可键入SUSP字符(通常是Ctrl-Z)发出这个信号。

暂停进程

SIGCHLD

子进程改变状态时,父进程会收到这个信号

忽略

SIGABORT

该信号用于结束进程

终止

用户空间对信号的处理:

1、每一个信号与相应的时间相关联。

2、一个进程可以设定对信号的相应方式。

3、信号处理方法有两种:①使用signal()函数。②使用信号集函数组。

其中signal()函数,需要制定要处理的信号和处理函数。

signal函数说明:

1、头文件:#include <signal.h>

2、函数原型:void (*signal(int signum, void (*handler)(int)))(int);

其中:signum:指定信号。

Handler:①SIG_IGN:忽略该信号。

②SIG_DEL:采用系统默认方式处理信号。

③自定义的信号处理函数指针。

3、返回值:出错返回-1.

SIGIO信号的说明:

SIGIO信号是驱动程序异步通知应用程序有事件发生的信号,应用程序一般忽略这个信号,如果需要处理该信号,需要进行配置:

1、设置驱动程序的拥有者是本进程。使用fcntl(fd, F_SETOWN, getpid()); 其中fd是打开设备文件的文件描述符,通过getpid()函数获取本进程的PID。

2、文件默认标志是没有设置FASYNC标志位,需要设备该标志位,才能同步。首先获取默认配置flags =fcntl(fd, F_GETFL); 然后将FASYNC标志设置进去fcntl(fd, F_SETFL, flags | FASYNC);

驱动程序处理异步信号说明:

1、当应用程序使用F_SETOWN,内核将进程的ID赋值给filp->f_owner.

2、当应用进程使用F_SETFL将FASYNC设置进去,则会调用驱动程序的fasync方法。

3、当产生事件时,驱动程序会向用户进程发送SIGIO信号。

4、驱动程序实现异步通知,包含一个结构体和两个函数:

其中:结构体:struct fasync_struct;

函数:fasync_helper()用于处理FASYNC标志变更:

代码语言:javascript
复制
fasync_helper(intfd,   //文件描述符
             strict file*filp,    //文件指针
             int on, //1-初始化,0-移除
             struct fasync_struct **fapp);//设置的结构体

kill_fasync()发送信号:

代码语言:javascript
复制
kill_fasync(struct fasync_struct **fp // fasync_helper初始化的结构体
            int sig, //要发送的信号。
            int band);//可读,设置为POLL_IN。可写,设置为POLL_OUT。

异步通知实现实例:

驱动程序的实现:

驱动程序只需要在之前的文章《中断机制》中进行修改一下,通过上面的介绍:驱动程序实现异步通知,只需要两个函数,一个结构体。

1、定义struct fasync_struct结构体:

代码语言:javascript
复制
static struct fasync_struct *fasync;

2、定义函数函数button_fasync(),然后将其注册到file_operations结构体中;通过fasync_helper()处理FASYNC标志变更。在驱动中增加一下定义。

代码语言:javascript
复制
static int button_fasync(int fd, struct file *filp, int on)
{
  return fasync_helper(fd, filp, on, &fasync);
}

static struct file_operations buttons_fops = {
  .owner    = THIS_MODULE,
  .open    = button_open,
  .read   = button_read,
  .release = button_close,
  .fasync   = button_fasync,
};

3、修改中断处理函数,当按键按下时,通过kill_fasync()函数发送信号。

代码语言:javascript
复制
static irqreturn_t button_irq(int irq, void *dev_id)
{
  struct pin_desc *pindesc = (struct pin_desc *)dev_id;
  unsigned int pin_val;
  
  pin_val = GPIO_GET_PIN(pindesc->pin);

  if(pin_val)
    key_val = 0x08 | pindesc->key_val;
  else
    key_val = pindesc->key_val;
  
  ev_press = 1;
  wake_up_interruptible(&button_wait_queue);

  kill_fasync(&fasync, SIGIO, POLL_IN);

  return IRQ_RETVAL(IRQ_HANDLED);  
}

应用程序的实现:

首先需要定义信号处理函数signal_function(),然后通过signal()函数指定信号以及信号对应的处理函数。通过fcntl()函数,设置驱动程序的拥有者是本进程。文件默认是没有配置FASYNC标志的,所以需要通过fcntl()函数配置FASYNC标志。

代码语言:javascript
复制
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>

#define file_name  "/dev/button"

int fd;

void signal_function(int signum)
{
  unsigned char key_val;
  read(fd, &key_val, 1);
  printf("key value: 0x%x\n",key_val);
}

int main(int argc, char **argv)
{
  int flags;

  signal(SIGIO, signal_function);

  fd = open(file_name, O_RDWR);
  if(fd < 0) {
    printf("error: can`t open %s", file_name);
    return 0;
  }

  fcntl(fd, F_SETOWN, getpid());
  flags = fcntl(fd, F_GETFL);
  fcntl(fd, F_SETFL, flags | FASYNC);
  
  for(;;)  {
    sleep(1000);
  }

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

本文分享自 Rice 嵌入式开发技术分享 微信公众号,前往查看

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

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

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