Linux poll and epoll

poll

问题:假如应用需要根据IO的状态来读或写多个IO,如何处理?如果是一个进程处理,一个一个IO的处理,那么就势必会出现阻塞等待某个IO的过程,此时就可能丢其他IO的数据。

解决以上问题,可以使用多个thread,每个thread直跟踪处理一个IO,不过只有少量IO还行,假如是大量的IO并且要监控这些IO状态呢,多线程显然不是最好的办法。

IO多路复用

解决上面那个问题的办法:内核提供了IO多路复用的机制,一共有三种

  • poll
  • select
  • epoll

poll

poll允许一个进程不阻塞的形式去读写多路IO。poll可以监听多路IO直至其IO事件的发生来触发处理。

事件有

events 含义
POLLIN 有数据可读,不阻塞
POLLPRI 紧急数据可读
POLLOUT 写数据,不阻塞
POLLRDHUP Linux 2.6.17之后提供,流socket关闭或者连接中途中断写操作
POLLERR 发生错误
POLLHUP 挂起
POLLRDNORM 等同于POLLIN
POLLWRNORM 等同于POLLOUT
POLLWRBAND priority data 写入

howto

如何在应用中使用poll

include/uapi/asm-generic/poll.h中定义

struct pollfd {int fd;       //监听的fdshort events;        //关心的事件short revents;
};

比如

#include <poll.h>
struct pollfd pfd;pfd.fd = test_fd;
pfd.events = (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)

同时监听多个fd,可以使用数组

struct pollfd pfd[num_of_fd];

示例

poll_driver.c模拟驱动层

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>                 //kmalloc()
#include <linux/uaccess.h>              //copy_to/from_user()
#include <linux/kthread.h>
#include <linux/wait.h>                 //Required for the wait queues
#include <linux/poll.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>//Waitqueue
DECLARE_WAIT_QUEUE_HEAD(wait_queue_poll_data);dev_t dev = 0;
static struct class *dev_class;
static struct cdev test_cdev;
struct kobject *kobj_ref;
static bool can_write = false;
static bool can_read  = false;
static char test_value[20];
/*
** Function Prototypes
*/
static int      __init poll_driver_init(void);
static void     __exit poll_driver_exit(void);/*************** Driver Fuctions **********************/
static int      test_open(struct inode *inode, struct file *file);
static int      test_release(struct inode *inode, struct file *file);
static ssize_t  test_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
static ssize_t  test_write(struct file *filp, const char *buf, size_t len, loff_t * off);
static unsigned int test_poll(struct file *filp, struct poll_table_struct *wait);
/*************** Sysfs Fuctions **********************/
static ssize_t  sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
static ssize_t  sysfs_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count);
struct kobj_attribute poll_attr = __ATTR(test_value, 0660, sysfs_show, sysfs_store);
/*
** File operation sturcture
*/
static struct file_operations fops =
{.owner          = THIS_MODULE,.read           = test_read,.write          = test_write,.open           = test_open,.release        = test_release,.poll           = test_poll
};
/*
** This function will be called when we read the sysfs file
*/
static ssize_t sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{pr_info("Sysfs Show - Write Permission Granted!!!\n");can_write = true;//wake up the waitqueuewake_up(&wait_queue_poll_data);return sprintf(buf, "%s", "Success\n");
}
/*
** This function will be called when we write the sysfs file
*/
static ssize_t sysfs_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count)
{pr_info("Sysfs Store - Read Permission Granted!!!\n");strcpy(test_value, buf);can_read = true;//wake up the waitqueuewake_up(&wait_queue_poll_data);return count;
}
/*
** This function will be called when we open the Device file
*/
static int test_open(struct inode *inode, struct file *file)
{pr_info("Device File Opened...!!!\n");return 0;
}
/*
** This function will be called when we close the Device file
*/
static int test_release(struct inode *inode, struct file *file)
{pr_info("Device File Closed...!!!\n");return 0;
}
/*
** This fuction will be called when we read the Device file
*/
static ssize_t test_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{pr_info("Read Function : test_value = %s\n",test_value);   len = strlen(test_value);strcpy(buf, test_value);#if 0  if( copy_to_user(buf, test_value, len) > 0){pr_err("ERROR: Not all the bytes have been copied to user\n");}
#endifreturn 0;
}
/*
** This fuction will be called when we write the Device file
*/
static ssize_t test_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{strcpy(test_value, buf);pr_info("Write function : test_value = %s\n", test_value);return len;
}
/*
** This fuction will be called when app calls the poll function
*/
static unsigned int test_poll(struct file *filp, struct poll_table_struct *wait)
{__poll_t mask = 0;poll_wait(filp, &wait_queue_poll_data, wait);pr_info("Poll function\n");if( can_read ){can_read = false;mask |= ( POLLIN | POLLRDNORM );}if( can_write ){can_write = false;mask |= ( POLLOUT | POLLWRNORM );}return mask;
}/*
** Module Init function
*/
static int __init poll_driver_init(void)
{/*Allocating Major number*/if((alloc_chrdev_region(&dev, 0, 1, "test_poll_Dev")) <0){pr_err("Cannot allocate major number\n");return -1;}pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));/*Creating cdev structure*/cdev_init(&test_cdev,&fops);test_cdev.owner = THIS_MODULE;test_cdev.ops = &fops;/*Adding character device to the system*/if((cdev_add(&test_cdev,dev,1)) < 0){pr_err("Cannot add the device to the system\n");goto r_class;}/*Creating struct class*/if((dev_class = class_create(THIS_MODULE,"test_poll_class")) == NULL){pr_err("Cannot create the struct class\n");goto r_class;}/*Creating device*/if((device_create(dev_class,NULL,dev,NULL,"test_poll_device")) == NULL){pr_err("Cannot create the Device 1\n");goto r_device;}/*Creating a directory in /sys/kernel/ */kobj_ref = kobject_create_and_add("test_poll_sysfs",kernel_kobj);/*Creating sysfs file for test_value*/if(sysfs_create_file(kobj_ref,&poll_attr.attr)){printk(KERN_INFO"Cannot create sysfs file......\n");goto r_sysfs;}//Initialize wait queue //init_waitqueue_head(&wait_queue_poll_data);pr_info("Poll Driver Insert...Done!!!\n");return 0;
r_sysfs:kobject_put(kobj_ref); sysfs_remove_file(kernel_kobj, &poll_attr.attr);
r_device:class_destroy(dev_class);
r_class:unregister_chrdev_region(dev,1);return -1;
}
/*
** Module exit function
*/
static void __exit poll_driver_exit(void)
{kobject_put(kobj_ref); sysfs_remove_file(kernel_kobj, &poll_attr.attr);device_destroy(dev_class,dev);class_destroy(dev_class);cdev_del(&test_cdev);unregister_chrdev_region(dev, 1);pr_info("Poll Driver Remove...Done!!!\n");
}module_init(poll_driver_init);
module_exit(poll_driver_exit);MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple linux driver (poll)");
MODULE_VERSION("1");

