Linux内核编程初探:块设备驱动程序——Ramdisk

https://blog.csdn.net/my_xxh/article/details/48785667

第一个步骤: 编写hello world驱动程序

       (1) 构造内核源码树

       (2) 到hello.c文件目录下执行make,生成hello.ko文件以及其他相关文件

       (3) 执行sudo insmod  ./hello.ko加载模块

       (4)执行Ismod   (查看模块的命令)就可以看到hello模块

       (5) sudo  rmmod hello

Makefile文件内容:

  1. obj-m := ramhd_req.o
  2. KERNELDIR := /lib/modules/$(shell uname -r)/build
  3. PWD := $(shell pwd)
  4. default:
  5. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  6. clean:
  7. rm -f *.o *.ko *.mod.* *.order *.symvers

代码分析:int register_blkdev(unsigned int major, const char *name);major 参数是块设备要使用的主设备号,name为设备名,它会在/proc/devices中被显示。 如果major为0,内核会自动分配一个新的主设备号register_blkdev()函数的返回值就是这个主设备号。如果返回1个负值,表明发生了一个错误。

kmalloc和vmalloc的区别
1、kmalloc保证分配的内存在物理上是连续的,vmalloc保证的是在虚拟地址空间上的连续
2、kmalloc能分配的大小有限,vmalloc能分配的大小相对较大
3、vmalloc比kmalloc要慢
4、kmallloc使用的是slab内存分配机制,而vmalloc使用的是伙伴系统分配机制,这也是造成它们区别的根本所在

return   -EFAULT;   //errno:14   地址错
return   -EAGAIN;   //errno:11   资源暂时不可用
return   -EINTR;   //errno:4     中断的函数调用
return   -ESPIPE     //errno:29   无效的文件指针重定位
return   -ENOTTY;//errno:25     不适当的IO控制操作 

