海思ubootsd卡协议

news2024/11/26 9:40:20

start_armboot()函数中调用mmc_initialize(0)初始化mmc;最终调用到int hi_mci_initialize(unsigned int dev_num)函数;内容如下:

static int hi_mci_initialize(unsigned int dev_num)
{
    struct mmc *mmc = NULL;
    static struct himci_host *host;
    unsigned int regval;
    unsigned long base_addr = 0;
    HIMCI_DEBUG_FUN("Function Call");

    /* enable SDIO clock and clock 50MHz 使能时钟且设置时钟为50M*/
    hi_mci_sys_init(dev_num);

    base_addr = SDIO0_BASE_REG; //SDIO基地址

    /* check controller version. 检查控制器协议*/
    regval = himci_readl(base_addr +  MCI_VERID);
    if ((regval != MCI_VERID_VALUE) && (regval != MCI_VERID_VALUE2)) {
        printf("MMC/SD/EMMC controller version incorrect.\n");
        return -ENODEV;
    }

    host = malloc(sizeof(struct himci_host)); //申请内存
    if (!host)
        return -ENOMEM;

    memset(host, 0, sizeof(struct himci_host));

    mmc = &host->mmc; //获得host中的mmc变量,下面准备填充mmc;
    mmc->priv = host;//私有指针放上级(父)对象;

    host->base = base_addr;//赋值基地址
    host->dma_des = hi_dma_des;
    host->dev_id = dev_num;//设备编号
    host->card_status = hi_mci_sys_card_detect(host);//探测卡状态
    host->port = 0;
#ifdef CONFIG_EMMC_SUPPORT
#ifdef CONFIG_EMMC_PORT
    host->port = CONFIG_EMMC_PORT;
#endif
#endif

    sprintf(mmc->name, DRIVER_NAME);
    mmc->send_cmd = hi_mci_request;//发送命令回调
    mmc->set_ios = hi_mci_set_ios;
    mmc->init = hi_mci_init;//初始化回调
    mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz
        | MMC_MODE_4BIT | MMC_MODE_8BIT;

    mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;//电压值范围

    mmc->f_min = MMC_CCLK_MIN;
    mmc->f_max = MMC_CCLK_MAX;
#ifdef CONFIG_HIMCI_V200
    mmc->is_init = 0;
#endif

    mmc_register(mmc);//注册mmc

    add_shutdown(himci_shutdown);//关机回调
    return 0;
}

重要的两个结构体struct mmc和struct himci_host

struct himci_host {
    struct mmc        mmc;
    unsigned long        base;//基地址
    unsigned int        card_status;//卡状态(插入/拔出)
    unsigned int        dev_id;//设备编号id
    unsigned int        port;
    int            cmd_id;
    struct mmc_cmd        *cmd;
    struct himci_dma_des    *dma_des;
};

mmc结构体其中几个变量对应下面sd卡协议中的寄存器;

struct mmc {
#ifdef CONFIG_HIMCI_V200
    int is_init;
#endif
    struct list_head link;
    char name[32];//名字
    void *priv;//私有指针
    uint voltages;//电压值
    uint version;//版本号
    uint f_min;
    uint f_max;
    int high_capacity;//最高容量
    uint bus_width;//总线宽度
    uint clock;//时钟
    uint card_caps;//卡容量
    uint host_caps;
    uint ocr; //操作条件寄存器; 32bit
    uint scr[2];//sd卡配置寄存器;有关sd存储卡特殊功能的信息;64bit
    uint csd[4];//卡特定数据;有关卡操作条件的信息; 128bit
    uint cid[4];//卡片识别号:用于识别的卡片个人编号; 128bit
    ushort rca;//相对卡地址;卡的本地系统地址,由卡动态建议并在初始化期间由主机批准;16bit
    uint tran_speed;//传输速度
    uint read_bl_len;
    uint write_bl_len;
    u64 capacity;
    block_dev_desc_t block_dev;
    int (*send_cmd)(struct mmc *mmc,
            struct mmc_cmd *cmd, struct mmc_data *data);
    void (*set_ios)(struct mmc *mmc);
    int (*init)(struct mmc *mmc);
};

OCR寄存器:

其中0~23为电压范围;

bit30位:卡容量状态位,当卡为大容量内存卡时,该位为1; 当卡为标准内存卡时,该位为0;卡容量状态位在卡上电过程完成且卡上电状态位设置为1后生效;

Bit31位:卡上电状态位,若上电状态完成,设置为1;

CID寄存器

卡标识寄存器,128bit; 包含了卡标识信息在卡识别阶段使用; 每个读写卡应该具有唯一的识别号;

对应结构体为struct mmc_cid;

struct mmc_cid {
    unsigned long psn; //产品序列号
    unsigned short oid;//一个2字符ASCII字符串,用于标识卡OEM和/或卡内容
    unsigned char mid;//制造商ID
    unsigned char prv;//产品修订版
    unsigned char mdt;//制造日期
    char pnm[7];//产品名称
};

CSD寄存器

卡特定数据寄存器提供有关访问卡内容的信息;定义了数据格式,纠错类型,最大数据访问时间,DSR寄存器寄存器的寄存器可编程部分可由CMD27更改;

TAAC

定义数据访问时间的异步部分;

NSAC

定义数据访问时间的时钟相关因素的最坏情况。NSAC的单位是100时钟周期。因此,数据访问时间的时钟相关部分的最大值是25.5k个时钟周期。

总访问时间NAC是TAAC和NSAC的总和。它应由主机根据实际时钟速率进行计算。读取访问时间应被解释为数据块或流的第一数据位的典型延迟。

TRAN_SPPED

定义了每一个数据行的最大传输速率;

CCC

SD存储卡命令集分为多个子集(命令类); CCC中的值为1bit表示支持相应的命令类;

READ_BL_LEN

最大读取数据长度计算为2的read_bl_len次方;

READ_BL_PARTIAL

SD存储卡中始终允许部分块读取;意味着可以使用最小的块,最小块为一个字节;

WRITE_BLK_MISALIGN/READ_BLK_MISALIGN

定义由一个命令写入/读的数据块是否可以分布在多个物理块上存储设备的块;内存块的大小在WRITE_BL_LEN/READ_BL_LEN中定义; =0表示跨越物理块边界无效; =1表示允许跨越物理块边界;

DSR_IMP

定义可配置驱动程序阶段是否集成在卡上。=0表未被实施 =1表示已实施;

C_SIZE

此参数用于计算用户的数据卡容量;

VDD_R_CURR_MIN, VDD_W_CURR_MIN

最小电源VDD处的读取和写入电流的最大值编码如下;

VDD_R_CURR_MAX, VDD_W_CURR_MAX

最大电源V DD处的读和写电流的最大值编码;

C_SIZE_MULT

此参数用于编码因子MULT,以计算总设备大小;

MULT = 2的(C_SIZE_MULT+2)次方;

ERASE_BLK_EN

定义要擦除的数据的单位大小的粒度,擦除操作可以擦除512字节的一个或多个单位SECTOR_SIZE;

如果ERASE_BLK_EN=0,主机可以擦除一个或多个SECTOR_SIZE单元。擦除将开始从包含起始地址的扇区的开头到包含结束地址;

如果ERASE_BLK_EN=1,主机可以擦除一个或多个512字节的单元。包含数据的所有块从起始地址到结束地址被擦除;

SECTOR_SIZE

可擦除扇区的大小;

WP_GRP_ENABLE

值为0表示不可能进行组写保护;

R2W_FACTOR

将典型块编程时间定义为读取访问时间的倍数;

WRITE_BL_LEN

最大写入数据块长度计算为2 的write_BL_LEN次方。最大块长度可能因此在512到2048字节的范围内。始终支持512字节的写入块长度。

WRITE_BL_PARTIAL

定义块写入命令中是否可以使用部分块大小。

WRITE_BL_PARTIAL=0表示在512字节单位的分辨率可用于面向块的数据写入。

WRITE_BL_PARTIAL=1表示也可以使用更小的块。最小块大小为1字节;

FILE_FORMAT_GRP

指示选定的文件格式组;

COPY

定义内容是原始(=0)还是已复制(=1);

PERM_WRITE_PROTECT