poll_user.c模拟用户层

#include <assert.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{char kernel_val[20];int fd, ret, n;struct pollfd pfd;fd = open("/dev/test_poll_device", O_RDWR | O_NONBLOCK);if( fd == -1 )  {perror("open");exit(EXIT_FAILURE);}pfd.fd = fd;pfd.events = ( POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM );while( 1 ) {puts("Starting poll...");ret = poll(&pfd, (unsigned long)1, 5000);   //wait for 5secsif( ret < 0 ) {perror("poll");assert(0);}if( ( pfd.revents & POLLIN )  == POLLIN ){read(pfd.fd, &kernel_val, sizeof(kernel_val));printf("POLLIN : Kernel_val = %s\n", kernel_val);}if( ( pfd.revents & POLLOUT )  == POLLOUT ){strcpy( kernel_val, "User Space");write(pfd.fd, &kernel_val, strlen(kernel_val));printf("POLLOUT : Kernel_val = %s\n", kernel_val);}}
}

Makefile

CONFIG_MODULE_SIG=n
obj-m += poll_driver.o
KDIR = /lib/modules/$(shell uname -r)/buildall:gcc -o poll_user poll_user.cmake -C $(KDIR)  M=$(shell pwd) modulesclean:make -C $(KDIR)  M=$(shell pwd) clean

运行效果

