操作系统实验3
来源:互联网 发布:淘宝子账号 编辑:程序博客网 时间:2024/06/05 01:12
linux 设备驱动程序开发
实验要求:
编写一个字符设备驱动程序,要求实现对该字符设备的打开、读、写、I/O 控制和关闭 5 个基本操作。为了避免牵涉到汇编语言,这个字符设备并非一个真实的字符设备,而是用一段内存空间来模拟的。以模块方式加载该驱动程序。
实验步骤
- 字符设备驱动
#include <linux/fs.h> //定义文件表结构(file 结构,buffer_head,m_inode 等)#include <linux/types.h> //对一些特殊的系统数据类型的定义,例如 dev_t, off_t,#include <linux/cdev.h> //包含了 cdev 结构及相关函数的定义。#include <linux/uaccess.h> //包含 copy_to_user(),copy_from_user()的定义#include <linux/module.h> //模块编程相关函数#include <linux/init.h> //模块编程相关函数#include <linux/kernel.h>#include <linux/slab.h>//包含内核的内存分配相关函数,如 kmalloc()/kfree()等MODULE_LICENSE("GPL");#define MEMSIZE 512static const int MAJOR = 255;static const int COUNT = 5;struct mymem_dev{ struct cdev cdev; unsigned char mem[MEMSIZE];}my_cdev;static int char_dev_open(struct inode *inode, struct file *file) { file->private_data = (void *)&my_cdev; printk(KERN_ALERT"open.\n"); return 0;}ssize_t char_dev_read(struct file *file,char __user *buff,size_t COUNT,loff_t *offp) { unsigned long off = *offp; struct mymem_dev *dev = file->private_data; int ret = 0; if (off >= MEMSIZE) return 0; if (COUNT + off > MEMSIZE) COUNT = MEMSIZE - off; if (copy_to_user((void*)buff, (void*)(dev->mem + off), COUNT)){ ret = -EFAULT; } else{ *offp += COUNT; ret = COUNT; } return ret;}ssize_t char_dev_write(struct file *file,const char __user *buff,size_t COUNT,loff_t *offp) { unsigned long off = *offp; int ret = 0; struct mymem_dev *dev = file->private_data; if (off >= MEMSIZE) return 0; if (COUNT + off > MEMSIZE) /*要写入的字节大于设备的内存空间*/ COUNT = MEMSIZE - off; if (copy_from_user(dev->mem + off, buff, COUNT)) ret = -EFAULT; else { *offp += COUNT; /*增加偏移位置*/ ret = COUNT; /*返回实际的写入字节数*/ } return ret;}static loff_t char_dev_llseek(struct file *file, loff_t offset, int whence) { loff_t newpos; switch(whence) { case 0: /* SEEK_SET */ /*相对文件开始位置偏移*/ newpos = offset; /*更新文件指针位置*/ break; case 1: /* SEEK_CUR */ newpos = file->f_pos + offset; break; case 2: /* SEEK_END */ newpos = MEMSIZE - 1 + offset; break; default: /* can't happen */ return -EINVAL; } if ((newpos < 0) || (newpos > MEMSIZE)) return -EINVAL; file->f_pos = newpos; return newpos;}static long char_dev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { printk(KERN_ALERT"ioctl\n"); return 0;}static int char_dev_release (struct inode *node, struct file *file) { printk(KERN_ALERT"release.\n"); return 0;} static struct file_operations char_dev_fops = { .owner = THIS_MODULE, .open = char_dev_open, // 打开设备 .read = char_dev_read, // 实现设备读功能 .write = char_dev_write, // 实现设备写功能 .unlocked_ioctl = char_dev_ioctl, .release = char_dev_release, .llseek = char_dev_llseek,}; // 实现设备控制功能static int __init my_dev_init(void) { int ret; ret = register_chrdev_region(MKDEV(MAJOR, 0), COUNT, "my_dev"); if (ret != 0) { printk(KERN_ALERT"register fail\n"); return 0; } cdev_init(&my_cdev.cdev, &char_dev_fops); my_cdev.cdev.owner = THIS_MODULE; cdev_add(&my_cdev.cdev, MKDEV(MAJOR, 0), COUNT); printk(KERN_ALERT"init\n"); return 0;}static void __exit my_dev_exit(void) { cdev_del(&my_cdev.cdev); unregister_chrdev_region(MKDEV(MAJOR, 0), COUNT); printk(KERN_ALERT"exit\n");}module_init(my_dev_init);module_exit(my_dev_exit);
2 测试程序
#include <stdio.h>#include <fcntl.h> // open函数#include <unistd.h> // write read lseek#include <string.h>int main(){ int fp = 0; char Buf[4096] = {0}; int ret = 0; /*打开设备文件*/ fp = open("/dev/my_cdev", O_RDWR); if (!fp) { printf("Open error!\n"); return -1; } while (1) { lseek(fp,0,SEEK_SET); /*读出设备*/ ret = read(fp, Buf, sizeof(Buf)); printf("%d bytes read\n", ret); /*检测结果*/ printf("BUF: %s\n",Buf); printf("please input new data:\n"); gets(Buf); lseek(fp,0,SEEK_SET); /*写入设备*/ ret = write(fp, Buf, strlen(Buf)); printf("%d byte written\n", ret); } return 0; }
3 编译、挂载、建立设备节点
$make#insmod [filename].ko#mknod /dev/my_dev c 255 0#chmod 777 /dev/my_dev
函数分析
设备号
#define MINORBITS 20#define MINORMASK ((1U << MINORBITS) - 1)#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
register_chrdev_region
int register_chrdev_region(dev_t from, unsigned count, const char *name){ …… for (n = from; n < to; n = next) { next = MKDEV(MAJOR(n)+1, 0); if (next > to) next = to; cd = __register_chrdev_region(MAJOR(n), MINOR(n), next - n, name); ……
从中我们可以看到,register_chrdev_region即是把from开始连续count个设备号都注册。
函数 __register_chrdev_region() 主要执行以下步骤:
- 分配一个新的 char_device_struct 结构,并用 0 填充。
- 如果申请的设备编号范围的主设备号为 0,那么表示设备驱动程序请求动态分配一个主设备号。动态分配主设备号的原则是从散列表的最后一个桶向前寻找,那个桶是空的,主设备号就是相应散列桶的序号。所以动态分配的主设备号总是小于 256,如果每个桶都有字符设备编号了,那动态分配就会失败。
- 根据参数设置 char_device_struct 结构中的初始设备号,范围大小及设备驱动名称。
- 计算出主设备号所对应的散列桶,为新的 char_device_struct 结构寻找正确的位置。同时,如果设备编号范围有重复的话,则出错返回。
- 将新的 char_device_struct 结构插入散列表中,并返回 char_device_struct 结构的地址。
参考网站:http://blog.csdn.net/tigerjibo/article/details/6412672
cdev_init
void cdev_init(struct cdev *cdev, const struct file_operations *fops){ memset(cdev, 0, sizeof *cdev); INIT_LIST_HEAD(&cdev->list); kobject_init(&cdev->kobj, &ktype_cdev_default); cdev->ops = fops;}
kobj是一个嵌入在该结构中的内核对象。它用于该数据结构的一般管理。
cdev_add
int cdev_add(struct cdev *p, dev_t dev, unsigned count){ int error; p->dev = dev; p->count = count; error = kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); if (error) return error; kobject_get(p->kobj.parent); return 0;}
linux 内核中所有字符设备都记录在一个 kobj_map 结构的 cdev_map 散列表里。
cdev_add()函数中的 kobj_map() 函数就是用来把设备号及 cdev 结构一起保存到
cdev_map 散列表里。当以后要打开这个字符设备文件时,通过调用 kobj_lookup() 函数,
根据设备号就可以找到 cdev 结构变量,从而取出其中的 ops 字段。
cdev_del
void cdev_del(struct cdev *p){ cdev_unmap(p->dev, p->count); //调用 kobj_unmap()释放 cdev_map散列表中的对象 kobject_put(&p->kobj); ////释放 cdev结构本身}
unregister_chrdev_region
void unregister_chrdev_region(dev_t from, unsigned count){ dev_t to = from + count; dev_t n, next; for (n = from; n < to; n = next) { next = MKDEV(MAJOR(n)+1, 0); if (next > to) next = to; kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); }}
参考网站:
简单字符设备驱动:https://www.cnblogs.com/geneil/archive/2011/12/03/2272869.html
关于private_data:http://blog.csdn.net/fivedoumi/article/details/50915634
linux char_dev源码分析 https://www.cnblogs.com/morris-tech/p/3918013.html
- 操作系统实验3
- 操作系统实验3
- MIT操作系统实验1-3
- 操作系统实验
- 操作系统实验3:内存分配与回收
- 哈工大操作系统实验3—进程管理
- 华师 操作系统实验 实验一
- 华师 操作系统实验 实验二
- 华师 操作系统实验 实验三
- 操作系统实验一实验报告
- 操作系统实验二实验报告
- 操作系统实验三实验报告
- 操作系统实验四实验报告
- 操作系统实验五实验报告
- 操作系统实验六实验报告
- 操作系统实验七实验报告
- 操作系统实验八实验报告
- 操作系统实验2
- 华为HBase性能调优
- 快速排序-C语言实现
- Android混合开发之——WebView页面栈遇上重定向
- JavaScript停止冒泡和阻止浏览器默认行为
- Thread-Per-Message Pattern
- 操作系统实验3
- PHP的几种排序算法的比较
- Spring Reactor 线程调度
- Halide学习笔记----Halide tutorial源码阅读2
- TCP的三次握手和四次挥手
- VS2013下新建静态库
- 【LeetCode】481. Magical String
- 2017hdu新生赛 1004 正品的概率
- Hadoop Eclipse插件的使用