写在前面

本文为本人当初提交操作系统课程设计报告的版本,整合了如下文章:
1.linux内核编译
https://blog.csdn.net/qq_46640863/article/details/122684580

2.本人课设主体任务
https://blog.csdn.net/qq_46640863/article/details/122952706

3.小组任务整合流程
https://blog.csdn.net/qq_46640863/article/details/122751766

目录

  • 写在前面
  • 一、课程设计目的
  • 二、设计内容及具体要求
  • 三、实验环境
  • 四、实验步骤-内核编译
  • 五、实验步骤-实现设计内容
    • 5.1设计思想
    • 5.2 设计实现与源码剖析
    • 5.3 操作流程
  • 六、个人结果测试与分析
  • 七、小组整合
    • 7.1本组分工情况
    • 7.2小组整合思路
    • 7.3编译新内核时遇到的问题与解决思路
  • 参考文献
  • 源程序清单
    • Makefile
    • zombotany_blkdev.c

一、课程设计目的

知识方面:
1.掌握操作系统功能模块的设计与实现方法。
能力与素质方面:
1.能够在阅读和分析开源操作系统的基础上,对其进行功能模块划分;能够指出现有功能模块的不足,并能够通过文献的研究给出解决方案。
2.能够完成操作系统功能模块的设计、实现与测试,同时在设计操作系统功能模块中,能体现优化和创新意识。
3.能够制定合理的实验方案及对实验结果进行分析并得出结论,针对实验结果分析解决过程的影响因素,论证解决方案的合理性,以获得有效结论。
4.能够根据设计任务和要求组成团队,分工协作,并能承担个体、团队成员以及负责人的角色。
5.能够用口头和书面方式清晰表述设计原理及相关概念与原理,包括陈述发言,清晰表达和回应指令。
6.能够撰写比较规范的课程设计报告。

二、设计内容及具体要求

题目3:新增Linux驱动程序
增加一个驱动程序(使用内存模拟设备),使用模块编译方式。
要求:
(1)可以动态加载和卸载新的驱动。
(2)通过程序或命令行使用该驱动。
(3)至少能通过该驱动保存256MB的数据,还能将这些数据读取出来。
(4)要重新编译Linux内核,可模仿ramdisk的实现方式。

三、实验环境

物理机:Windows11 教育版
虚拟机:VMware Workstation Pro 16
虚拟机操作系统:CentOS 8 64位(CentOS-8.5.2111-x86_64)
Linux内核版本:linux-4.18.20

四、实验步骤-内核编译

在实验开始前,需要预先与队友统一实验环境中的虚拟机操作系统与Linux内核版本,以在后续的整合步骤中便于操作。
首先,在CentOS官网上点击“Download”,在该页面点击“x86_64”,并进入找到CentOS在国内的镜像下载站,选择进入南京大学的镜像下载站。http://mirrors.nju.edu.cn/centos/8.5.2111/isos/x86_64/
并下载文件大小约为10GB的iso文件,而不是文件大小约为800m的boot文件。

安装VMware Workstation 16 pro。由于之前已经装过VMware14,在更新过程中可能系统会提示安装失败的情况。经查阅相关资料,需要按图中流程进行操作:以管理员身份打开cmd,执行”sc stop vmx86& sc delete vmx86”和”sc stop vstor2-mntapi20-shared &sc delete vstor2-mntapi20-shared”。在”C:\Windows\System32\drivers\”目录下删除”vmx86.sys”、”vstor2-mntapi20-shared.sys”、”vstor2-x64.sys”。在”C:\windows\system32\drvstore”目录下删除所有前缀为”vmx86”的文件。同时,也要以管理员身份打开组策略编辑器(gpedit),在HEKY_LOCAL_MACHINE\SOFTWARE中删除与VMware有关的项目。
最后重启主机。运行VMware 16 Pro安装程序。至此,VMware虚拟机得以正确地安装。


打开VMware,点击“创建新的虚拟机”,运行安装向导。采用典型配置,进行简易安装,如图所示。

为了方便后续操作,可以将用户账户和根账户(root)的密码设置成1。

虚拟机网络采用网络地址转换NAT。在虚拟机操作系统安装完成后,即可直接连接外部网络。

在配置虚拟机硬件时,虚拟机内存可以分3GB。处理器可以分4个,每个处理器有两个内核,以在编译内核时可以使用8个内核运行编译。磁盘空间需要开足够大,使用80GB而不是默认推荐的20GB,以防止内核编译过程中出现磁盘空间不足的情况。同时,需要注意删去虚拟机硬件设置中的打印机与USB接口。否则,在物理机中可能会出现蓝屏的情况。

