vmem驱动设备
vmem是内存多字符设备。包含vfs的open、read、write、ioctl、poll、fasync和release函数,device文件的读写。
virtual_mem.c
1 #include <linux/module.h> 2 #include <linux/fs.h> 3 #include <linux/sched.h> 4 #include <linux/types.h> 5 #include <linux/init.h> 6 #include <linux/cdev.h> 7 #include <linux/device.h> 8 #include <linux/slab.h> 9 #include <linux/poll.h> 10 11 12 //#undef DEBUG 13 #define DEBUG 14 #ifdef DEBUG 15 #define dprintk(fmt, arg...) printk(KERN_DEBUG fmt, ##arg) 16 #else 17 #define dprintk(...) do { } while (0) 18 #endif 19 20 static struct class *virtualmem_class = NULL; 21 22 static int virtualmem_major = 0; //模块virtualmem_major参数,默认为0 23 static int virtualmem_minor = 0; 24 module_param(virtualmem_major, int, S_IRUGO); 25 26 static int dev_num = 1; //模块dev_num参数,默认为1 27 module_param(dev_num, int, S_IRUGO); 28 29 30 struct virtualmem_dev { 31 unsigned int current_len; 32 unsigned char mem[PAGE_SIZE]; 33 struct cdev cdev; 34 struct mutex mutex; 35 struct device_attribute device_attribute; 36 wait_queue_head_t r_wait; 37 wait_queue_head_t w_wait; 38 struct fasync_struct *async_queue; 39 40 }; 41 static struct virtualmem_dev *virtualmem_devp; 42 43 44 ssize_t virtual_show(struct device *dev, struct device_attribute *attr, char *buf) 45 { 46 struct virtualmem_dev *devp = (struct virtualmem_dev *)dev_get_drvdata(dev); 47 mutex_lock(&devp->mutex); 48 snprintf(buf, devp->current_len + 1, "%s", devp->mem); 49 mutex_unlock(&devp->mutex); 50 return devp->current_len + 1; 51 } 52 53 ssize_t virtual_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 54 { 55 struct virtualmem_dev *devp = (struct virtualmem_dev *)dev_get_drvdata(dev); 56 mutex_lock(&devp->mutex); 57 if (PAGE_SIZE == devp->current_len) { 58 devp->current_len = 0; 59 return -1; 60 } 61 memcpy(devp->mem, buf, count); 62 devp->current_len = count; 63 mutex_unlock(&devp->mutex); 64 return count; 65 } 66 67 static int virtual_open(struct inode *inode, struct file *filp) 68 { 69 struct virtualmem_dev *devp; 70 devp = container_of(inode->i_cdev, struct virtualmem_dev, cdev); 71 filp->private_data = devp; 72 return 0; 73 } 74 75 static ssize_t virual_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) 76 { 77 int ret; 78 struct virtualmem_dev *devp = filp->private_data; 79 DECLARE_WAITQUEUE(wait, current); 80 81 mutex_lock(&devp->mutex); 82 add_wait_queue(&devp->r_wait, &wait); //定义读队列唤醒 83 84 while (!devp->current_len) { 85 if (filp->f_flags & O_NONBLOCK) { 86 ret = -EAGAIN; 87 goto out1; 88 } 89 90 set_current_state(TASK_INTERRUPTIBLE); //读阻塞休眠 91 mutex_unlock(&devp->mutex); 92 schedule(); 93 if (signal_pending(current)) { 94 ret = -ERESTARTSYS; 95 goto out2; 96 } 97 mutex_lock(&devp->mutex); 98 } 99 100 if (count > devp->current_len) 101 count = devp->current_len; 102 if (copy_to_user(buf, devp->mem, count)) { 103 ret = -EFAULT; 104 goto out1; 105 } else { 106 devp->current_len -= count; 107 memcpy(devp->mem, devp->mem + count, devp->current_len); 108 dprintk("read %d bytes,current_len:%d\n", (int)count, devp->current_len); 109 wake_up_interruptible(&devp->w_wait); //唤醒读阻塞休眠 110 ret = count; 111 } 112 out1: 113 mutex_unlock(&devp->mutex); 114 out2: 115 remove_wait_queue(&devp->r_wait, &wait); 116 set_current_state(TASK_RUNNING); 117 return ret; 118 } 119 120 static ssize_t virual_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) 121 { 122 int ret; 123 struct virtualmem_dev *devp = filp->private_data; 124 DECLARE_WAITQUEUE(wait, current); 125 126 mutex_lock(&devp->mutex); 127 add_wait_queue(&devp->w_wait, &wait); //定义写队列唤醒 128 129 while (devp->current_len == PAGE_SIZE) { 130 if (filp->f_flags & O_NONBLOCK) { 131 ret = -EAGAIN; 132 goto out1; 133 } 134 135 set_current_state(TASK_INTERRUPTIBLE); //写阻塞休眠 136 mutex_unlock(&devp->mutex); 137 schedule(); 138 if (signal_pending(current)) { 139 ret = -ERESTARTSYS; 140 goto out2; 141 } 142 mutex_lock(&devp->mutex); 143 } 144 if (count > PAGE_SIZE - devp->current_len) 145 count = PAGE_SIZE - devp->current_len; 146 if (copy_from_user(devp->mem + devp->current_len, buf, count)) { 147 ret = -EFAULT; 148 goto out1; 149 } else { 150 devp->current_len += count; 151 dprintk("written %d bytes,current_len:%d\n", (int)count, devp->current_len); 152 wake_up_interruptible(&devp->r_wait); //唤醒读阻塞休眠 153 154 if (devp->async_queue) { //写信号进行异步通知应用 155 kill_fasync(&devp->async_queue, SIGIO, POLL_IN); 156 dprintk("%s kill SIGIO\n", __func__); 157 } 158 ret = count; 159 } 160 out1: 161 mutex_unlock(&devp->mutex); 162 out2: 163 remove_wait_queue(&devp->w_wait, &wait); 164 set_current_state(TASK_RUNNING); 165 return ret; 166 } 167 168 //这里利用IO CMD宏定义 169 #define MEM_CLEAR _IO('V',1) 170 #define MEM_FULL _IOW('V', 2, unsigned char) 171 172 static long virtual_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 173 { 174 struct virtualmem_dev *devp = filp->private_data; 175 switch(cmd) { 176 case MEM_CLEAR: 177 mutex_lock(&devp->mutex); 178 devp->current_len = 0; 179 memset(devp->mem, 0, PAGE_SIZE); 180 mutex_unlock(&devp->mutex); 181 dprintk("cmd = %d, clear the memory", cmd); 182 break; 183 case MEM_FULL: 184 mutex_lock(&devp->mutex); 185 devp->current_len = PAGE_SIZE; 186 memset(devp->mem, (unsigned char )arg, PAGE_SIZE); 187 mutex_unlock(&devp->mutex); 188 dprintk("cmd = %d, fill the memory using %ld", cmd, arg); 189 break; 190 default: 191 return -EINVAL; 192 } 193 return 0; 194 } 195 196 static unsigned int virtual_poll(struct file *filp, poll_table *wait) 197 { 198 unsigned int mask = 0; 199 struct virtualmem_dev *devp = filp->private_data; 200 mutex_lock(&devp->mutex); 201 poll_wait(filp, &devp->r_wait, wait); //声明读写队列到poll table唤醒线程 202 poll_wait(filp, &devp->w_wait, wait); 203 204 if (devp->current_len != 0 ) 205 mask |= POLLIN | POLLRDNORM; 206 207 if (devp->current_len != PAGE_SIZE) 208 mask |= POLLOUT | POLLWRNORM; 209 210 mutex_unlock(&devp->mutex); 211 return mask; 212 } 213 214 static int virtual_fasync(int fd, struct file *filp, int mode) 215 { 216 struct virtualmem_dev *devp = filp->private_data; 217 return fasync_helper(fd, filp, mode, &devp->async_queue); 218 } 219 220 static int virtual_release(struct inode *inode, struct file *filp) 221 { 222 virtual_fasync(-1, filp, 0); 223 return 0; 224 } 225 226 227 228 static struct file_operations virtual_fops = { 229 .owner = THIS_MODULE, 230 .read = virual_read, 231 .write = virual_write, 232 .unlocked_ioctl = virtual_ioctl, 233 .poll = virtual_poll, 234 .fasync = virtual_fasync, 235 .open = virtual_open, 236 .release = virtual_release, 237 }; 238 239 static int virtualmem_setup_dev(struct virtualmem_dev *devp, int index) 240 { 241 int ret; 242 dev_t devno = MKDEV(virtualmem_major, virtualmem_minor + index); 243 244 cdev_init(&devp->cdev, &virtual_fops); 245 devp->cdev.owner = THIS_MODULE; 246 ret = cdev_add(&devp->cdev, devno, 1); 247 return ret; 248 } 249 250 251 static int __init virtualmem_init(void) 252 { 253 int ret; 254 int i; 255 dev_t devno = MKDEV(virtualmem_major, virtualmem_minor); 256 struct device *temp[dev_num]; 257 258 dprintk("Initializing virtualmem device.\n"); 259 if(virtualmem_major) 260 ret = register_chrdev_region(devno, dev_num, "virtualmem"); 261 else { 262 ret = alloc_chrdev_region(&devno, 0, dev_num, "virtualmem"); 263 virtualmem_major = MAJOR(devno); 264 virtualmem_minor = MINOR(devno); 265 } 266 if(ret < 0) { 267 dprintk("Failed to alloc char dev region.\n"); 268 goto err; 269 } 270 271 virtualmem_devp = kzalloc(sizeof(struct virtualmem_dev) * dev_num, GFP_KERNEL); 272 if (!virtualmem_devp) { 273 ret = -ENOMEM; 274 dprintk("Failed to alloc virtualmem device.\n"); 275 goto unregister; 276 } 277 278 for(i = 0; i < dev_num; i++) { 279 ret = virtualmem_setup_dev(virtualmem_devp + i, i); 280 if (ret) { 281 dprintk("Failed to setup dev %d.\n", i); 282 while(--i >= 0) 283 cdev_del(&(virtualmem_devp + i)->cdev); 284 goto kfree; 285 } 286 } 287 288 virtualmem_class = class_create(THIS_MODULE, "virtualmem"); //建立virtualmem类 289 if (IS_ERR(virtualmem_class)) { 290 ret = PTR_ERR(virtualmem_class); 291 dprintk("Failed to create virtualmem class.\n"); 292 goto destroy_cdev; 293 } 294 295 for(i = 0; i < dev_num; i++) { 296 //在virtualmem这个类里建立多个virtualmem文件 297 temp[i] = device_create(virtualmem_class, NULL, devno + i, (void *)(virtualmem_devp + i), "%s%d", "virtualmem", i); 298 if (IS_ERR(temp[i])) { 299 ret = PTR_ERR(temp[i]); 300 dprintk("Failed to create virtualmem device.\n"); 301 while(--i >= 0) 302 device_destroy(virtualmem_class, devno + i); 303 goto destory_class; 304 } 305 } 306 for(i = 0; i < dev_num; i++) { 307 (virtualmem_devp + i)->device_attribute.attr.name = "mem"; //对于单设备一般用宏 DEVICE_ATTR,这里多设备需要完成宏的代码 308 (virtualmem_devp + i)->device_attribute.attr.mode = S_IRUGO | S_IWUSR; 309 (virtualmem_devp + i)->device_attribute.show = virtual_show; 310 (virtualmem_devp + i)->device_attribute.store = virtual_store; 311 ret = device_create_file(temp[i], &(virtualmem_devp + i)->device_attribute); 312 if(ret < 0) { 313 dprintk("Failed to create attribute mem."); 314 while(--i >= 0) 315 device_remove_file(temp[i], &(virtualmem_devp + i)->device_attribute); 316 goto destroy_device; 317 } 318 } 319 for(i = 0; i < dev_num; i++) { 320 mutex_init(&(virtualmem_devp + i)->mutex); //初始化互斥锁 321 init_waitqueue_head(&(virtualmem_devp + i)->r_wait); //初始化读写队列 322 init_waitqueue_head(&(virtualmem_devp + i)->w_wait); 323 } 324 325 326 dprintk("Succedded to Initialize virtualmem device.\n"); 327 return 0; 328 329 destroy_device: 330 for(i = 0; i < dev_num; i++) 331 device_destroy(virtualmem_class, devno + i); 332 333 destory_class: 334 class_destroy(virtualmem_class); 335 336 destroy_cdev: 337 for(i = 0; i < dev_num; i++) 338 cdev_del(&(virtualmem_devp + i)->cdev); 339 340 kfree: 341 kfree(virtualmem_devp); 342 343 unregister: 344 unregister_chrdev_region(MKDEV(virtualmem_major, virtualmem_minor), dev_num); 345 346 err: 347 return ret; 348 } 349 350 static void __exit virualmem_exit(void) 351 { 352 int i; 353 dprintk("Destroy virtualmem device.\n"); 354 if(virtualmem_class) { 355 for(i = 0; i < dev_num; i++) 356 device_destroy(virtualmem_class, MKDEV(virtualmem_major, virtualmem_minor + i)); 357 class_destroy(virtualmem_class); 358 } 359 if(virtualmem_devp) { 360 for(i = 0; i < dev_num; i++) 361 cdev_del(&(virtualmem_devp + i)->cdev); 362 kfree(virtualmem_devp); 363 } 364 unregister_chrdev_region(MKDEV(virtualmem_major, virtualmem_minor), dev_num); 365 } 366 367 module_init(virtualmem_init); 368 module_exit(virualmem_exit); 369 370 MODULE_AUTHOR("Kevin Hwang <kevin.hwang@live.com"); 371 MODULE_LICENSE("GPL v2");
测试程序virtual_test.c:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <fcntl.h> 4 #include <signal.h> 5 #include <sys/ioctl.h> 6 #include <sys/epoll.h> 7 #include <sys/stat.h> 8 9 //#undef EPOLL 10 #define EPOLL 11 #define MEM_CLEAR _IO('V',1) 12 #define MEM_FULL _IOW('V', 2, unsigned char) 13 14 char buff[4096]; 15 int fd; 16 void signalio_handler(int signum) 17 { 18 printf("receive a signal \n"); 19 read(fd, buff, 4096); 20 printf("%s \n", buff); 21 } 22 23 int main(int argc, char const **argv) 24 { 25 int oflags, input_num = 0, dev_num; 26 char tmp[10]; 27 char path[30]; 28 29 #ifdef EPOLL 30 int ret; 31 struct epoll_event ev_virtualmem; 32 int epfd; 33 #endif 34 35 fd = open("/sys/module/virtual_mem/parameters/dev_num", O_RDONLY, S_IRUSR); //读取模块设备数量参数dev_num 36 if (fd == -1) { 37 printf("Failed to open parameters for dev_num.\n"); 38 return -1; 39 } 40 read(fd, tmp, 10); 41 dev_num = atoi(tmp); //获取dev_num 42 if (dev_num > 1) { //如果dev_num = 1默认不需要参数 43 if (argc == 2) 44 input_num = atoi(*(++argv)); 45 else { 46 printf("please input the dev_num between 0 and %d.\n", dev_num - 1); 47 return -1; 48 } 49 if (**argv < '0' || **argv > '9' || dev_num <= input_num) { //第一个参数第一位不为0~9返回错误信息 50 printf("please input the dev_num between 0 and %d.\n", dev_num - 1); 51 return -1; 52 } 53 } 54 55 snprintf(path, 30 + 1, "/dev/virtualmem%d", input_num); //获取设备路径名字 56 57 fd = open(path, O_RDWR | O_NONBLOCK, S_IRUSR | S_IWUSR); 58 if (fd == -1) { 59 printf("Failed to open /dev/virtualmem%d.\n", input_num); 60 return -1; 61 } 62 printf("open /dev/virtualmem%d success.\n", input_num); 63 64 if (ioctl(fd, MEM_FULL, 0xFF) < 0) 65 printf("ioctl command = 0x%lx failed.\n", MEM_FULL); 66 67 if (ioctl(fd, MEM_CLEAR, 0xFF) < 0) 68 printf("ioctl command = 0x%x failed.\n", MEM_CLEAR); 69 #ifdef EPOLL 70 epfd = epoll_create(1); //创建epoll 71 if (epfd < 0) { 72 printf("epoll_create failed.\n"); 73 return -1; 74 } 75 ev_virtualmem.events = EPOLLIN | EPOLLPRI; //epoll触发事件 76 if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev_virtualmem) < 0) { //增加这个事件 77 printf("epfd_ctl add failed.\n"); 78 return -1; 79 } 80 81 ret = epoll_wait(epfd, &ev_virtualmem, 1, 5000); //等待事件,调用驱动poll 82 if (ret < 0) 83 printf("epoll_wait failed.\n"); 84 else if (!ret) 85 printf("no data input in virtualmem%d\n", input_num); 86 else { 87 printf("receive data.\n"); 88 read(fd, buff, 4096); 89 printf("%s \n", buff); 90 } 91 if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev_virtualmem)) { 92 printf("epfd_ctl delete failed.\n"); 93 return -1; 94 } 95 #else 96 signal(SIGIO, signalio_handler); //声明signalio_handler信号处理函数 97 fcntl(fd, F_SETOWN, getpid()); //获取程序pid 98 oflags = fcntl(fd, F_GETFL); 99 fcntl(fd, F_SETFL, oflags | FASYNC); //添加FASYNC标志 100 while (1) 101 sleep(100); 102 #endif 103 close(fd); 104 return 0; 105 }
Makefile:
1 obj-m += virtual_mem.o 2 3 KERN_DIR = /lib/modules/$(shell uname -r)/build 4 all: 5 make -C $(KERN_DIR) M=$(CURDIR) modules 6 7 clean: 8 make -C $(KERN_DIR) M=$(CURDIR) clean
github代码地址 https://github.com/kevinhwang91/virtual_mem
代码在linux3.0-4.2测试过,使用方法:
make
insmod virtual_mem.ko (dev_num=具体数字,dev_num缺省默认为1)
可以直接在/dev/virtual_mem?或者/sys/class/virtualmem/virtualmem?/mem(非阻塞)进行读写
gcc -o virtual_test virtual_test.c(可以在源码预处理选择处理方法)
./virtual_test (0 - dev_num-1)其中一个设备