Mr Dk.'s BlogMr Dk.'s Blog
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 🐧 Linux Kernel Comments
    • Chapter 2 - 微型计算机组成结构

      • Chapter 2 - 微型计算机组成结构
    • Chapter 3 - 内核编程语言和环境

      • Chapter 3 - 内核编程语言和环境
    • Chapter 4 - 80X86 保护模式及其编程

      • Chapter 4.1-4.2 - 80X86 系统寄存器和系统指令 & 保护模式内存管理
      • Chapter 4.3 - 分段机制
      • Chapter 4.4 - 分页机制
      • Chapter 4.5 - 保护
      • Chapter 4.6 - 中断和异常处理
      • Chapter 4.7 - 任务管理
      • Chapter 4.8 - 保护模式编程初始化
      • Chapter 4.9 - 一个简单的多任务内核实例
    • Chapter 5 - Linux 内核体系结构

      • Chapter 5.1-5.2 - Linux 内核模式 & 体系结构
      • Chapter 5.3 - Linux 内核对内存的管理和使用
      • Chapter 5.4-5.6 - 中断机制 & 系统调用 & 系统时间和定时
      • Chapter 5.7-5.9 - Linux 进程控制 & 堆栈使用 & 文件系统
    • Chapter 6 - 引导启动程序 (boot)

      • Chapter 6 - 引导启动程序 (boot)
    • Chapter 7 - 初始化程序 (init)

      • Chapter 7 - 初始化程序 (init)
    • Chapter 8 - 内核代码

      • Chapter 8.1 - 内核代码总体功能
      • Chapter 8.2 - asm.s 程序
      • Chapter 8.3 - traps.c 程序
      • Chapter 8.4 - sys_call.s 程序
      • Chapter 8.5 - mktime.c 程序
      • Chapter 8.6 - sched.c 程序
      • Chapter 8.7 - signal.c 程序
      • Chapter 8.8 - exit.c 程序
      • Chapter 8.9 - fork.c 程序
      • Chapter 8.10 - sys.c 程序
    • Chapter 9 - 块设备驱动程序

      • Chapter 9.1 - 块设备驱动程序 总体功能
      • Chapter 9.2 - blk.h 文件
      • Chapter 9.3 - hd.c 程序
      • Chapter 9.4 - ll_rw_blk.c 程序
      • Chapter 9.5 - ramdisk.c 程序
    • Chapter 10 - 字符设备驱动程序

      • Chapter 10.1 - 字符设备驱动程序 总体功能
      • Chapter 10.2 - keyboard.S 程序
      • Chapter 10.3 - console.c 程序
      • Chapter 10.4 - serial.c 程序
      • Chapter 10.5 - rs_io.s 程序
      • Chapter 10.6 - tty_io.c 程序
      • Chapter 10.7 - tty_ioctl.c 程序
    • Chapter 12 - 文件系统

      • Chapter 12.1 - 文件系统 总体功能
      • Chapter 12.2 - buffer.c 程序
      • Chapter 12.3 - bitmap.c 程序
      • Chapter 12.4 - truncate.c 程序
      • Chapter 12.5 - inode.c 程序
      • Chapter 12.6 - super.c 程序
      • Chapter 12.7 - namei.c 程序
      • Chapter 12.9 - block_dev.c 程序
      • Chapter 12.10 - file_dev.c 程序
      • Chapter 12.11 - pipe.c 程序
      • Chapter 12.12 - char_dev.c 程序
      • Chapter 12.13 - read_write.c 程序
      • Chapter 12.14 - open.c 程序
      • Chapter 12.15 - exec.c 程序
      • Chapter 12.16 - stat.c 程序
      • Chapter 12.17 - fcntl.c 程序
      • Chapter 12.18 - ioctl.c 程序
      • Chapter 12.19 - select.c 程序

Chapter 12.9 - block_dev.c 程序

Created by : Mr Dk.

2019 / 09 / 09 20:51

Nanjing, Jiangsu, China


接下来的部分是文件系统的数据访问操作,即系统调用 sys_write() 和 sys_read(),以及不同设备的低层支持函数:

  • 访问正规文件:file_write() / file_read()
  • 访问管道文件:pipe_write() / pipe_read()
  • 访问块设备文件:block_write() / block_read()
  • 访问字符设备文件:rw_char()

