首页 > 系统相关 >linux 驱动向应用程序发射信号

linux 驱动向应用程序发射信号

时间:2023-09-24 21:34:14浏览次数:37  
标签:int irq button 应用程序 IRQT 信号 linux 驱动 include

系统支持信号

在linux终端输入kill -l可以查看系统所支持的信号,可以看出,每个信号的名字都是以SIG开头:

root@zhengyang:/work/sambashare/linux-5.2.8# kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

除了SIGSTOP和SIGKILL两个信号外,进程能够忽略或捕获其他的全部信号。一个信号被捕获的意思是当一个信号到达时有相应的代码处理它,如果一个信号没有被这个进程所捕获,内核将采用默认行为处理。

应用程序注册信号回调函数

linux中有两个函数实现信号的安装:

  • signal:它只有两个参数,不支持信号传递消息,主要用于SIGRTMIN之前的非实时信号的安装,这里的不可靠主要是不支持信号队列,就是当多个信号发生在进程中的时候(收到信号的速度超过进程处理的速度的时候),这些没来的及处理的信号就会被丢掉,仅仅留下一个信号;

  • sigaction:sigaction是较新的函数(由两个系统调用实现:sys_signal以及sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与 sigqueue系统调用配合使用,当然,sigqueue同样支持非实时信号的安装。sigaction优于signal主要体现在支持信号带有参数;

signal使用实例:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>

void sig_handler(int signo);
int main(void)
{
    printf("mian is waiting for a signal\n");
    if(signal(SIGINT,sig_handler) == SIG_ERR){
        perror("signal errror");
        exit(EXIT_FAILURE);
    }
    for(; ;);//有时间让我们发送信号


    return 0;
}

void sig_handler(int signo)
{
    printf("catch the signal SIGINT %d\n",signo);
}

 

sigaction:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>

// 信号处理函数
static void signal_handler(int signum, siginfo_t *info, void *context)
{
    // 打印接收到的信号值
    printf("signal_handler: signum = %d \n", signum);
}

int main(void)
{
    int count = 0;
    // 注册信号处理函数
    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = &signal_handler;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGUSR1, &sa, NULL);
    sigaction(SIGUSR2, &sa, NULL);

    // 一直循环打印信息,等待接收发信号
    while (1)
    {
        printf("app_handle_signal is running...count = %d \n", ++count);
        sleep(5);
    }

    return 0;
}

设备驱动向进程发送信号

  • 信号的发送方:必须知道向谁(PID)发送信号,发送哪个信号;
  • 信号接收方:必须定义信号处理函数,并安装信号以及信号处理函数的映射关系;

获取进程PID

驱动程序如何才能知道用户进程的PID呢?可以让应用程序通过fcntl函数,把自己的PID主动告诉驱动程序;

fcntl函数可以对一个已经打开的文件描述符执行一系列控制操作,在ubuntu下运行man 2  fcntl可以查看帮助信息;

fcntl函数原型如下所示:

int fcntl(int fd, int cmd,... /* arg */);

函数参数:

  • fd: 文件描述符。
  • cmd: 操作命令。此参数表示我们将要对 fd 进行什么操作, cmd 参数支持很多操作命令,大家可以打开 man 手册查看到这些操作命令的详细介绍,这些命令都是以 F_XXX 开头的,譬如 F_DUPFD 、 F_GETFD 、F_SETFD 等,不同的 cmd 具有不同的作用, cmd 操作命令大致可以分为以下 5 种功能:
    •  复制文件描述符,对应的cmd:F_DUPFD、F_DUPFD_CLOEXEC。当使用这两个cmd时,需要传入第三个参数,fcntl返回复制后的文件描述符,此返回值是之前未被占用的描述符,并且必须一个大于等于第三个参数值。F_DUPFD命令要求返回的文件描述符会清除对应的FD_CLOEXEC标志;F_DUPFD_CLOEXEC要求设置新描述符的FD_CLOEXEC标志;
    • 获取/设置文件描述符标志,对应的cmd:F_GETFD、F_SETFD。用于设置FD_CLOEXEC标志,此标志的含义是:当进程执行exec系统调用后此文件描述符会被自动关闭;
    • 获取/设置文件状态标志,对应的cmd:F_GETFL、F_SETFL。获取当前打开文件的访问标志,设置对应的文件访问标志;
    • 管理IO信号,对应的cmd:F_GETOWN、F_SETOWN。获取和设置用来接收SIGIO/SIGURG信号的进程id或者进程组id,返回对应的进程id或者进程组id。
    • 获取/设置记录锁,对应的cmd:F_GETLK、F_SETLK、F_SETLKW;
  • fcntl 函数是一个可变参函数,第三个参数需要根据不同的 cmd 来传入对应的实参,配合 cmd 来使用;

