📄 m_p_cdevicepipe.c
字号:
/* * m_p_cdevicepipe.c -- fifo driver for mdevice */ #include <linux/module.h>#include <linux/moduleparam.h>#include <linux/wait.h> /*wait_queue_head_t*/#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> /*poll_table*/#include <linux/cdev.h>#include <asm/uaccess.h>#ifndef MDEVICE_P_MAJOR#define MDEVICE_P_MAJOR 245 /* 主设备号245 */#endif#ifndef MDEVICE_P_NR_DEVS#define MDEVICE_P_NR_DEVS 4 /* 次设备 数 为4*/#endif#ifndef MDEVICE_P_BUFFER#define MDEVICE_P_BUFFER 4000 /*管道设备是一个简单的环形缓冲区,定义初始大小为4000*/#endifstruct mdevice_pipe { wait_queue_head_t inq, outq; /* 读写队列*/ char *buffer, *end; /* 缓冲起始指针,结尾指针 */ int buffersize; /* 用于指针计算 */ char *rp, *wp; /* 读写指针*/ int nreaders, nwriters; /* 等待读和写的进程数 */ struct fasync_struct *async_queue; /* 协调读者*/ struct semaphore sem; /* 互斥信号量*/ struct cdev cdev; /* 字符设备结构*/};/* 参数定义*/int mdevice_p_major=MDEVICE_P_MAJOR;static int mdevice_p_nr_devs = MDEVICE_P_NR_DEVS; /* 管道设备数 */int mdevice_p_buffer = MDEVICE_P_BUFFER; /* 缓冲区大小 */module_param(mdevice_p_major, int, S_IRUGO);module_param(mdevice_p_nr_devs, int, 0);module_param(mdevice_p_buffer, int, 0);MODULE_LICENSE("Dual BSD/GPL");static struct mdevice_pipe *mdevice_p_devices;static int mdevice_p_fasync(int fd, struct file *filp, int mode);static int spacefree(struct mdevice_pipe *dev);/* * 设备的打开与关闭,当一个进程打开/关闭设备文件时,这两个函数会被调用 */static int mdevice_p_open(struct inode *inode, struct file *filp){ struct mdevice_pipe *dev; dev = container_of(inode->i_cdev, struct mdevice_pipe, cdev);/*找到设备*/ filp->private_data = dev; if (down_interruptible(&dev->sem)) /*防止一个设备被两个进程读*/ return -ERESTARTSYS; if (!dev->buffer) { /*初始化缓冲区,如果缓冲区设置失败,释放此设备的信号量并且返回一个错误代号*/ dev->buffer = kmalloc(mdevice_p_buffer, GFP_KERNEL); if (!dev->buffer) { up(&dev->sem); return -ENOMEM; } } dev->buffersize = mdevice_p_buffer; dev->end = dev->buffer + dev->buffersize; dev->rp = dev->wp = dev->buffer; /*将读写指针初始化为最前端 */ /*使用f_mode,不是f_flages判断是读进程还是写进程*/ 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 mdevice_p_release(struct inode *inode, struct file *filp){ struct mdevice_pipe *dev = filp->private_data; /*文件关闭时,从异步将文件从激活异步读者队列中除去*/ mdevice_p_fasync(-1, filp, 0); 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;}/* *数据处理:读和写,当进程对设备进行读写时,这两个函数会被调用 */static ssize_t mdevice_p_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos){ struct mdevice_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) /*是非阻塞型的IO则立即结束*/ 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 mdevice_getwritespace(struct mdevice_pipe *dev, struct file *filp){ while (spacefree(dev) == 0) { /*如果缓冲区满则初始化一个队列*/ DEFINE_WAIT(wait); up(&dev->sem);/*释放信号量*/ if (filp->f_flags & O_NONBLOCK) /*如果设备文件的打开方式是非阻塞I/O,则立即退出*/ 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;} /* 计算缓冲区中空闲空间的大小*/static int spacefree(struct mdevice_pipe *dev){ if (dev->rp == dev->wp)/*缓冲区已满*/ return dev->buffersize - 1; return ((dev->rp + dev->buffersize - dev->wp) % dev->buffersize) - 1;}/*设备读操作*/static ssize_t mdevice_p_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos){ struct mdevice_pipe *dev = filp->private_data; int result; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; /* 确保缓冲区有空间写入数据*/ result = mdevice_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); /*有数据到达时,通知异步读者 */ if (dev->async_queue) kill_fasync(&dev->async_queue, SIGIO, POLL_IN); return count;}static unsigned int mdevice_p_poll(struct file *filp, poll_table *wait){ struct mdevice_pipe *dev = filp->private_data; unsigned int mask = 0; /* * 环形缓冲区满为wp==rp;空时spacefree(dev)为0; */ down(&dev->sem); poll_wait(filp, &dev->inq, wait); poll_wait(filp, &dev->outq, wait); if (dev->rp != dev->wp) mask |= POLLIN | POLLRDNORM; /* 可读*/ if (spacefree(dev)) mask |= POLLOUT | POLLWRNORM; /* 可写 */ up(&dev->sem); return mask;}/*文件关闭时,从异步将文件从激活异步读者队列中除去*/static int mdevice_p_fasync(int fd, struct file *filp, int mode){ struct mdevice_pipe *dev = filp->private_data; /*从相关进程队列中添加或出去该文件*/ return fasync_helper(fd, filp, mode, &dev->async_queue);}/* 实现/proc文件 */#ifdef MDEVICE_DEBUGstatic void mdevicep_proc_offset(char *buf, char **start, off_t *offset, int *len){ if (*offset == 0) return; if (*offset >= *len) { *offset -= *len; *len = 0; } else { /*设置文件偏移 */ *start = buf + *offset; *offset = 0; }}static int mdevice_read_p_mem(char *buf, char **start, off_t offset, int count, int *eof, void *data){ int i, len; struct mdevice_pipe *p;#define LIMIT (PAGE_SIZE-200) /* don't print any more after this size */ *start = buf; len = sprintf(buf, "Default buffersize is %i\n", mdevice_p_buffer); for(i = 0; i<mdevice_p_nr_devs && len <= LIMIT; i++) { p = &mdevice_p_devices[i]; if (down_interruptible(&p->sem)) return -ERESTARTSYS; len += sprintf(buf+len, "\nDevice %i: %p\n", i, p); len += sprintf(buf+len, " Buffer: %p to %p (%i bytes)\n", p->buffer, p->end, p->buffersize); len += sprintf(buf+len, " rp %p wp %p\n", p->rp, p->wp); len += sprintf(buf+len, " readers %i writers %i\n", p->nreaders, p->nwriters); up(&p->sem); mdevicep_proc_offset(buf, start, &offset, &len); } *eof = (len <= LIMIT); return len;}#endif/* * 管道字符设备的文件操作 */struct file_operations mdevice_pipe_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = mdevice_p_read, .write = mdevice_p_write, .poll = mdevice_p_poll, .open = mdevice_p_open, .release = mdevice_p_release, .fasync = mdevice_p_fasync,};/* * 初始化cdev entry. */static void mdevice_p_setup_cdev(struct mdevice_pipe *dev, int index){ int err, devno = MKDEV(mdevice_p_major, index); cdev_init(&dev->cdev, &mdevice_pipe_fops); dev->cdev.owner = THIS_MODULE; err = cdev_add (&dev->cdev, devno, 1); /* Fail gracefully if need be */ if (err) printk(KERN_NOTICE "Error %d adding mdevicepipe%d", err, index);} /* * 模块加载时调用。初始化管道设备,返回初始化的数量. */int mdevice_p_init(void){ int i, result=0; dev_t dev = 0; if (mdevice_p_major) { dev = MKDEV(mdevice_p_major, 0); result = register_chrdev_region(dev, mdevice_p_nr_devs, "mdevicep"); } if (result < 0) { printk(KERN_WARNING "mdevicep: can't get major %d\n", mdevice_p_major); return result; } mdevice_p_devices = kmalloc(mdevice_p_nr_devs * sizeof(struct mdevice_pipe), GFP_KERNEL); if (mdevice_p_devices == NULL) { unregister_chrdev_region(dev, mdevice_p_nr_devs); return 0; } memset(mdevice_p_devices, 0, mdevice_p_nr_devs * sizeof(struct mdevice_pipe)); for (i = 0; i < mdevice_p_nr_devs; i++) { init_waitqueue_head(&(mdevice_p_devices[i].inq)); init_waitqueue_head(&(mdevice_p_devices[i].outq)); init_MUTEX(&mdevice_p_devices[i].sem); mdevice_p_setup_cdev(mdevice_p_devices + i, i); }#ifdef MDEVICE_DEBUG create_proc_read_entry("mdevicepipe", 0, NULL, mdevice_read_p_mem, NULL);#endif return mdevice_p_nr_devs;}/* * 模块卸载时或者设备失败时调用. */void mdevice_p_cleanup(void){ int i; dev_t mdevice_p_devno = MKDEV(mdevice_p_major, 0);#ifdef MDEVICE_DEBUG remove_proc_entry("mdevicepipe", NULL);#endif if (!mdevice_p_devices) return; /* 没有内存需要释放*/ for (i = 0; i < mdevice_p_nr_devs; i++) { cdev_del(&mdevice_p_devices[i].cdev); kfree(mdevice_p_devices[i].buffer); } kfree(mdevice_p_devices); unregister_chrdev_region(mdevice_p_devno, mdevice_p_nr_devs); mdevice_p_devices = NULL; /* pedantic */}module_init(mdevice_p_init);module_exit(mdevice_p_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -