在用户程序中,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");
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
测试文件:

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 }
下一章 5、并发控制