linux0.12-9-5-ramdisk.c

news2024/11/24 21:03:37

1、 需要配合其他代码一起看,才能有深刻的理解。

[432页]

9-5 ramdisk.c程序

9-5-1 功能描述

本文件是内存虚拟盘(Ram Disk)驱动程序,由Theodore Ts’o编制。虚拟盘设备是一种利用物理内存来模拟实际磁盘存储数据的方式。其目的主要是为了提高对"磁盘"数据的读写操作速度。除了需要占用一些宝贵的内存资源外,其主要缺点是一旦系统崩溃或关闭,虚拟盘中的所有数据将全部消失。因此虚拟盘中通常存放一些系统命令等常用工具程序或临时数据,而非重要的输入文档。

当在linux/Makefile文件中定义了常量RAMDISK,内核初始化程序就会在内存中划出一块该常量值指定大小的内存区域用于存放虚拟盘数据。虚拟盘在物理内存中所处的具体位置是在内核初始化阶段确定的(init/main.c,123行),它位于内核高速缓冲区和主内存区之间。若运行的机器含有16MB的物理内存,那么虚拟盘区域会被设置在内存4MB开始处,虚拟盘容量即等于RAMDISK的值(KB)。若RAMDISK=512,则此时内存情况如图9-6所示。

图9-6

对虚拟盘设备的读写访问操作原则上完全按照对普通磁盘的操作进行,也需要按照块设备的访问方式对其进行读写操作。由于在实现上不牵涉与外部控制器或设备进行同步操作,因此其实现方式比较简单。对于数据在系统与设备之间的传送只需执行内存数据块复制操作即可。

本程序包含3个函数。
rd_init()会在系统初始化时被init/main.c程序调用,用于确定虚拟盘在
物理内存中的具体位置和打;
do_rd_request()是虚拟盘设备的请求项操作函数,对当前请求
项实现虚拟盘数据的访问操作;
rd_load()是虚拟盘根文件加载函数。在系统初始化阶段,该函数被用于尝试从启动
引导盘上指定的磁盘块位置开始处把一个根文件系统加载到虚拟盘中。

在函数中,这个起始磁盘块位置被定为256.当然你也可以根据自己的具体要求修改这个值,只要保证这个值所规定的磁盘容量能容纳内核映射文件即可。这样一个由内核引导映像文件(Bootimage)加上根文件系统映像(Rootimage)组合而成的"二合一"磁盘,就可以想启动DOS系统盘那样来启动Linux系统。关于这种组合盘(集成盘)的制作方式可参见第14章中相关内容。

在进行正常的根文件系统加载之前,系统会首先执行rd_load()函数,试图从磁盘的第257块中读取根文件系统超级块。若成功,就把该根文件映像文件读到内存虚拟盘中,并把根文件系统设备标志ROOT_DEV设置为虚拟盘设备(0x0101),否则退出rd_load(),系统继续从别的设备上执行根文件加载操作。

操作流程如图9-7所示
在这里插入图片描述

如果在编译Linux0.12内核源码代码时,在其linux/Makefile配置文件中定义了RAMDISK的大小,则内核代码在引导并初始化RAMDISK区域后就会首先尝试检测启动盘上的第256磁盘块(每个磁盘块为1KB,即2个扇区)开始处是否存在一个根文件系统。检测方法是判断第257磁盘块中是否存在一个有效的文件系统超级块信息。如果有,则将该文件系统加载到RAMDISK区域中,并将其作为根文件系统使用。从而我们就可以使用一张集成了根文件系统的启动盘来引导系统到shell命令提示符状态。若启动盘上指定磁盘块位置(第256磁盘块)上没有存放一个有效的根文件系统,那么内核就会提示插入根文件系统盘。在用户按下回车键确认后,内核就把处于独立盘上的根文件系统整个地读入到内存的虚拟盘区域中去执行。

在一张1.44MB的内核引导启动盘上把一个基本的根文件系统放在盘的第256个磁盘块开始的地方就可以组合成一张集成盘,其结构如图9-8所示。
在这里插入图片描述

9-5-2 代码注释

/*
 *  linux/kernel/blk_drv/ramdisk.c
 *
 *  Written by Theodore Ts'o, 12/2/91
 */
/* 由Theodore Ts'o编制,12/2/91
 */
