Read the Hard Disk

Một phần của tài liệu The art of linux kernel design (Trang 144 - 149)

3.3.1 Preparing to Install the Hard Disk File System by Process 1

3.3.1.4 Read the Hard Disk

Execute the do_hd_request function to prepare for reading the hard disk, as shown in Figure 3.25.

First, analyze the members of the request item to get the head, sector, cylinder, and the number of sectors needed to operate. Then, establish the necessary hard drive read parameters and move the head to 0 cylinder. Then, send operation command(read/wrote) to the hard disk; now that the command is read, read boot block of the hard drive and call the hd_out function to send operation command to the hard disk. Take note of the last two real parameters: WIN_READ means read operation and read_intr is an interrupt ser- vice program corresponding to read operation, as shown in the third step in Figure 3.25.

The code is as follows:

//Code path:kernel/blk_dev/hd.c:

void do_hd_request(void) {

int i,r;

unsigned int block,dev;

unsigned int sec,head,cyl;

unsigned int nsect;

INIT_REQUEST;

dev = MINOR(CURRENT->dev);

block = CURRENT->sector;

if (dev > = 5*NR_HD || block+2 > hd[dev].nr_sects) { end_request(0);

goto repeat;

Kernel

ROM BIOS

and VGA Enable

interrupt

0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

Kernel code area Kernel data area

do_hd_request blk_dev[7]

Disk

Idle Using the structure of blk_dev,

find disk request processing function, prepare to send reading disk command Process status

Process 0 Process 1

Ready Interruptible

Current process

Figure 3.24 Bind the request item with the hard disk processing function.

Send read hard disk command as shown in the first step in Figure 3.26.

The specific code is as follows:

}

block + = hd[dev].start_sect;

dev/= 5;

__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));

sec++;

nsect = CURRENT->nr_sectors;

if (reset) {

reset = 0; //set, prevent mutiply execute if (reset) recalibrate = 1; //set, assure executing if(recalibrate) reset_hd(CURRENT_DEV); //send the command “WIN_SPECIFY” to hard

//disk by calling hd_out, and establish the //necessary parameter for reading hard disk return;

}

if (recalibrate) {

recalibrate = 0; //set, prevent mutiply execute if //(recalibrate)

hd_out(dev,hd_info[CURRENT_DEV].sect,0,0,0,

WIN_RESTORE,&recal_intr); //send the command “WIN_RESTORE” to //hard disk, and move the magnetic //head to cycle 0 for reading data //from hard disk

return;

}

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++) /* nothing */;

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); //Note two parameters!

} else

panic(“unknown hd-command”);

}

//Code path:kernel/blk_dev/hd.c:

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)) //the parameter is WIN_READ,&read_intr {

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; //determine it’s read_intr or

//write_intr according to the parameter, //in this case, it’s read_intr

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);

do_hd = intr_addr binds the reading disk service routine with the hard disk interrupt service routine; here the do_hd is the content of “xchgl _do_hd,%edx” in _hd_interrupt function (system_call.s).

In this case, the operation is read disk, so attach to the read_intr(), if in the case of write disk, attach to the write_intr() functions.

Do read disk order!

The hard disk reads the data of the boot block into its cache; at the same time, the pro- gram also returns and will call hd_out(), do_hd_request(), add_request(), make_request(), and ll_rw_block() in the opposite direction until it returns to the bread() function.

outb_p(0xA0|(drive<<4)|head,++port);

outb(cmd,++port);

}

//Code path:kernel/system_call.s:

_hd_interrupt:

……

1: jmp 1f 1: xorl%edx,%edx xchgl _do_hd,%edx testl%edx,%edx jne 1f

movl $_unexpected_hd_interrupt,%edx

……

ROM BIOS and VGA

Kernel

Enable interrupt

0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

Kernel code area Kernel data area

Disk

Process status

Process 0 Process 1

hd_interrupt read_intr

hd_info hd

Request[32]

Step 1:

analyzing data sent to hard disk port register Step 2:

detect the state of hard disk

Step 3:

hook up interrupt response program

Ready Interrupt wait state

Current process

Figure 3.25 Prepare for reading the hard disk.

Now, the hard disk continues to read the boot block; if the program continues to execute, it needs to manipulate the data in the boot block, but these data are not read from the hard drive, so call the wait_on_buffer function and suspend the process!

The execution code is as follows:

//code path:fs/buffer.c:

struct buffer_head * bread(int dev,int block) {

struct buffer_head * bh;

if (!(bh = getblk(dev,block)))

panic(“bread: getblk returned NULL\n”);

if (bh->b_uptodate) return bh;

ll_rw_block(READ,bh);

wait_on_buffer(bh); //suspend the process waiting //for unlock buffer

if (bh->b_uptodate) return bh;

brelse(bh);

return NULL;

}

Enable interrupt ROM BIOS

and VGA

0x00000

Kernel 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

Kernel code area Kernel data area

Bread ll_rw_block

Step 1: send disk the command to read

Step 2:

after sending the command, return to bread to run make_request add_request do_hd_request hd_out

Disk Process status

Process 0 Process 1

Ready Interruptible

Current process

Figure 3.26 Send parameters to hard drive port registers and suspend the current process.

After entering the wait_on_buffer, it checks whether the buffer block is locked or not.

If the buffer block is locked, it calls the sleep_on. The code is as follows:

Enter the sleep_on function and set process 1 to uninterruptible state, as shown in step 3 in Figure 3.27. Process 1 suspends then call the schedule function and be ready to switch processes. The execution code is as follows:

//code path:fs/buffer.c:

static inline void wait_on_buffer(struct buffer_head * bh) {

cli();

while (bh->b_lock) //has been lock before sleep_on(&bh->b_wait);

sti();

} Kernel

Disable interrupt

0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

Kernel code area Kernel data area

Schedule sleep_on wait_on_buffer

Disk

Process status

Process 0 Process 1

Uninterruptible Interruptible

Current process

Step 1: disable interrupt

Step 2: call sleep_on

Step 3: suspend process 1

ROM BIOS and VGA

Step 4:

execute schedule

Reading data continuously from disk

Figure 3.27 Process 1 suspends and executes schedule.

Một phần của tài liệu The art of linux kernel design (Trang 144 - 149)

Tải bản đầy đủ (PDF)

(524 trang)