linux0.11 初始化磁盘

435 阅读4分钟

sys_setup

磁盘的初始化是由任务1的工作,任务0 fork一个子进程(任务1)后,子进程干的第一件事就是这个。 参数BIOS是磁盘信息的地址drive_info,这个方法只会被调用一次,所以方法中有个callable,linux0.11最多支持2个硬盘。设备号0x300表示的是第一个硬盘, 设备号0x305表示的是第二个硬盘。程序先把读取整个硬盘的扇区数信息,然后分别读取每个硬盘的0号块,读取里面的分区信息。然后把当前设备的高级块挂载到当前进程的root节点上。

逻辑设备号对应设备文件说明
0x300/dev/hd0代表整个第1个硬盘
0x301/dev/hd1表示第1个硬盘的第1个分区
0x302/dev/hd2表示第1个硬盘的第2个分区
0x303/dev/hd3表示第1个硬盘的第3个分区
0x304/dev/hd4表示第1个硬盘的第4个分区
0x305/dev/hd5代表整个第2个硬盘
0x306/dev/hd6表示第2个硬盘的第1个分区
0x307/dev/hd7表示第2个硬盘的第2个分区
0x308/dev/hd8表示第2个硬盘的第3个分区
0x309/dev/hd9表示第2个硬盘的第4个分区

对于块大小是1024字节的块设备,0号块是保留的,里面含有分区的信息。

kernel/blk_drv/hd.c

// 0表示整个硬盘信息,1表示第一个硬盘的第一个分区,2表示第一个硬盘的第二个分区,以此类推
static struct hd_struct {
	long start_sect;
	long nr_sects;
} hd[5*MAX_HD]={{0,0},}; 

int sys_setup(void * BIOS)
{
	static int callable = 1; 
	int i,drive;
	unsigned char cmos_disks;
	struct partition *p;
	struct buffer_head * bh;

	if (!callable)
		return -1;
	callable = 0;
#ifndef HD_TYPE
	for (drive=0 ; drive<2 ; drive++) {
		hd_info[drive].cyl = *(unsigned short *) BIOS;  //柱面数
		hd_info[drive].head = *(unsigned char *) (2+BIOS); // 磁头数
		hd_info[drive].wpcom = *(unsigned short *) (5+BIOS); //过时的技术,不管这个
		hd_info[drive].ctl = *(unsigned char *) (8+BIOS);
		hd_info[drive].lzone = *(unsigned short *) (12+BIOS);  // 硬盘上用于存放读/写磁头的安全非数据区域
		hd_info[drive].sect = *(unsigned char *) (14+BIOS);  // 每磁道扇区数
		BIOS += 16;
	}
    
    // 用来确定是否有第2块硬盘
	if (hd_info[1].cyl)
		NR_HD=2;
	else
		NR_HD=1;
#endif

    	// 获取每个硬盘扇区数
	for (i=0 ; i<NR_HD ; i++) {
		hd[i*5].start_sect = 0;   //每个硬盘的起始扇区
		hd[i*5].nr_sects = hd_info[i].head*hd_info[i].sect*hd_info[i].cyl; // 整个硬盘的扇区数
	}
	if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
		if (cmos_disks & 0x0f)
			NR_HD = 2;
		else
			NR_HD = 1;
	else
		NR_HD = 0;
	for (i = NR_HD ; i < 2 ; i++) {
		hd[i*5].start_sect = 0;
		hd[i*5].nr_sects = 0;
	}
    
    
        // 读取硬盘的第0,1两个扇区,并判断第一个扇区是不是以0x55aa结束
	for (drive=0 ; drive<NR_HD ; drive++) {
		if (!(bh = bread(0x300 + drive*5,0))) { //读取0号块
			printk("Unable to read partition table of drive %d\n\r",
				drive);
			panic("");
		}
      	
        	//并判断是不是以0x55aa结束
		if (bh->b_data[510] != 0x55 || (unsigned char)
		    bh->b_data[511] != 0xAA) {
			printk("Bad partition table on drive %d\n\r",drive);
			panic("");
		}
        
        	//根据引导块中的分区信息设置hd[]
            // 获取前面的446字节
		p = 0x1BE + (void *)bh->b_data;  
		for (i=1;i<5;i++,p++) {
			hd[i+5*drive].start_sect = p->start_sect;
			hd[i+5*drive].nr_sects = p->nr_sects;
		}
		brelse(bh);
	}
	if (NR_HD)
		printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
	rd_load(); //格式化虚拟盘
	mount_root();
	return (0);
}

