首页 > 技术文章 > 4、poll()、select()和epoll()

Lioker 2019-05-12 15:54 原文

 

在用户程序中,poll()和select()系统调用用于对设备进行无阻塞访问。poll()和select()最终会调用设备驱动中的poll()函数,在我所使用的Linux内核中,还有扩展的poll()函数epoll()

 

一、poll()函数

应用程序中的poll()函数原型为:

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

函数参数以及返回值:

fds:用于描述监听的文件描述符集

nfds:fds的数量

timeout:监听超时时间

返回值:成功返回0;出错返回-1。


 

示例代码如下:

 1 struct pollfd fdsa[1];
 2 
 3 fdsa[0].fd        = fd;        /* 监听fd */
 4 fdsa[0].events    = POLLIN;    /* 监听输入事件,除 */
 5 
 6 while(1) {
 7     /* 5000ms内若有输入,返回大于0的数;否则返回0 */
 8     ret = poll(&fdsa[0], 1, 5000);
 9     if (!ret)
10         printf("time out\n");
11     else {
12         read(fd, buf, 1);
13         printf("buf = %d\n", buf[0]);
14     }
15 }

 

现在,我们来看看poll()函数的调用过程:

SYSCALL_DEFINE3(poll, ...)
  -> do_sys_poll(ufds, nfds, to);
    -> poll_initwait(&table);    // 初始化等待队列
    -> do_poll(nfds, head, &table, end_time);
      -> do_pollfd(pfd, pt, &can_busy_loop, busy_flag);    // 处理进程的每一个fd的poll操作
        -> f.file->f_op->poll(f.file, pwait);    // 执行驱动程序的poll()函数

 

 

二、select()函数

应用程序中的select()函数原型为:

#include <sys/select.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);

 

select()函数中参数nfs表示监听所有的fd的最大值 + 1;readfds、writefds和exceptfds分别是被监听的读、写和异常的文件描述符集;timeout表示监听超时时间,结构体如下:

struct timeval {
    __kernel_time_t        tv_sec;        /**/
    __kernel_suseconds_t    tv_usec;    /* 微秒 */
};

FD_SET()、FD_ZERO()、FD_CLR()、FD_ISSET()分别用于加入fd、清除fd集合、清除fd、判断fd是否被加入集合中

 

示例代码如下:

 1 fd_set rfds;
 2 
 3 FD_ZERO(&rfds);
 4 FD_SET(fd, &rfds);
 5 
 6 tv.tv_sec = 5;
 7 tv.tv_usec = 0;    // 设置等待时间5s
 8 
 9 ret = select(fd + 1, &rfds, NULL, NULL, &tv);
10 
11 if (ret > 0) {
12     if(FD_ISSET(fd, &rfds))    /* 测试是否有数据 */ {
13         read(fd, buf, 1);
14         printf("buf = %d\n", buf[0]);
15     }
16 }

 

select()函数的调用过程:

SYSCALL_DEFINE5(select, ...)
  -> core_sys_select(n, inp, outp, exp, to);
    -> do_select(n, &fds, end_time);    // 
      -> poll_initwait(&table);    // 初始化等待队列
      -> mask = (*f_op->poll)(f.file, wait);    // 执行驱动程序的poll()函数

 

 

当poll()和select()的文件数量庞大、I/O流量频繁时,poll()和select()的性能表现较差,我们宜使用epoll(),epoll()不会随着fd的数目增长而降低效率

三、epoll()函数

epoll()函数原型为:

#include <sys/epoll.h>

/* 创建epoll文件描述符 */
int epoll_create(int size);

/* 添加、修改或删除需要监听的文件描述符及其事件 */
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

/* 等待被监听的描述符的I/O事件 */
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

代码中maxevents表示每次能处理的事件数

 

代码中的struct epoll_event声明为:

struct epoll_event {  
    uint32_t events;    /* epoll事件 */  
    epoll_data_t data;    /* epoll数据 */
};

typedef union epoll_data {  
    void *ptr;
    int fd;
    uint32_t u32;
    uint64_t u64;
} epoll_data_t;

 

示例代码如下:

 1 int efd, nfds, i;
 2 struct epoll_event event;
 3 
 4 efd = epoll_create(256);
 5 if (efd == -1) {
 6     return -1;
 7 }
 8 
 9 event.data.fd = fd;
10 /* 
11  * EPOLLIN: 表示对应的文件描述符可以读
12  * EPOLLOUT: 表示对应的文件描述符可以写
13  * EPOLLET: 表示对应的文件描述符有事件发生
14  */
15 event.events  = EPOLLIN | EPOLLET;
16 
17 /* 除EPOLL_CTL_ADD之外还有EPOLL_CTL_DEL(删除)和EPOLL_CTL_MOD(修改) */
18 s = epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event);
19 
20 while (1) {
21     nfds = epoll_wait(epfd, event, 20, 500);
22     
23     for (i = 0; i < nfds; ++i) {
24         if (event[i].events & EPOLLIN) /* 有数据可读 */ {
25             read(event[i].data.fd, buf, 1);
26             printf("buf = %d\n", buf[0]);
27         }
28     }
29 }

 