在系统调用中,根据参数提供的文件描述符的属性,判断出文件属于哪种类型,分别调用相应的处理函数,并进入对应的驱动程序中。

12.9 block_dev.c 程序

12.9.1 功能描述

block_read() 和 block_write() 两个函数分别用于读写块设备上的原始数据。由于块设备对磁盘的读写是以盘块为单位的,因此函数中,首先需要把参数中的文件指针映射为 数据块号 和 块内偏移,然后将对应的块读入缓冲区中,并从指定的块内偏移开始处理数据。若还有数据,则继续读取下一块,之后块内偏移总是为 0。原理如图所示:

12-27

12.9.2 代码注释

block_write() - 数据块写函数

将 buf 中长度为 count 的数据写到设备 dev 上 pos 开始的位置上。从内核的角度来说,只需要将数据写入缓冲区即可,由块设备驱动程序负责将缓冲区中的数据同步到块设备上。

int block_write(int dev, long * pos, char * buf, int count)
{
    int block = *pos >> BLOCK_SIZE_BITS; // pos 所在的数据块号
    int offset = *pos & (BLOCK_SIZE - 1); // 块内偏移
    int chars;
    int written = 0; // 累计写入字节数
    int size;
    struct buffer_head * bh;
    register char * p;
    
    // 设备允许的最大数据块总数
    if (blk_size[MAJOR(dev)])
        size = blk_size[MAJOR(dev)][MINOR(dev)];
    else
        size = 0x7fffffff;
    
    // 仍有字节需要被写
    while (count > 0) {
        // 要写的数据块不能超过设备容许的最大数据块数
        if (block >= size)
            return written ? written : -EIO;
        chars = BLOCK_SIZE - offset; // 本数据块可写入的字节数
        if (chars > count)
            // 只需要写 count 即可
            chars = count;
        if (chars == BLOCK_SIZE)
            // 恰好需要写一整块,直接申请
            bh = getblk(dev, block);
        else
            // 读入当前块,并预读下两块
            bh = breada(dev, block, block + 1, block + 2, -1);
        block++; // 下一块
        if (!bh)
            return written ? written : -EIO;
        p = bh->b_data + offset; // p 指向缓冲区中待写位置
        offset = 0; // 除了第一块,之后 offset 都为 0
        
        *pos += chars;
        written += chars;
        count -= chars;
        
        // 从用户空间写入缓冲区
        while (chars-- > 0)
            *(p++) = get_fs_byte(buf++);
        
        bh->b_dirt = 1;
        brelse(bh); // 释放缓冲区
    }
    
    return written; // 返回已写入字节数
}

block_read() - 数据块读函数

从设备 dev 的 pos 位置,将 count 字节的数据读入用户缓冲区 buf 中。

int block_read(int dev, unsigned long * pos, char * buf, int count)
{
    int block = *pos >> BLOCK_SIZE_BITS; // 数据块号
    int offset = *pos & (BLOCK_SIZE - 1); // 块内偏移
    int chars;
    int size;
    int read = 0; // 累积读入字节数
    struct buffer_head * bh;
    register char * p;
    
    if (blk_size[MAJOR(dev)])
        size = blk_size[MAJOR(dev)][MINOR(dev)];
    else
        size = 0x7fffffff;
    
    // 仍有字节需要读取
    while (count > 0) {
        if (block >= size)
            // 当前读入块号已经大于等于设备上的总块数
            return read ? read : -EIO;
        chars = BLOCK_SIZE - offset; // 本块中需要读取的字节数
        
        if (chars > count)
            // 要读取的字节数不满一块
            chars = count;
        if (!(bh = breada(dev, block, block + 1, block + 2, -1)))
            // 读入需要读取的数据块,并预读后两块
            return read ? read : -EIO;
        block++;
        
        p = bh->b_data + offset; // p 指向缓冲区读取位置开始处
        offset = 0; // 除第一块以外,之后块内偏移都为 0
        
        *pos += chars;
        read += chars;
        count -= chars;
        
        while (chars-- > 0)
            put_fs_byte(*(p++), buf++);
        
        brelse(bh);
    }
}
Edit this page on GitHub
Prev
Chapter 12.7 - namei.c 程序
Next
Chapter 12.10 - file_dev.c 程序