操作系统真象还原:实现文件的读取

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_readfile_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_sizei_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);
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值