永久保护整个卡内容不被覆盖或擦除(所有写入和擦除此卡的命令被永久禁用)。默认值为0,即不永久写入受保护的。

TMP_WRITE_PROTECT

暂时保护整个卡内容不被覆盖或擦除(所有写入和擦除此卡的命令暂时禁用)。该位可以设置和重置。默认值为0,即不写保护。

FILE_FORMAT

指示卡上的文件格式;

CRC

CRC字段携带CSD内容的校验和;

uboot中对应结构体如下:

struct mmc_csd
{
    u8    csd_structure:2,
        spec_vers:4,
        rsvd1:2;
    u8    taac;
    u8    nsac;
    u8    tran_speed;
    u16    ccc:12,
        read_bl_len:4;
    u64    read_bl_partial:1,
        write_blk_misalign:1,
        read_blk_misalign:1,
        dsr_imp:1,
        rsvd2:2,
        c_size:12,
        vdd_r_curr_min:3,
        vdd_r_curr_max:3,
        vdd_w_curr_min:3,
        vdd_w_curr_max:3,
        c_size_mult:3,
        sector_size:5,
        erase_grp_size:5,
        wp_grp_size:5,
        wp_grp_enable:1,
        default_ecc:2,
        r2w_factor:3,
        write_bl_len:4,
        write_bl_partial:1,
        rsvd3:5;
    u8    file_format_grp:1,
        copy:1,
        perm_write_protect:1,
        tmp_write_protect:1,
        file_format:2,
        ecc:2;
    u8    crc:7;
    u8    one:1;
};

hi_mci_initialize()函数又调用了mmc_register(mmc);

int mmc_register(struct mmc *mmc)
{
#ifdef CONFIG_HIMCI_V200
    struct himci_host *host = mmc->priv;
#endif

    /* Setup the universal parts of the block interface just once */
    mmc->block_dev.if_type = IF_TYPE_MMC;
    mmc->block_dev.part_type = PART_TYPE_DOS;
#ifdef CONFIG_HIMCI_V200
    mmc->block_dev.dev = host->dev_id;
#else
    mmc->block_dev.dev = cur_dev_num++;
#endif
    mmc->block_dev.removable = 1;
    mmc->block_dev.block_read = mmc_mbread;
    mmc->block_dev.block_write = mmc_bwrite;

    INIT_LIST_HEAD (&mmc->link);

    list_add_tail (&mmc->link, &mmc_devices);//将mmc加入全局mmc设备链表

    return 0;
}

其中主要初始化block_dev结构体,结构体如下:

typedef struct block_dev_desc {
    int        if_type;    /* type of the interface 接口类型*/
    int        dev;        /* device number 设备号*/
    unsigned char    part_type;    /* partition type 分区类型*/
    unsigned char    target;        /* target SCSI ID 目标SCSI ID*/
    unsigned char    lun;        /* target LUN 目标LUN*/
    unsigned char    type;        /* device type 设备类型*/
    unsigned char    removable;    /* removable device 可移动的设备*/
#ifdef CONFIG_LBA48
    unsigned char    lba48;        /* device can use 48bit addr (ATA/ATAPI v7) 设备可以使用48bit地址*/
#endif
    lbaint_t        lba;        /* number of blocks 块数量*/
    unsigned long    blksz;        /* block size 块大小*/
    char        vendor [40+1];    /* IDE model, SCSI Vendor */
    char        product[20+1];    /* IDE Serial no, SCSI product IDE型号,SCSI供应商*/
    char        revision[8+1];    /* firmware revision 固件版本*/
    unsigned long    (*block_read)(int dev,
                      unsigned long start,
                      lbaint_t blkcnt,
                      void *buffer);
    unsigned long    (*block_write)(int dev,//设备号
                       unsigned long start,//块起始地址
                       lbaint_t blkcnt,//块数量
                       const void *buffer);
    void        *priv;        /* driver private struct pointer */
}block_dev_desc_t;

其中block_read和block_write回调函数用于块读/写;

两个回调函数都调用了mmc_send_cmd命令;

int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
    return mmc->send_cmd(mmc, cmd, data);
}

又回调了send_cmd函数, 该函数在最初的hi_mci_initialize函数中被赋值为hi_mci_request;

