驱动学习(十一)异步IO机制
1. 什么是异步IO机制?
读进程去设备中读数据,设备没有数据可读,那么当前进程继续执行等待通知,若内核检测到设备有数据可读之后,会给读进程发送通知信号,读进程会执行该信号对应的处理函数去读数据。
2. 实现步骤
应用层
A(读数据进程)
1.注册一个信号处理函数
signal(signo,handler)
2.打开设备文件
fd=open("/dev/设备文件名",O_RDWR);
3.设置文件描述符的宿主为当前进程
fcntl(fd,FD_SETOWN,getpid());
4.读取文件描述符标志
int flags=fcntl(fd,F_GETFL);
5.设置文件描述符标志
fcntl(fd,F_SETFL,flags|FASYNC)
while(1);
handler--->read
注:fcntl是一个系统调用函数,用来获取或者设置文件描述符的的属性
B (写数据进程)
1.open("/dev/设备文件名")
2.write-->驱动对应的write函数-->给A进程发信号
3.close
驱动层
1.定义一个全局的异步通知结构体 struct fasync_struct *fasync=NULL;
2.写一个被file_operationsz结构体成员(驱动操作方法结构体).fasync指向的函数:testFasync-->异步通知
testFasync(int fd,struct file *pFile,int on)异步通知函数
{
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
作用:(信号的发送方和信号的接收方关联)
fd:打开设备文件返回的文件描述符
* filp:指向内核打开的文件
on:相当于开关 1/0
*fapp:异步通知结构体指针地址
}
3. 在file_operationsz结构体成员(驱动操作方法结构体).write指向的testwrite中调用kill_fasync给用户进程A发信号
void kill_fasync(struct fasync_struct **fp, int sig, int band)
**fp:异步通知结构体指针地址
sig:信号 kill -l SIGIO
band:POLL_IN/POLL_OUT(信号携带的指令)
SIGIO(POLL_IN)--->异步通知结构体--》fd-->A进程
--》signal(SIGIO,handler)-->handler(read)
3. 测试
代码
##### fasync.c 用来读数据
#include <linux/module.h> //模块驱动的头文件
#include <linux/cdev.h> //设备信息描述的头文件
#include <linux/fs.h> //静态申请设备号头文件
#include <linux/kdev_t.h> //设备号用到的头文件和宏函数
#include <linux/uaccess.h>
#include <linux/device.h>
#include "common.h"
#include <linux/sched.h>
#include <linux/poll.h>
#define BUF_SIZE 100
int major = 0; //主设备号
int min = 0; //次设备号
int deviceNum = 0; //完整设备号
struct cdev* pCdev = NULL; //描述设备信息的结构体
char *deviceName = "fasync";//设备名
char buff[BUF_SIZE] = "chrdev-test-2022-7-19";
struct class *pClass = NULL; //设备文件类指针
int devNum = 2; //设备文件数量
wait_queue_head_t q; //定义休眠等待队列头
int con = 0; //休眠唤醒条件 1唤醒 0休眠
int charCount = 0; //实际写入字符数量
struct fasync_struct *fasync = NULL;
int testOpen(struct inode *pNode,struct file *pFile)
{
printk("------into test open------\n");
con = 0;
printk("------leave test open------\n");
return 0;
}
int testClose(struct inode *pNode,struct file *pFile)
{
printk("------into test close------\n");
con = 0;
printk("------leave test close------\n");
return 0;
}
ssize_t testRead(struct file *pFile,char __user *buf,size_t count,loff_t *pOffset)
{
int res = -1;
printk("------into testRead------\n");
if(charCount <= 0)
{
if(pFile->f_flags&O_NONBLOCK)
{
printk("O_NONBLOCK is setted\n");
return -EAGAIN;
}
else
{
wait_event_interruptible(q,con);
}
}
if(count > BUF_SIZE-1)
{
count = BUF_SIZE - 1;
}
if(count > charCount)
{
count = charCount;
}
res = copy_to_user(buf,buff,count);
if(res)
{
printk("copy_to_user error\n");
return -EFAULT;
}
charCount -= count;
printk("copy_to_user ok\n");
printk("\t buff = %s\t\n",buff);
printk("------leave testRead------\n");
return count;
}
ssize_t testWrite(struct file *pFile,const char __user *buf,size_t count,loff_t *pOffset)
{
int res = -1;
printk("------into testWrite------\n");
con = 1;
wake_up_interruptible(&q);
if(count > BUF_SIZE-1)
{
count = BUF_SIZE - 1;
}
res = copy_from_user(buff,buf,count);
if(res)
{
printk("copy_from_user error\n");
return -EFAULT;
}
else
{
con = 1;
wake_up_interruptible(&q);
printk("wake up \n");
}
charCount = count;
printk("copy_from_user ok\n");
printk("\t buff = %s\t\n",buff);
kill_fasync(&fasync,SIGIO,POLL_IN);
printk("------leave testWrite------\n");
return count;
}
long testIoctl(struct file *pFile,unsigned int cmd,unsigned long arg)
{
switch(cmd)
{
case TEST_CMD:
{
printk("test cmd-------arg = %ld \n",arg);
}
break;
case TEST_CMD1:
{
printk("test cmd1-------arg = %ld \n",arg);
}
break;
case TEST_CMD2:
{
printk("test cmd2-------arg = %ld \n",arg);
}
break;
default:
printk("error cmd \n");
}
return 0;
}
unsigned int testPoll(struct file *pFile,struct poll_table_struct *ptable)
{
unsigned int mask = 0;
printk("into testpoll\n");
poll_wait(pFile,&q,ptable);
if(charCount > 0)
{
printk("read mask id setted \n");
mask = POLLIN|POLLRDNORM;
}
printk("leave testpoll \n");
return mask;
}
int testFasync(int fd,struct file *pFile,int on)
{
int res = -1;
printk("into Fasync\n");
res = fasync_helper(fd,pFile,on,&fasync);
printk("res = %d,on = %d\n",res,on);
printk("leave Fasync\n");
return res;
}
struct file_operations fp_arr =
{
.owner = THIS_MODULE,
.open = testOpen,
.read = testRead,
.write = testWrite,
.release = testClose,
.unlocked_ioctl = testIoctl,
.poll = testPoll,
.fasync = testFasync
};
int driverr_init(void) //模块初始化函数
{
int res = 0;
int i = 0;
struct device *pDevTmp = NULL;
printk("*********into driver init\n");
//动态申请设备号
res = alloc_chrdev_region(&deviceNum,min,devNum,deviceName);
if(res)
{
printk("alloc_chrdev_region error\n");
return res;
}
printk("alloc_chrdev_region OK!\n");
printk("major = %d minor = %d \n",MAJOR(deviceNum),MINOR(deviceNum));
major = MAJOR(deviceNum);
//创建设备
pCdev = cdev_alloc();
if(NULL == pCdev)
{
printk("cdev_alloc error\n");
unregister_chrdev_region(deviceNum,devNum);
return -1;
}
printk("cdev_alloc ok\n");
//设备初始化
cdev_init(pCdev,&fp_arr);
printk("cdev_init ok\n");
//设备与设备号关联
res = cdev_add(pCdev,deviceNum,devNum);
if(res)
{
printk("cdev_add error\n");
cdev_del(pCdev);
}
printk("cdev_add ok\n");
//创建设备文件类
pClass = class_create(THIS_MODULE,"fasync");
if(NULL == pClass)
{
printk("class_create error\n");
cdev_del(pCdev);
}
printk("class_create ok\n");
//创建设备文件
for(;i < devNum;i++)
{
pDevTmp = device_create(pClass,NULL,MKDEV(major,i),NULL,"fasync%d",i);
if(IS_ERR(pDevTmp))
{
printk("device_create error\n");
for(i = 0;i < devNum;i++)
{
device_destroy(pClass,MKDEV(major,i));
}
class_destroy(pClass);
return -2;
}
}
printk("device_create ok\n");
//初始化休眠等待队列
init_waitqueue_head(&q);
printk("init_waitqueue_head ok\n");
printk("*********leave driver init\n");
return 0;
}
void driver_clear(void) //模块清除函数
{
int i = 0;
printk("*********into driver clear\n");
for(;i < devNum; i++)
{
device_destroy(pClass,MKDEV(major,i));
}
class_destroy(pClass);
cdev_del(pCdev);
unregister_chrdev_region(deviceNum,devNum);
printk("*********leave driver clear\n");
}
module_init(driverr_init); //模块加载函数
module_exit(driver_clear); //模块卸载函数
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cfy");
MODULE_ALIAS("liangzai");
MODULE_DESCRIPTION("2022-7-19");
##### test.c 用来读数据
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <string.h>
#include <signal.h>
#define BUF_SIZE 100
int fd=-1;
void handler(int signo)
{
printf("into hander:signo=%d\r\n",signo);
char buf[BUF_SIZE]={0};
read(fd,buf,BUF_SIZE-1);
printf("buf:%s\r\n",buf);
}
int main()
{
//1 signal
signal(SIGIO,handler);
//2 open fasync0
fd=open("/dev/fasync0",O_RDWR);
if(fd<0)
{
printf("open fasync0 error!\r\n");
return -1;
}
printf("open fasync0 ok!\r\n");
//3 set owner
fcntl(fd,F_SETOWN,getpid());
//4 get flags
int flags=fcntl(fd,F_GETFL);
//5 set flags
fcntl(fd,F_SETFL,flags|FASYNC);
while(1)
{
printf("看看会不会阻塞\n");
sleep(3);
}
return 0;
}
##### test.c 用来写数据
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define BUF_SIZE 100
int main()
{
int fd = open("/dev/fasync0",O_RDWR);
if(fd < 0)
{
printf("open fasync0 err\n");
return -1;
}
printf("open fasync0 ok\n");
char buf[BUF_SIZE] = {0};
memcpy(buf,"123456",7);
write(fd,buf,sizeof(buf));
while(1)
{
//测试写
printf(">> input:");
bzero(buf,sizeof(buf));
scanf("%s",buf);
write(fd,buf,sizeof(buf));
}
close(fd);
return 0;
}
现象
不会阻塞,在test1写入的时候,会通知test去读,在没有通知去读的时候test一直在执行自己的程序。
结论
好使,就是有点费人。