// Theodore Ts'o (Ted Ts'o)是Linux社区中的著名人物。Linux在世界范围内的流行也有他很
// 大的功劳。早在Linux操作系统刚问世时,他就怀着极大的热情为Linux的发展提供了电子邮
// 件列表服务maillist,并在北美地区最早设立了Linux的ftp服务器站点(tsx-11.mit.edu),
// 而且至今仍为广大Linux用户提供服务。他对Linux作出的最大贡献之一是提出并实现了ext2
// 文件系统。该文件系统已成为Linux世界中事实上的文件系统标准。最近他又推出了ext3文件
// 系统,大大提高了文件系统的稳定性、可恢复性和访问效率。作为对他的推崇,第97期(2002
// 年5月)的LinuxJournal期刊将他作为了封面人物,并对他进行了采访。目前他为IBM Linux
// 技术中心工作,并从事着有关LSB (Linux Standard Base)等方面的工作。(他的个人主页是:
// http://thunk.org/tytso/)
#include <string.h>			// 字符串头文件。主要定义了一些有关字符串操作的嵌入函数。

#include <linux/config.h>	// 内核配置头文件。定义键盘语言和硬盘类型(HD_TYPE)可选项。
#include <linux/sched.h>	// 调度程序头文件,定义了任务结构task_struct、任务0的数据,
							// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
#include <linux/fs.h>		// 文件系统头文件。定义文件表结构(file、m_inode)等。
#include <linux/kernel.h>	// 内核头文件。含有一些内核常用函数的原型定义。
#include <asm/system.h>		// 系统头文件。定义了设置或修改描述符/中断门等嵌入式汇编宏。
#include <asm/segment.h>	// 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。
#include <asm/memory.h>		// 内存拷贝头文件。含有memcpy()嵌入式汇编宏函数。

// 定义RAM盘主设备号符号常数。在驱动程序中主设备号必须在包含blk.h文件之前被定义。
// 因为blk.h文件中要用到这个符号常数值来确定一些列的其他常数符号和宏。
#define MAJOR_NR 1
#include "blk.h"

// 虚拟盘在内存中的起始位置。该位置会在第52行上初始化函数rd_init()中确定。参见内核
// 初始化程序init/main.c,第124行。'rd'是'ramdisk'的缩写。
char	*rd_start;			// 虚拟盘在内存中的开始地址。
int	rd_length = 0;			// 虚拟盘所占内存大小(字节)。

// 虚拟盘当前请求项操作函数。
// 该函数的程序结构与硬盘的do_hd_request()函数类似,参见hd.c,294行。在低级块设备
// 接口函数ll_rw_block()建立起虚拟盘(rd)的请求项并添加到 rd的链表中之后,就会调
// 用该函数对 rd当前请求项进行处理。该函数首先计算当前请求项中指定的起始扇区对应虚
// 拟盘所处内存的起始位置addr 和要求的扇区数对应的字节长度值 len,然后根据请求项中
// 的命令进行操作。若是写命令WRITE,就把请求项所指缓冲区中的数据直接复制到内存位置
// addr处。若是读操作则反之。数据复制完成后即可直接调用 end_request() 对本次请求项
// 作结束处理。然后跳转到函数开始处再去处理下一个请求项。若已没有请求项则退出。
void do_rd_request(void)
{
	int	len;
	char	*addr;
    // 首先检测请求项的合法性,若已没有请求项则退出(参见blk.h,第127行)。然后计算请
    // 求项处理的虚拟盘中起始扇区在物理内存中对应的地址addr和占用的内存字节长度值len。
    // 下句用于取得请求项中的起始扇区对应的内存起始位置和内存长度。其中sector << 9表示
    // sector * 512,换算成字节值。CURRENT被定义为 (blk_dev[MAJOR_NR].current_request)。
	INIT_REQUEST;
	addr = rd_start + (CURRENT->sector << 9);
	len = CURRENT->nr_sectors << 9;
	// 如果当前请求项中子设备号不为1或者对应内存起始位置大于虚拟盘末尾,则结束该请求项,
    // 并跳转到 repeat 处去处理下一个虚拟盘请求项。标号 repeat 定义在宏 INIT_REQUEST 内,
    // 位于宏的开始处,参见blk.h文件第127行。
	if ((MINOR(CURRENT->dev) != 1) || (addr+len > rd_start+rd_length)) {
		end_request(0);
		goto repeat;
	}
	// 然后进行实际的读写操作。如果是写命令(WRITE),则将请求项中缓冲区的内容复制到地址
    // addr处,长度为len字节。如果是读命令(READ),则将addr开始的内存内容复制到请求项
    // 缓冲区中,长度为len字节。否则显示命令不存在,死机。
	if (CURRENT-> cmd == WRITE) {
		(void ) memcpy(addr,
			      CURRENT->buffer,
			      len);
	} else if (CURRENT->cmd == READ) {
		(void) memcpy(CURRENT->buffer, 
			      addr,
			      len);
	} else
		panic("unknown ramdisk-command");
	// 然后在请求项成功后处理,置更新标志。并继续处理本设备的下一请求项。
	end_request(1);
	goto repeat;
}