mmc_send_cmd调用示例:

int mmc_read_block(struct mmc *mmc, void *dst, uint blocknum)
{
    struct mmc_cmd cmd;
    struct mmc_data data;

    cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;

    if (mmc->high_capacity)
        cmd.cmdarg = blocknum;
    else
        cmd.cmdarg = blocknum * mmc->read_bl_len;

    cmd.resp_type = MMC_RSP_R1;
    cmd.flags = 0;

    data.dest = dst;
    data.blocks = 1;
    data.blocksize = mmc->read_bl_len;
    data.flags = MMC_DATA_READ;

    return mmc_send_cmd(mmc, &cmd, &data);
}

其中将cmd和data结构体填充后调用mmc_send_cmd(); 读buf地址被赋值到data.dest处返回;

hi_mci_request()函数中先调用hi_mci_setup_data()准备数据,如将目的地址赋值给dma对应地址等;填充好dma结构体; 然后调用hi_mci_idma_start()开启dma; 完成数据的读/写;

在函数hi_mci_initialize()中还初始化了初始化回调mmc->init = hi_mci_init;

然后在start_armboot中调用mmc_flash_init(0); 该函数最终调用了hi_mci_init();

hi_mci_init()调用了hi_mci_init_card();

static void hi_mci_init_card(struct himci_host *host)
{
    unsigned int tmp_reg;

    HIMCI_DEBUG_FUN("Function Call");
    HIMCI_ASSERT(host);

    hi_mci_sys_reset(host);

    /* card reset */
    himci_writel(~(1<<host->port), host->base + MCI_RESET_N);
    __udelay(CONFIG_MMC_RESET_LOW_TIMEOUT);

    /* card power off and power on */
    hi_mci_ctrl_power(host, POWER_OFF);
    __udelay(CONFIG_MMC_POWER_OFF_TIMEOUT * 1000);
    hi_mci_ctrl_power(host, POWER_ON);
    __udelay(CONFIG_MMC_POWER_ON_TIMEROUT * 1000);

    /* card reset cancel */
    himci_writel(1<<host->port, host->base + MCI_RESET_N);
    __udelay(CONFIG_MMC_RESET_HIGH_TIMEROUT);

    /* set drv/smpl phase shift 时钟相位*/
    tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT);
    tmp_reg &= ~(DRV_PHASE_MASK | SMPL_PHASE_MASK);
    tmp_reg |= DRV_PHASE_SHIFT | SMPL_PHASE_SHIFT;
    himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT);

    /* clear MMC host intr  MCI_RINTSTS:原始中断状态寄存器*/
    himci_writel(ALL_INT_CLR, host->base + MCI_RINTSTS);

    /* MASK MMC host intr  MCI_INTMASK:中断屏蔽寄存器*/
    tmp_reg = himci_readl(host->base + MCI_INTMASK);
    tmp_reg &= ~ALL_INT_MASK;
    himci_writel(tmp_reg, host->base + MCI_INTMASK);

    /* enable inner DMA mode and close intr of MMC host controler */
    tmp_reg = himci_readl(host->base + MCI_CTRL);
    tmp_reg &= ~INTR_EN; //全局中断使能 1:=使能
    tmp_reg |= USE_INTERNAL_DMA;//使用内置DMA搬移数据
    himci_writel(tmp_reg, host->base + MCI_CTRL);

    /* enable dma intr */
    tmp_reg = himci_readl(host->base + MCI_IDINTEN);
    tmp_reg &= ~MCI_IDINTEN_MASK;
    tmp_reg = TI | RI | NI;//使能发送接收中断
    himci_writel(tmp_reg, host->base + MCI_IDINTEN);

    /* set timeout param [31-8]:卡数据传输超时时间[7-0]:回复超时时间*/
    himci_writel(DATA_TIMEOUT | RESPONSE_TIMEOUT, host->base + MCI_TIMEOUT);

    /* set FIFO param */
    himci_writel(BURST_SIZE | RX_WMARK | TX_WMARK, host->base + MCI_FIFOTH);
}