返回值:执行失败情况下,返回 -1 ,并且会设置 errno ;执行成功的情况下,其返回值与cmd (操作命令)有关,譬如 :

  • cmd=F_DUPFD (复制文件描述符)将返回一个新的文件描述符;
  • cmd=F_GETFD (获取文件描述符标志)将返回文件描述符标志;
  • cmd=F_GETFL (获取文件状态标志)将返回文件状态标志等;

这里以设置进程id为例:

fcntl(fd, F_SETOWN, getpid());    // 对文件描述符设置SIGIO、SIGURG信号所通知的进程

 

使能信号驱动IO

通过F_SETFL 控制命令设置设备文件以支持O_ASYNC, 即信号驱动IO,示例:

int fl;
fl = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, fl | O_FASYNC );

cmd设置为F_GETFL、F_SETFL时。 args参数传入文件状态标志位,使用open函数时可以指定:O_APPEND、O_ASYNC、O_CLOEXEC、O_CREAT、O_DIRECT、O_DSYNC、O_NONBLOCK、O_SYNC等。

而fcntl只可以使用open函数中的部分标志:O_APPEND, O_DIRECT、O_NONBLOCK,O_SYNC和O_ASYNC

  • O_ASYNC:使能信号驱动IO,当输入缓存中的输入数据就绪时(输入数据可读),内核向用F_SETOWN来绑定的那个进程发送SIGIO信号;

注意:在修改文件描述符标志或文件状态标志时必须谨慎,先要取得现在的标志值,然后按照希望修改它,最后设置新标志值。不能只是执行F_SETFD或F_SETFL命令,这样会关闭以前设置的标志位。

按键测试应用程序

#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

int fd,ret;

/* 信号处理函数 */
void signal_fun(int signum)
{
    unsigned int key_val = 0;
    static int count = 0;
    printf("signal = %d, %d count\n",signum,++count);

    /* 读取按键值 */
    ret = read(fd, &key_val, 1);    
    if(ret < 0){
        printf("read error\n");
    }
    printf("key_val = 0x%x\n", key_val);    
}

int main(int argc,char **argv)
{
    int flags;
    
    fd = open("/dev/buttons", O_RDWR);
    if (fd < 0)
    {
        printf("can't open!\n");
        return -1;
    }

    /* 对文件描述符设置SIGIO、SIGURG信号所通知的进程 */
    fcntl(fd, F_SETOWN, getpid());    
    /* 获取文件状态描述符 */
    flags = fcntl(fd,F_GETFL);
    /* 设置信号驱动IO */
    fcntl(fd,F_SETFL, flags | O_ASYNC);

    /* 调用signal函数,让指定的信号SIGIO与处理函数signal_fun对应 */
    signal(SIGIO,signal_fun);  

    while (1)
    {
        sleep(10);    // 休眠10s
    }
    
    return 0;
}

按键驱动程序

按键测试应用程序端负责捕获信号,则设备驱动负责释放信号,需要在设备驱动程序中增加信号释放的相关代码。 

为了使设备支持信号驱动IO,设备驱动程序中涉及3项工作:

  • 支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。不过此项工作已由内核完成,设备驱动程序中无须处理;
  • 支持F_SETFL命令的处理,添加O_ASYNC标志,会调用驱动程序中的.fasync函数。因此,我们在按键驱动中实现了button_fasync函数;
  • 当有按键发生变化时,向按键测试应用程序发送SIGIO信号,调用kill_fasync函数激发相应的信号;

上述的3项工作和按键测试应用程序中的3项是对应的,他们之间的关系,如下图:

 

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/irq.h>        // 包含了mach/irqs.h
#include <linux/interrupt.h>
#include <linux/gpio/machine.h>
#include <mach/gpio-samsung.h>
/*
 全局静态变量:希望全局变量仅限于在本源文件中使用,在其他源文件中不能引用,也就是说限制其作用域只在
 定义该变量的源文件内有效,而在同一源程序的其他源文件中不能使用
 */
#define OK   (0)
#define ERROR  (-1)
#define __IRQT_FALEDGE IRQ_TYPE_EDGE_FALLING
#define __IRQT_RISEDGE IRQ_TYPE_EDGE_RISING
#define __IRQT_LOWLVL IRQ_TYPE_LEVEL_LOW
#define __IRQT_HIGHLVL IRQ_TYPE_LEVEL_HIGH
#define IRQT_NOEDGE (0)
#define IRQT_RISING (__IRQT_RISEDGE)
#define IRQT_FALLING (__IRQT_FALEDGE)
#define IRQT_BOTHEDGE (__IRQT_RISEDGE|__IRQT_FALEDGE)
#define IRQT_LOW (__IRQT_LOWLVL)
#define IRQT_HIGH (__IRQT_HIGHLVL)
#define IRQT_PROBE IRQ_TYPE_PROBE

