《ORANGE-s-一个操作系统实现》文件管理(7-1)

在本部分,将通过硬盘驱动程序直接操作硬盘。在编写这个文件系统的过程中,通过几个消息、用户进程、文件系统和驱动程序之间就可以方便的协同工作。

硬盘操作的I/O端口

和键盘控制器、VGA控制器等类似,对硬盘控制器的操作仍然是通过I/O端口来进行,这些端口分为两组,它们对应命令块寄存器和控制块寄存器,如下表:
mark
对硬盘的操作并不复杂,只需要往命令块寄存器写入正确的值,再通过控制块寄存器发送命令就可以了。

硬盘驱动程序

驱动程序的作用在于隐藏硬件细节,向上层提供统一的接口。由于进程通过收发消息来相互通信,那么驱动程序的接口自然也是消息了。所以只要定义了驱动程序可以接受的消息,也就定义了驱动程序的接口。首先想硬盘驱动器发送一个IDENTIFY命令,这个命令可用来获取硬盘参数。
下面是Device寄存器的格式:
mark
可以看到寄存器主要有LBA模式位、DRV位和低四位。DRV用于指定主盘或从盘,0表示主盘,1表示从盘。LBA模式位用于指定操作模式,当此位为0时,对磁盘的操作使用CHS模式;当为1时,对磁盘使用LBA模式。
我们使用函数hd_cmd_out()来进行实际的向硬件驱动器发送命令的工作,因为今后我们还会用到它。当发送命令之后,我们用interrupt_wait()来等待中断的发生。
驱动程序获知中断发生的情形如下:
mark
建立如下文件系统进程:

1
2
3
4
5
6
7
8
9
10
11
PUBLIC void task_fs()
{
printl("Task FS begins.\n");

/* open the device: hard disk */
MESSAGE driver_msg;
driver_msg.type = DEV_OPEN;
send_recv(BOTH, TASK_HD, &driver_msg);

spin("FS");
}

make运行,结果如下:
mark

文件系统

我们结合前面学习的FAT12来了解文件系统,从下图可知,文件系统有4个部分:

  • 引导扇区:存放Metadata—占用整整一个扇区的superblock
  • FAT表:记录扇区使用情况—sector map
  • 根目录区:文件的索引—inode map
  • 数据区:记录任一文件的信息—root数据区

mark
superblock通常也叫超级块,关于文件系统的Metadata都记录在这里。sector map是哟个位图,用来映射扇区的使用情况,用1表示扇区已经被使用。i-node是UNIX各种文件系统的核心数据结构之一。

硬盘分区表

硬盘分区表是一个结构体数组,数组的每个成员是一个16字节的结构体,如下:
mark
我们将80MB的硬盘映像分成了一个主分区和一个扩展分区,扩展分区中又分成了5个逻辑分区。我们以后把写的操作系统放在第一个逻辑分区。先把它的分区类型(Systen ID)改成99h,又维他设定了“可启动”标志。

设备号

下图是所有分区:
mark
其中数字1~4为主引导扇区中的分区表项所示,从5开始依次表示逻辑分区。
这些数字其实就是设备号,其作用是给每个设备起一个名字,这样驱动程序就能方便地管理他们。
对硬盘而言,采用如下的编号规则:
mark
下面是遍历所有分区的情况:
mark

完善硬盘驱动程序

目前的驱动程序只能处理DEV_OPEN消息,这显然是不够的,起码也要有DEV_READ和DEV_WRITE。下面先添加读写硬盘的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/*****************************************************************************
* task_hd
*****************************************************************************/
/**
* Main loop of HD driver.
*
*****************************************************************************/
PUBLIC void task_hd()
{
MESSAGE msg;

init_hd();

while (1) {
send_recv(RECEIVE, ANY, &msg);

int src = msg.source;

switch (msg.type) {
case DEV_OPEN:
hd_open(msg.DEVICE);
break;

case DEV_CLOSE:
hd_close(msg.DEVICE);
break;

case DEV_READ:
case DEV_WRITE:
hd_rdwt(&msg);
break;

case DEV_IOCTL:
hd_ioctl(&msg);
break;

default:
dump_msg("HD driver::unknown msg", &msg);
spin("FS::main_loop (invalid msg.type)");
break;
}

send_recv(SEND, src, &msg);
}
}