[ 1753.044204] Major = 236 Minor = 0
[ 1753.044778] Poll Driver Insert...Done!!!
[ 1769.107944] Sysfs Show - Write Permission Granted!!!
[ 1841.664799] Poll Driver Remove...Done!!!
[ 1944.369715] Major = 236 Minor = 0
[ 1944.369938] Poll Driver Insert...Done!!!
[ 1951.598629] Device File Opened...!!!
[ 1951.598771] Poll function
[ 1956.604188] Poll function
[ 1956.604264] Poll function
[ 1961.609649] Poll function
[ 1961.609725] Poll function
[ 1966.615150] Poll function
[ 1966.615216] Poll function
[ 1971.620544] Poll function
[ 1971.620605] Poll function
[ 1976.625845] Poll function
[ 1976.625897] Poll function
[ 1980.624459] Sysfs Show - Write Permission Granted!!!
[ 1980.624565] Poll function
[ 1980.624592] Write function : test_value = User Space
[ 1980.624663] Poll function
[ 1985.629895] Poll function
[ 1985.629961] Poll function
[ 1990.635099] Poll function
[ 1990.635138] Poll function
[ 1995.640312] Poll function
[ 1995.640366] Poll function
[ 2000.645546] Poll function
[ 2000.645629] Poll function
[ 2002.373522] Sysfs Store - Read Permission Granted!!!
[ 2002.373596] Poll function
[ 2002.373628] Read Function : test_value = aaaaaaaaa[ 2002.373670] Poll function

驱动层模拟出/dev/test_poll_dev设备,用户程序监听该设备,每当该设备可以读或写时,用户程序就进行读写。然后内核中通过/sys/kernel/test_poll_data/test_value变量来控制该设备的可读或可写信号。

select

select本应早就弃用了,但它就是很坚挺。

几个宏

macros
void FD_ZERO(fd_set *set); 清除fd集set,一般用于初始化监听fd集第一步
void FD_SET(int fd, fd_set *set); 添加fd进入到set集
void FD_CLR(int fd, fd_set *set); 从set集中移除fd描述符
int FD_ISSET(int fd, fd_set *set); 判断fd是否还在set集中

select

    #include <sys/select.h>/* According to earlier standards */#include <sys/time.h>#include <sys/types.h>#include <unistd.h>int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);void FD_CLR(int fd, fd_set *set);int  FD_ISSET(int fd, fd_set *set);void FD_SET(int fd, fd_set *set);void FD_ZERO(fd_set *set);#include <sys/select.h>int pselect(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, const struct timespec *timeout,const sigset_t *sigmask);

示例