epoll()系列函数的调用过程:

/* epoll_create() */
SYSCALL_DEFINE1(epoll_create, int, size)
  -> sys_epoll_create1(0);
    -> evetpoll_init();

/* epoll_ctl() */
SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
        struct epoll_event __user *, event)
  -> case EPOLL_CTL_ADD:
  -> ep_insert(ep, &epds, tfile, fd);
    -> tfile->f_op->poll(tfile, &epq.pt);    /* 调用驱动的poll()函数 */

/* epoll_wait() */
SYSCALL_DEFINE4(epoll_wait, int, epfd, struct epoll_event __user *, events,
        int, maxevents, int, timeout)
  -> ep_poll(ep, events, maxevents, timeout);
    -> 判断timeout

 

 

四、poll()、select()和epoll()的区别

 

 

五、驱动程序的poll()函数

poll()函数需要#include <linux/poll.h>

为了更方便演示poll()函数,我在代码中加入了一个全局变量ev_press,如果有按键按下置1;然后重新置0

static struct file_operations key_fops = {
    .owner    = THIS_MODULE,
    .read    = key_read,
    .poll    = key_poll,        /* 加入poll()函数 */
    .open        = key_open,
    .release    = key_release,
};

 

poll()函数示例如下:

static unsigned int key_poll(struct file *filp, struct poll_table_struct *table)
{
    struct key_device *dev = filp->private_data;

    unsigned int mask = 0;

    poll_wait(filp, &dev->r_head, table);

    if (ev_press)
        mask |= POLLIN | POLLRDNORM;

    return mask;
}

代码中的poll_wait()并不会像wait_event()系列函数一样阻塞地等待事件发生,poll_wait()并不会引起阻塞。它只是把当前进程加入到poll_table_struct等待列表

 

