前言
磁盘既是输入设备又是输出设备。 输出设备(OutputDevice)是人与计算机交互的一种部件,用于数据的输出。 输入设备:向计算机输入数据和信息的设备。
所以,使用磁盘大致上与显示器和键盘一样
提示:以下是本篇文章正文内容
一、磁盘的介绍
磁盘由一个个盘片组成的磁盘立体结构,一个盘片上下两面都是可读写的
磁盘利用了电流的磁效应,对一些电信号进行磁化,保存在磁盘中,用来表示一些信息。
硬盘又划分为磁头(Heads)、柱面(Cylinder)、扇区(Sector) 磁头(Heads):每张盘面的正反两面各有一个磁头,一个磁头对应一张磁片的一个面。用第几磁头就可以表示数据在哪个磁面
柱面(Cylinder):所有盘面中半径相同的同心磁道构成“柱面”,这一系列的磁道垂直叠在一起,就形成一个柱面的形状
扇区(Sector):将磁道划分为若干个小的区段,就是扇区,每个扇区的容量为512字节,大小本质上是对磁盘数据的传输时间和磁盘的碎片浪费这2项参数的折中
硬盘容量=磁头数×柱面数×扇区数×512字节
结构概况:
二、生磁盘的使用
1.IO过程简介
磁盘I/O过程: 控制器–>寻道–>旋转–>传输
1.磁头移动到相应的磁道上 2.磁道开始旋转,转到相应的扇区 3.此时再转的时候就是磁生电,磁信号就变成电信号,然后就读取数据 4.读到内存的缓冲区,将这个内存缓冲区修改一个字节 5.然后继续里面再转,此时是电生磁,把字节写到磁道上
移动磁头,移动到对应的磁道上,然后转动磁道,移动到对应的扇区上,一边旋转一边进行磁生电,电生磁,和内存缓冲区进行数据的交互读和写
2.直接使用磁盘
只要往控制器中写柱面、 磁头、 扇区、 缓存位置 假如要往磁盘的某个扇区写一个字节,那么需要知道这个扇区对应的哪个柱面中的哪个磁头把这些参数传到磁盘控制器,磁盘控制器再根据这些参数进行驱动磁盘写数据
(1)磁盘读写的请求函数do_hd_request()
void do_hd_request(void)
{
.....
if (CURRENT->cmd == WRITE) {
hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
for(i=0 ; i<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
;
if (!r) {
bad_rw_intr();
goto repeat;
}
port_write(HD_DATA,CURRENT->buffer,256);
} else if (CURRENT->cmd == READ) {
hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
} else
panic("unknown hd-command");
}
(2)磁盘驱动的核心代码hd_out()
static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
unsigned int head,unsigned int cyl,unsigned int cmd,
void (*intr_addr)(void))
{
register int port asm("dx");
if (drive>1 || head>15)
panic("Trying to write bad sector");
if (!controller_ready())
panic("HD controller not ready");
do_hd = intr_addr;
outb_p(hd_info[drive].ctl,HD_CMD);
port=HD_DATA;
outb_p(hd_info[drive].wpcom>>2,++port);
outb_p(nsect,++port);
outb_p(sect,++port);
outb_p(cyl,++port);
outb_p(cyl>>8,++port);
outb_p(0xA0|(drive<<4)|head,++port);
outb(cmd,++port);
}
以上方法需要的传递的参数太多,不够方便
3.盘块号读写磁盘(第一层抽象)
将柱面、磁头、扇区包装成一个磁盘块 磁盘驱动负责从block计算出cyl, head, sec(CHS) 假设扇区的编址如下,(block相邻的盘块可以快速读出) 1号扇区在0号扇区旋转方向的下一扇区,假设一个盘面有六个扇区,则0-5扇区在第一个盘面,6号扇区在0号扇区竖直方向的下的扇区 计算公式:block = c * (heads * sectors) + h*heads * sectors + s
Sectors 是每个盘面的扇区数,Heads 是磁盘的磁头数量
扇区号 = 柱面号 × (一个柱面有多少扇区)+ 盘面号 ×(一个盘面有多少扇区)+ 扇区号
根据扇区号 sector 来算出 C、H、S
S = sector%Sectors H = sector/Sectors%Heads C = sector/Sectors/Heads
通过编址建立从 C、H、S 扇区地址到扇区号的一个映射,这就是文件系统第一层抽象的中心任务。扇区号连续的多个扇区就是一个磁盘块
磁盘的访问时间 磁盘的访问时间 = 写入控制器时间 + 寻道时间 + 旋转时间 + 传输时间 (其中主要是寻道时间, 旋转时间长,同时相邻的盘块应能快速读出)
通过磁盘号进行读写磁盘
static void make_request()
{
struct requset *req;
req=request+NR_REQUEST;
req->sector=bh->b_blocknr<<1;
add_request(major+blk_dev,req);
}
void do_hd_request(void)
{
unsigned int block=CURRENT->sector;
__asm__(“divl %4”:”=a”(block),”=d”(sec):”0”(block),
“1”(0),”r”(hd_info[dev].sect));
__asm__(“divl %4”:”=a”(cyl),”=d”(head):”0”(block),
“1”(0),”r”(hd_info[dev].head));
hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,...);
...
}
有了磁盘块,用户发出的磁盘读写请求就是盘块号 blocknr 了,由于磁盘块是连续的多个扇区,可以容易地算出扇区号,即:sector = blocknr × blocksize( blocksize 是描述磁盘块大小)
4.队列读写磁盘(第二层抽象)
操作系统中一般有多个进程,每个进程都会提出磁盘块访问请求,所以需要用队列来管理访问请求,这就是操作系统对磁盘管理的第二层抽象
多个磁盘访问请求出现在请求队列,需要对磁盘进行调度
调度目标:平均延迟小 调度算法:
(1)FCFS磁盘调度算法
最直观最公平的调度
(2)SSTF磁盘调度
在移动过程中把经过的请求处理(Shortest-seek-time First)
(3)SCAN磁盘调度
SSTF+中途不回折: 每个请求都有处理机会
(4)C-SCAN磁盘调度(电梯算法)
SCAN+直接移到另一端: 两端请求都能很快处理 这借鉴了生活中电梯的模型,电梯在运行的时候有上升和下降2种情况,当电梯上升时,本次上升的终点就是最高的请求楼层; 当电梯下降时,本次下降的终点就是最低的请求楼层
IN_ORDER()
#define IN_ORDER(s1,s2) \
((s1)->cmd<(s2)->cmd || ((s1)->cmd==(s2)->cmd && \
((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \
(s1)->sector < (s2)->sector))))
知道了IN_ORDER()的作用,可以分析一下电梯算法
static void add_request(struct blk_dev_struct * dev, struct request * req)
{
struct request * tmp;
req->next = NULL;
cli();
if (req->bh)
req->bh->b_dirt = 0;
if (!(tmp = dev->current_request)) {
dev->current_request = req;
sti();
(dev->request_fn)();
return;
}
for ( ; tmp->next ; tmp=tmp->next)
if ((IN_ORDER(tmp,req) ||
!IN_ORDER(tmp,tmp->next)) &&
IN_ORDER(req,tmp->next))
break;
req->next=tmp->next;
tmp->next=req;
sti();
}
总结
提示:这里对文章进行总结:
小结:生磁盘(raw disk)写过程
|