Linux系统驱动(九)IO模型---异步通知IO模型

一、概念

当硬件设备的数据就绪时,会产生中断,然后在中断处理函数中给上层的进程发送信号SIGIO(29),进程收到信号时执行信号处理函数,进程没有收到信号时可以执行其他代码。

异步通知:信号的发送和进程的执行是异步的

二、异步通知IO模型驱动实现

(一)异步通知IO模型实现

#include <my_head.h>

void my_handler(int signum){
    printf("This is my_handler\n");
}

int main(int argc, char const *argv[])
{
    int fd=0;
	//此处打开的是键盘的驱动文件
    if(-1 == (fd = open("/dev/input/event1",O_RDWR))){
        //打开文件失败
        ERR_LOG("open error");
    }

    //第一步:注册信号
    if(SIG_ERR == signal(SIGIO,my_handler))
        ERR_LOG("signal error");

    //第二步:调用底层的fasync函数
    unsigned int flags = fcntl(fd,F_GETFL);
    fcntl(fd,F_SETFL,flags|FASYNC);

    //第三步:告诉驱动,该进程可以接收信号
    fcntl(fd,F_SETOWN,getpid());
    while(1){
        //此时程序可以执行其他操作
        //当收到信号后触发信号处理函数
    }
    return 0;
}

1. fcntl(fd,F_GETFL)调用流程

US:
 unsigned int flags = fcntl(fd,F_GETFL)
---------------------------------------------
KS:  vi -t sys_fcntl
SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
	==>err = do_fcntl(fd, cmd, arg, f.file); 
		==>
        	switch (cmd) {  
             	case F_GETFL:
                err = filp->f_flags;
          		break;}
   return err;
  • 注:操作为F_GETFL时,本质是将struct file结构体中的f_flags返回

  • 注:此处使用的 vi -t 命令需要安装exuberant-ctags软件包,执行命令sudo apt-get install exuberant-ctags安装

2. fcntl(fd,F_SETFL,flags|FASYNC)

US:
 fcntl(fd,F_SETFL,flags|FASYNC);
-------------------------------------------------------------------
KS:  vi -t sys_fcntl
SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
	==>err = do_fcntl(fd, cmd, arg, f.file); 
		==>switch (cmd) {  
            	case F_SETFL:
                err = setfl(fd, filp, arg);
                	==>  //arg = filp->f_flags | FASYNC;
                    	if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op->fasync) {
                        	//调用驱动的fasync函数执行
                        	error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
                    	}
                    	break;   
-------------------------------------------------------------------
int mycdev_fasync(int fd, struct file *filp, int on)
{ }
  • 注:当操作为F_SETFL时,会执行arg^filp->flags操作,执行结果最后会剩下FASYNC标志位,此时与FASYNC相与,如果为真,则说明置位了FASYNC标志位,进而通过struct file结构体找到驱动中的fasync函数

3. fcntl(fd,F_SETOWN,getpid())

US:
 fcntl(fd,F_SETOWN,getpid())