在虚拟机创建完成后,即可以启动虚拟机,配置系统。需要说明的是,CentOS 8的系统配置过程较为简单,只需等待其进度条读完后,即可以直接进入图形化界面。
为了方便地调出terminal控制台,可以在桌面右键设置Display Settings,在键盘上添加快捷键,如图,设置可以用快捷键打开控制台。

调出terminal控制台后,输入cat /proc/version,即可以查看当前使用的Linux内核版本号与gcc编译器版本号。

由于windows11系统可能与vmware的vmware tools不兼容,不能从主机直接拖文件向虚拟机,所以需要使用xftp共享文件夹的方式从主机向虚拟机传文件。Centos8的网络IP地址配置文件在/etc/sysconfig/network-scripts 文件夹下,名为ifcfg-ens160。在cmd输入ifconfig,得到本机dhcp时的IP地址inet。利用xftp进行连接,会话的主机就是本次ifconfig得到的ip地址。利用sftp协议。



下载需要进行编译的新的内核的版本。在本文中,选用linux-4.18.20版本。如图,已经成功将linux源码文件包传入。但是,如需直接传入/usr/src/kernels文件夹中,可能会因为权限问题而传输失败。所以,需要先将文件传入~/Desktop.在CentOS 8 中,可以直接双击解压,无需输入命令行。但在把Desktop的文件复制到/usr/src/kernels时,因为用户没有权限,所以只能通过命令行的形式将文件复制进入。执行如下语句:
sudo cp -r /home/zombotany/Desktop/linux-4.18.20 /usr/src/kernels
接下来的步骤为配置编译环境。需要说明的是,CentOS 8的编译环境安装过程与其他Linux发行版的安装流程区别较大。在此,将CentOS8的编译环境安装过程进行详细介绍。
首先,安装gcc。sudo yum install gcc
安装g++。在centos中,安装g++的命令需要写成sudo yum install gcc-c++ 在安装过程中可能会提示未找到匹配的包。经查阅相关资料,知道了是yum源的原因。于是安装 EPEL 源,使用最新版本8。执行语句:yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
在CentOS8中,安装devtoolset时,不能用yum,而是使用dnf.需要注意的是,scl在CentOS8的yum源中是不提供的。需要执行语句:sudo dnf -y group install "Development Tools"
接下来,同样地,执行语句sudo dnf install devtoolset-9-gcc
还需要安装的是bison、flex、gdb、make、bc等工具。这些语句在CentOS各个版本是相同的,不作赘述。
sudo yum install gdb -y
sudo yum install make -y
sudo yum install bison -y
sudo yum install flex -y
sudo yum install bc -y
在想要编译的文件夹中输入make,会被提示缺文件。被系统建议安装libelf-devlibelf-develefutils-libelf-devel

但是,只能安装第三个,前两个都不是centos能装的,只能执行sudo yum -y install efutils-libelf-devel
在生成编译配置.config之前,还需要执行如下两条语句:
sudo yum install ncurses-devel -y
sudo yum install openssl-devel -y
在安装完如上文所述的所有依赖后,进入需要编译的内核所在的文件目录,执行命令:cd /usr/src/kernels/linux-4.18.20
在编译之前,首先需要执行清理命令。执行:make mrproper。在执行完之前残留的编译结果后,需要生成编译配置文件。执行make menuconfig,在等待数秒后,控制台会显示如图14的图形界面。利用键盘方向键进行选择,直接全部使用默认参数。选定save项,并采用默认的配置文件名.config,如图15所示。
输入make -j8,启动8个核开始编译。这一步骤大约需要一小时左右。如果发现系统很快执行完,则说明该步骤没有被正确地执行完毕,仍然存在较多文件没有被正确地编译。若报错missing file,则make相应的文件。若在make bzImage时又被提示”no rule to make target ‘certs/rhel.pem’”,则用vi编辑器打开.config文件,中注释掉CONFIG_SYSTEM_TRUSTED_KEYS或写死CONFIG_SYSTEM_TRUSTED_KEYS=””,解决该问题。该配置项大约在文件结束处。修改完成后,输入键盘的esc,并输入”:wq”退出并写入文件。
编译完成后生成了bzImage,该文件在目录arch/x86_64/boot下,如图17所示。编译完成后输入make modules编译模块。输入make modules_install安装模块,输入make install安装新内核。安装完成新内核后,输入reboot,进入新内核。