select_driver.c

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>                 //kmalloc()
#include <linux/uaccess.h>              //copy_to/from_user()
#include <linux/kthread.h>
#include <linux/wait.h>                 //Required for the wait queues
#include <linux/poll.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>//Waitqueue
DECLARE_WAIT_QUEUE_HEAD(wait_queue_select_test_data);dev_t dev = 0;
static struct class *dev_class;
static struct cdev select_test_cdev;
struct kobject *kobj_ref;
static bool can_write = false;
static bool can_read  = false;
static char select_test_value[20];
/*
** Function Prototypes
*/
static int      __init select_test_driver_init(void);
static void     __exit select_test_driver_exit(void);/*************** Driver Fuctions **********************/
static int      select_test_open(struct inode *inode, struct file *file);
static int      select_test_release(struct inode *inode, struct file *file);
static ssize_t  select_test_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
static ssize_t  select_test_write(struct file *filp, const char *buf, size_t len, loff_t * off);
static unsigned int select_test_poll(struct file *filp, struct poll_table_struct *wait);
/*************** Sysfs Fuctions **********************/
static ssize_t  sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
static ssize_t  sysfs_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count);
struct kobj_attribute select_test_attr = __ATTR(select_test_value, 0660, sysfs_show, sysfs_store);
/*
** File operation sturcture
*/
static struct file_operations fops =
{.owner          = THIS_MODULE,.read           = select_test_read,.write          = select_test_write,.open           = select_test_open,.release        = select_test_release,.poll           = select_test_poll
};
/*
** This function will be called when we read the sysfs file
*/
static ssize_t sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{pr_info("Sysfs Show - Write Permission Granted!!!\n");can_write = true;//wake up the waitqueuewake_up(&wait_queue_select_test_data);return sprintf(buf, "%s", "Success\n");
}
/*
** This function will be called when we write the sysfsfs file
*/
static ssize_t sysfs_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count)
{pr_info("Sysfs Store - Read Permission Granted!!!\n");strcpy(select_test_value, buf);can_read = true;//wake up the waitqueuewake_up(&wait_queue_select_test_data);return count;
}
/*
** This function will be called when we open the Device file
*/
static int select_test_open(struct inode *inode, struct file *file)
{pr_info("Device File Opened...!!!\n");return 0;
}
/*
** This function will be called when we close the Device file
*/
static int select_test_release(struct inode *inode, struct file *file)
{pr_info("Device File Closed...!!!\n");return 0;
}
/*
** This function will be called when we read the Device file
*/
static ssize_t select_test_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{pr_info("Read Function : select_test_value = %s\n",select_test_value);   len = strlen(select_test_value);strcpy(buf, select_test_value);#if 0  if( copy_to_user(buf, select_test_value, len) > 0){pr_err("ERROR: Not all the bytes have been copied to user\n");}
#endifreturn 0;
}
/*
** This function will be called when we write the Device file
*/
static ssize_t select_test_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{strcpy(select_test_value, buf);pr_info("Write function : select_test_value = %s\n", select_test_value);return len;
}
/*
** This function will be called when app calls the poll function
*/
static unsigned int select_test_poll(struct file *filp, struct poll_table_struct *wait)
{__poll_t mask = 0;poll_wait(filp, &wait_queue_select_test_data, wait);pr_info("Poll function\n");if( can_read ){can_read = false;mask |= ( POLLIN | POLLRDNORM );}if( can_write ){can_write = false;mask |= ( POLLOUT | POLLWRNORM );}return mask;
}/*
** Module Init function
*/
static int __init select_test_driver_init(void)
{/*Allocating Major number*/if((alloc_chrdev_region(&dev, 0, 1, "select_test_Dev")) <0){pr_err("Cannot allocate major number\n");return -1;}pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));/*Creating cdev structure*/cdev_init(&select_test_cdev,&fops);select_test_cdev.owner = THIS_MODULE;select_test_cdev.ops = &fops;/*Adding character device to the system*/if((cdev_add(&select_test_cdev,dev,1)) < 0){pr_err("Cannot add the device to the system\n");goto r_class;}/*Creating struct class*/if((dev_class = class_create(THIS_MODULE,"select_test_class")) == NULL){pr_err("Cannot create the struct class\n");goto r_class;}/*Creating device*/if((device_create(dev_class,NULL,dev,NULL,"select_test_device")) == NULL){pr_err("Cannot create the Device 1\n");goto r_device;}/*Creating a directory in /sys/kernel/ */kobj_ref = kobject_create_and_add("select_test_sysfs",kernel_kobj);/*Creating sysfs file for select_test_value*/if(sysfs_create_file(kobj_ref,&select_test_attr.attr)){printk(KERN_INFO"Cannot create sysfs file......\n");goto r_sysfs;}//Initialize wait queue//init_waitqueue_head(&wait_queue_select_test_data);pr_info("Select Driver Insert...Done!!!\n");return 0;
r_sysfs:kobject_put(kobj_ref); sysfs_remove_file(kernel_kobj, &select_test_attr.attr);
r_device:class_destroy(dev_class);
r_class:unregister_chrdev_region(dev,1);return -1;
}
/*
** Module exit function
*/
static void __exit select_test_driver_exit(void)
{kobject_put(kobj_ref); sysfs_remove_file(kernel_kobj, &select_test_attr.attr);device_destroy(dev_class,dev);class_destroy(dev_class);cdev_del(&select_test_cdev);unregister_chrdev_region(dev, 1);pr_info("Select Driver Remove...Done!!!\n");
}module_init(select_test_driver_init);
module_exit(select_test_driver_exit);MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple linux driver (poll)");
MODULE_VERSION("1");

select_user.c

#include <assert.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{char   kernel_val[20];fd_set read_fd, write_fd;struct timeval timeout;int    ret;int    fd = open("/dev/select_test_device", O_RDWR | O_NONBLOCK);if( fd == -1 )  {perror("open");exit(EXIT_FAILURE);}while( 1 ) {puts("Starting Select...");/* Initialize the file descriptor set. */FD_ZERO( &read_fd );FD_SET( fd, &read_fd );FD_ZERO( &write_fd );FD_SET( fd, &write_fd );/* Initialize the timeout */timeout.tv_sec  = 5;       //5 Secondstimeout.tv_usec = 0;ret = select(FD_SETSIZE, &read_fd, &write_fd, NULL, &timeout);if( ret < 0 ) {perror("select");assert(0);}if( FD_ISSET( fd, &read_fd ) ){read(fd, &kernel_val, sizeof(kernel_val));printf("READ : Kernel_val = %s\n", kernel_val);}if( FD_ISSET( fd, &write_fd ) ){strcpy( kernel_val, "User Space");write(fd, &kernel_val, strlen(kernel_val));printf("WRITE : Kernel_val = %s\n", kernel_val);}}return 0;
}

Makefile