-------------------------------------------------------------------
KS:  vi -t sys_fcntl
SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
	==>err = do_fcntl(fd, cmd, arg, f.file); 
		==>switch (cmd) {  
           		case F_SETOWN:  
             		err = f_setown(filp, arg, 1);
                		==>__f_setown(filp, pid, type, force);
     						filp->f_owner.pid = get_pid(pid);
            		break;
  • 注:操作为F_SETOWN时,本质就是将pid写入到struct file结构体中的f_owner中的pid
struct file {struct fown_struct	f_owner;}
		|
struct fown_struct{	struct pid *pid;/* pid or -pgrp where SIGIO should be sent */};

(二)驱动层提供异步通知模型

1. 驱动层中实现异步通知IO模型对应的函数指针

struct file_operations {
	int (*fasync) (int, struct file *, int);
}

2. 驱动层使用示例

//定义异步通知队列头
struct fasync_struct * my_fasync=NULL;

ssize_t mycdev_write(struct file* file,
    const char __user* ubuf, size_t size, loff_t* offs)
{
    int ret;
    memset(kbuf, 0, sizeof(kbuf));
    if (size > sizeof(kbuf))
        size = sizeof(kbuf);
    ret = copy_from_user(kbuf, ubuf, size);
    if (ret) {
        pr_err("copy_from_user error\n");
        return -EIO;
    }
    cond=1;
    //此示例中仍采用,当调用write时,发送信号给驱动的方式
    kill_fasync(&my_fasync,SIGIO,POLL_IN);
    return size;
}

int mycdev_fasync(int fd, struct file * filp, int on){
    return fasync_helper(fd,filp,on,&my_fasync);
}

const struct file_operations myfops={
    .open=mycdev_open,
    .release=mycdev_close,
    .write=mycdev_write,
    .read=mycdev_read,
    .poll=mycdev_poll,
    .fasync=mycdev_fasync, //指定fasync函数指针指向的函数
};

3. 关于fasync_helper函数

int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp){	return fasync_add_entry(fd, filp, fapp);}
==>
static int fasync_add_entry(int fd, struct file *filp, struct fasync_struct **fapp)
{
	new = fasync_alloc(); //动态分配内存
	if (fasync_insert_entry(fd, filp, fapp, new)) 
}
==>
struct fasync_struct *fasync_insert_entry(int fd, struct file *filp, struct fasync_struct **fapp, struct fasync_struct *new)
{
	1.判断*fapp是否为NULL,不为NULL则说明new不是队列头,采用类似头插的操作
	2.初始化new的值,包括fd和filp
	3.返回队列头
}
  • 注:即用户使用时需要定义一个struct fasync_struct * 的变量,用来保存函数回传的异步通知队列头。

三、实现区分用户发来的信号

使用上述方法,无论收到的是POLL_IN信号还是POLL_OUT信号都会触发中断。
如果想要区分POLL_IN信号和POLL_OUT信号,需要更改应用程序,使用sigaction函数

(一)sigaction函数

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
功能:获取或者设置信号的属性
参数:
    signum:信号的编号 除了 SIGKILL 和 SIGSTOP
    act:信号新的属性
    oldact:信号之前的属性
返回值:
    成功 0
    失败 -1 重置错误码

struct sigaction {
    void     (*sa_handler)(int);//信号处理函数
    void     (*sa_sigaction)(int, siginfo_t *, void *);//信号处理函数
    sigset_t   sa_mask;//关于阻塞的掩码
    int        sa_flags;//属性标志位
        SA_RESTART  表示信号的自重启属性,关闭自重启属性后 被中断的系统调用会失败 错误码为 EINTR
        SA_SIGINFO	如果使用第2个信号处理函数时,需要置位这个属性
    void     (*sa_restorer)(void);
};

(二)实现示例

test.c

#include <my_head.h>

void my_handler(int signum, siginfo_t *info, void *arg){
    printf("This is my_handler\n");
    if(signum == SIGIO){
        switch (info->si_code)
        {
        case POLL_IN:
            printf("The signal is POLL_IN\n");
            break;
        
        case POLL_OUT:
            printf("The signal is POLL_OUT\n");
            break;
        }
    }
}

int main(int argc, char const *argv[])
{
    int fd=0;

    if(-1 == (fd = open("/dev/mycdev",O_RDWR))){
        //打开文件失败
        ERR_LOG("open error");
    }

    //第一步:注册信号
    fcntl(fd,__F_SETSIG,SIGIO); //让内核空间的数据拷贝到用户空间
    struct sigaction action={ //定义结构体
        .sa_sigaction = my_handler, //信号处理函数
        .sa_flags = SA_SIGINFO, //使用第二个信号处理函数必须置位这个
    };
    if(sigaction(SIGIO,&action,NULL))
        ERR_LOG("signal error");

    //第二步:调用底层的fasync函数
    unsigned int flags = fcntl(fd,F_GETFL);
    fcntl(fd,F_SETFL,flags|FASYNC);

    //第三步:告诉驱动,该进程可以接收信号
    fcntl(fd,F_SETOWN,getpid());
    while(1){
        //此时程序可以执行其他操作
        //当收到信号后触发信号处理函数
    }
    return 0;
}

在这里插入图片描述

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值