五、实验步骤-实现设计内容

5.1设计思想

题目要求使用内存模拟设备增加一个驱动程序。而内存模拟设备可以模仿Ram Disk的实现方式。经查阅相关资料可得知:Ram Disk的功能是将一部分内存挂载(mount)为外存空间(磁盘)的分区进行使用。从用户的视角看,Ram Disk分区就像磁盘的分区一样,也能对文件进行读写。
但是,Ram Disk与真正的磁盘仍然存在一定区别。在虚拟机重启后,Ram Disk分区消失,Ram Disk分区内部的数据也将消失。
Ram Disk也存在自己的意义。若有几个文件需要被频繁的读写,则可以将其放到由内存开辟的Ram Disk上,大大提高了读写的速度。
在本题目中,采取的就是模仿Ram Disk的实现。在第六章节中,将展示模仿Ram Disk的实现能得到的类似于Ram Disk的效果。
Linux系统将所有设备都视作文件,/dev/设备名 不是目录,而类似于指针指向该块设备,不能直接对其进行读写而需要先进行mount挂载的操作。要读写设备中的文件时,需要先把设备的分区挂载到系统中的一个目录上,通过访问该目录来访问设备。

5.2 设计实现与源码剖析

在设计属于自己的驱动时,需要实现加载模块时的初始化函数即驱动模块的入口函数。还需要实现卸载模块时的函数,即模块的出口函数。同时,也要实现设备自己的请求处理函数。
首先,对该模块的数据结构进行设计。先定义该块的块设备名、主设备号、大小(25610241024bytes,即256MB)、扇区数为9。

#define SIMP_BLKDEV_DISKNAME "zombotany_blkdev"
#define SIMP_BLKDEV_DEVICEMAJOR COMPAQ_SMART2_MAJOR
#define SIMP_BLKDEV_BYTES (256*1024*1024)
#define SECTOR_SIZE_SHIFT 9

定义gendisk表示一个简单的磁盘设备、定义该块设备的拥有者、定义块设备的请求队列指针、开辟该块设备的存储空间。

static struct gendisk * zombotany_blkdev_disk;
static struct block_device_operations  zombotany_blkdev_fops = { .owner = THIS_MODULE,
};
static struct request_queue * zombotany_blkdev_queue;
unsigned char  zombotany_blkdev_data[SIMP_BLKDEV_BYTES];

入口函数与出口函数这两个函数的方法头如下:

static int __init _init(void)
static void __exit _exit(void)

在入口函数中需要实现的功能包括4个步骤。1.申请设备资源。若申请失败,则退出。2.设置设备有关属性。3.初始化请求队列,若失败则退出。4.添加磁盘块设备。
首先,申请设备资源。判断申请是否成功,若失败则退出。

zombotany_blkdev_disk = alloc_disk(1);
if(! zombotany_blkdev_disk){ret = -ENOMEM;goto err_alloc_disk;
}

接下来,设置设备有关属性。设置设备名、设备号、fops指针、扇区数

strcpy( zombotany_blkdev_disk->disk_name,SIMP_BLKDEV_DISKNAME);
zombotany_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR;
zombotany_blkdev_disk->first_minor = 0;
zombotany_blkdev_disk->fops = & zombotany_blkdev_fops;
set_capacity( zombotany_blkdev_disk, SIMP_BLKDEV_BYTES>>9);

初始化请求队列,若失败则退出。

zombotany_blkdev_queue = blk_init_queue( zombotany_blkdev_do_request, NULL);if(! zombotany_blkdev_queue){ret = -ENOMEM;goto err_init_queue;}
zombotany_blkdev_disk->queue =  zombotany_blkdev_queue;

最后添加磁盘块设备。

    add_disk( zombotany_blkdev_disk);return 0;

模块的出口函数较为简单,只需释放磁盘块设备、释放申请的设备资源、清除请求队列。

static void __exit  zombotany_blkdev_exit(void){del_gendisk( zombotany_blkdev_disk);put_disk( zombotany_blkdev_disk);   blk_cleanup_queue( zombotany_blkdev_queue);
}

在实现完入口与出口函数后,需要再声明模块出入口。

module_init(xxxx_init);
module_exit(xxxx_exit);

实现模块的请求处理函数。请求处理函数涉及到的数据结构如下:当前请求、当前请求bio(通用块层用bio来管理一个请求)、当前请求bio的段链表、当前磁盘区域、缓冲区。

struct request *req;
struct bio *req_bio;
struct bio_vec *bvec;
char *disk_mem;
char *buffer;

对于某个请求,先判断该请求是否合法。判断请求是否合法的办法就是判断其是否出现了地址越界的情况。

if((blk_rq_pos(req)<<SECTOR_SIZE_SHIFT)+blk_rq_bytes(req)>SIMP_BLKDEV_BYTES){blk_end_request_all(req, -EIO);continue;}

若请求合法,则获取当前地址位置。

disk_mem =zombotany_blkdev_data + (blk_rq_pos(req) << SECTOR_SIZE_SHIFT);
req_bio = req->bio;

判断请求类型,处理读请求与写请求的过程大同小异的。在处理读请求时,遍历请求列表,找到缓冲区与bio,将磁盘内容复制到缓冲区。找到磁盘下一区域,然后处理请求队列下一个请求。

while(req_bio != NULL){for(i=0; i<req_bio->bi_vcnt; i++){bvec = &(req_bio->bi_io_vec[i]);buffer = kmap(bvec->bv_page) + bvec->bv_offset;memcpy(buffer, disk_mem, bvec->bv_len);kunmap(bvec->bv_page);disk_mem += bvec->bv_len;}req_bio = req_bio->bi_next;}__blk_end_request_all(req, 0);break;

在处理写请求时,是把缓冲区内容复制到磁盘上。只需在调用memcpy时将两个参数互换即可,其余相同。
memcpy(disk_mem, buffer, bvec->bv_len);
该部分代码如下:

while(req_bio != NULL){for(i=0; i<req_bio->bi_vcnt; i++){bvec = &(req_bio->bi_io_vec[i]);buffer = kmap(bvec->bv_page) + bvec->bv_offset;memcpy(disk_mem, buffer, bvec->bv_len);kunmap(bvec->bv_page);disk_mem += bvec->bv_len;}req_bio = req_bio->bi_next;}__blk_end_request_all(req, 0);break;

该模块完整代码见附录,文件名为zombotany_blkdev.c

在编写完模块代码后,还需要编写Makefile文件。Linux的文件系统中,文件没有扩展名。Makefile文件没有扩展名。
首先,在第一次读取执行此Makefile时,KERNELRELEASE没有被定义,所以make只会执行else之后的内容。
ifneq ($(KERNELRELEASE),)
得到内核源码的路径与当前的工作路径
KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
若之前执行过Makefile,则需要清理掉之前编译过的模块。

modules:$(MAKE) -C $(KDIR) M=$(PWD) modulesmodules_install:
$(MAKE) -C $(KDIR) M=$(PWD) modules_install
clean:rm -rf *.o *.ko .depend *.mod.o *.mod.c Module.* modules.*
.PHONY:modules modules_install clean

生成.o文件

elseobj-m := simp_blkdev.oendif

Makefile完整代码见附录。

5.3 操作流程

将模块源码zombotany_blkdev.c和Makefile文件放在同一目录下,如图18。

在该目录下打开控制台,输入make。则能正确地生成.o文件和.ko文件

该模块编译完成后,对其进行的测试与分析的结果详见第六章节。

六、个人结果测试与分析

模块编译完成后,首先回到该目录,执行语句insmod zombotany_blkdev.ko,将刚编译完成的zombotany_blkdev.ko模块插入。执行完成后,再执行lsmod,查看当前系统中的块设备列表。可以看到,zombotany_blkdev已经存在,已经被插入过了,且大小为256MB。也可以执行lsblk,查看当前块设备。


在根目录下的/dev/ 路径中,可以看到zombotany_blkdev已经被插入了。执行ls /dev/

在插入完成后,需要对该模块进行格式化,建立文件系统。输入mkfs.ext3 /dev/zombotany_blkdev,则在该内存模拟设备上建立了ext3文件系统。

在建立完成文件系统后,就可以将该设备挂载到文件系统的目录下。首先需要创建需要挂载的目录。mkdir -p /mnt/temp1。将块设备挂载到该目录下。mount /dev/zombotany_blkdev /mnt/temp1。再运行mount | grep zombotany_blkdev。即挂载完成。
再次执行lsmod,查看模块被调用的情况。该模块被一个用户调用。