COFNIG_MODULE_SIG = nobj-m += select_driver.o
KDIR = /lib/modules/$(shell uname -r)/buildall:gcc -o select_user select_user.cmake -C $(KDIR)  M=$(shell pwd) modulesclean:make -C $(KDIR)  M=$(shell pwd) cleanrm select_user -f

运行效果

[96278.259626] Major = 236 Minor = 0
[96278.259940] Select Driver Insert...Done!!!
[96285.859192] Device File Opened...!!!
[96285.859355] Poll function
[96290.864418] Poll function
[96290.864469] Poll function
[96295.869532] Poll function
[96295.869593] Poll function
[96299.433314] Sysfs Show - Write Permission Granted!!!
[96299.433357] Poll function
[96299.433368] Write function : select_test_value = User Space
[96299.433417] Poll function
[96304.438472] Poll function
[96304.438525] Poll function
[96309.443590] Poll function
[96309.443644] Poll function
[96314.448706] Poll function
[96314.448763] Poll function
[96317.134654] Sysfs Store - Read Permission Granted!!!
[96317.134697] Poll function
[96317.134708] Read Function : select_test_value = aaaaabbbb[96317.134739] Poll function
[96322.139807] Poll function
[96322.139860] Poll function
[96327.144924] Poll function
[96327.144998] Poll function
[96327.631104] Sysfs Store - Read Permission Granted!!!
[96327.631127] Poll function
[96327.631137] Read Function : select_test_value = aaaaabbbb[96327.631188] Poll function
[96331.230864] Sysfs Store - Read Permission Granted!!!
[96331.230921] Poll function
[96331.230936] Read Function : select_test_value = aaaaabbbb[96331.231000] Poll function
[96335.842223] Sysfs Store - Read Permission Granted!!!
[96335.842267] Poll function
[96335.842279] Read Function : select_test_value = aaaaabbbb[96335.842346] Poll function
[96340.847414] Poll function
[96340.847468] Poll function
[96345.378400] Poll function
[96345.378589] Device File Closed...!!!
[96362.631257] Select Driver Remove...Done!!!

epoll

epoll有两种调用触发方式:edge trigger 和 level trigger

触发 说明
edge trigger epoll_wait()只有在新的event发生时才返回
level trigger epoll_wait()在条件发生时返回

poll vs select vs epoll

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9hT4GX7r-1665454286147)(images/image-20220923112632122.png)]

epoll接口

创建和控制epoll需要用到一下API

epoll_create

int epoll_create(int size);

2.6.8之后的内核,size参数只要大于0即可,无实际意义。用新的接口epoll_create1()。

/* Creates an epoll instance.  Returns an fd for the new instance.The "size" parameter is a hint specifying the number of filedescriptors to be associated with the new instance.  The fdreturned by epoll_create() should be closed with close().  */
extern int epoll_create (int __size) __THROW;/* Same as epoll_create but with an FLAGS parameter.  The unused SIZEparameter has been dropped.  */
extern int epoll_create1 (int __flags) __THROW;

epoll_ctl

/* Manipulate an epoll instance "epfd". Returns 0 in case of success,-1 in case of error ( the "errno" variable will contain thespecific error code ) The "op" parameter is one of the EPOLL_CTL_*constants defined above. The "fd" parameter is the target of theoperation. The "event" parameter describes which events the calleris interested in and any associated user data.  */
extern int epoll_ctl (int __epfd, int __op, int __fd,struct epoll_event *__event) __THROW;

其中

__op参数有:

__op 含义
EPOLL_CTL_ADD 注册__epfd到__fd集中
EPOLL_CTL_MOD 修改event事件
EPOLL_CTL_DEL 删除__epfd

event事件

events 含义
EPOLLIN 有数据可读
EPOLLOUT 可写
EPOLLPRI 紧急数据可读
EPOLLERR 发生错误
EPOLLHUP 发生挂起
EPOLLET 设置edge触发模式,默认是level触发模式
EPOLLONESHOT
EPOLLRDHUP
EPOLLWAKEUP
EPOLLEXCLUSIVE

epoll_wait

/* Wait for events on an epoll instance "epfd". Returns the number oftriggered events returned in "events" buffer. Or -1 in case oferror with the "errno" variable set to the specific error code. The"events" parameter is a buffer that will contain triggeredevents. The "maxevents" is the maximum number of events to bereturned ( usually size of "events" ). The "timeout" parameterspecifies the maximum wait time in milliseconds (-1 == infinite).This function is a cancellation point and therefore not marked with__THROW.  */
extern int epoll_wait (int __epfd, struct epoll_event *__events,int __maxevents, int __timeout);