写预补偿wpcomWrite Precompensation ,在1990年代初期以后,所有常用的硬盘类型都在实际的驱动器机箱中内置了特定于驱动器的控制器。其中包括所有IDE,SCSI,SATA和SAS硬盘驱动器类型。这些内部控制器知道他们需要了解的有关其特定驱动器的所有信息,包括在磁盘的哪些部分上需要哪种预补偿强度。因此,它们将忽略存储在计算机CMOS内存中的任何WPcom编号。直到1990年代后期,许多PC BIOS设置程序仍允许用户设置WPcom编号和其他驱动器参数,以便在需要时与较旧的硬盘类型结合使用;用户并不总是很清楚他的更现代的驱动器几乎肯定会忽略该设置。

从那时起,WPcom号甚至不再作为BIOS设置提供,因为它被认为是过时的技术。

软盘控制器仍然需要进行预补偿,但是由于PC上使用的通用软驱类型从来没有超过五到六种,而所有这些软盘驱动器都需要相同的预补偿,因此就不再需要有关BIOS设置的问题了。软盘驱动器上的预补偿

mount_root

加载根系统,将当前设备的超级块挂载到当前进程的root节点。

void mount_root(void)
{
	int i,free;
	struct super_block * p;
	struct m_inode * mi;

    	// 若磁盘i节点结构不是32字节,则出错停机。该判断用于防止修改代码时出现不一致情况。
	if (32 != sizeof (struct d_inode))
		panic("bad i-node size");
        
。
	// 初始化文件表
	for(i=0;i<NR_FILE;i++)
		file_table[i].f_count=0;  
        
        //软盘
	if (MAJOR(ROOT_DEV) == 2) {
		printk("Insert root floppy and press ENTER");   // 提示插入根文件系统盘
		wait_for_keypress();
	}
    
    	//每个硬盘最大4个分区,每个分区一个超级块,一个8个超级,先初始化超级快
	for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++) {
		p->s_dev = 0;
		p->s_lock = 0;
		p->s_wait = NULL;
	}
    
 
 	//读取设备超级块
	if (!(p=read_super(ROOT_DEV)))
		panic("Unable to mount root");
        
        //获取超级块inode信息,超级块inode位于1号块中
	if (!(mi=iget(ROOT_DEV,ROOT_INO)))
		panic("Unable to read root i-node");
        
        
	mi->i_count += 3 ;	//又被引用了3次
	p->s_isup = p->s_imount = mi;
	current->pwd = mi;
	current->root = mi;
   
   	//统计闲置块数
	free=0; 
	i=p->s_nzones; // 逻辑块数
	while (-- i >= 0)
		if (!set_bit(i&8191,p->s_zmap[i>>13]->b_data))
			free++;      
	printk("%d/%d free blocks\n\r",free,p->s_nzones);
    
    	// 统计闲置inode数
	free=0;
	i=p->s_ninodes+1;
	while (-- i >= 0)
		if (!set_bit(i&8191,p->s_imap[i>>13]->b_data))
			free++;
	printk("%d/%d free inodes\n\r",free,p->s_ninodes);
}

每个块是2个扇区,一个扇区512字节,那么一个块就是2 * 512 * 8 = 8192 bit,也就是块位图可以表达8192个块,因为块号是从0开始的,那么最后一个块就是8191号块。i>>13就是除以8192,set_bit(i&8191,p->s_zmap[i>>13]->b_data)就是返回i号块位置的位图值,1就是已经被使用,0就是没有使用。

参考:

en.wikipedia.org/wiki/Write_… www.computerhope.com/jargon/l/lz…