这篇文章来讲讲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标志变更:
fasync_helper(intfd, //文件描述符
strict file*filp, //文件指针
int on, //1-初始化,0-移除
struct fasync_struct **fapp);//设置的结构体
kill_fasync()发送信号:
kill_fasync(struct fasync_struct **fp // fasync_helper初始化的结构体
int sig, //要发送的信号。
int band);//可读,设置为POLL_IN。可写,设置为POLL_OUT。
异步通知实现实例:
驱动程序的实现:
驱动程序只需要在之前的文章《中断机制》中进行修改一下,通过上面的介绍:驱动程序实现异步通知,只需要两个函数,一个结构体。
1、定义struct fasync_struct结构体:
static struct fasync_struct *fasync;
2、定义函数函数button_fasync(),然后将其注册到file_operations结构体中;通过fasync_helper()处理FASYNC标志变更。在驱动中增加一下定义。
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()函数发送信号。
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标志。
#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;
}
本文分享自 Rice 嵌入式开发技术分享 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!