驱动学习(十)poll机制
1. io多路复用思想:
1 构建一张文件描述符集合表 fd_set stfdr;//1024bit
1024Bit用来存放1024个文件描述符对应的IO通道上是否有数据?
有数据,该位为1;无数据,该位为0。
2 select 用来监控指定范围的文件描述符上是否有数据发生?
若有数据发生,select返回有数据发生的通道个数;同时将有数据发生的文件描述符对应的集合表相应BIT置为1,并将其他Bit置0(监控 置位)
3 对文件描述符集合表的置位结果做判断并响应
FD_ISSET(i,&stfdr)
2. 驱动如何实现poll机制呢?
应用层:
fd=open("/dev/设备文件名",)
select
read/write
驱动层:
需要实现对应的Poll函数
1 将监控文件描述符的进程放入监控队列中
poll_wait(pFile,&q,ptable)
ptable:轮询表
(将当前操作文件描述符的进程加入到休眠等待队列中,将该队列加到ptable中)
2 系统对监控队列中的进程监控,若被监控的进程上的文件描述符对应的通道有IO数据发生,系统会将该进程激活-->return mask;
mask:是用来描述操作是否可以立即无阻塞执行的位掩码
是一个32bits的整数,它的每个bit都代表了设备的一种状态(每个bit位都可以用一个宏来表示)
POLLIN:有数据可读
POLLRDNORM:有普通数据可读
POLLOUT:有数据可写
POLLERR:指定文件描述符发生错误
3.测试
代码
###### poll.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 = "poll";//设备名
char buff[BUF_SIZE] = "chrdev-test-2022-7-14";
struct class *pClass = NULL; //设备文件类指针
int devNum = 2; //设备文件数量
wait_queue_head_t q; //定义休眠等待队列头
int con = 0; //休眠唤醒条件 1唤醒 0休眠
int charCount = 0; //实际写入字符数量
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");
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;
}
charCount = count;
printk("copy_from_user ok\n");
printk("\t buff = %s\t\n",buff);
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;
}
struct file_operations fp_arr =
{
.owner = THIS_MODULE,
.open = testOpen,
.read = testRead,
.write = testWrite,
.release = testClose,
.unlocked_ioctl = testIoctl,
.poll = testPoll
};
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,"poll");
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,"poll%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-14");
##### 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/poll0",O_RDWR);
if(fd < 0)
{
printf("open poll0 err\n");
return -1;
}
printf("open poll0 ok\n");
fd_set stfdr;
char buf[BUF_SIZE] = {0};
int res = -1;
while(1)
{
FD_ZERO(&stfdr);
FD_SET(fd,&stfdr);
res = select(fd+1,&stfdr,NULL,NULL,NULL);
if(res <= 0)
{
continue;
}
if(FD_ISSET(fd,&stfdr))
{
//测试读
bzero(buf,sizeof(buf));
read(fd,buf,BUF_SIZE-1);
printf("read data:%s\n",buf);
}
}
close(fd);
return 0;
}
##### test1.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/poll0",O_RDWR);
if(fd < 0)
{
printf("open poll0 err\n");
return -1;
}
printf("open poll0 ok\n");
char buf[BUF_SIZE] = {0};
while(1)
{
//测试写
printf(">> input:");
bzero(buf,sizeof(buf));
scanf("%s",buf);
write(fd,buf,sizeof(buf));
}
close(fd);
return 0;
}
现象
安装驱动模块,运行测试程序,此时test未读到数据,说明驱动中MASK的值不是POLLIN或者POLLRDNORM
在test1中写入数据,可以看到test读到了数据
结论
poll机制测试成功,虽然没有看到read不是无脑阻塞,而是因为select通过驱动的poll收到了POLLIN或者是POLLRDNORM而调用的read,emmm,没问题!
在测试的过程中突然想到就一个设备文件,为什么要使用select呢?不如直接阻塞啊,阻塞还能让进程放弃cpu的资源呢,select遍历加轮询不是占用资源嘛,还是有点尬的,这是个测试,只是打开了一个设备文件,调用了多个驱动后就有意义了啊!