高级字符驱动程序操作之poll(实践篇)
2010-12-28 14:32
477 查看
1. scull.h 驱动程序由scullpipe改过来的
2. poll.c
3. Makefile
4. poll_testw.c 带参数,0表示打开第一设备,1表示打开第二个设备
5. poll_testr.c
6. Makefile
7. 开始测试
装载驱动程序
sudo insmod ./poll.ko
查看主设备号,这里假设为251
cat /proc/devices
建立设备节点
sudo mknod /dev/poll_pipe0 c 251 0
sudo mknod /dev/poll_pipe1 c 251 1
更改权限
sudo chgrp staff /dev/poll_pipe[0-1]
sudo chmod 664 /dev/poll_pipe[0-1]
在第一个终端打开读程序,程序阻塞在poll函数
sudo ./poll_testr.o
在第二个终端打开带参数为0的写程序
sudo ./poll_testw.o 0
结果:
第二个终端打印:Write 24 bytes to polltest
第一个终端打印:read Hello, Character driver! from polltest0
接着在第二个终端打开参数为1的写程序
sudo ./poll_testw.o 1
结果:
第二个终端打印:Write 24 bytes to polltest
第一个终端打印:read Hello, Character driver! from polltest1
#ifndef _SCULL_H_ #define _SCULL_H_ #ifndef SCULL_P_NR_DEVS #define SCULL_P_NR_DEVS 2 #endif #ifndef SCULL_P_BUFFER #define SCULL_P_BUFFER 4000 #endif extern int scull_p_buffer; int scull_p_init(void); void scull_p_cleanup(void); #endif /* _SCULL_H_ */
2. poll.c
#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> /* printk(), min() */ #include <linux/slab.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ #include <linux/proc_fs.h> #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/fcntl.h> #include <linux/poll.h> #include <linux/cdev.h> #include <asm/uaccess.h> #include "scull.h" /* local definitions */ struct scull_pipe { wait_queue_head_t inq, outq; /* read and write queues */ char *buffer, *end; /* begin of buf, end of buf */ int buffersize; /* used in pointer arithmetic */ char *rp, *wp; /* where to read, where to write */ int nreaders, nwriters; /* number of openings for r/w */ struct fasync_struct *async_queue; /* asynchronous readers */ struct semaphore sem; /* mutual exclusion semaphore */ struct cdev cdev; /* Char device structure */ }; /* parameters */ static int scull_p_nr_devs = SCULL_P_NR_DEVS; /* number of pipe devices */ int scull_p_buffer = SCULL_P_BUFFER; /* buffer size */ static int poll_major = 0; static struct scull_pipe *scull_p_devices; static int spacefree(struct scull_pipe *dev); static int scull_p_open(struct inode *inode, struct file *filp) { struct scull_pipe *dev; dev = container_of(inode->i_cdev, struct scull_pipe, cdev); filp->private_data = dev; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; if (!dev->buffer) { /* allocate the buffer */ dev->buffer = kmalloc(scull_p_buffer, GFP_KERNEL); if (!dev->buffer) { up(&dev->sem); return -ENOMEM; } } dev->buffersize = scull_p_buffer; dev->end = dev->buffer + dev->buffersize; dev->rp = dev->wp = dev->buffer; /* rd and wr from the beginning */ /* use f_mode,not f_flags: it's cleaner (fs/open.c tells why) */ if (filp->f_mode & FMODE_READ) dev->nreaders++; if (filp->f_mode & FMODE_WRITE) dev->nwriters++; up(&dev->sem); return nonseekable_open(inode, filp); } static int scull_p_release(struct inode *inode, struct file *filp) { struct scull_pipe *dev = filp->private_data; down(&dev->sem); if (filp->f_mode & FMODE_READ) dev->nreaders--; if (filp->f_mode & FMODE_WRITE) dev->nwriters--; if (dev->nreaders + dev->nwriters == 0) { kfree(dev->buffer); dev->buffer = NULL; /* the other fields are not checked on open */ } up(&dev->sem); return 0; } static ssize_t scull_p_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct scull_pipe *dev = filp->private_data; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; while (dev->rp == dev->wp) { /* nothing to read */ up(&dev->sem); /* release the lock */ if (filp->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp))) return -ERESTARTSYS; /* signal: tell the fs layer to handle it */ /* otherwise loop, but first reacquire the lock */ if (down_interruptible(&dev->sem)) return -ERESTARTSYS; } /* ok, data is there, return something */ if (dev->wp > dev->rp) count = min(count, (size_t)(dev->wp - dev->rp)); else /* the write pointer has wrapped, return data up to dev->end */ count = min(count, (size_t)(dev->end - dev->rp)); if (copy_to_user(buf, dev->rp, count)) { up (&dev->sem); return -EFAULT; } dev->rp += count; if (dev->rp == dev->end) dev->rp = dev->buffer; /* wrapped */ up (&dev->sem); /* finally, awake any writers and return */ wake_up_interruptible(&dev->outq); return count; } static int scull_getwritespace(struct scull_pipe *dev, struct file *filp) { while (spacefree(dev) == 0) { /* full */ DEFINE_WAIT(wait); up(&dev->sem); if (filp->f_flags & O_NONBLOCK) return -EAGAIN; prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE); if (spacefree(dev) == 0) schedule(); finish_wait(&dev->outq, &wait); if (signal_pending(current)) return -ERESTARTSYS; /* signal: tell the fs layer to handle it */ if (down_interruptible(&dev->sem)) return -ERESTARTSYS; } return 0; } /* How much space is free? */ static int spacefree(struct scull_pipe *dev) { if (dev->rp == dev->wp) return dev->buffersize - 1; return ((dev->rp + dev->buffersize - dev->wp) % dev->buffersize) - 1; } static ssize_t scull_p_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct scull_pipe *dev = filp->private_data; int result; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; /* Make sure there's space to write */ result = scull_getwritespace(dev, filp); if (result) return result; /* scull_getwritespace called up(&dev->sem) */ /* ok, space is there, accept something */ count = min(count, (size_t)spacefree(dev)); if (dev->wp >= dev->rp) count = min(count, (size_t)(dev->end - dev->wp)); /* to end-of-buf */ else /* the write pointer has wrapped, fill up to rp-1 */ count = min(count, (size_t)(dev->rp - dev->wp - 1)); if (copy_from_user(dev->wp, buf, count)) { up (&dev->sem); return -EFAULT; } dev->wp += count; if (dev->wp == dev->end) dev->wp = dev->buffer; /* wrapped */ up(&dev->sem); /* finally, awake any reader */ wake_up_interruptible(&dev->inq); /* blocked in read() and select() */ return count; } static unsigned int scull_p_poll(struct file *filp, poll_table *wait) { struct scull_pipe *dev = filp->private_data; unsigned int mask = 0; down(&dev->sem); poll_wait(filp, &dev->inq, wait); poll_wait(filp, &dev->outq, wait); if (dev->rp != dev->wp) mask |= POLLIN | POLLRDNORM; /* readable */ if (spacefree(dev)) mask |= POLLOUT | POLLWRNORM; /* writable */ up(&dev->sem); return mask; } struct file_operations scull_pipe_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = scull_p_read, .write = scull_p_write, .poll = scull_p_poll, .open = scull_p_open, .release = scull_p_release, }; int scull_p_init(void) { int result; int i; dev_t dev = 0; struct scull_pipe *device; if (poll_major) { dev = MKDEV(poll_major, 0); result = register_chrdev_region(dev, 1, "poll"); } else { result = alloc_chrdev_region(&dev, 0, 1, "poll"); poll_major = MAJOR(dev); } if (result < 0) { return result; } scull_p_devices = kmalloc(scull_p_nr_devs * sizeof(struct scull_pipe), GFP_KERNEL); if (scull_p_devices == NULL) { result = -ENOMEM; goto fail; } memset(scull_p_devices, 0, scull_p_nr_devs * sizeof(struct scull_pipe)); for (i = 0; i < scull_p_nr_devs; i++) { init_waitqueue_head(&(scull_p_devices[i].inq)); init_waitqueue_head(&(scull_p_devices[i].outq)); init_MUTEX(&scull_p_devices[i].sem); device = scull_p_devices + i; cdev_init(&device->cdev, &scull_pipe_fops); device->cdev.owner = THIS_MODULE; cdev_add(&device->cdev, dev + i, 1); } return scull_p_nr_devs; fail: scull_p_cleanup(); return result; } void scull_p_cleanup(void) { int i; dev_t devno; devno = MKDEV(poll_major, 0); if (!scull_p_devices) return; for (i = 0; i < scull_p_nr_devs; i++) { cdev_del(&scull_p_devices[i].cdev); kfree(scull_p_devices[i].buffer); } kfree(scull_p_devices); unregister_chrdev_region(devno, scull_p_nr_devs); scull_p_devices = NULL; /* pedantic */ } MODULE_AUTHOR("victorsummer"); MODULE_LICENSE("Dual BSD/GLP"); module_init(scull_p_init); module_exit(scull_p_cleanup);
3. Makefile
KERNELDIR = /usr/src/linux-headers-2.6.31-14-generic PWD := $(shell pwd) obj-m := poll.o modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
4. poll_testw.c 带参数,0表示打开第一设备,1表示打开第二个设备
#include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int main(int argc, char **argv) { char *write_buffer = "Hello, Character driver!"; int poll_fd; int code; if(argc == 2 && !strcmp(argv[1],"1")) poll_fd = open("/dev/poll_pipe1", O_WRONLY); else if(argc == 2 && !strcmp(argv[1],"0")) poll_fd = open("/dev/poll_pipe0", O_WRONLY); else { printf("You have to input option 0, 1 or all! /n"); return; } while(*write_buffer != '/0') { code = write(poll_fd , write_buffer , 24); printf("Write %d bytes to polltest/n", code); write_buffer += code; } close(poll_fd); exit(0); }
5. poll_testr.c
#include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <linux/poll.h> int main() { int polltest0, polltest1; int retval; struct pollfd poll_list[2]; polltest0 = open("/dev/poll_pipe0", O_RDONLY); polltest1 = open("/dev/poll_pipe1", O_RDONLY); poll_list[0].fd = polltest0; poll_list[1].fd = polltest1; poll_list[0].events = POLLIN | POLLRDNORM; poll_list[1].events = POLLIN | POLLRDNORM; while(1) { retval = poll(poll_list, 2, -1); if(retval < 0) { printf("Error in poll! /n"); return -1; } if(poll_list[0].revents&(POLLIN | POLLRDNORM)) { char *read_buffer = malloc(sizeof(char)*30); if (read(polltest0, read_buffer, 24) != -1) { printf("read %s from polltest0 /n", read_buffer); } free(read_buffer); } if(poll_list[1].revents&(POLLIN | POLLRDNORM)) { char *read_buffer = malloc(sizeof(char)*30); if (read(polltest1, read_buffer, 24) != -1) { printf("read %s from polltest1 /n", read_buffer); } free(read_buffer); } } close(polltest0 ); close(polltest1 ); exit(0); }
6. Makefile
all : poll_testw.o poll_testr.o gcc -o poll_testw.o poll_testw.c gcc -o poll_testr.o poll_testr.c
7. 开始测试
装载驱动程序
sudo insmod ./poll.ko
查看主设备号,这里假设为251
cat /proc/devices
建立设备节点
sudo mknod /dev/poll_pipe0 c 251 0
sudo mknod /dev/poll_pipe1 c 251 1
更改权限
sudo chgrp staff /dev/poll_pipe[0-1]
sudo chmod 664 /dev/poll_pipe[0-1]
在第一个终端打开读程序,程序阻塞在poll函数
sudo ./poll_testr.o
在第二个终端打开带参数为0的写程序
sudo ./poll_testw.o 0
结果:
第二个终端打印:Write 24 bytes to polltest
第一个终端打印:read Hello, Character driver! from polltest0
接着在第二个终端打开参数为1的写程序
sudo ./poll_testw.o 1
结果:
第二个终端打印:Write 24 bytes to polltest
第一个终端打印:read Hello, Character driver! from polltest1
相关文章推荐
- Linux设备驱动程序——高级字符驱动程序操作(poll机制)
- 高级字符驱动程序操作--增加了poll功能
- 高级字符驱动程序操作之异步通知IO(实践篇)基于内核2.6.35-30
- 高级字符驱动程序操作——poll和select
- 高级字符驱动程序操作之异步通知IO(实践篇)
- 高级字符驱动程序操作之休眠(实践篇)
- Linux设备驱动程序第三版学习(7)- 高级字符驱动程序操作(续2)- poll/select
- 高级字符驱动程序操作(poll机制)
- 高级字符驱动程序操作之ioctl(实践篇)
- 高级字符驱动程序操作之异步通知IO(实践篇)基于内核2.6.35-30
- 高级字符驱动程序操作之poll(理论篇)
- 【Linux 驱动】第六章 高级字符驱动程序操作----poll,select,epoll
- Linux设备驱动程序第三版学习(7)- 高级字符驱动程序操作(续2)- poll/select .
- ldd3学习之十二(3):高级字符驱动程序操作--poll/select、异步通知
- Linux高级字符设备之Poll操作
- linux设备驱动学习(5) 高级字符驱动程序操作--ioctl
- ldd3学习之十二(4):高级字符驱动程序操作--llseek、设备文件的访问控制
- 高级字符驱动程序操作之异步通知IO(理论篇)
- Linux设备驱动程序学习(4)-高级字符驱动程序操作[(1)ioctl and llseek]
- Linux设备驱动程序——高级字符驱动程序操作(阻塞型I/O)