该函数实现了 (a)复位mmc主机控制器; (b)卡复位 (c)卡掉电后上电 (d)取消卡复位 (e)设置时钟相位 (f)清空mmc主机中断寄存器 (g)设置中断掩码 (h)使能dma模式且关闭mmc主机控制器中断使能;(i)使能dma中断 (j)设置超时参数 (k)设置fifo参数;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/403096.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

磨皮插件portraiture2023最新中文版

Portraiture滤镜是一款 Photoshop&#xff0c;Lightroom 和 Aperture 插件&#xff0c;DobeLighttroom 的 Portraiture 消除了选择性掩蔽和逐像素处理的繁琐的手工劳动&#xff0c;以帮助您在肖像修整方面取得卓越的效果。它是一个强大的&#xff0c;但用户友好的插件照明.这是…

深度解析首个Layer3 链 Nautilus Chain,有何优势?

以流支付为主要概念的Zebec生态&#xff0c;正在推动流支付这种新兴的支付方式向更远的方向发展&#xff0c;该生态最初以Zebec Protocol的形态发展&#xff0c;并从初期的Solana进一步拓展至BNB Chian以及Near上。与此同时&#xff0c;Zebec生态也在积极的寻求从协议形态向公链…

观察UE4里“在外部存储Actor”功能的基础行为

目标 一般情况下&#xff0c;Actor保存于关卡文件中。 但是&#xff0c;如果将Actor的 packaging mode 设置为 External&#xff1a; 则此Actor就会存储在另一个文件而非关卡文件中。 本篇目标是&#xff1a; 观察此功能的基础行为观察外部文件的路径名规则 “在外部存储A…

Nacos安装指南,Windows安装

Nacos安装指南 1.Windows安装 1.1.下载安装包 在Nacos的GitHub页面&#xff0c;提供有下载链接&#xff0c;可以下载编译好的Nacos服务端或者源代码&#xff1a; GitHub主页&#xff1a;https://github.com/alibaba/nacos GitHub的Release下载页&#xff1a;https://githu…

一文打通Sleuth+Zipkin 服务链路追踪

1、为什么用 微服务架构是一个分布式架构&#xff0c;它按业务划分服务单元&#xff0c;一个分布式系统往往有很多个服务单元。由于服务单元数量众多&#xff0c;业务的复杂性&#xff0c;如果出现了错误和异常&#xff0c;很难去定位。主要体现在&#xff0c;一个请求可能需要…

学习服务器上运行论文代码(二)

文章目录程序运行异常VScode 报shell集成无法激活的问题无法加载文件 C:\Users\haoqi\Documents\WindowsPowerShell\profile.ps1&#xff0c;连接服务器插件程序运行异常 书接上回。 程序能跑起来&#xff0c;但是会出现两种异常。 异常一&#xff1a;运行进度条在走&#xff…

Lambda表达式和steram流

目录 引言&#xff1a; 语法: Lambda 表达式实例&#xff1a; demo演示&#xff1a; Stream流&#xff1a; 引言&#xff1a; Lambda 表达式&#xff0c;也可称为闭包&#xff0c;它是推动 Java 8 发布的最重要新特性。 Lambda 允许把函数作为一个方法的参数&#xff08;函…

Redis技术分享——缓存常见应用场景问题?

什么是redis&#xff1f; Redis是Remote Dictionary Server的简称&#xff0c;是一个由意大利人Salvatore Sanfilippo开发的key-value存储系统&#xff0c;具有极高的读写性能&#xff0c;读的速度可达110000次/s&#xff0c;写的速度可达81000次/s 。今天主要是分享redis的缓…

Excel查找函数(VLOOKUP\SEARCH\FIND\MATCH)

文章目录查找纵向VLOOKUP函数eg1已知身份证查姓名eg2已知身份证查地区Find函数FindB函数SEARCH函数SEARCHBMATCH函数查找 纵向VLOOKUP函数 VLOOKUP(lookup_value,table_array,col_index_num,[range_lookup])lookup_value&#xff1a;要查找的值 table_array&#xff1a;要查找…

【测试】性能测试

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录目标一、性能测试概述二、常见的性能测试指标三、性能测试的分类四、loadrunner工具介绍&#xff08;LR&#xff09;一&#xff09;VUG&#xff1a;虚拟用户发生器二&#xff09;Controller三&#xff09;Analysis小…