/*****************************************************************************
* init_hd
*****************************************************************************/
/**
* <Ring 1> Check hard drive, set IRQ handler, enable IRQ and initialize data
* structures.
*****************************************************************************/
PRIVATE void init_hd()
{
int i;

/* Get the number of drives from the BIOS data area */
u8 * pNrDrives = (u8*)(0x475);
printl("NrDrives:%d.\n", *pNrDrives);
assert(*pNrDrives);

put_irq_handler(AT_WINI_IRQ, hd_handler);
enable_irq(CASCADE_IRQ);
enable_irq(AT_WINI_IRQ);

for (i = 0; i < (sizeof(hd_info) / sizeof(hd_info[0])); i++)
memset(&hd_info[i], 0, sizeof(hd_info[0]));
hd_info[0].open_cnt = 0;
}

/*****************************************************************************
* hd_open
*****************************************************************************/
/**
* <Ring 1> This routine handles DEV_OPEN message. It identify the drive
* of the given device and read the partition table of the drive if it
* has not been read.
*
* @param device The device to be opened.
*****************************************************************************/
PRIVATE void hd_open(int device)
{
int drive = DRV_OF_DEV(device);
assert(drive == 0); /* only one drive */

hd_identify(drive);

if (hd_info[drive].open_cnt++ == 0) {
partition(drive * (NR_PART_PER_DRIVE + 1), P_PRIMARY);
print_hdinfo(&hd_info[drive]);
}
}

/*****************************************************************************
* hd_close
*****************************************************************************/
/**
* <Ring 1> This routine handles DEV_CLOSE message.
*
* @param device The device to be opened.
*****************************************************************************/
PRIVATE void hd_close(int device)
{
int drive = DRV_OF_DEV(device);
assert(drive == 0); /* only one drive */

hd_info[drive].open_cnt--;
}


/*****************************************************************************
* hd_rdwt
*****************************************************************************/
/**
* <Ring 1> This routine handles DEV_READ and DEV_WRITE message.
*
* @param p Message ptr.
*****************************************************************************/
PRIVATE void hd_rdwt(MESSAGE * p)
{
int drive = DRV_OF_DEV(p->DEVICE);

u64 pos = p->POSITION;
assert((pos >> SECTOR_SIZE_SHIFT) < (1 << 31));

/**
* We only allow to R/W from a SECTOR boundary:
*/
assert((pos & 0x1FF) == 0);

u32 sect_nr = (u32)(pos >> SECTOR_SIZE_SHIFT); /* pos / SECTOR_SIZE */
int logidx = (p->DEVICE - MINOR_hd1a) % NR_SUB_PER_DRIVE;
sect_nr += p->DEVICE < MAX_PRIM ?
hd_info[drive].primary[p->DEVICE].base :
hd_info[drive].logical[logidx].base;

struct hd_cmd cmd;
cmd.features = 0;
cmd.count = (p->CNT + SECTOR_SIZE - 1) / SECTOR_SIZE;
cmd.lba_low = sect_nr & 0xFF;
cmd.lba_mid = (sect_nr >> 8) & 0xFF;
cmd.lba_high = (sect_nr >> 16) & 0xFF;
cmd.device = MAKE_DEVICE_REG(1, drive, (sect_nr >> 24) & 0xF);
cmd.command = (p->type == DEV_READ) ? ATA_READ : ATA_WRITE;
hd_cmd_out(&cmd);

int bytes_left = p->CNT;
void * la = (void*)va2la(p->PROC_NR, p->BUF);

while (bytes_left) {
int bytes = min(SECTOR_SIZE, bytes_left);
if (p->type == DEV_READ) {
interrupt_wait();
port_read(REG_DATA, hdbuf, SECTOR_SIZE);
phys_copy(la, (void*)va2la(TASK_HD, hdbuf), bytes);
}
else {
if (!waitfor(STATUS_DRQ, STATUS_DRQ, HD_TIMEOUT))
panic("hd writing error.");

port_write(REG_DATA, la, bytes);
interrupt_wait();
}
bytes_left -= SECTOR_SIZE;
la += SECTOR_SIZE;
}
}


/*****************************************************************************
* hd_ioctl
*****************************************************************************/
/**
* <Ring 1> This routine handles the DEV_IOCTL message.
*
* @param p Ptr to the MESSAGE.
*****************************************************************************/
PRIVATE void hd_ioctl(MESSAGE * p)
{
int device = p->DEVICE;
int drive = DRV_OF_DEV(device);

struct hd_info * hdi = &hd_info[drive];

if (p->REQUEST == DIOCTL_GET_GEO) {
void * dst = va2la(p->PROC_NR, p->BUF);
void * src = va2la(TASK_HD,
device < MAX_PRIM ?
&hdi->primary[device] :
&hdi->logical[(device - MINOR_hd1a) %
NR_SUB_PER_DRIVE]);

phys_copy(dst, src, sizeof(struct part_info));
}
else {
assert(0);
}
}

您的支持将鼓励我继续创作!