/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86 */
static unsigned char key_val;

struct pin_desc{
    unsigned int pin;
    unsigned int key_val;
};

/*
 * S3C2410_GPG 定义在arch/arm/mach-s3c24xx/include/mach/gpio-samsung.h
 */
static struct pin_desc pins_desc[4] = {
    {S3C2410_GPG(0), 0x01},
    {S3C2410_GPG(3), 0x02},
    {S3C2410_GPG(5), 0x03},
    {S3C2410_GPG(6), 0x04},
    {S3C2410_GPG(7), 0x05},
    {S3C2410_GPG(11), 0x06},
};

/*  异步信号结构体变量  */
static struct fasync_struct * button_async;

/*
 * 中断处理服务
 */
static irqreturn_t button_irq(int irq, void *dev_id)
{
    struct pin_desc *pindesc = (struct pin_desc *)dev_id;
    unsigned int pinval;

   pinval = gpio_get_value(pindesc->pin);
 
    if (pinval){
        /* 松开 */
        key_val = 0x80 | pindesc->key_val;
    }
    else{
        /* 按下 */
        key_val = pindesc->key_val;
    }
 
    kill_fasync(&button_async, SIGIO, POLL_IN);   /* 发送SIGIO信号给应用层 */
 
    return IRQ_RETVAL(IRQ_HANDLED);
}

/* 
   GPG0、GPG3、GPG5、GPG6、GPG7、GPG11配置为中断功能 
   IRQ_EINTX 定义在 arch/arm/mach-s3c24xx/include/mach/irqs.h
*/
static int button_open(struct inode *inode, struct file *file)
{
    /* 注册中断 */
    int ret;
    ret = request_irq(IRQ_EINT8,button_irq,IRQT_BOTHEDGE,"K1",&pins_desc[0]);
    if(ret){
        printk("open failed K1");
    }

    ret = request_irq(IRQ_EINT11,button_irq,IRQT_BOTHEDGE,"K2",&pins_desc[1]);
    if(ret){
        printk("open failed K2");
    }

    ret = request_irq(IRQ_EINT13,button_irq,IRQT_BOTHEDGE,"K3",&pins_desc[2]);
    if(ret){
        printk("open failed K3");
    }

    ret = request_irq(IRQ_EINT14,button_irq,IRQT_BOTHEDGE,"K4",&pins_desc[3]);
    if(ret){
        printk("open failed K4");
    }

    ret = request_irq(IRQ_EINT15,button_irq,IRQT_BOTHEDGE,"K5",&pins_desc[4]);
    if(ret){
        printk("open failed K5");
    }

    ret = request_irq(IRQ_EINT19,button_irq,IRQT_BOTHEDGE,"K6",&pins_desc[5]);
    if(ret){
        printk("open failed K6");
    }

    return 0;
}

static ssize_t button_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
    int count;
    if (size != 1){
        printk("read error\n");
        return -EINVAL;
    }

    /* 如果有按键动作, 上传key_val给用户层 */
    count = copy_to_user(buf, &key_val, 1);
    
    return count;
}

/*
 * 释放中断资源
 */
static int button_close(struct inode *inode, struct file *file)
{
    free_irq(IRQ_EINT8, &pins_desc[0]);
    free_irq(IRQ_EINT11, &pins_desc[1]);
    free_irq(IRQ_EINT13, &pins_desc[2]);
    free_irq(IRQ_EINT14, &pins_desc[3]);
    free_irq(IRQ_EINT15, &pins_desc[4]);
    free_irq(IRQ_EINT19, &pins_desc[5]);

    return 0;
}

static int button_fasync (int fd, struct file *file, int on)
{
      printk("button_fasync ok\n");
    /* 初始化button_async结构体,就能使用kill_fasync函数 */
    return fasync_helper(fd, file, on, &button_async); 
}

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

static dev_t devid;                      // 起始设备编号
static struct cdev button_cdev;          // 保存操作结构体的字符设备 
static struct class *button_cls;

static int button_init(void)
{
    
    /* 动态分配字符设备: (major,0) */
    if(OK == alloc_chrdev_region(&devid, 0, 1,"button")){   // ls /proc/devices看到的名字
        printk("register_chrdev_region ok\n");
    }else {
        printk("register_chrdev_region error\n");
        return ERROR;
    }
    
     cdev_init(&button_cdev, &button_fops);
     cdev_add(&button_cdev, devid, 1);


    /* 创建类,它会在sys目录下创建/sys/class/button这个类  */
     button_cls = class_create(THIS_MODULE, "button");
     if(IS_ERR(button_cls)){
         printk("can't create class\n");
         return ERROR;
     }
    /* 在/sys/class/button下创建buttons设备,然后mdev通过这个自动创建/dev/buttons这个设备节点 */
     device_create(button_cls, NULL, devid, NULL, "buttons"); 

     return 0;
}