执行ls/mnt/temp1/ 则看到当前块设备有且只有一个文件:lost+found文件。将当前目录所有文件都复制到块设备上,例如当前在该模块的源代码文件夹目录上。执行cp ./* /mnt/temp1/完成复制,再查看当前块设备文件名单。执行ls /mnt/temp1/,则可以看到该块设备被正确地写入了文件,并可以被读取到。

执行df -H,则查看当前各个设备使用情况。新增的设备zombotany_blkdev已用了2.9MB。

执行vi /mnt/temp1/zombotany_blkdev.c,还能读取这个文件。

最后对该模块进行卸载。首先删除该目录内所有文件。rm -rf /mnt/temp1/*
先取消挂载。执行umount /mnt/temp1后执行lsmod | grep zombotany_blkdev。可以看到,这个256MB大小的设备被0个用户调用。
执行rmmod zombotany_blkdev。该语句的作用是移除该模块。运行完成后,再次执行lsmod grep zombotany_blkdev。可以在控制台上看到系统并没有任何输出。说明:zombotany_blkdev模块已经彻底被移除了。

简而言之,通过测试与分析,本新增模块正确地完成了题目要求:使用模块编译的方式。可以动态加载和卸载新的驱动,如图21与28所示。可以通过命令行使用该驱动,在测试过程中每一步都有所体现。至少能保存256MB数据,并且能读取出来,如图26与27所示。需要重新编译Linux内核,模仿ramdisk实现方式。详见章节四与章节5.1。

七、小组整合

7.1本组分工情况

如表1所示
表1 小组分工

姓名 分工
1 题目1:新增Linux系统调用
2 题目2:实现基于模块的文件系统
zombotany 题目3:新增Linux驱动程序,并负责小组的工作的整合与答辩
4 题目4:统计Linux系统缺页的次数并协助进行小组工作的整合
5 题目5:进程/线程通信

7.2小组整合思路

题目1与题目4存在的共同点是:在进行内核的编译之前需要修改内核中的文件。题目2与题目3存在的共同点是:需要编译内核,在编译完成的新内核进行模块的编译。而题目5完全不涉及内核。
因此,整合的总体流程如下:首先修改内核源码文件中涉及到题目1与题目4的部分。接下来,进行长达1~2小时的编译内核、编译模块与安装新内核。然后,进入新的内核,传入题目2与题目3涉及到的源代码文件,安装并卸载相应的模块进行测试。最后,编译并测试题目5涉及到的源代码。
在本文的章节四中,本小组已经事先统一了用同一个版本的Linux发行版与同一个版本的Linux内核源码。因此,整合的过程得到了一定的简化。
题目1涉及到的内核源码的文件包括:
arch/x86/entry/syscalls/syscall_64.tbl
kernel/sys.c
include/linux/syscalls.h
题目4涉及到的内核源码的文件包括:
arch/x86/mm/fault.c
include/linux/mm.h
kernel/kallsyms.c
在替换了这6个文件后,按第四章节的流程操作,对内核进行重新编译。
进入新的内核。
在新的内核中,题目1需要使用程序对内核进行测试。
题目2涉及到的文件包括:super.c、sysfs.c、file.c、Makefile。将这4个文件放在同一个目录下,进行模块编译。
题目3涉及到的文件包括:zombotany_blkdev.c、Makefile。将这2个文件放在同一个目录下,进行模块编译。
题目4涉及到的文件包括:readpfcount.c、Makefile。将这2个文件放在同一个目录下,进行模块编译。利用模块的形式,对缺页中断次数进行了测试。
题目5涉及到的文件包括:share.c、read.c。这2个文件不涉及也不调用内核。在这2个文件中,就可以加入题目1设计到的系统调用。例如,可以系统调用,计算当前图书馆内已有人数的三次方。如图29所示。可以用gcc -c share.c -o share.out 和gcc -c read.c -o read.out直接编译运行。

其余运行结果不再贴图赘述。

7.3编译新内核时遇到的问题与解决思路

因为在完成个人的题目时,反复编译了内核,所以在整合小组工作并重新编译时出现了boot分区不够的情况,不能在boot分区安装新内核,如图30所示。运行df -hl,发现boot分区只开了300M,且空间即将耗尽。所以,在安装新内核之前需要先对boot分区进行扩容。

首先关机,创建新的磁盘,重新开机后将/boot分区取消挂载。对新的磁盘(nvme0n2)分区,执行命令fdist /dev/nvme0n2,创建一个新分区,全部采取默认选项。

运行lsblk命令,查看新磁盘的新分区。对新分区进行格式化 mkfs.ext4 /dev/nvme0n2p1

将旧内核复制到boot_old文件夹(cp -r /boot/ /boot_old),备份旧内核中的文件。之后,把boot分区挂载回来,挂载到新分区。mount /dev/nvme-n2p1 /boot。在挂载完成后,再把boot_old的备份文件复制回来。cp -r /boot_old/. /boot
将永久挂载写入到/etc/fstab里。先执行blkid查看所有分区的uuid。如图33。

打开/etc/fstab,找到nvme0n2p1的分区填入。加入记录:
UUID=5b624350-9fce-495d-934e-650f62cfe189 ext4 /boot defaults 0 1
保存并退出后更新挂载信息。mount -alsblk。可以看到,/boot分区被挂载到了有20GB的新磁盘上。

重新挂载/boot分区后,重新make install安装内核模块。但是,还需要运行grub2-mkconfig -o /boot/grub2/grub.cfg更新引导文件。否则,会出现如下情况:在旧内核中,/boot分区被正确地识别到并挂载到nvme0n2p1分区,但在新内核找不到/boot。更新引导文件后,新内核也能找到/boot分区。再次重启虚拟机,终于可以成功进入新内核。编译新内核时可能遇到的/boot分区不足的情况被通过这种办法成功得以解决。
编译安装新内核过程中还可能遇到的情况如图35:客户机操作系统已禁用CPU。此问题解决办法较为简单:在物理机开机时按f12进入bios,在bios中设置允许虚拟机。若已经设置允许虚拟机,则需要关掉windows defender或腾讯电脑管家或360。当虚拟机占用主机过多资源时就有可能也会出现该情况。

参考文献

[1] https://blog.csdn.net/cxy_chen/article/details/80998510
[2] https://blog.csdn.net/wys7250578/article/details/9045237
[3] https://blog.csdn.net/skywalker_123/article/details/102587813
[4]https://blog.csdn.net/m0_46362426/article/details/118879627
[5]https://forums.pvpgn.pro/viewtopic.php?id=2226
[6]https://stackoverflow.com/questions/61590926/how-to-install-gcc-g-9-on-centos-8-docker-centoslatest
[7]https://communities.vmware.com/t5/VMware-Workstation-Pro/Update-to-Workstation-14-1-2-fails-and-destroys-existing/td-p/2735925

源程序清单

Makefile

ifeq ($(KERNELRELEASE),)
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:$(MAKE) -C $(KDIR) M=$(PWD) modules
modules_install:$(MAKE) -C $(KDIR) M=$(PWD) modules_install
clean:rm -rf *.o *.ko .depend *.mod.o *.mod.c Module.* modules.*
.PHONY:modules modules_install clean
elseobj-m := zombotany_blkdev.o
endif

zombotany_blkdev.c

#include <linux/module.h>
#include <linux/blkdev.h>#define SIMP_BLKDEV_DISKNAME "zombotany_blkdev"//设备名称
#define SIMP_BLKDEV_DEVICEMAJOR COMPAQ_SMART2_MAJOR //主设备号
#define SIMP_BLKDEV_BYTES (256*1024*1024)            // 块设备大小为256MB
#define SECTOR_SIZE_SHIFT 9//9个扇区static struct gendisk * zombotany_blkdev_disk;// gendisk结构表示一个简单的磁盘设备
static struct block_device_operations  zombotany_blkdev_fops = { .owner = THIS_MODULE,//设备主体
};
static struct request_queue * zombotany_blkdev_queue;//指向块设备请求队列的指针
unsigned char  zombotany_blkdev_data[SIMP_BLKDEV_BYTES];// 虚拟磁盘块设备的存储空间//请求处理函数
static void  zombotany_blkdev_do_request(struct request_queue *q){struct request *req;// 正在处理的请求队列中的请求struct bio *req_bio;// 当前请求的biostruct bio_vec *bvec;// 当前请求的bio的段(segment)链表char *disk_mem;      // 需要读/写的磁盘区域char *buffer;        // 磁盘块设备的请求在内存中的缓冲区while((req = blk_fetch_request(q)) != NULL){//得到请求// 判断当前请求是否合法if((blk_rq_pos(req)<<SECTOR_SIZE_SHIFT) + blk_rq_bytes(req) > SIMP_BLKDEV_BYTES){//判断地址是否越界访问printk(KERN_ERR SIMP_BLKDEV_DISKNAME":bad request:block=%llu, count=%u\n",(unsigned long long)blk_rq_pos(req),blk_rq_sectors(req));//越界访问了,则输出blk_end_request_all(req, -EIO);continue;//获取下一请求}//获取需要操作的内存位置disk_mem =  zombotany_blkdev_data + (blk_rq_pos(req) << SECTOR_SIZE_SHIFT);req_bio = req->bio;// 获取当前请求的bioswitch (rq_data_dir(req)) {  //判断请求的类型case READ:// 遍历req请求的bio链表while(req_bio != NULL){// for循环处理bio结构中的bio_vec结构体数组(bio_vec结构体数组代表一个完整的缓冲区)for(int i=0; i<req_bio->bi_vcnt; i++){bvec = &(req_bio->bi_io_vec[i]);buffer = kmap(bvec->bv_page) + bvec->bv_offset;memcpy(buffer, disk_mem, bvec->bv_len);//把内存中数据复制到缓冲区kunmap(bvec->bv_page);disk_mem += bvec->bv_len;}req_bio = req_bio->bi_next;//请求链表下一个项目}__blk_end_request_all(req, 0);//被遍历完了break;case WRITE:while(req_bio != NULL){for(int i=0; i<req_bio->bi_vcnt; i++){bvec = &(req_bio->bi_io_vec[i]);buffer = kmap(bvec->bv_page) + bvec->bv_offset;memcpy(disk_mem, buffer, bvec->bv_len);//把缓冲区中数据复制到内存kunmap(bvec->bv_page);disk_mem += bvec->bv_len;}req_bio = req_bio->bi_next;//请求链表下一个项目}__blk_end_request_all(req, 0);//请求链表遍历结束break;default:/* No default because rq_data_dir(req) is 1 bit */break;}}
}//模块入口函数
static int __init  zombotany_blkdev_init(void){int ret;//添加设备之前,先申请设备的资源zombotany_blkdev_disk = alloc_disk(1);if(! zombotany_blkdev_disk){ret = -ENOMEM;goto err_alloc_disk;}//设置设备的有关属性(设备名,设备号,fops指针strcpy( zombotany_blkdev_disk->disk_name,SIMP_BLKDEV_DISKNAME);zombotany_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR;zombotany_blkdev_disk->first_minor = 0;zombotany_blkdev_disk->fops = & zombotany_blkdev_fops;//将块设备请求处理函数的地址传入blk_init_queue函数,初始化一个请求队列zombotany_blkdev_queue = blk_init_queue( zombotany_blkdev_do_request, NULL);if(! zombotany_blkdev_queue){ret = -ENOMEM;goto err_init_queue;}zombotany_blkdev_disk->queue =  zombotany_blkdev_queue;//初始化扇区数set_capacity( zombotany_blkdev_disk, SIMP_BLKDEV_BYTES>>9);//入口处添加磁盘块设备add_disk( zombotany_blkdev_disk);return 0;err_alloc_disk:return ret;err_init_queue:return ret;
}//模块的出口函数
static void __exit  zombotany_blkdev_exit(void){// 释放磁盘块设备del_gendisk( zombotany_blkdev_disk);
// 释放申请的设备资源put_disk( zombotany_blkdev_disk);
// 清除请求队列blk_cleanup_queue( zombotany_blkdev_queue);
}module_init( zombotany_blkdev_init);// 声明模块的入口
module_exit( zombotany_blkdev_exit);// 声明模块的出口