vector的使用及模拟实现

目录 一.vector的介绍及使用 1.vector的介绍 2.vector的使用 1.vector的定义 2.vector iterator的使用 3. vector 空间增长问题 4.vector 增删查改 3.vector 迭代器失效问题&#xff08;重点&#xff09; 1. 会引起其底层空间改变的操作 2.指定位置元素的删除操作--erase 3. Li…

python 从0到批量下载某站视频

简介&#xff1a;真实从0到1&#xff0c;童叟无欺&#xff5e; 目标&#xff1a;用python批量下载某站搜索视频&#xff0c;以“CG 服装”为例 本章主要介绍如何用python把搜索到的视频直接下载到自己的本地文件夹中&#xff5e; 介绍一下工作流1. 下载并安装python2. 测试pyt…

Ansible自动运维————实验

0、创建新的虚拟机&#xff0c;使用镜像RHEL8.5,要求/boot目录为512N&#xff0c;/home目录为1Gb&#xff0c;交换空间为2Gb&#xff0c;其他空间给&#xff09;目录。1、将该主机作为 Ansible 控制节点&#xff0c;克隆该虚拟机&#xff0c;将克隆的虚拟机作为受控节点&#x…

Koa源码学习

前言 koa是一个非常流行的Node.js http框架。本文我们来学习下它的使用和相关源码 来自官网的介绍&#xff1a; Koa 是一个新的 web 框架&#xff0c;由 Express 幕后的原班人马打造&#xff0c; 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。…

【数据分析】Excel必备函数汇总

文章目录求和单条件求和SUMIF多条件求和SUMIFS求平均AVERAGE单条件多条件查找纵向VLOOKUP函数eg1已知身份证查姓名eg2已知身份证查地区Find函数FindB函数SEARCH函数SEARCHBMATCH函数Subtotal函数最值小数点去小数点TRUNC四舍五入ROUND随机数求余奇偶统计条件多条件时间当前日期…

LearnOpenGL-光照-5.投光物

本人刚学OpenGL不久且自学&#xff0c;文中定有代码、术语等错误&#xff0c;欢迎指正 我写的项目地址&#xff1a;https://github.com/liujianjie/LearnOpenGLProject 文章目录投光物平行光点光源聚光不平滑的例子平滑例子投光物 前面几节使用的光照都来自于空间中的一个点 即…

IR 808 Alkyne,IR-808 alkyne,IR 808炔烃,近红外吲哚类花菁染料

【产品理化指标】&#xff1a;中文名&#xff1a;IR-808炔烃英文名&#xff1a;IR-808 alkyne&#xff0c;Alkyne 808-IR CAS号&#xff1a;N/AIR-808结构式&#xff1a;规格包装&#xff1a;10mg&#xff0c;25mg&#xff0c;50mg&#xff0c;接受各种复杂PEGS定制服务&#x…

Git的下载、安装、配置、使用、卸载

前言 我是跟着狂神老师学的。该博客仅用于笔记所用。 下面是老师的B站和笔记 B站&#xff1a;https://www.bilibili.com/video/BV1FE411P7B3?p1&vd_source9266cf72b1f398b63abe0aefe358d7d6 笔记&#xff1a;https://mp.weixin.qq.com/s/Bf7uVhGiu47uOELjmC5uXQ 一、准备工…

【18】组合逻辑 - VL18 实现3-8译码器①

VL18 实现3-8译码器① 1 题目 【这题我的思路非常绝境】奈斯 !! 看真值表的思路:Yi所在列【0仅一个其余全1】,故【以0为对象求解】 观察发现:E3 E2_n E1_n = 100 时 是 译码的使能信号 ; 并且E3 E2_n E1_n为其他值时,都不使能译码 然后就很简单,没有仿真就成功了 2 代…

Linux:文件流指针 与 文件描述符

目录一、文件描述符二、文件流指针三、缓冲区之前讲解过了IO库函数和IO接口&#xff0c;库函数是对系统调用接口的封装&#xff0c;也就是说实际上在库函数内部是通过调用系统调用接口来完成最终功能的。 库函数通过文件流指针操作文件&#xff0c;系统调用接口通过文件描述符操…