#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/fs.h>//file_operations, file #include <linux/proc_fs.h>//proc #include <linux/errno.h> #include <linux/types.h> #include <linux/fcntl.h> #include <linux/poll.h> #include <linux/cdev.h>//cdev #include <asm/uaccess.h>//copy_to_user & copy_from_user #include <linux/init.h> #include <linux/ioctl.h>//ioctl MODULE_LICENSE("Dual BSD/GPL"); static int scullp_major = 250; static int scullp_minor = 0; static int scull_p_nr_devs = 1; static int scull_p_buffer = 4000; dev_t scull_p_devno; struct scull_pipe{ wait_queue_head_t inq, outq; char *buffer, *end; int buffersize; char *rp, *wp; int nreaders, nwriters; struct semaphore sem; struct cdev cdev; }; struct scull_pipe *scull_p_devices; 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){ up(&dev->sem); if(filp->f_flags & O_NONBLOCK) return -EAGAIN; if(wait_event_interruptible(dev->inq, (dev->rp != dev->wp))) return -ERESTARTSYS; if(down_interruptible(&dev->sem)) return -ERESTARTSYS; } if(dev->wp > dev->rp) count = min(count, (size_t)(dev->wp - dev->rp)); else 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; up(&dev->sem); wake_up_interruptible(&dev->outq); return count; } 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 int scull_getwritespace(struct scull_pipe *dev, struct file *filp) { while(spacefree(dev) == 0){ 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; if(down_interruptible(&dev->sem)) return -ERESTARTSYS; } return 0; } ssize_t scull_p_write(struct file *filp, 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; result = scull_getwritespace(dev, filp); if(result) return result; count = min(count, (size_t)spacefree(dev)); if(dev->wp >= dev->rp) count = min(count, (size_t)(dev->end - dev->wp)); else 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; up(&dev->sem); wake_up_interruptible(&dev->inq); return count; } 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){ 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; 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; } up(&dev->sem); return 0; } struct file_operations scull_pipe_fops = { .owner = THIS_MODULE, .open = scull_p_open, .release = scull_p_release, .read = scull_p_read, .write = scull_p_write, }; static void scull_p_setup_cdev(struct scull_pipe *dev, int index) { int err, devno = scull_p_devno + index; cdev_init(&dev->cdev, &scull_pipe_fops); dev->cdev.owner = THIS_MODULE; err = cdev_add(&dev->cdev, devno, 1); if(err) printk(KERN_NOTICE "Error %d adding scull%d", err, index); } static void scull_cleanup_module(void) { int i; if(!scull_p_devices) return; if(scull_p_devices){ 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(scull_p_devno, scull_p_nr_devs); scull_p_devices = NULL; } static int __init scull_init_module(void) { int result, i; dev_t firstdev = 0; firstdev = MKDEV(scullp_major, scullp_minor); result = register_chrdev_region(firstdev, scull_p_nr_devs, "scullp"); if(result < 0){ printk(KERN_WARNING "scull:can't get major %d\n", scullp_major); return result; } scull_p_devno = firstdev; scull_p_devices = kmalloc(scull_p_nr_devs * sizeof(struct scull_pipe), GFP_KERNEL); if(!scull_p_devices){ unregister_chrdev_region(firstdev, scull_p_nr_devs); return 0; } 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); scull_p_setup_cdev(&scull_p_devices[i], i); } return 0; } module_init(scull_init_module); module_exit(scull_cleanup_module);
obj-m:= pipe.o modules-objs:= pipe.o KDIR:= /usr/src/linux-headers-2.6.31-14-generic/ PWD:= $(shell pwd) default: make -C $(KDIR) M=$(PWD) modules clean: rm -rf *.ko *.mod.c *.mod.o *.o *.markers *.symvers *.order
mknod /dev/scullp c 250 0
insmod pipe.ko
打开一个终端,输入:
cat /dev/scullp
此时,线程阻塞。
打开另一个终端,输入:
echo "Test" > /dev/scullp
此时,被阻塞的线程运行,并输出:Test
rmmod pipe