static void __exit button_exit(void)
{
    printk("button driver exit\n");
    /* 注销类、以及类设备 /sys/class/button会被移除*/
    device_destroy(button_cls, devid);
    class_destroy(button_cls);

    cdev_del(&button_cdev);
    unregister_chrdev_region(devid, 1);
    return;
}

module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");

 

标签:int,irq,button,应用程序,IRQT,信号,linux,驱动,include
From: https://www.cnblogs.com/god-of-death/p/17726717.html

相关文章

  • Angular 应用程序的 Hydration 概念详解
    Angular应用程序的Hydration概念Hydration概念是Angular应用程序中的一个关键概念,它涉及到Angular框架在客户端渲染(Client-siderendering,CSR)中的运作方式。要深入理解Hydration,首先需要了解CSR和SSR(Server-siderendering,服务器端渲染)之间的基本区别,以及Angular是如何利用Hydra......
  • 无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri:[http://java.sun.com/jsp/
    今天解决了一个很早之前的问题!!!无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri:[http://java.sun.com/jsp/jstl/core]之前一直以为是jar包不匹配,但是改了jar包之后连uri都分辨不出来了后来在网上查到是tomcat的问题,将tomcat的conf目录下的catalina.properties的tomc......
  • 应用程序读取磁盘的数据流程
    应用程序请求文件读取:用户态的应用程序发起文件读取请求,通常是通过标准的文件操作函数(例如,在Linux中是read(),在Windows中是ReadFile())系统调用:操作系统内核接收到应用程序的读取请求,这将触发一个系统调用(systemcall)。系统调用是用户态和内核态之间的通信机制,用于执行操......
  • 尚观6410开发板移植linux 3.6.6问题记录及经验小结
    原文:https://www.cnblogs.com/iwantcomputer/p/8489831.html尚观6410开发板移植linux3.6.6问题记录及经验小结把开发板右上角的红色启动选项开关,两个都拨到下面(NAND),连接串口,已经内置了uboot1.16。根文件系统使用ext2的ramdisk,由于网卡无法驱动故无法使用nfs的根文件系统,网卡......
  • 高效的浏览 Linux 目录结构:pushd和popd命令
    在这个教程中,我们将讨论两个相关的命令:pushd和popd,使用它们可以高效的浏览Linux目录结构。这两个命令在大多数 shell ,比如bash、tcsh中都存在。pushd和popd命令在Linux系统中如何工作pushd和popd命令根据‘LIFO’(后进先出)原则工作。在这个原则之下,只有两个操作是允......
  • linux 第10章
    sh编程sh脚本是一个包含sh语句的文本文件,命令解释程序sh要执行该语句,所有的sh基本都执行相同的任务,脚本在语法上略有不同,shebang允许允许主sh调用适当版本的sh来执行脚本。如果未指定shebang,默认执行sh,即下列程序的第一行。例:创建一个一个文本文件mysh,包含:!/bin/bash//sh第一......
  • Linux第一次周总结
    第一章 初识Linux1.Linux简介Linux,一般指GNU/Linux(单独的Linux内核并不可直接使用,一般搭配GNU套件,故得此称呼),是一种免费使用和自由传播的类UNIX操作系统,其内核由林纳斯·本纳第克特·托瓦兹(LinusBenedictTorvalds)于1991年10月5日首次发布,它主要受到Minix和Unix思想的启发,是......
  • 第三周Linux教材第十章学习笔记——sh编程
    第十章——sh编程sh脚本1.sh脚本是一种为shell编写的脚本程序。这个程序用纯文本文件,将一些shell的语法与命令(含外部命令)写在里面,搭配正则表达式、管道命令与数据流重定向等功能。第一行必须为#!/bin/sh2.命令行使用chmod+x <filename>编译后才能用./filename来运行。3.......
  • Linux是什么?它与Windows有什么区别?
    前言本文将讲解Linux是什么、它的应用领域、未来、优缺点以及它与Windows的区别。一、Linux是什么?Linux是一种自由的、开放源码的类Unix操作系统。目前存在着许多不同的Linux系统,但它们都使用了Linux内核。Linux可安装在各种计算机设备中,从手机、平板电脑、路由器和视频游戏控制台,......
  • linux 中 conda配置文件 .condarc
     001、.condarc存在于个人用户的家目录下,在初次安装完conda并不会生成,在运行完condaconfig后才会生成(base)[root@pc1~]#cd~##回到用户个人家目录(base)[root@pc1~]#ls##列出文件anaconda3DesktopDownloads......