示例

epoll_driver.c

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>                 //kmalloc()
#include <linux/uaccess.h>              //copy_to/from_user()
#include <linux/kthread.h>
#include <linux/wait.h>                 //Required for the wait queues
#include <linux/poll.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
//Waitqueue
DECLARE_WAIT_QUEUE_HEAD(wait_queue_epoll_test_data);
dev_t dev = 0;
static struct class *dev_class;
static struct cdev epoll_test_cdev;
struct kobject *kobj_ref;
static bool can_write = false;
static bool can_read  = false;
static char epoll_test_value[20];
/*
** Function Prototypes
*/
static int      __init epoll_test_driver_init(void);
static void     __exit epoll_test_driver_exit(void);/*************** Driver functions **********************/
static int      epoll_test_open(struct inode *inode, struct file *file);
static int      epoll_test_release(struct inode *inode, struct file *file);
static ssize_t  epoll_test_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
static ssize_t  epoll_test_write(struct file *filp, const char *buf, size_t len, loff_t * off);
static unsigned int epoll_test_poll(struct file *filp, struct poll_table_struct *wait);
/*************** Sysfs functions **********************/
static ssize_t  sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
static ssize_t  sysfs_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count);
struct kobj_attribute epoll_test_attr = __ATTR(epoll_test_value, 0660, sysfs_show, sysfs_store);
/*
** File operation sturcture
*/
static struct file_operations fops =
{.owner          = THIS_MODULE,.read           = epoll_test_read,.write          = epoll_test_write,.open           = epoll_test_open,.release        = epoll_test_release,.poll           = epoll_test_poll
};
/*
** This function will be called when we read the sysfs file
*/
static ssize_t sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{pr_info("Sysfs Show - Write Permission Granted!!!\n");can_write = true;//wake up the waitqueuewake_up(&wait_queue_epoll_test_data);return sprintf(buf, "%s", "Success\n");
}
/*
** This function will be called when we write the sysfsfs file
*/
static ssize_t sysfs_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count)
{pr_info("Sysfs Store - Read Permission Granted!!!\n");strcpy(epoll_test_value, buf);can_read = true;//wake up the waitqueuewake_up(&wait_queue_epoll_test_data);return count;
}
/*
** This function will be called when we open the Device file
*/
static int epoll_test_open(struct inode *inode, struct file *file)
{pr_info("Device File Opened...!!!\n");return 0;
}
/*
** This function will be called when we close the Device file
*/
static int epoll_test_release(struct inode *inode, struct file *file)
{pr_info("Device File Closed...!!!\n");return 0;
}
/*
** This function will be called when we read the Device file
*/
static ssize_t epoll_test_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{pr_info("Read Function : epoll_test_value = %s\n",epoll_test_value);   len = strlen(epoll_test_value);strcpy(buf, epoll_test_value);#if 0  if( copy_to_user(buf, epoll_test_value, len) > 0){pr_err("ERROR: Not all the bytes have been copied to user\n");}
#endifreturn 0;
}
/*
** This function will be called when we write the Device file
*/
static ssize_t epoll_test_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{strcpy(epoll_test_value, buf);pr_info("Write function : epoll_test_value = %s\n", epoll_test_value);return len;
}
/*
** This function will be called when app calls the poll function
*/
static unsigned int epoll_test_poll(struct file *filp, struct poll_table_struct *wait)
{__poll_t mask = 0;poll_wait(filp, &wait_queue_epoll_test_data, wait);pr_info("Poll function\n");if( can_read ){can_read = false;mask |= ( POLLIN | POLLRDNORM );}if( can_write ){can_write = false;mask |= ( POLLOUT | POLLWRNORM );}return mask;
}/*
** Module Init function
*/
static int __init epoll_test_driver_init(void)
{/*Allocating Major number*/if((alloc_chrdev_region(&dev, 0, 1, "epoll_test_Dev")) <0){pr_err("Cannot allocate major number\n");return -1;}pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));/*Creating cdev structure*/cdev_init(&epoll_test_cdev,&fops);epoll_test_cdev.owner = THIS_MODULE;epoll_test_cdev.ops = &fops;/*Adding character device to the system*/if((cdev_add(&epoll_test_cdev,dev,1)) < 0){pr_err("Cannot add the device to the system\n");goto r_class;}/*Creating struct class*/if((dev_class = class_create(THIS_MODULE,"epoll_test_class")) == NULL){pr_err("Cannot create the struct class\n");goto r_class;}/*Creating device*/if((device_create(dev_class,NULL,dev,NULL,"epoll_test_device")) == NULL){pr_err("Cannot create the Device 1\n");goto r_device;}/*Creating a directory in /sys/kernel/ */kobj_ref = kobject_create_and_add("epoll_test_sysfs",kernel_kobj);/*Creating sysfs file for epoll_test_value*/if(sysfs_create_file(kobj_ref,&epoll_test_attr.attr)){pr_err("Cannot create sysfs file......\n");goto r_sysfs;}//Initialize wait queue//init_waitqueue_head(&wait_queue_epoll_test_data);pr_info("Epoll Driver Insert...Done!!!\n");return 0;
r_sysfs:kobject_put(kobj_ref); sysfs_remove_file(kernel_kobj, &epoll_test_attr.attr);
r_device:class_destroy(dev_class);
r_class:unregister_chrdev_region(dev,1);return -1;
}
/*
** Module exit function
*/
static void __exit epoll_test_driver_exit(void)
{kobject_put(kobj_ref); sysfs_remove_file(kernel_kobj, &epoll_test_attr.attr);device_destroy(dev_class,dev);class_destroy(dev_class);cdev_del(&epoll_test_cdev);unregister_chrdev_region(dev, 1);pr_info("Epoll Driver Remove...Done!!!\n");
}module_init(epoll_test_driver_init);
module_exit(epoll_test_driver_exit);MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple linux driver (Poll / Select / E-Poll driver )");
MODULE_VERSION("1");

