基础知识
poll和select方法在Linux用户空间的API接口函数定义如下。
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
poll()函数的第一个参数fds是要监听的文件描述符集合,类型为指向struct pollfd的指针。struct pollfd数据结构定义如下。
struct pollfd { int fd; short events; short revents; };
fd表示要监听的文件描述符,events表示监听的事件,revents表示返回的事件。常用的监听的事件有如下类型(掩码)。
- POLLIN:数据可以立即被读取。
- POLLRDNORM:等同于POLLIN,表示数据可以立即被读取。
- POLLERR:设备发生了错误。
- POLLOUT:设备可以立即写入数据。
poll()函数的第二个参数nfds是要监听的文件描述符的个数;第三个参数timeout是单位为ms的超时,负数表示一直监听,直到被监听的文件描述符集合中有设备发生了事件。
Linux内核的file_operations方法集提供了poll方法的实现。
<include/linux/fs.h> struct file_operations { … unsigned int (*poll) (struct file *, struct poll_table_struct *); … };
当用户程序打开设备文件后执行poll或者select系统调用时,驱动程序的poll方法就会被调用。设备驱动程序的poll方法会执行如下步骤。
1)在一个或者多个等待队列中调用poll_wait()函数。poll_wait()函数会把当前进程添加到指定的等待列表(poll_table)中,当请求数据准备好之后,会唤醒这些睡眠的进程。
2)返回监听事件,也就是POLLIN或者POLLOUT等掩码。因此,poll方法的作用就是让应用程序同时等待多个数据流。
驱动代码:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/kfifo.h>
#include <linux/wait.h>
#include <linux/poll.h>
#define DEBUG_INFO(format, ...) printk("%s:%d -- "format"\n",\
__func__,__LINE__,##__VA_ARGS__)
struct ch5_kfifo_struct{
struct miscdevice misc;
struct file_operations fops;
struct kfifo fifo;
char buf[64];
char name[64];
wait_queue_head_t read_queue;
wait_queue_head_t write_queue;
};
static int ch5_open (struct inode *inode, struct file *file){
struct ch5_kfifo_struct *p = (struct ch5_kfifo_struct *)container_of(file->f_op,struct ch5_kfifo_struct,fops);
file->private_data = p;
DEBUG_INFO("major = %d, minor = %d\n",MAJOR(inode->i_rdev),MINOR(inode->i_rdev));
DEBUG_INFO("name = %s",p->misc.name);
return 0;
}
static int ch5_release (struct inode *inode, struct file *file){
DEBUG_INFO("close");
return 0;
}
static unsigned int ch5_poll(struct file *file, poll_table *wait){
int mask = 0;
struct ch5_kfifo_struct *p __attribute__((unused)) = (struct ch5_kfifo_struct *)file->private_data;
DEBUG_INFO("begin wait:%s",p->name);
poll_wait(file, &p->read_queue, wait);
poll_wait(file, &p->write_queue, wait);
DEBUG_INFO("poll:%s",p->name);
if (!kfifo_is_empty(&p->fifo)){
mask |= POLLIN | POLLRDNORM;
DEBUG_INFO("POLLIN:%s",p->name);
}
if (!kfifo_is_full(&p->fifo)){
mask |= POLLOUT | POLLWRNORM;
DEBUG_INFO("POLLOUT:%s",p->name);
}
return mask;
}
static ssize_t ch5_read (struct file *file, char __user *buf, size_t size, loff_t *pos){
struct ch5_kfifo_struct *p __attribute__((unused)) = (struct ch5_kfifo_struct *)file->private_data;
int ret;
int actual_readed = 0;
if(kfifo_is_empty(&p->fifo)){
if(file->f_flags & O_NONBLOCK){
DEBUG_INFO("kfifo is null");
return -EAGAIN;
}
ret = wait_event_interruptible(p->read_queue,kfifo_is_empty(&p->fifo) == 0);
if(ret){
DEBUG_INFO("wait_event_interruptible error");
return ret;
}
DEBUG_INFO("");
}
ret = kfifo_to_user(&p->fifo, buf, size, &actual_readed);
if (ret){
DEBUG_INFO("kfifo_to_user error");
return -EIO;
}
DEBUG_INFO("size = %d,actual_readed = %d\n",size,actual_readed);
if (!kfifo_is_full(&p->fifo)){
wake_up_interruptible(&p->write_queue);
}
memset(p->buf,0,sizeof(p->buf));
ret = copy_from_user(p->buf, buf, actual_readed);
if(ret != 0){
DEBUG_INFO("copy_from_user error ret = %d\n",ret);
}else{
DEBUG_INFO("read p->buf = %s\n",p->buf);
}
*pos = *pos + actual_readed;
return actual_readed;
}
static ssize_t ch5_write (struct file *file, const char __user *buf, size_t size, loff_t* pos){
struct ch5_kfifo_struct *p = (struct ch5_kfifo_struct *)file->private_data;
int actual_writed = 0;
int ret;
if(kfifo_is_full(&p->fifo)){
if(file->f_flags & O_NONBLOCK){
DEBUG_INFO("kfifo is full");
return -EAGAIN;
}
ret = wait_event_interruptible(p->write_queue, kfifo_is_full(&p->fifo) == 0);
if(ret){
DEBUG_INFO("wait_event_interruptible error");
return ret;
}
DEBUG_INFO("");
}
ret = kfifo_from_user(&p->fifo, buf, size, &actual_writed);
if (ret){
DEBUG_INFO("kfifo_from_user error");
return -EIO;
}
DEBUG_INFO("actual_writed = %d\n",actual_writed);
if (!kfifo_is_empty(&p->fifo)){
wake_up_interruptible(&p->read_queue);
}
memset(p->buf,0,sizeof(p->buf));
ret = copy_from_user(p->buf, buf, actual_writed);
if(ret != 0){
DEBUG_INFO("copy_from_user error ret = %d\n",ret);
}else{
DEBUG_INFO("write:p->buf = %s\n",p->buf);
}
*pos = *pos + actual_writed;
return actual_writed;
}
struct ch5_kfifo_struct ch5_kfifo[8];
// = {
// .misc = {
// .name = "ch5-04-block",
// .minor = MISC_DYNAMIC_MINOR,
// },
// .fops = {
// .owner = THIS_MODULE,
// .read = ch5_read,
// .write = ch5_write,
// .open = ch5_open,
// .release = ch5_release,
// },
// };
static int __init ch5_init(void){
int ret = 0;
int i = 0;
struct ch5_kfifo_struct *p;
DEBUG_INFO("start init\n");
for(i = 0;i < sizeof(ch5_kfifo)/sizeof(ch5_kfifo[0]);i++){
p = &ch5_kfifo[i];
snprintf(p->name,sizeof(p->name),"ch5-05-poll-%d",i);
p->misc.name = p->name;
p->misc.minor = MISC_DYNAMIC_MINOR;
p->fops.owner = THIS_MODULE;
p->fops.read = ch5_read;
p->fops.write = ch5_write;
p->fops.open = ch5_open;
p->fops.release = ch5_release;
p->fops.poll = ch5_poll;
p->misc.fops = &p->fops;
ret = kfifo_alloc(&p->fifo,
8,
GFP_KERNEL);
if (ret) {
DEBUG_INFO("kfifo_alloc error: %d\n", ret);
ret = -ENOMEM;
return ret;
}
DEBUG_INFO("kfifo_alloc size = %d",kfifo_avail(&p->fifo));
init_waitqueue_head(&p->read_queue);
init_waitqueue_head(&p->write_queue);
ret = misc_register(&p->misc);
if(ret < 0){
DEBUG_INFO("misc_register error: %d\n", ret);
return ret;
}
}
DEBUG_INFO("misc_register ok");
return 0;
}
static void __exit ch5_exit(void){
int i = 0;
struct ch5_kfifo_struct *p;
for(i = 0;i < sizeof(ch5_kfifo)/sizeof(ch5_kfifo[0]);i++){
p = &ch5_kfifo[i];
misc_deregister(&p->misc);
kfifo_free(&p->fifo);
}
DEBUG_INFO("exit\n");
}
module_init(ch5_init);
module_exit(ch5_exit);
MODULE_LICENSE("GPL");
应用测试代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include <linux/input.h>
#include <unistd.h>
#define DEBUG_INFO(format, ...) printf("%s:%d -- "format"\n",\
__func__,__LINE__,##__VA_ARGS__)
int main(int argc, char**argv){
char bufs[8][1024];
char name[8][1024];
int fd[8];
int i = 0;
int ret;
int len;
struct pollfd fds[8];
for(i = 0;i < 8;i++){
memset(name[i], 0, sizeof(name[i]));
snprintf(name[i], sizeof(name[i]),"/dev/ch5-05-poll-%d",i);
fd[i] = open(name[i],O_RDONLY | O_NONBLOCK);
if(fd[i] < 0){
perror("open");
DEBUG_INFO("open %s failed",name[i]);
return -1;
}
fds[i].fd = fd[i];
fds[i].events = POLLIN;
fds[i].revents = 0;
}
DEBUG_INFO("start poll\n");
while(1){
ret = poll(fds,8,-1);
if(ret < 0){
perror("poll");
return 0;
}
DEBUG_INFO("ret %d\n",ret);
for(i = 0;i < 8;i++){
if(fds[i].revents & POLLIN){
while(1){
memset(bufs[i],0,sizeof(bufs[i]));
len = read(fd[i],(void*)&bufs[i][0],sizeof(bufs[i]));
if(len <= 0){
DEBUG_INFO("read %s finish",name[i]);
break;
}
DEBUG_INFO("read %s :buf = %s",name[i],bufs[i]);
}
}
}
}
return 0;
}
测试:
加载模块生成8个设备
后台运行应用:
/mnt # ./app &
/mnt # [ 519.185812] ch5_open:30 -- major = 10, minor = 58
[ 519.185812]
[ 519.186175] ch5_open:31 -- name = ch5-05-poll-0
[ 519.186849] ch5_open:30 -- major = 10, minor = 57
[ 519.186849]
[ 519.187772] ch5_open:31 -- name = ch5-05-poll-1
[ 519.190276] ch5_open:30 -- major = 10, minor = 56
[ 519.190276]
[ 519.190553] ch5_open:31 -- name = ch5-05-poll-2
[ 519.191129] ch5_open:30 -- major = 10, minor = 55
[ 519.191129]
[ 519.191604] ch5_open:31 -- name = ch5-05-poll-3
[ 519.192090] ch5_open:30 -- major = 10, minor = 54
[ 519.192090]
[ 519.192283] ch5_open:31 -- name = ch5-05-poll-4
[ 519.192759] ch5_open:30 -- major = 10, minor = 53
[ 519.192759]
[ 519.193020] ch5_open:31 -- name = ch5-05-poll-5
[ 519.193486] ch5_open:30 -- major = 10, minor = 52
[ 519.193486]
[ 519.193677] ch5_open:31 -- name = ch5-05-poll-6
[ 519.194148] ch5_open:30 -- major = 10, minor = 51
[ 519.194148]
[ 519.194340] ch5_open:31 -- name = ch5-05-poll-7
main:38 -- start poll
[ 519.199679] ch5_poll:43 -- begin wait:ch5-05-poll-0
[ 519.200029] ch5_poll:46 -- poll:ch5-05-poll-0
[ 519.200236] ch5_poll:54 -- POLLOUT:ch5-05-poll-0
[ 519.200358] ch5_poll:43 -- begin wait:ch5-05-poll-1
[ 519.200598] ch5_poll:46 -- poll:ch5-05-poll-1
[ 519.200770] ch5_poll:54 -- POLLOUT:ch5-05-poll-1
[ 519.201213] ch5_poll:43 -- begin wait:ch5-05-poll-2
[ 519.201531] ch5_poll:46 -- poll:ch5-05-poll-2
[ 519.201646] ch5_poll:54 -- POLLOUT:ch5-05-poll-2
[ 519.201727] ch5_poll:43 -- begin wait:ch5-05-poll-3
[ 519.201847] ch5_poll:46 -- poll:ch5-05-poll-3
[ 519.201998] ch5_poll:54 -- POLLOUT:ch5-05-poll-3
[ 519.202154] ch5_poll:43 -- begin wait:ch5-05-poll-4
[ 519.202347] ch5_poll:46 -- poll:ch5-05-poll-4
[ 519.202504] ch5_poll:54 -- POLLOUT:ch5-05-poll-4
[ 519.202661] ch5_poll:43 -- begin wait:ch5-05-poll-5
[ 519.202850] ch5_poll:46 -- poll:ch5-05-poll-5
[ 519.203122] ch5_poll:54 -- POLLOUT:ch5-05-poll-5
[ 519.203276] ch5_poll:43 -- begin wait:ch5-05-poll-6
[ 519.203490] ch5_poll:46 -- poll:ch5-05-poll-6
[ 519.203643] ch5_poll:54 -- POLLOUT:ch5-05-poll-6
[ 519.203798] ch5_poll:43 -- begin wait:ch5-05-poll-7
[ 519.203964] ch5_poll:46 -- poll:ch5-05-poll-7
[ 519.204109] ch5_poll:54 -- POLLOUT:ch5-05-poll-7
写数据
/mnt # echo "hello world" > /dev/ch5-05-poll-0
[ 576.205375] ch5_open:30 -- major = 10, minor = 58
[ 576.205375]
[ 576.206863] ch5_open:31 -- name = ch5-05-poll-0
[ 576.208285] ch5_write:122 -- actual_writed = 8
[ 576.208285]
[ 576.208757] ch5_write:132 -- write:p->buf = hello wo
[ 576.208757]
[ 576.209448] ch5_poll:43 -- begin wait:ch5-05-poll-0
[ 576.209675] ch5_poll:46 -- poll:ch5-05-poll-0
[ 576.209874] ch5_poll:49 -- POLLIN:ch5-05-poll-0
[ 576.210058] ch5_poll:43 -- begin wait:ch5-05-poll-1
[ 576.210291] ch5_poll:46 -- poll:ch5-05-poll-1
[ 576.210451] ch5_poll:54 -- POLLOUT:ch5-05-poll-1
[ 576.210645] ch5_poll:43 -- begin wait:ch5-05-poll-2
[ 576.211496] ch5_poll:46 -- poll:ch5-05-poll-2
[ 576.212140] ch5_poll:54 -- POLLOUT:ch5-05-poll-2
[ 576.212315] ch5_poll:43 -- begin wait:ch5-05-poll-3
[ 576.212517] ch5_poll:46 -- poll:ch5-05-poll-3
[ 576.212707] ch5_poll:54 -- POLLOUT:ch5-05-poll-3
[ 576.212928] ch5_poll:43 -- begin wait:ch5-05-poll-4
[ 576.213169] ch5_poll:46 -- poll:ch5-05-poll-4
[ 576.213333] ch5_poll:54 -- POLLOUT:ch5-05-poll-4
[ 576.213515] ch5_poll:43 -- begin wait:ch5-05-poll-5
[ 576.213752] ch5_poll:46 -- poll:ch5-05-poll-5
[ 576.213869] ch5_poll:54 -- POLLOUT:ch5-05-poll-5
[ 576.214067] ch5_poll:43 -- begin wait:ch5-05-poll-6
[ 576.214238] ch5_poll:46 -- poll:ch5-05-poll-6
[ 576.214392] ch5_poll:54 -- POLLOUT:ch5-05-poll-6
[ 576.214545] ch5_poll:43 -- begin wait:ch5-05-poll-7
[ 576.214963] ch5_poll:46 -- poll:ch5-05-poll-7
[ 576.215300] ch5_poll:54 -- POLLOUT:ch5-05-poll-7
main:45 -- ret 1
[ 576.216522] ch5_read:84 -- size = 1024,actual_readed = 8
[ 576.216522]
[ 576.216920] ch5_read:95 -- read p->buf = hello wo
[ 576.216920]
[ 576.217219] ch5_write:114 --
main:55 -- read /dev/ch5-05-poll-0 :buf = hello wo
[ 576.217652] ch5_write:122 -- actual_writed = 4
[ 576.217652]
[ 576.217989] ch5_write:132 -- write:p->buf = rld
[ 576.217989]
[ 576.217989]
main:55 -- read /dev/ch5-05-poll-0 :buf = rld[ 576.218140] ch5_read:84 -- size = 1024,actual_readed = 4
[ 576.218140]
[ 576.218294] ch5_read:95 -- read p->buf = rld
[ 576.218294]
[ 576.218294]
[ 576.219434] ch5_read:67 -- kfifo is null
main:52 -- read /dev/ch5-05-poll-0 finish[ 576.220169] ch5_release:36 -- close
[ 576.220874] ch5_poll:43 -- begin wait:ch5-05-poll-0
[ 576.221959] ch5_poll:46 -- poll:ch5-05-poll-0
[ 576.222639] ch5_poll:54 -- POLLOUT:ch5-05-poll-0
[ 576.224333] ch5_poll:43 -- begin wait:ch5-05-poll-1
[ 576.225068] ch5_poll:46 -- poll:ch5-05-poll-1
[ 576.225443] ch5_poll:54 -- POLLOUT:ch5-05-poll-1
[ 576.225890] ch5_poll:43 -- begin wait:ch5-05-poll-2
[ 576.226298] ch5_poll:46 -- poll:ch5-05-poll-2
[ 576.226829] ch5_poll:54 -- POLLOUT:ch5-05-poll-2
[ 576.227435] ch5_poll:43 -- begin wait:ch5-05-poll-3
/mnt # [ 576.227639] ch5_poll:46 -- poll:ch5-05-poll-3
[ 576.227892] ch5_poll:54 -- POLLOUT:ch5-05-poll-3
[ 576.228124] ch5_poll:43 -- begin wait:ch5-05-poll-4
[ 576.228344] ch5_poll:46 -- poll:ch5-05-poll-4
[ 576.228573] ch5_poll:54 -- POLLOUT:ch5-05-poll-4
[ 576.228902] ch5_poll:43 -- begin wait:ch5-05-poll-5
[ 576.229155] ch5_poll:46 -- poll:ch5-05-poll-5
[ 576.229349] ch5_poll:54 -- POLLOUT:ch5-05-poll-5
[ 576.229573] ch5_poll:43 -- begin wait:ch5-05-poll-6
[ 576.229761] ch5_poll:46 -- poll:ch5-05-poll-6
[ 576.230005] ch5_poll:54 -- POLLOUT:ch5-05-poll-6
[ 576.230233] ch5_poll:43 -- begin wait:ch5-05-poll-7
[ 576.230421] ch5_poll:46 -- poll:ch5-05-poll-7
[ 576.231095] ch5_poll:54 -- POLLOUT:ch5-05-poll-7