【操作系统】课程设计:新增Linux驱动程序(重制版)相关推荐

  1. 操作系统课程设计:Linux系统调用/基于模块的文件系统/Linux驱动/统计Linux系统缺页的次数 整合

    目录 一.可选题目 题目1:新增Linux系统调用 题目2:实现基于模块的文件系统 题目3:新增Linux驱动程序 题目4:统计Linux系统缺页的次数 二.操作顺序 附录:参考资料 题1 题2 题3 ...

  2. 操作系统课程设计--在Linux环境下模拟实现简单命令解释器(C++代码)

    操作系统课程设计要求 一.设计目的 熟悉Linux编程环境,加强对Linux命令的理解及函数的运用 二.设计内容 1. 在Linux环境下模拟实现简单命令解释器. (1)要求实现的基本命令包括: pw ...

  3. linux课程设计题目主存空间的分配与回收,可变分区分配与回收,哈尔滨理工大学操作系统课程设计报告.doc-资源下载在线文库www.lddoc.cn...

    可变分区分配与回收,哈尔滨理工大学 操作系统课程设计报告.doc 哈 尔 滨 理 工 大 学 课 程 设 计 ( 操 作 系 统 ) 题 目 可变分区分配与回收 (首次适应算法) 班 级 计算机科学与 ...

  4. linux课程设计死锁避免,linux操作系统课程设计—车辆死锁.doc

    linux操作系统课程设计-车辆死锁.doc 键入文字"操作系统原理"课程设计BX090709吴沛儒操作系统原理课程设计报告姓名吴沛儒班级BX0907学号9指导老师胡静二〇一一年十 ...

  5. Linux并发程序课程设计报告,网络操作系统课程设计--进程机制与并发程序设计-linux下生产者与消费者的问题实现.doc...

    网 络 操 作 系 统 课 程 设 计 网络操作系统课程设计 设计内容:进程机制与并发程序设计inux下生产者与消费者的问题实现进程机制与并发程序设计inux下生产者与消费者的问题实现 (1)掌握基本 ...

  6. 检索上Linux操作系统课程的教师名,Linux操作系统课程设计.docx

    课 程 设 计 报 告 课程名称 Linux操作系统课程设计 指导教师 起止日期 2016-03-21 至 2016-06-13 学 院 信息与通信工程学院 专 业 电子信息工程 学生姓名 班级/学号 ...

  7. 操作系统课程设计geekos project1-3

    概述 实验环境 GeekOS-0.3.0 Bochs和Vmware介绍 开发过程 编译运行 配置文件 前导知识 一.全局描述符表GDT(Global Descriptor Table) 二.段选择子( ...

  8. 华科计算机课程设计,华中科大操作系统课程设计报告(附源码).doc

    华中科技大学计算机学院 操作系统课程设计报告 班级: 学号: 姓名:彭博 时间:2010年3月 设计内容一:熟悉和理解Linux编程环境 编写一个C程序,实现文件拷贝功能. 2)编写一个C程序,使用下 ...

  9. 华中科技大学计算机课程设计,华中科技大学计算机学院操作系统课程设计资料报告材料[1].doc...

    <华中科技大学计算机学院操作系统课程设计资料报告材料[1].doc>由会员分享,提供在线免费全文阅读可下载,此文档格式为doc,更多相关<华中科技大学计算机学院操作系统课程设计资料报 ...

