Linux之poll/select/epoll代码示例
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代码示例相关推荐
- linux下poll和epoll内核源代码剖析
作者:董昊 博客链接http://donghao.org/uii/ poll和epoll的使用应该不用再多说了.当fd很多时,使用epoll比poll效率更高. 我们通过内核源码分析来看看到底是为什么 ...
- Linux下socket(select,epoll)
1. Linuxsocket的简介 在linux支持select模式,poll模式,在内核2.6版本以后支持epoll模式: epoll模式的优点: A:支持进程打开的最大socket数据 B:IO效 ...
- linux IO多路复用 select epoll
概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程 通俗理解(摘自网上一大神) 这些名词比较绕口,理解涵义就好.一个epoll场景:一个酒吧服务员(一个线程),前 ...
- Linux C简单日志打印代码示例
背景 项目代码的打印函数,有的用printf,有的用std::cout,风格不统一,也不方便查看,因此需要编写一个统一的函数接口. 需求及实现 时间戳 该打印函数需要有时间戳,精确到毫秒.这样能直观观 ...
- msvcrt python linux,Python msvcrt.CrtSetReportMode方法代码示例
# 需要导入模块: import msvcrt [as 别名] # 或者: from msvcrt import CrtSetReportMode [as 别名] def __enter__(self ...
- Linux下select, poll和epoll IO模型的详解
http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll 介绍 Epoll 可是当前在 Linux 下开发大规模并发网络程序的热 ...
- 【Linux系统编程】I/O多路复用select、poll、epoll的区别使用
I/O 多路复用技术是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的 I/O 系统调用. select(),poll(),epoll()都是I/O多路复用的机制. ...
- Linux系统编程——I/O多路复用select、poll、epoll
参考:https://segmentfault.com/a/1190000003063859 Linux下的I/O复用与epoll详解:https://www.cnblogs.com/lojunren ...
- Linux IO复用:select、poll、epoll的理解与对比
目录 IO多路复用 select系统调用 poll系统调用 Epoll *系统调用 Epoll vs select/poll 相关文章 Linux(实际上是Unix)的一个基本概念是Unix / Li ...
最新文章
- Spring Cloud第九篇:链路追踪Sleuth
- 对象拷贝类PropertyUtils,BeanUtils,BeanCopier的技术沉淀
- C语言中声明和定义的区别
- mysql Insert on duplicate引发的死锁
- 在BAE上搭建python,django环境小记
- 第八讲:tapestry组件
- 手机应用开发的方式不能完全套用到iPad上
- wp config.php mysql_WordPress手动配置wp-config.php文件
- mac显示无法连接adobe服务器,Mac安装Adobe软件,如遇Error提示解决方法
- Leetcode--8
- Android签名机制---签名过程
- 第十章:内核同步方法
- linux 挂载ntfs移动硬盘,centos6.5 挂载ntfs格式移动硬盘
- 这个高颜值的开源第三方网易云音乐播放器你值得拥有
- 全新版windows terminal 更换背景
- 【无标题】printf was not declared in this scope
- 利用python画空间分布图
- linux基础——echo
- 可视化神器Plotly绘制热力图
- 计算机单片机实训报告,单片机实训总结