epoll_user.c

#include <assert.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define EPOLL_SIZE ( 256 )
#define MAX_EVENTS (  20 )
int main()
{char kernel_val[20];int fd, epoll_fd, ret, n;struct epoll_event ev,events[20];fd = open("/dev/epoll_test_device", O_RDWR | O_NONBLOCK);if( fd == -1 )  {perror("open");exit(EXIT_FAILURE);}//Create epoll instanceepoll_fd = epoll_create(EPOLL_SIZE);if( epoll_fd < 0 )  {perror("epoll_create");exit(EXIT_FAILURE);}ev.data.fd = fd;ev.events  = ( EPOLLIN | EPOLLOUT );//Add the fd to the epollif( epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev ) ){perror("Failed to add file descriptor to epoll\n");close(epoll_fd);exit(EXIT_FAILURE);}while( 1 ) {puts("Starting epoll...");ret = epoll_wait( epoll_fd, events, MAX_EVENTS, 5000);;   //wait for 5secsif( ret < 0 ) {perror("epoll_wait");close(epoll_fd);assert(0);}for( n=0; n<ret; n++ ){    if( ( events[n].events & EPOLLIN )  == EPOLLIN ){read(events[n].data.fd, &kernel_val, sizeof(kernel_val));printf("EPOLLIN : Kernel_val = %s\n", kernel_val);}if( ( events[n].events & EPOLLOUT )  == EPOLLOUT ){strcpy( kernel_val, "User Space");write(events[n].data.fd, &kernel_val, strlen(kernel_val));printf("EPOLLOUT : Kernel_val = %s\n", kernel_val);}}}if(close(epoll_fd)){perror("Failed to close epoll file descriptor\n");}if(close(fd)){perror("Failed to close file descriptor\n");}return 0;
}

Makefile

COFNIG_MODULE_SIG = nobj-m += epoll_driver.o
KDIR = /lib/modules/$(shell uname -r)/buildall:gcc -o epoll_user epoll_user.cmake -C $(KDIR)  M=$(shell pwd) modulesclean:make -C $(KDIR)  M=$(shell pwd) cleanrm epoll_user -f

运行效果

root@pc:epoll# insmod epoll_driver.ko
root@pc:epoll# dmesg
[98088.735810] Major = 236 Minor = 0
[98088.736130] Epoll Driver Insert...Done!!!
root@pc:epoll# ./epoll_user
Starting epoll...
Starting epoll...
EPOLLOUT : Kernel_val = User Space
Starting epoll...
Starting epoll...
Starting epoll...
EPOLLIN : Kernel_val = aaaaStarting epoll...
^C
root@pc:epoll# rmmod epoll_driver.ko
root@pc:epoll# dmesg
[98088.735810] Major = 236 Minor = 0
[98088.736130] Epoll Driver Insert...Done!!!
[98095.263105] Device File Opened...!!!
[98095.263134] Poll function
[98104.927227] Sysfs Show - Write Permission Granted!!!
[98104.927289] Poll function
[98104.927301] Write function : epoll_test_value = User Space
[98104.927349] Poll function
[98115.347506] Sysfs Store - Read Permission Granted!!!
[98115.347549] Poll function
[98115.347561] Read Function : epoll_test_value = aaaa[98115.347613] Poll function
[98118.965764] Device File Closed...!!!
[98127.784259] Epoll Driver Remove...Done!!!