重点内容:.owner = THIS_MODULE为什么加“点”的原因
这种方式称为指定初始化  源自ISO C99标准 初始化不必严格按照定义时的顺序

  1. #include <linux/module.h> //支持动态添加和卸载模块
  2. #include <linux/kernel.h> //驱动要写入内核,与内核相关的头文件
  3. #include <linux/init.h> //初始化头文件
  4. #include <linux/fs.h> //包含了文件操作相关struct的定义
  5. #include <linux/types.h> //对一些特殊类型的定义
  6. #include <linux/fcntl.h> //定义了文件操作等所用到的相关宏
  7. #include <linux/vmalloc.h> //vmalloc()分配的内存虚拟地址上连续,物理地址不连续
  8. #include <linux/blkdev.h> //采用request方式 块设备驱动程序需要调用blk_init_queue 分配请求队列
  9. #include <linux/hdreg.h> //硬盘参数头文件,定义访问硬盘寄存器端口、状态码和分区表等信息。
  10. #define RAMHD_NAME "Mao_rd" //设备名称
  11. #define RAMHD_MAX_DEVICE 2 //最大设备数
  12. #define RAMHD_MAX_PARTITIONS 4 //最大分区数
  13. #define RAMHD_SECTOR_SIZE 512 //扇区大小
  14. #define RAMHD_SECTORS 16 //扇区数 http://www.embedu.org/Column/Column863.htm
  15. #define RAMHD_HEADS 4 //磁头数
  16. #define RAMHD_CYLINDERS 256 //磁道(柱面)数
  17. #define RAMHD_SECTOR_TOTAL (RAMHD_SECTORS * RAMHD_HEADS * RAMHD_CYLINDERS) //总大小
  18. #define RAMHD_SIZE (RAMHD_SECTOR_SIZE * RAMHD_SECTOR_TOTAL) //8MB
  19. typedef struct{
  20. unsigned char *data; //设备数据空间首地址
  21. struct request_queue *queue; //设备请求队列
  22. spinlock_t lock; //互斥自旋锁
  23. struct gendisk *gd; //通用磁盘结构体
  24. }RAMHD_DEV;
  25. static char *sdisk[RAMHD_MAX_DEVICE]; //分配内存的首地址
  26. static RAMHD_DEV *rdev[RAMHD_MAX_DEVICE]; //分配内存的首地址
  27. static dev_t ramhd_major; //主设备号
  28. static int ramhd_space_init(void)
  29. {
  30. int i;
  31. int err = 0;
  32. for(i = 0; i < RAMHD_MAX_DEVICE; i++){
  33. sdisk[i] = vmalloc(RAMHD_SIZE); //申请RAMBLK_SIZE内存 物理地址不连续,虚拟地址连续
  34. if(!sdisk[i]){
  35. err = -ENOMEM; //errno:12 内存不足
  36. return err;
  37. }
  38. memset(sdisk[i], 0, RAMHD_SIZE); //用0来初始化分配的内存空间
  39. }
  40. return err;
  41. }
  42. static void ramhd_space_clean(void)
  43. {
  44. int i;
  45. for(i = 0; i < RAMHD_MAX_DEVICE; i++){
  46. vfree(sdisk[i]); //释放分配的内存
  47. }
  48. }
  49. static int alloc_ramdev(void)
  50. {
  51. int i;
  52. for(i = 0; i < RAMHD_MAX_DEVICE; i++){
  53. rdev[i] = kzalloc(sizeof(RAMHD_DEV), GFP_KERNEL); //向内核申请存放RAMHD_DEV结构体的内存空间
  54. if(!rdev[i])
  55. return -ENOMEM; //errno:12 内存不足
  56. }
  57. return 0;
  58. }
  59. static void clean_ramdev(void)
  60. {
  61. int i;
  62. for(i = 0; i < RAMHD_MAX_DEVICE; i++){
  63. if(rdev[i])
  64. kfree(rdev[i]); //释放分配的内存
  65. }
  66. }
  67. int ramhd_open(struct block_device *bdev, fmode_t mode) //设备打开用到
  68. {
  69. return 0;
  70. }
  71. void ramhd_release(struct gendisk *gd, fmode_t mode) //设备关闭用到
  72. {
  73. }
  74. static int ramhd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) //IO控制
  75. {
  76. int err;
  77. struct hd_geometry geo; //hd_geometry结构体包含磁头,扇区,柱面等信息
  78. switch(cmd)
  79. {
  80. case HDIO_GETGEO: //获取块设备的物理参数
  81. err = !access_ok(VERIFY_WRITE, arg, sizeof(geo));//检查指针所指向的存储块是否可写
  82. if(err) return -EFAULT; //errno:14 地址错
  83. geo.cylinders = RAMHD_CYLINDERS; //柱面数
  84. geo.heads = RAMHD_HEADS; //磁头数
  85. geo.sectors = RAMHD_SECTORS; //扇区数
  86. geo.start = get_start_sect(bdev); //起始地址
  87. if(copy_to_user((void *)arg, &geo, sizeof(geo)))
  88. //把内核地址&geo指示的数据复制到arg指代的用户空间的地址上
  89. return -EFAULT; //errno:14 地址错
  90. return 0;
  91. }
  92. return -ENOTTY; //errno:25 不适当的IO控制操作
  93. }
  94. static struct block_device_operations ramhd_fops = //用来描述一个块设备的操作函数集
  95. { .owner = THIS_MODULE,//“加点”这种方式称为指定初始化 源自ISO C99标准 初始化不必严格按照定义时的顺序
  96. .open = ramhd_open,
  97. .release = ramhd_release,
  98. .ioctl = ramhd_ioctl,
  99. };
  100. void ramhd_req_func (struct request_queue *q) //处理传递给这个设备的请求
  101. {
  102. struct request *req; //用来提取req
  103. RAMHD_DEV *pdev;
  104. char *pData;
  105. unsigned long addr, size, start;
  106. req = blk_fetch_request(q); //从块设备队列提取存储的req;
  107. //blk_fetch_request()可以多次调用,如果queue里面没有内容,req将返回NULL
  108. while (req) { //判断当前request是否合法 循环从请求队列中获取下一个要处理的请求
  109. start = blk_rq_pos(req); // 获取当前request结构的起始扇区
  110. pdev = (RAMHD_DEV *)req->rq_disk->private_data; //获得设备结构体指针
  111. pData = pdev->data;//设备地址
  112. addr = (unsigned long)pData + start * RAMHD_SECTOR_SIZE;//计算地址
  113. size = blk_rq_cur_bytes(req); //访问 req 的下一段数据
  114. if (rq_data_dir(req) == READ) //获得数据传送方向.返回0表示从设备读取,否则表示写向设备.
  115. memcpy(req->buffer, (char *)addr, size); //读
  116. else
  117. memcpy((char *)addr, req->buffer, size); //写
  118. if(!__blk_end_request_cur(req, 0)) //这个函数处理完返回false
  119. req = blk_fetch_request(q); //继续取出请求队列中的请求
  120. }
  121. }
  122. int ramhd_init(void) //初始化
  123. {
  124. int i;
  125. ramhd_space_init();
  126. alloc_ramdev();
  127. ramhd_major = register_blkdev(0, RAMHD_NAME); //块设备驱动注册到内核中
  128. //major为0,内核会自动分配一个新的主设备号(ramhd_major )
  129. for(i = 0; i < RAMHD_MAX_DEVICE; i++)
  130. {
  131. rdev[i]->data = sdisk[i];
  132. rdev[i]->gd = alloc_disk(RAMHD_MAX_PARTITIONS);
  133. spin_lock_init(&rdev[i]->lock); //初始化自旋锁
  134. rdev[i]->queue = blk_init_queue(ramhd_req_func, &rdev[i]->lock);//初始化将ramhd_req_func函数与队列绑定
  135. rdev[i]->gd->major = ramhd_major;
  136. rdev[i]->gd->first_minor = i * RAMHD_MAX_PARTITIONS;
  137. rdev[i]->gd->fops = &ramhd_fops; //关联到这个设备的方法集合
  138. rdev[i]->gd->queue = rdev[i]->queue;
  139. rdev[i]->gd->private_data = rdev[i]; //使用这个成员来指向分配的数据
  140. sprintf(rdev[i]->gd->disk_name, "ram_MaoRi_%c", 'a'+i);
  141. set_capacity(rdev[i]->gd, RAMHD_SECTOR_TOTAL);
  142. add_disk(rdev[i]->gd); //向系统中添加这个块设备
  143. }
  144. return 0;
  145. }
  146. void ramhd_exit(void) //模块卸载函数
  147. {
  148. int i;
  149. for(i = 0; i < RAMHD_MAX_DEVICE; i++)
  150. {
  151. del_gendisk(rdev[i]->gd); //删除gendisk结构体
  152. put_disk(rdev[i]->gd); //减少gendisk结构体的引用计数
  153. blk_cleanup_queue(rdev[i]->queue); //清除请求对列
  154. }
  155. unregister_blkdev(ramhd_major,RAMHD_NAME); ; //注销块设备
  156. clean_ramdev();
  157. ramhd_space_clean();
  158. }
  159. module_init(ramhd_init);
  160. module_exit(ramhd_exit);
  161. MODULE_AUTHOR("MaoRi");
  162. MODULE_DESCRIPTION("The Ramdisk implementation with request function");
  163. MODULE_LICENSE("GPL");

参考文献:(1) ubantu14.04 32位下第一个hello world驱动程序

                         http://blog.csdn.net/damotiansheng/article/details/44463193


猜你喜欢

转载自blog.csdn.net/xiaodingqq/article/details/80941530