key源代码:

  1 #include <linux/module.h>
  2 #include <linux/fs.h>
  3 #include <linux/init.h>
  4 #include <linux/cdev.h>
  5 #include <linux/slab.h>
  6 #include <linux/device.h>
  7 #include <linux/irq.h>
  8 #include <linux/interrupt.h>
  9 #include <linux/wait.h>
 10 #include <linux/timer.h>
 11 #include <linux/gpio.h>
 12 #include <linux/sched.h>
 13 #include <linux/poll.h>
 14 
 15 #include <asm/uaccess.h>
 16 #include <asm/irq.h>
 17 #include <asm/io.h>
 18 
 19 #include <mach/gpio.h>
 20 
 21 #define KEY_MAJOR        255
 22 
 23 struct pin_desc {
 24     int gpio;
 25     int val;
 26     char *name;
 27 };
 28 
 29 struct key_device {
 30     struct cdev cdev;
 31     wait_queue_head_t r_head;
 32     wait_queue_head_t w_head;
 33 };
 34 
 35 static struct pin_desc desc[4] = {
 36     { EXYNOS4_GPX3(2), 0x01, "KEY0" },
 37     { EXYNOS4_GPX3(3), 0x02, "KEY1" },
 38     { EXYNOS4_GPX3(4), 0x03, "KEY2" },
 39     { EXYNOS4_GPX3(5), 0x04, "KEY3" },
 40 };
 41 
 42 static int g_major = KEY_MAJOR;
 43 module_param(g_major, int, S_IRUGO);
 44 
 45 static struct key_device*    dev;
 46 static struct class*        scls;
 47 static struct device*        sdev;
 48 static unsigned char        key_val;
 49 static volatile int            ev_press = 0;
 50 
 51 static irqreturn_t key_interrupt(int irq, void *dev_id)
 52 {
 53     struct pin_desc *pindesc = (struct pin_desc *)dev_id;
 54     unsigned int tmp;
 55 
 56     tmp = gpio_get_value(pindesc->gpio);
 57 
 58     /* active low */
 59     printk(KERN_DEBUG "KEY %d: %08x\n", pindesc->val, tmp);
 60 
 61     if (tmp)
 62         key_val = pindesc->val;
 63     else
 64         key_val = pindesc->val | 0x80;
 65 
 66     set_current_state(TASK_RUNNING);
 67 
 68     ev_press = 1;
 69 
 70     return IRQ_HANDLED;
 71 }
 72 
 73 static ssize_t key_read(struct file *filp, char __user *buf, size_t len, loff_t * loff)
 74 {
 75     struct key_device *dev = filp->private_data;
 76 
 77     // 声明等待队列
 78     DECLARE_WAITQUEUE(rwait, current);
 79     add_wait_queue(&dev->r_head, &rwait);
 80 
 81     // 休眠
 82     __set_current_state(TASK_INTERRUPTIBLE);
 83     schedule();
 84 
 85     // 有数据
 86     copy_to_user(buf, &key_val, 1);
 87 
 88     remove_wait_queue(&dev->r_head, &rwait);
 89     set_current_state(TASK_RUNNING);
 90 
 91     ev_press = 0;
 92 
 93     return 1;
 94 }
 95 
 96 static unsigned int key_poll(struct file *filp, struct poll_table_struct *table)
 97 {
 98     struct key_device *dev = filp->private_data;
 99 
100     unsigned int mask = 0;
101 
102     poll_wait(filp, &dev->r_head, table);
103 
104     if (ev_press)
105         mask |= POLLIN | POLLRDNORM;
106 
107     return mask;
108 }
109 
110 static int key_open(struct inode *nodep, struct file *filp)
111 {
112     struct key_device *dev = container_of(nodep->i_cdev, struct key_device, cdev);
113     // 放入私有数据中
114     filp->private_data = dev;
115 
116     int irq;
117     int i, err = 0;
118 
119     for (i = 0; i < ARRAY_SIZE(desc); i++) {
120         if (!desc[i].gpio)
121             continue;
122 
123         irq = gpio_to_irq(desc[i].gpio);
124         err = request_irq(irq, key_interrupt, IRQ_TYPE_EDGE_BOTH, 
125                 desc[i].name, (void *)&desc[i]);
126         if (err)
127             break;
128     }
129     
130     if (err) {
131         i--;
132         for (; i >= 0; i--) {
133             if (!desc[i].gpio)
134                 continue;
135 
136             irq = gpio_to_irq(desc[i].gpio);
137             free_irq(irq, (void *)&desc[i]);
138         }
139         return -EBUSY;
140     }
141     
142     init_waitqueue_head(&dev->r_head);
143 
144     return 0;
145 }
146 
147 static int key_release(struct inode *nodep, struct file *filp)
148 {
149     // 释放中断
150     int irq, i;
151 
152     for (i = 0; i < ARRAY_SIZE(desc); i++) {
153         if (!desc[i].gpio)
154             continue;
155 
156         irq = gpio_to_irq(desc[i].gpio);
157         free_irq(irq, (void *)&desc[i]);
158     }
159 
160     return 0;
161 }
162 
163 static struct file_operations key_fops = {
164     .owner    = THIS_MODULE,
165     .read    = key_read,
166     .poll    = key_poll,
167     .open        = key_open,
168     .release    = key_release,
169 };
170 
171 static int keys_init(void)
172 {
173     int ret;
174     int devt;
175     if (g_major) {
176         devt = MKDEV(g_major, 0);
177         ret = register_chrdev_region(devt, 1, "key");
178     }
179     else {
180         ret = alloc_chrdev_region(&devt, 0, 1, "key");
181         g_major = MAJOR(devt);
182     }
183 
184     if (ret)
185         return ret;
186 
187     dev = kzalloc(sizeof(struct key_device), GFP_KERNEL);
188     if (!dev) {
189         ret = -ENOMEM;
190         goto fail_alloc;
191     }
192 
193     cdev_init(&dev->cdev, &key_fops);
194     ret = cdev_add(&dev->cdev, devt, 1);
195     if (ret)
196         return ret;
197 
198     scls = class_create(THIS_MODULE, "key");
199     sdev = device_create(scls, NULL, devt, NULL, "key");
200 
201     return 0;
202 
203 fail_alloc:
204     unregister_chrdev_region(devt, 1);
205 
206     return ret;
207 }
208 
209 static void keys_exit(void)
210 {
211     dev_t devt = MKDEV(g_major, 0);
212 
213     device_destroy(scls, devt);
214     class_destroy(scls);
215 
216     cdev_del(&(dev->cdev));
217     kfree(dev);
218 
219     unregister_chrdev_region(devt, 1);
220 }
221 
222 module_init(keys_init);
223 module_exit(keys_exit);
224 
225 MODULE_LICENSE("GPL");
View Code

Makefile:

 1 KERN_DIR = /work/tiny4412/tools/linux-3.5
 2 
 3 all:
 4     make -C $(KERN_DIR) M=`pwd` modules 
 5 
 6 clean:
 7     make -C $(KERN_DIR) M=`pwd` modules clean
 8     rm -rf modules.order
 9 
10 obj-m    += key.o
View Code

测试文件:

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <sys/types.h>
 4 #include <sys/stat.h>
 5 #include <sys/select.h>
 6 #include <fcntl.h>
 7 #include <string.h>
 8 
 9 int main(int argc, char** argv)
10 {
11     int fd, ret;
12     fd = open("/dev/key", O_RDWR);
13     if (fd < 0) {
14         printf("can't open /dev/key\n");
15         return -1;
16     }
17 
18     unsigned char key_val;
19     
20     fd_set rfds;
21     FD_ZERO(&rfds);
22     FD_SET(fd, &rfds);
23     
24     struct timeval tv;
25     tv.tv_sec = 5;
26     tv.tv_usec = 0;
27 
28     while (1) {
29         ret = select(fd + 1, &rfds, NULL, NULL, &tv);
30         
31         if(ret == 0)    /* 超时 */ {
32             printf("select time out!\n");
33             break;
34         }
35         else if (ret > 0) {
36             if (FD_ISSET(fd, &rfds)) {
37                 read(fd, &key_val, 1);
38                 printf("key_val = 0x%x\n", key_val);
39             }
40         }
41     }
42     
43     close(fd);
44 
45     return 0;
46 }
View Code

 

下一章  5、并发控制

 

 

推荐阅读