/* 返回内存虚拟盘ramdisk所需的内存量 */
// 虚拟盘初始化函数。
// 该函数首先设置虚拟盘设备的请求项处理函数指针指向do_rd_request(),然后确定虚拟盘
// 在物理内存中的起始地址、占用字节长度值。并对整个虚拟盘区清零。最后返回盘区长度。
// 当linux/Makefile文件中设置过RAMDISK值不为零时,表示系统中会创建RAM虚拟盘设备。
// 在这种情况下的内核初始化过程中,本函数就会被调用(init/main.c,L151行)。该函数
// 的第2个参数length会被赋值成RAMDISK * 1024,单位为字节。
long rd_init(long mem_start, int length)
{
	int	i;
	char	*cp;

	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;		// do_rd_request()。
	rd_start = (char *) mem_start;						// 对于16MB系统该值为4MB。
	rd_length = length;									// 虚拟盘区域长度值。
	cp = rd_start;
	for (i=0; i < length; i++)							// 盘区清零。
		*cp++ = '\0';
	return(length);
}

/*
 * 如果根文件系统设备(root device)是ramdisk的话,则尝试加载它。
 * root device原先是指向软盘的,我们将它改成指向ramdisk。
 */
 尝试把根文件系统加载到虚拟盘中。
// 该函数将在内核设置函数setup()(hd.c,156行)中被调用。另外,1磁盘块 = 1024字节。
// 第75行上的变量block=256表示根文件系统映像文件被存储于boot盘第256磁盘块开始处。
void rd_load(void)
{
	struct buffer_head *bh;			// 高速缓冲块头指针。
	struct super_block	s;			// 文件超级块结构。
	int		block = 256;			/* 开始于256盘块 */
	int		i = 1;
	int		nblocks;				// 文件系统盘块总数。
	char		*cp;				/* Move pointer */
    // 首先检查虚拟盘的有效性和完整性。如果ramdisk的长度为零,则退出。否则显示ramdisk
    // 的大小以及内存起始位置。如果此时根文件设备不是软盘设备,则也退出。	
	if (!rd_length)
		return;
	printk("Ram disk: %d bytes, starting at 0x%x\n", rd_length,
		(int) rd_start);
	if (MAJOR(ROOT_DEV) != 2)
		return;
	// 然后读根文件系统的基本参数。即读软盘块256+1、256和256+2。这里block+1是指磁盘上
    // 的超级块。breada() 用于读取指定的数据块,并标出还需要读的块,然后返回含有数据块的
    // 缓冲区指针。如果返回NULL,则表示数据块不可读(fs/buffer.c,322)。然后把缓冲区中
    // 的磁盘超级块(d_super_block是磁盘超级块结构)复制到s变量中,并释放缓冲区。 接着
    // 我们开始对超级块的有效性进行判断。 如果超级块中文件系统魔数不对,则说明加载的数据
    // 块不是MINIX文件系统,于是退出。有关MINIX超级块的结构请参见文件系统一章内容。	
	bh = breada(ROOT_DEV,block+1,block,block+2,-1);
	if (!bh) {
		printk("Disk error while looking for ramdisk!\n");
		return;
	}
	*((struct d_super_block *) &s) = *((struct d_super_block *) bh->b_data);
	brelse(bh);
	if (s.s_magic != SUPER_MAGIC)
		/* 磁盘中没有ramdisk映像文件,退出去执行通常的软盘引导 */
		return;
	// 然后我们试图把整个根文件系统读入到内存虚拟盘区中。对于一个文件系统来说,其超级块
    // 结构的 s_nzones 字段中保存着总逻辑块数(或称为区段数)。一个逻辑块中含有的数据块
    // 数则由字段s_log_zone_size指定。因此文件系统中的数据块总数nblocks 就等于 (逻辑块
    // 数 * 2^(每区段块数的次方)),即nblocks = (s_nzones * 2^s_log_zone_size)。如果遇到
    // 文件系统中数据块总数大于内存虚拟盘所能容纳的块数的情况,则不能执行加载操作,而只
    // 能显示出错信息并返回。	
	nblocks = s.s_nzones << s.s_log_zone_size;
	if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) {
		printk("Ram disk image too big!  (%d blocks, %d avail)\n", 
			nblocks, rd_length >> BLOCK_SIZE_BITS);
		return;
	}
	printk("Loading %d bytes into ram disk... 0000k", 
		nblocks << BLOCK_SIZE_BITS);
	cp = rd_start;
	while (nblocks) {
		if (nblocks > 2) 		// 若读取块数多于2块则采用超前预读。
			bh = breada(ROOT_DEV, block, block+1, block+2, -1);
		else					// 否则就单块读取。
			bh = bread(ROOT_DEV, block);
		if (!bh) {
			printk("I/O error on block %d, aborting load\n", 
				block);
			return;
		}
		(void) memcpy(cp, bh->b_data, BLOCK_SIZE);	// 复制到cp处。
		brelse(bh);
		printk("\010\010\010\010\010%4dk",i);		// 打印加载块计数值。
		cp += BLOCK_SIZE;							// 虚拟盘指针前移。
		block++;
		nblocks--;
		i++;
	}
	// 当boot盘中从256盘块开始的整个根文件系统加载完毕后,我们显示“done”,并把目前
    // 根文件设备号修改成虚拟盘的设备号0x0101,最后返回。
	printk("\010\010\010\010\010done \n");
	ROOT_DEV=0x0101;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/540198.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Conda 安装Pytorch

