/******************************************************************************* 该模块在内核空间定义一段空间mem,用于与用户空间进行数据交互,并具备FIFO、并发控制特性。 旨在学习信号量、等待队列以及死锁等知识点。 #insmod globalmem.ko #mknod /dev/globalmem c 254 0 #cat /dev/globalmem & #echo 'hello world' > /dev/globalmem #echo 'One dream, one world' > /dev/globalmem 可用以上测试方法或其它测试程序进行测试程序本身正确性、是否存在死锁现象。 ******************************************************************************/ #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/poll.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h>
#define GLOBALMEM_SIZE 128 #define MEM_CLEAR 0x01 #define MEM_FILL 0x02 #define GLOBALMEM_MAJOR 254
MODULE_AUTHOR("Septem 2008.08.22 "); MODULE_LICENSE("Dual BSD/GPL");
static globalmem_major = GLOBALMEM_MAJOR; struct globalmem_dev { struct cdev cdev; struct semaphore sem; //互斥信号量
wait_queue_head_t r_wait; //读等待队列头
wait_queue_head_t w_wait; //写等待队列头
unsigned int current_len; //当前可读数据长度
unsigned char mem[GLOBALMEM_SIZE]; };
struct globalmem_dev *globalmem_devp;
int globalmem_open(struct inode *inode, struct file *filp) { filp->private_data = globalmem_devp; return 0; }
int globalmem_release(struct inode *inode, struct file *filp) { return 0; } static unsigned int globalmem_poll(struct file *filp, poll_table *wait) { struct globalmem_dev *dev = filp->private_data; unsigned int mask = 0; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; poll_wait(filp, &dev->r_wait, wait); poll_wait(filp, &dev->w_wait, wait); if (dev->current_len != 0) mask |= POLLIN | POLLRDNORM; if (dev->current_len != GLOBALMEM_SIZE) mask |= POLLOUT | POLLWRNORM; up(&dev->sem); return mask; }
static int globalmem_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct globalmem_dev *dev = filp->private_data; switch (cmd) { case MEM_CLEAR: //将内核空间mem全填充为0
if (down_interruptible(&dev->sem))//先获得信号量,以防其他进程进入临界区代码
return -ERESTARTSYS; memset(dev->mem, 0, GLOBALMEM_SIZE); dev->current_len = 0; wake_up_interruptible(&dev->w_wait);//
up(&dev->sem); //释放信号量
printk(KERN_INFO"globalmem is set to zero\n"); break; case MEM_FILL: //将内核空间mem全填充为H
if (down_interruptible(&dev->sem)) return -ERESTARTSYS; memset(dev->mem, 'H', GLOBALMEM_SIZE); dev->current_len = GLOBALMEM_SIZE; wake_up_interruptible(&dev->r_wait); up(&dev->sem); printk(KERN_INFO"globalmem is set to 'H'\n"); break; default: return -EINVAL; } return 0; }
static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) { unsigned int count = size; int ret = 0; struct globalmem_dev *dev = filp->private_data;
if (down_interruptible(&dev->sem)) //尝试获取信号量
return -ERESTARTSYS; //down_interruptible()返回值非0时,表明这是被某个中断信号打断
if (dev->current_len == 0) //判断可读数据长度是否为0
{ up(&dev->sem); //无可读数据,先释放信号量
if (filp->f_flags & O_NONBLOCK) //判断文件标志是否为非阻塞模式
{ ret = -EAGAIN; //为非阻塞模式,直接返回
return ret; } //以下为阻塞模式的处理
if (wait_event_interruptible(dev->r_wait, dev->current_len != 0))//进入休眠,直到r_wait被唤醒
return -ERESTARTSYS; //非零值表示休眠被某个中断信号打断
/*?这里我不能确定的是,在wait_event_interruptible()正确返回后,是否还要再次检查condition为真?*/ if (down_interruptible(&dev->sem)) //获取信号量
return -ERESTARTSYS; }
if (count > dev->current_len) //比较count与可读数据长度大小
count = dev->current_len; if (copy_to_user(buf, dev->mem, count)) //将内核空间mem的数据copy到用户空间buf
{ ret = -EFAULT; //copy_to_user()返回的是不能复制的字节数
} else { memcpy(dev->mem, dev->mem + count, dev->current_len - count);//将后面的可读数据往mem头移动,实现fifo
dev->current_len -= count; wake_up_interruptible(&dev->w_wait); //空出一定空间,唤醒等待在w_wait上的进程
ret = count; } up(&dev->sem); //释放信号量
return ret; }
static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) { unsigned int count = size; int ret = 0; struct globalmem_dev *dev = filp->private_data; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; if (dev->current_len == GLOBALMEM_SIZE) { up(&dev->sem); if (filp->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(dev->w_wait, dev->current_len != GLOBALMEM_SIZE)) return -ERESTARTSYS; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; }
if (count > GLOBALMEM_SIZE-dev->current_len) count = GLOBALMEM_SIZE-dev->current_len; if (copy_from_user(dev->mem+dev->current_len, buf, count)) ret = EFAULT; else { dev->current_len += count; wake_up_interruptible(&dev->r_wait); ret = count; }
up(&dev->sem); return ret; }
static const struct file_operations globalmem_fops = { .owner = THIS_MODULE, .read = globalmem_read, .write = globalmem_write, .ioctl = globalmem_ioctl, .poll = globalmem_poll, .open = globalmem_open, .release = globalmem_release, };
static void globalmem_setup_cdev(struct globalmem_dev *dev, int index) { int err, devno = MKDEV(globalmem_major, index); cdev_init(&dev->cdev, &globalmem_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &globalmem_fops; err = cdev_add(&dev->cdev, devno, 1); if (err) printk(KERN_NOTICE"Error %d adding LED%d", err, index); }
int globalmem_init(void) { int result; dev_t devno = MKDEV(globalmem_major, 0); if (globalmem_major) result = register_chrdev_region(devno, 1, "globalmem"); else { result = alloc_chrdev_region(&devno, 0, 1, "globalmem"); globalmem_major = MAJOR(devno); }
if (result < 0) return result;
globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL); if (!globalmem_devp) { result = -ENOMEM; goto fail_malloc; } memset(globalmem_devp, 0, sizeof(struct globalmem_dev)); globalmem_setup_cdev(globalmem_devp, 0); init_MUTEX(&globalmem_devp->sem); init_waitqueue_head(&globalmem_devp->r_wait); init_waitqueue_head(&globalmem_devp->w_wait); globalmem_devp->current_len = 0; return 0; fail_malloc: unregister_chrdev_region(devno, 1); return result; }
void globalmem_exit(void) { cdev_del(&globalmem_devp->cdev); kfree(globalmem_devp); unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); }
module_param(globalmem_major, int, S_IRUGO);
module_init(globalmem_init); module_exit(globalmem_exit);
|