在用戶程序中,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、並發控制