1、conda 切换虚拟环境 activate 虚拟环境名称 C:\Windows\System32>activate python310 温馨提示&#xff1a;查询虚拟环境列表 conda env list C:\Windows\System32>conda env list # conda environments: # base D:\anaconda3 python310 …

15-01通信安全

网络设备安全——防火墙 状态检测 应用级代理 防火墙实战 默认禁止&#xff1a;Ingress和Egress高安全域优先&#xff1a;允许高安全域发起请求规则具体&#xff1a;源地址&#xff08;标签&#xff09;、源端口&#xff0c;目标地址&#xff08;标签&#xff09;、目标端口、…

CSAPP复习(1)

分析方法只讲了amuda定理 Hello的一生 hello的执行过程 了解hello的执行过程 和响应的汇编器生成的可执行文件是.out不是.exe gcc -E hello.c -o hello.i//预处理 cpp hello.c>hello.i gcc -S hello.i -o hello.s//编译 gcc -C hello.s -o hello.o//汇编 二进制文件看不到…

【PDF软件篇】PDF轻量化电子笔记编辑利刃-Xodo软件优化

【PDF软件篇】PDF轻量化电子笔记编辑利刃-Xodo软件优化 默认配置已经够强&#xff0c;但是我还是推荐自定义&#xff0c;适合自己的就是最好的—【蘇小沐】 文章目录 【PDF软件篇】PDF轻量化电子笔记编辑利刃-Xodo软件优化1.实验环境 &#xff08;一&#xff09;日常办公导出无…

Redis使用lua脚本实现库存扣减

为什么使用Lua脚本为什么能合并多个原子操作&#xff1f; 这里参考官方文档地址&#xff1a;Scripting with Lua | Redis Redis 保证Lua脚本的原子执行。在执行脚本时&#xff0c;所有服务器活动在其整个运行期间都被阻止。这些语义意味着脚本的所有效果要么尚未发生&#xff…

矢量图形设计工具Affinity Designer 2.04版本在win10系统上的下载与安装配置教程

目录 前言一、Affinity Designer安装二、使用配置总结 前言 Affinity Designer 是一款由 Serif 公司开发的矢量图形设计工具&#xff0c;可用于创建各种类型的设计项目&#xff0c;例如图标、UI 设计、品牌标识、插图和其他类型的矢量图形。 Affinity Designer 工具的详细介绍…

JS解密入门案例:python有道JS解密,做一个简单的翻译程序

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 目录标题 前言本次使用知识点&#xff1a;开发环境:模块使用:思路流程:代码展示尾语 &#x1f49d; 本次使用知识点&#xff1a; 系统分析网页结构 动态数据抓包演示 json数据解析 JS解密 开发环境: Python 3.8 Pycha…

Java形参实参的使用

Java形参实参的使用 形参实参的介绍简单例子稍复杂例子String类 compareTo方法 形参实参的介绍 在JAVA中&#xff0c;形参和实参是非常常见的概念。形参是指在方法定义时声明的参数&#xff0c;用于描述方法需要接收的输入数据类型和变量名&#xff1b;而实参是指在方法调用时…

