zoukankan      html  css  js  c++  java
  • 学习笔记10

    一、块设备IO

    1.块设备基本概念
    块设备将信息存储在固定大小的块中,每个块都有自己的地址。对操作系统而言,块设备是以字符设备的外观展现的,例如/dev/sda,虽然对这种字符设备可以按照字节为单位访问,但是实际上到块设备上却是以块为单位(最小512byte,即一个扇区),这之间的转换是由操作系统来实现的。
    下面介绍几个块设备的基本概念:
    1)扇区:磁盘盘片上的扇形区域,逻辑化数据,方便管理磁盘空间,是硬件设备数据传送的基本单位,一般512Byte;
    2)块:块是VFS和文件系统数据传送的基本单位,必须是扇区的整数倍,格式化文件系统时,可以指定块大小(一般512,1024,2048,4096字节);
    3)段:一个内存页或者内存页中的一部分,包含一些相邻磁盘扇区中的数据;磁盘的每个I/O操作就是在磁盘与一些RAM单元之间相互传一些相邻扇区的内容,大多数情况下,磁盘控制器采用DMA方式进行数据传送。如果不同的段在RAM中相应的页框是连续的并且在磁盘上相应的数据块也是相邻的,就可以在通用块层合并它们,产生更大的内存区域,这个区域称为物理段。
    通常情况下,我们是通过文件系统来访问块设备,也可以直接使用裸设备,通过指定偏移和大小来读写裸设备。
    常见的块存储设备就是物理磁盘,在Linux系统下,还提供基于其他块设备之上的逻辑设备,如Device Mapper,软RAID等。

    2.块设备I/O栈
    2.1基本概念
    介绍块设备的I/O栈之前,我们先来了解一下块I/O栈的几个基本概念。
    1)bio:bio是通用块层I/O请求的数据结构,表示上层提交的I/O请求,一个bio包含多个page,这些page必须对应磁盘上一段连续的空间。由于文件在磁盘上并不连续存放,文件I/O提交到块设备之前,极有可能被拆成多个bio结构;
    2)request:表示块设备驱动层I/O请求,经由I/O调度层转换后的I/O请求,将会发到块设备驱动层进行处理;
    3)request_queue: 维护块设备驱动层I/O请求的队列,所有的request都插入到该队列,每个磁盘设备都只有一个queue(多个分区也只有一个);
    这3个结构的关系如下图示:一个request_queue中包含多个request,每个request可能包含多个bio,请求的合并就是根据各种原则将多个bio加入到同一个requesst中。

    二、I/O缓冲区

    一、缓冲区的概念和作用
    缓冲区是一个存储区域,可以由专门的硬件寄存器组成,也可利用内存作为缓冲区。

    使用硬件作为缓冲区的成本较高,容量也较小,一般仅用在对速度要求非常高的场合(如存储器管理中所用的相联寄存器,由于对页表的访问频率极高,因此使用速度很快的相联寄存器来存放页表项的副本)
    一般情况下,更多的是利用内存作为缓冲区,“设备独立性软件”的缓冲区管理就是要组织管理好这些缓冲区
    缓冲区的作用如下:

    • 缓和 CPU 与 I/O 设备之间速度不匹配的矛盾
    • 减少对 CPU 的中断频率,放宽对 CPU 中断相应时间的限制
    • 解决数据粒度不匹配的问题(如:输出进程每次可以生成一块数据,但 I/O 设备每次只能输出一个字符)
    • 提高 CPU 与 I/O 设备之间的并行性

    三、缓冲区管理算法

    1.实现菜单显示及相应处理
    显示菜单实现函数:

    void show_menu()
    {
        printf("------ 1. Linux   ------\n");
        printf("------ 2. Windows ------\n");
        printf("------ 3. Mac OS  ------\n");
        printf("please input:[1 - 3]: ");
    }
    

    main函数:

    int main(void)
    {
        int n;
        int quit = 0;
    
        while (!quit)
        {
            show_menu();
            scanf("%d", &n);        
            switch(n)
            {
                case 1:
                    printf("\nos: Linux\n\n");
                    break;
                case 2:
                    printf("\nos: Windows\n\n");
                    break;
                case 3:
                    printf("\nos: Mac OS\n\n");
                    break;
                case -1:
                    quit = 1;
                    break;
                default:                
                    break;
            }       
        }   
    
        return 0;
    }
    

    如上程序若键入数字可以正确运行,但是键入非数字的字符,程序便陷入循环打印中。
    scanf()和getchar()函数都是从标准输入中去获取数据,前者获取成功返回获取到的数据个数,失败返回0;后者获取一个字符,获取成功返回该字符,失败返回-1。程序之所以会陷入循环打印中,是因为标准输入缓冲区的缘故:scanf函数会先从输入缓存区获取数据,假设输入缓存区数据为空就会去终端陷入阻塞获取,反之就去读缓存区上的数据。在这它要获取的是int型的数据,假设程序使用者键入的是非数字的字符,那么scanf获取数据失败,缓存区上的非数字数据会一直遗留(若是数字数据会被读取,缓存区就不存在该数据),程序会往下执行。下次调用scanf时候,同理会先去判断缓冲区上的数据,缓冲区有数据,所以不会使得终端阻塞等待输入,但该数据又不是int型数据,依次获取失败,程序又会接下往下执行…因此便陷入循环了。解决办法就是不管scanf()函数获取成功与否,都把输入缓存区的数据清空。

    在Linux启动时候会启动bash进程,该进程会默认打开着3个设备文件。由于Linux上最小的文件描述符是从0开始,且打开文件时 返回最小可用文件描述符,所以它们的文件描述符分别为0、1、2。
    既然知道了文件的描述符,那么自然可以调用系统调用open、write来读写文件了:

    int main(void)
    {
        int ret;
        int i;
        char buf[1024] = {};
    
        //往标准输出缓冲区写入数据
        write(1, "hello\n", 6);  //在终端打印"hello\n"
    
        //读取标准输入缓存区的数据
        ret = read(0, buf, 1024);   //阻塞获取终端输入
        for (i = 0; i < 1024; i++)
            printf("%c", buf[i]);
    
        return 0;
    }
    

    三、代码练习

    https://gitee.com/zhang_yu_peng/practice-code/blob/master/代码练习.cpp

  • 相关阅读:
    图像特征工程
    神经网络在多分类上的应用——数据预处理
    Robotics Lab3 ——图像特征匹配、跟踪与相机运动估计
    Robotics Lab2——相机模型,点云图拼接与深度测量
    Robotics Lab1 —— 基于颜色特征的目标识别与追踪实验
    【Ubuntu16.04】解决Qt安装包(.run文件)不能用./命令执行的问题
    【ROS系统】创建消息(msg)后使用rosmsg命令报错的解决办法
    【ROS系统】执行roslaunch命令启动launch文件提示Invalid roslaunch XML syntax错误的解决办法
    【ROS系统】解决找不到用户工作空间下的程序包的问题——E:No such package
    【UEFI+GPT/BIOS+MBR】两种模式在Windows系统下安装Ubuntu系统
  • 原文地址:https://www.cnblogs.com/1208499954qzone/p/15585166.html
Copyright © 2011-2022 走看看