《[arm驱动]Linux内核开发之阻塞非阻塞IO----轮询操作》涉及内核驱动函数二个,内核结构体零个,分析了内核驱动函数二个;可参考的相关应用程序模板或内核驱动模板二个,可参考的相关应用程序模板或内核驱动一个
一、概念:Poll是非阻塞IO----轮询操作
非阻塞 I/O 的应用程序常常使用 poll, select, 和 epoll 系统调用. poll, select 和 epoll 本质上有相同的功能: 每个允许一个进程来决定它是否可读或者写一个或多个文件而不阻塞.
Tip:select()和poll(),epoll查询是否可对设备进行无阻塞的访问,这几个系统调用最终又会引发设备驱动中的poll()函数被执行
PS:看到这感觉晕了,暂且不理会
二、使用场景:
它们常常用在必须使用多输入输出流的应用程序(如调用read,write字符设备驱动文件/dev/****)。因为这些调用也可阻塞进程直到任何一个给定集合的文件描述符可用来读或写.
三、相关函数
1、内核函数
内核驱动函数一)a)poll()函数原型:
unsigned int (*poll) (struct file *filp, poll_table *wait);
作用:调用poll_wait(),将可能引起设备文件状态变化的等待队列头添加到poll_table.
返回值:返回是否能对设备进行无阻塞读写访问的掩码
放回值mask常量及函数
常量 说明
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级数据可读
POLLOUT 普通数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件
内核驱动函数二)b)poll_wait()函数原型:
void poll_wait(struct file *filp, wait_queue_head_t *queue, poll_table *wait);
作用:将可能引起设备文件状态变化的等待队列头添加到poll_table
2、应用程序poll
函数
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
a) 参数:
fds 指向 struct pollfd 数组
nfds 指定 pollfd 数组元素的个数,也就是要监测几个 pollfd
timeout 时间参数,单位ms,1000ms=1s
Tip:fds可以是很多个文件(如网卡,按键),poll可以论寻fds[n]
b)结构体pollfd
struct pollfd {
int fd;
short events;
short revents;
};
3、总结:从应用程序的调用来看,并不需要理会内核函数中的参数poll_table *wait是什么,只需要调用poll_wait()
四、使用模板
模板一)a)内核程序模板
static DECLARE_WAIT_QUEUE_HEAD(waitq);//定义结构体名称为waitq poll(struct file *file, poll_table *wait){//返回mask unsigned int mask = 0; poll_wait(file, &waitq, wait); if(...)//可读 { mask |= POLLIN | POLLRDNORM; //标识数据可获得 } if(...)//可写 { mask |= POLLOUT | POLLRDNORM; //标识数据可写入 } return mask; }
模板二)b)测试程序模板
struct pollfd fds[n]; fds[0].fd = fd; fds[0].events = POLLIN; poll(fds, n, 5000);
c)再次理解下面几句
fds 指向 struct pollfd 数组
nfds 指定 pollfd 数组元素的个数,也就是要监测几个 pollfd
timeout 时间参数,单位ms,1000ms=1s
Tip:fds可以是很多个文件(如网卡,按键),poll可以论寻fds[n]
实例一)五、案例jz2440中断非阻塞驱动实例
1、 非阻塞内核按键驱动。
//“irq_drv”,"irq_","irq" #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/poll.h> #include <linux/delay.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> static DECLARE_WAIT_QUEUE_HEAD(button_waitq);//定义结构体名称为button_waitq static struct class *irq_class; static struct class_device *irq_class_dev; static int ev_press = 0; static unsigned char key_val; struct pin_desc{ unsigned int pin; unsigned int key_val; }; struct pin_desc pins_desc[3] = { {S3C2410_GPF0, 0x01}, {S3C2410_GPF2, 0x02}, {S3C2410_GPG3, 0x03}, }; static irqreturn_t irq_handle(int irq, void *dev__id){ //printk("irq = %d\n", irq); int pinval; struct pin_desc *pindesc = (struct pin_desc *)dev__id; pinval = s3c2410_gpio_getpin(pindesc->pin); if(!pinval){//按下 key_val = pindesc->key_val; }else{//松开 key_val = 0x80 | pindesc->key_val; } ev_press = 1; wake_up_interruptible(&button_waitq); return IRQ_RETVAL(IRQ_HANDLED);//warn:返回IRQ_HANDLED } static unsigned irq_drv_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; poll_wait(file, &button_waitq, wait); // 不会立即休眠 if (ev_press) mask |= POLLIN | POLLRDNORM; return mask; } static int irq_drv_open(struct inode *inode, struct file *file) { printk("irq_dev read\n"); // request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char * devname, void * dev_id); dev_id随意 request_irq(IRQ_EINT0, irq_handle, IRQ_TYPE_EDGE_BOTH, "s2", &pins_desc[0]); request_irq(IRQ_EINT2, irq_handle, IRQ_TYPE_EDGE_BOTH, "s3", &pins_desc[1]); request_irq(IRQ_EINT11, irq_handle, IRQ_TYPE_EDGE_BOTH, "s4", &pins_desc[2]); return 0; } static ssize_t irq_drv_read (struct file *file, char __user *buf, size_t count, loff_t *ppos){ if(count != 1)return -EINVAL; wait_event_interruptible(button_waitq, ev_press);//ev_press标志(if!(ev_press)),那么一直休眠 copy_to_user(buf, &key_val, 1);//一个 char 0xff ev_press = 0; return 1;//warn :return the size of val } static ssize_t irq_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { printk("irq_dev write\n"); return 0; } static ssize_t irq_drv_release(struct inode *inode, struct file *file){ free_irq(IRQ_EINT0, &pins_desc[0]); free_irq(IRQ_EINT2, &pins_desc[1]); free_irq(IRQ_EINT11, &pins_desc[2]); return 0; } static struct file_operations irq_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = irq_drv_open, .write = irq_drv_write, .read = irq_drv_read, .release = irq_drv_release, .poll = irq_drv_poll, }; int major; static int irq_drv_init(void) { major = register_chrdev(0, "irq_drv", &irq_drv_fops); // 注册, 告诉内核 if (major < 0) { printk(" can't register major number\n"); return major; } irq_class = class_create(THIS_MODULE, "irq_drv"); if (IS_ERR(irq_class)) return PTR_ERR(irq_class); irq_class_dev = class_device_create(irq_class, NULL, MKDEV(major, 0), NULL, "irq"); /* /dev/xyz */ if (IS_ERR(irq_class_dev)) return PTR_ERR(irq_class_dev); return 0; } static void irq_drv_exit(void) { unregister_chrdev(major, "irq_drv"); // 卸载 class_device_unregister(irq_class_dev); class_destroy(irq_class); } module_init(irq_drv_init); module_exit(irq_drv_exit); MODULE_LICENSE("GPL");
2、测试应用程序
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <poll.h> /* irq */ int main(int argc, char **argv) { int fd; unsigned char key_val; int cnt = 0; int ret; struct pollfd fds[1]; fd = open("/dev/irq", O_RDWR); if (fd < 0) { printf("can't open!\n"); exit(1); } fds[0].fd = fd; fds[0].events = POLLIN; while (1) { ret = poll(fds, 1, 5000); if(ret == 0){ printf("time out!\n"); }else{ read(fd, &key_val, 1); printf("key_Vals = 0x%x\n", key_val); } } return 0; } Makefile #myirq.bin objs := $(patsubst %c, %o, $(shell ls *.c)) myarmgcc := /workspacearm/armlinuxgcc2626/bin/arm-linux-gcc myirq.bin:$(objs) $(myarmgcc) -o $@ $^ cp *.bin /opt/fsmini/ %.o:%.c $(myarmgcc) -c -o $@ $< clean: rm -f *.bin *.o
Curtiss Falcon