如何安装EasyRecovery14中文版数据恢复软件

好消息&#xff01;听说数据恢复软件EasyRecovery有了新版本&#xff0c;想必大家在听到这个消息的时候都非常激动&#xff0c;都想看看这个新版本的神秘面纱吧。别着急&#xff0c;下面小编就来教大家使用EasyRecovery14快速恢复数据。 首先我们需要在电脑上下载并安装Ontrac…

​数据库原理及应用上机(实验二 SQL数据定义功能实验)

✨作者&#xff1a;命运之光 ✨专栏&#xff1a;数据库原理及应用上机实验 目录 ✨一、实验目的和要求 ✨二、实验内容与步骤 ✨三、附加练习 ✨四、实验总结 &#x1f353;&#x1f353;前言&#xff1a; 数据库原理及应用上机实验报告的一个简单整理后期还会不断完善&am…

Docker创建Springboot项目镜像文件

Docker创建Springboot项目镜像文件 本文章展示的是以Windows10系统为例进行操作 一.首先在Windows系统安装WSL(Windows系统的Linux子系统) //该命令能够直接在Windows系统安装wsl wsl --install //如果已经安装则可以进行update wsl --update二.安装Docker Desktop在本机电…

C调用Java代码 图文详解

环境搭建 1. android studio2021.2.1 2. JDK版本1.8 一、创建一个android项目 File ——> New ——> New Project ——> Empty Activity 创建后如下图所示 二、C调用java代码过程 2.1 写java类代码 创建一个java的JNI类&#xff0c;写触发C代码&#xff0c;让C…

有意思的各类算法,思维题目分享

1.统计子矩阵 思路&#xff1a;二维前缀和超时&#xff0c;下面是前缀和加双指针&#xff0c;对列前缀和&#xff0c;两个玄幻控制行号&#xff0c;双指针控制列的移动 考查&#xff1a;前缀和双指针 import os import sys# 请在此输入您的代码 # 矩阵大小 N M n,m,kmap(int,…

某大学信息安全竞赛——shellcode1 绕过strlen检查,绕过沙箱检查,执行orw shellcode拿到flag

题目自取&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1HrMqh-lX-mkfueVeLzoEJg 提取码&#xff1a;oyel 介绍下这可恶的沙箱机制 这是一道非常让人蛋疼的题目&#xff0c;之前我只听说过沙箱&#xff0c;但是并没有自己实际接触过沙箱这个保护机制&#xff0c;大…

PX4从放弃到精通(二十八):垂起过渡控制

文章目录 前言一、主程序二、update_transition_state&#xff08;&#xff09;三、update_transition_state&#xff08;&#xff09; 前言 固件版本&#xff1a;1.14.0 可加名片交流学习 一、主程序 代码位置&#xff1a; 构造函数&#xff0c;用初始化列表进行初始化工作…

图可视分析

G6简介 AntVG6是一个基于WebGL的图形可视化引擎&#xff0c;它提供了一种简单、高效、灵活的方式来创建各种类型的图形&#xff0c;包括流程图、关系图、树形图、桑基图、饼图等等。它的主要思想是将图形分解为节点和边&#xff0c;并使用数据来描述它们之间的关系。 它的特性包…

BMPFont使用教程--免费的位图字体制作工具字体制作(2)

1、下载windows免费的位图字体制作工具Bitmap Font Generator 下载地址&#xff1a;BMFont - AngelCode.com 2、打开软件-> Edit -> Open Image Manager 3、点击Image -> Import Image,选择字符对应的图片&#xff0c;id就填写下面的48&#xff0c;代表0&#xff0c;…

Redis 主从复制 哨兵 集群

哨兵&#xff1a;在主从复制的基础上&#xff0c;哨兵实现了自动化的故障恢复。缺陷&#xff1a;写操作无法负载均衡&#xff1b;存储能力收到单机的限制&#xff1b; Cluster集群&#xff1a;通过集群&#xff0c;Redis解决了写操作无法负载均衡&#xff0c;以及存储能力收到…

应用现代化中的弹性伸缩

作者&#xff1a;马伟&#xff0c;青云科技容器顾问&#xff0c;云原生爱好者&#xff0c;目前专注于云原生技术&#xff0c;云原生领域技术栈涉及 Kubernetes、KubeSphere、KubeKey 等。 2019 年&#xff0c;我在给很多企业部署虚拟化&#xff0c;介绍虚拟网络和虚拟存储。 2…