Linux之poll/select/epoll代码示例相关推荐

  1. linux下poll和epoll内核源代码剖析

    作者:董昊 博客链接http://donghao.org/uii/ poll和epoll的使用应该不用再多说了.当fd很多时,使用epoll比poll效率更高. 我们通过内核源码分析来看看到底是为什么 ...

  2. Linux下socket(select,epoll)

    1. Linuxsocket的简介 在linux支持select模式,poll模式,在内核2.6版本以后支持epoll模式: epoll模式的优点: A:支持进程打开的最大socket数据 B:IO效 ...

  3. linux IO多路复用 select epoll

    概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程 通俗理解(摘自网上一大神) 这些名词比较绕口,理解涵义就好.一个epoll场景:一个酒吧服务员(一个线程),前 ...

  4. Linux C简单日志打印代码示例

    背景 项目代码的打印函数,有的用printf,有的用std::cout,风格不统一,也不方便查看,因此需要编写一个统一的函数接口. 需求及实现 时间戳 该打印函数需要有时间戳,精确到毫秒.这样能直观观 ...

  5. msvcrt python linux,Python msvcrt.CrtSetReportMode方法代码示例

    # 需要导入模块: import msvcrt [as 别名] # 或者: from msvcrt import CrtSetReportMode [as 别名] def __enter__(self ...

  6. Linux下select, poll和epoll IO模型的详解

    http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll 介绍 Epoll 可是当前在 Linux 下开发大规模并发网络程序的热 ...

  7. 【Linux系统编程】I/O多路复用select、poll、epoll的区别使用

    I/O 多路复用技术是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的 I/O 系统调用. select(),poll(),epoll()都是I/O多路复用的机制. ...

  8. Linux系统编程——I/O多路复用select、poll、epoll

    参考:https://segmentfault.com/a/1190000003063859 Linux下的I/O复用与epoll详解:https://www.cnblogs.com/lojunren ...

  9. Linux IO复用:select、poll、epoll的理解与对比

    目录 IO多路复用 select系统调用 poll系统调用 Epoll *系统调用 Epoll vs select/poll 相关文章 Linux(实际上是Unix)的一个基本概念是Unix / Li ...

最新文章

  1. Spring Cloud第九篇:链路追踪Sleuth
  2. 对象拷贝类PropertyUtils,BeanUtils,BeanCopier的技术沉淀
  3. C语言中声明和定义的区别
  4. mysql Insert on duplicate引发的死锁
  5. 在BAE上搭建python,django环境小记
  6. 第八讲:tapestry组件
  7. 手机应用开发的方式不能完全套用到iPad上
  8. wp config.php mysql_WordPress手动配置wp-config.php文件
  9. mac显示无法连接adobe服务器,Mac安装Adobe软件,如遇Error提示解决方法
  10. Leetcode--8
  11. Android签名机制---签名过程
  12. 第十章:内核同步方法
  13. linux 挂载ntfs移动硬盘,centos6.5 挂载ntfs格式移动硬盘
  14. 这个高颜值的开源第三方网易云音乐播放器你值得拥有
  15. 全新版windows terminal 更换背景
  16. 【无标题】printf was not declared in this scope
  17. 利用python画空间分布图
  18. linux基础——echo
  19. 可视化神器Plotly绘制热力图
  20. 计算机单片机实训报告,单片机实训总结

热门文章

  1. 【Solidity】8. 杂项 - 深入理解Solidity
  2. pl/sql 本机不安装oracle服务端连接服务器
  3. 【软剑攻城队】用户需求分析文档发布!
  4. html语言怎么让字横过来,css怎么让文字竖着排列?
  5. 推荐系统实践学习系列(六)利用网络社交数据
  6. 会Vue还有必要学React吗?
  7. 苹果Mac特殊符号快捷键输入方法有哪些?
  8. CMake加入第三方库
  9. shell 十三问:
  10. ENVI_IDL: 批量制作专题地图