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;		//监听的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

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 

       /* 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

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 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;

epoll_ctl

/* 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

epoll_wait

/* 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!!!

你可能感兴趣的:(Linux内核,网络通信子系统,linux)