14.8 读取文件
这是一个网站有所有小节的代码实现,同时也包含了Bochs等文件
14.8.1 实现file_read
//file.c
/*冲文件file中读取count个字节写入buf,返回读取的字节数,若到文件尾返回-1*/
int32_t file_read(struct file* file, void* buf, uint32_t count){
uint8_t* buf_dst = (uint8_t*)buf;
uint32_t size = count, size_left = size;
/*若要读取的字节数超过了文件可读的剩余量,就用剩余量作为待读取的字节数*/
if((file->fd_pos + count) > file->fd_inode->i_size){
size = file->fd_inode->i_size - file->fd_pos;
size_left = size;
if(size==0){//若到文件尾,则返回-1
return -1;
}
}
uint8_t* io_buf = sys_malloc(BLOCK_SIZE);
if(io_buf==NULL){
printk("file_read:sys_malloc for io_buf failed\n");
}
uint32_t* all_blocks = (uint32_t*)sys_malloc(BLOCK_SIZE+48); //用来记录文件所有的块地址
if(all_blocks == NULL){
printk("file_read: sys_malloc for all_blocks failed\n");
return -1;
}
uint32_t block_read_start_idx = file->fd_pos / BLOCK_SIZE; //数据所在块的起始地址
uint32_t block_read_end_idx = (file->fd_pos+size) / BLOCK_SIZE; //数据所在块的终止地址
uint32_t read_blocks = block_read_start_idx - block_read_end_idx; //如果增量为0,表示数据在同一扇区
ASSERT(block_read_start_idx < 139 && block_read_end_idx < 139);
int32_t indirect_block_table; //用来获取一级间接表地址
uint32_t block_idx; //获取待读的块地址
/*以下开始构建all_blocks块地址数组,专门存储用到的块地址(本程序中块大小通扇区大小)*/
if(read_blocks==0){ //在同一扇区内读取数据,不涉及到夸扇区读取
ASSERT(block_read_end_idx == block_read_start_idx);
if(block_read_end_idx<12){ //待读的数据在12个直接块内
block_idx = block_read_end_idx;
all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];
}else{ //若用到了一级间接块,需要将表中间接块读进来
indirect_block_table = file->fd_inode->i_sectors[12];
ide_read(cur_part->my_disk,indirect_block_table,all_blocks+12,1);
}
}else{ //若要读多个块
/*第一种情况:起始块和终止块属于直接块*/
if(block_read_end_idx<12){ //数据结束所爱的块属于直接块
block_idx = block_read_start_idx;
while(block_idx<block_read_end_idx){
all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];
block_idx++;
}
}else if (block_read_start_idx<12&&block_read_end_idx >= 12){
/*第二种情况:起始块属于直接块,终止块属于间接块*/
/*先将直接块地址写入all_blocks*/
block_idx = block_read_start_idx;
while(block_idx<12){
all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];
block_idx++;
}
ASSERT(file->fd_inode->i_sectors[12]!=0); //确保已经分配了一级间接块
/*在将间接块地址写入all_blocks*/
indirect_block_table = file->fd_inode->i_sectors[12];
ide_read(cur_part->my_disk,indirect_block_table,all_blocks+12,1); //将一级间接块表读进来写入到第 13 个块的位置之后
}else{
/*第三种情况:数据在间接块中*/
ASSERT(file->fd_inode->i_sectors[12]!=0); //确保已经分配了一级间接块
indirect_block_table = file->fd_inode->i_sectors[12];
ide_read(cur_part->my_disk,indirect_block_table,all_blocks+12,1); //将一级间接块表读进来写入到第 13 个块的位置之后
}
}
/*用到的块地址已经收集到 all_blocks 中,下面开始读数据*/
uint32_t sec_idx, sec_lba, sec_off_bytes, sec_left_bytes, chunk_size;
uint32_t bytes_read = 0;
while(bytes_read<size){ //直到读完为止
sec_idx = file->fd_pos / BLOCK_SIZE;
sec_lba = all_blocks[sec_idx];
sec_off_bytes = file->fd_pos % BLOCK_SIZE;
sec_left_bytes = BLOCK_SIZE - sec_off_bytes;
chunk_size = size_left < sec_left_bytes ? size_left : sec_left_bytes; //待读入的数据大小
memset(io_buf,0,BLOCK_SIZE);
ide_read(cur_part->my_disk,sec_lba,io_buf,1);
memcpy(buf_dst,io_buf+sec_off_bytes,chunk_size);
buf_dst += chunk_size;
file->fd_pos += chunk_size;
bytes_read += chunk_size;
size_left -= chunk_size;
}
sys_free(all_blocks);
sys_free(io_buf);
return bytes_read;
}
file_read
:file_read
接受3个参数,读取的文件file
、数据写入的缓冲区buf
、读取的字节数count
,功能是从文件file
中读取count
个字节写入buf
,返回读出的字节数。
流程:1.还是先判断这个读取的数据长度是否超过了文件的可读剩余量,若要读取的字节数超过了文件可读的剩余量,就用剩余量作为待读取的字节数。2.构建all_blocks数据结构,用来记录文件所有的块地址。3.开始读取块,也是要分三种情况。
核心原理:文件结构struct file
内有个fd_pos
表示当前操作的文件内容位置,其实就是要读取的内容在文件中的起始位置,比如1个1KB大小的文本文件,fd_pos = 500
,那么就是表示读取当前文件中偏移500字节的这个字符开始的内容。通过struct file
中的fd_pos
与传入函数的count
,我们可以确定要读取的内容相对于整个文件的字节偏移量。然后struct file
中有个指向操作文件inode
的指 针,通过这个inode
中的i_size
与i_sectors[ ]
,我们可以顺利知道文件存储位置信息。所以,自然就能知道要读取内容在磁盘中的位置。由于我们操作单位是块,所以对于起始位置,我们要抛弃这个块前面的无用数据,对于结束位置,我们要抛弃这个块后面的无用数据。
//fs.c
/*从文件描述符 fd 指向的文件中读取 count 个字节到 buf,若成功则返回读出的字节数,到文件尾则返回-1*/
int32_t sys_read(int32_t fd, void* buf, uint32_t count){
if(fd<0){
printk("sys_read:fd error\n");
return -1;
}
ASSERT(buf!=NULL);
uint32_t _fd = fd_local2global(fd);
return file_read(&file_table[_fd],buf,count);
}