问题:假如应用需要根据IO的状态来读或写多个IO,如何处理?如果是一个进程处理,一个一个IO的处理,那么就势必会出现阻塞等待某个IO的过程,此时就可能丢其他IO的数据。
解决以上问题,可以使用多个thread,每个thread直跟踪处理一个IO,不过只有少量IO还行,假如是大量的IO并且要监控这些IO状态呢,多线程显然不是最好的办法。
解决上面那个问题的办法:内核提供了IO多路复用的机制,一共有三种
poll允许一个进程不阻塞的形式去读写多路IO。poll可以监听多路IO直至其IO事件的发生来触发处理。
事件有
events | 含义 |
---|---|
POLLIN | 有数据可读,不阻塞 |
POLLPRI | 紧急数据可读 |
POLLOUT | 写数据,不阻塞 |
POLLRDHUP | Linux 2.6.17之后提供,流socket关闭或者连接中途中断写操作 |
POLLERR | 发生错误 |
POLLHUP | 挂起 |
POLLRDNORM | 等同于POLLIN |
POLLWRNORM | 等同于POLLOUT |
POLLWRBAND | priority data 写入 |
如何在应用中使用poll
include/uapi/asm-generic/poll.h
中定义
struct pollfd {
int fd; //监听的fd
short events; //关心的事件
short revents;
};
比如
#include
struct pollfd pfd;
pfd.fd = test_fd;
pfd.events = (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)
同时监听多个fd,可以使用数组
struct pollfd pfd[num_of_fd];
poll_driver.c模拟驱动层
#include
#include
#include
#include
#include
#include
#include
#include //kmalloc()
#include //copy_to/from_user()
#include
#include //Required for the wait queues
#include
#include
#include
//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 waitqueue
wake_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 waitqueue
wake_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");
}
#endif
return 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
#include
#include
#include
#include
#include
#include
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 5secs
if( 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)/build
all:
gcc -o poll_user poll_user.c
make -C $(KDIR) M=$(shell pwd) modules
clean:
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本应早就弃用了,但它就是很坚挺。
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集中 |
#include
/* According to earlier standards */
#include
#include
#include
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
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
#include
#include
#include
#include
#include
#include
#include //kmalloc()
#include //copy_to/from_user()
#include
#include //Required for the wait queues
#include
#include
#include
//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 waitqueue
wake_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 waitqueue
wake_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");
}
#endif
return 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
#include
#include
#include
#include
#include
#include
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 Seconds
timeout.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 = n
obj-m += select_driver.o
KDIR = /lib/modules/$(shell uname -r)/build
all:
gcc -o select_user select_user.c
make -C $(KDIR) M=$(shell pwd) modules
clean:
make -C $(KDIR) M=$(shell pwd) clean
rm 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有两种调用触发方式: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需要用到一下API
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 file
descriptors to be associated with the new instance. The fd
returned 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 SIZE
parameter has been dropped. */
extern int epoll_create1 (int __flags) __THROW;
/* Manipulate an epoll instance "epfd". Returns 0 in case of success,
-1 in case of error ( the "errno" variable will contain the
specific error code ) The "op" parameter is one of the EPOLL_CTL_*
constants defined above. The "fd" parameter is the target of the
operation. The "event" parameter describes which events the caller
is 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 |
/* Wait for events on an epoll instance "epfd". Returns the number of
triggered events returned in "events" buffer. Or -1 in case of
error with the "errno" variable set to the specific error code. The
"events" parameter is a buffer that will contain triggered
events. The "maxevents" is the maximum number of events to be
returned ( usually size of "events" ). The "timeout" parameter
specifies 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
#include
#include
#include
#include
#include
#include
#include //kmalloc()
#include //copy_to/from_user()
#include
#include //Required for the wait queues
#include
#include
#include
//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 waitqueue
wake_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 waitqueue
wake_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");
}
#endif
return 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
#include
#include
#include
#include
#include
#include
#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 instance
epoll_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 epoll
if( 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 5secs
if( 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 = n
obj-m += epoll_driver.o
KDIR = /lib/modules/$(shell uname -r)/build
all:
gcc -o epoll_user epoll_user.c
make -C $(KDIR) M=$(shell pwd) modules
clean:
make -C $(KDIR) M=$(shell pwd) clean
rm 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 = aaaa
Starting 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!!!