最新文章

  1. android studio adb
  2. 添加与编辑共用一个jsp页面时,控制按钮的显示与隐藏
  3. 08-Scrum过程-办公环境 每日立会(Standup Meeting)
  4. 高并发中,那些不得不说的线程池与ThreadPoolExecutor类
  5. Theano 中文文档 0.9 - 7.1.1 Python教程
  6. atom系列服务器,这才叫四两拨千斤!Atom将登陆服务器
  7. ​618购物节,我用python逛淘宝、亚马逊,摸鱼被领导发现了
  8. ~~筛法求欧拉函数(附模板题)
  9. Java基础002 Java9开发环境搭建(Win10平台)
  10. 贵州大学旧物交易系统
  11. Perforce携手龙智成功举办2021年度中国用户大会助力中国企业“加速开发”
  12. Nutch爬虫爬取视频研究文档
  13. HR问:“对我们公司你有什么问题要问的吗”,怎样回答才算完美!
  14. 即时通信软件实现原理
  15. Android 读取bin文件方法
  16. Python爬虫之知乎回答
  17. 工作组和域的概念及辨析
  18. OpenVAS下载与安装
  19. SAP批量修改物料标准成本
  20. 学习笔记——基于FPGA的SD卡学习(1)

热门文章

  1. uniapp的picker日期转时间戳多8小时问题
  2. DateUtils日期转时间戳/时间戳转日期
  3. python 作业 2
  4. python数据库去重复_mysql数据库如何去重复数据
  5. i2c adapter 和client 还有i2c_core之间的联系
  6. 使用表单对象时,报错 form is undefine
  7. 0基础快速入门CSS技术栈(5)—图解详细阐述说透CSS的盒子模型(超级重要)、圆角边框、盒子阴影及相关重要的笔试题——css的核心中的核心(附详细案例源码解析过程)2021.01.07更新
  8. xgboost坏账预测(二分类问题)
  9. 【最新计算机毕业设计】JAVA基于微信小程序的英语学习激励系统
  10. 企业该如何选择合适的固定资产管理系统?