文章目录

  • 标准C库IO函数工作流程
  • 标准C库函数与Linux系统函数之间的关系
  • 虚拟地址空间
  • 系统函数IO
    • open
    • read 与 write
    • lseek

标准C库IO函数工作流程


fopen 打开文件,返回类型为 FILE *,其是一个结构体,包含

  • 文件描述符(整型值)——索引到对应的磁盘文件
  • 文件读写指针位置——读写文件过程中指针的实际位置
  • I/O缓冲区(内存地址)——通过寻址到对应的内存块

读写文件不是直接写入或读磁盘,而是先写入内存中的缓冲区,待缓冲区满了才放入磁盘中。

IO缓冲区的作用?

  • 大部分硬盘都是机械硬盘,读取寻道时间和写入寻道时间都是在毫秒级ms;
  • 相对来说,内存读写速度都非常块,因为内存属于电子设备,读写速度是纳秒级ns;
  • 两者之间的读写速度相差一百万倍;

而将缓冲区的数据刷新到磁盘有三种方式

  • fflush 刷新缓冲区
  • 缓冲区满了
  • 正常关闭文件,包括 fclosereturn(main函数)exit (main函数)

标准C库函数与Linux系统函数之间的关系


C标准函数调用Linux系统的API,然后调用内核层设备驱动函数通过设备驱动操作硬件。


虚拟地址空间


内核区存在PCB进程控制块,其包含文件描述符表,0~2是默认打开的,之后每打开一个新文件,则占用一个文件描述符,而且使用的是空间的最小的一个文件描述符。

  • 环境变量(env)——存放进程需要的环境变量
  • 命令行参数——由 char* argv[] 获取
  • 栈空间——存放局部变量等,地址空间使用是从上往下的
  • 共享库——存放C标准库,Linux系统IO函数,动态库最后也是加载到共享库中
  • 堆空间——malloc 申请的空间就存放在这
  • .bss(未初始化全局变量)—— 为0的全局变量就是未被初始化的全局变量
  • .data(已初始化全局变量)
  • .text(代码段,二进制机器指令)
  • 受保护的地址空间(NULL 就被定为这里的0地址)

当然虚拟地址空间是4G并不代表程序实际使用了4G的内存空间,虚拟地址空间会映射到实际的内存空间中。
虚拟内存的存在主要有三个作用

  1. 方便进程使用不连续的大内存区
  2. 不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程使用的物理内存
  3. 程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。

系统函数IO

open

  • 使用 open 打开未存在的文件。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main()
{int fd;// 打开不存在的文件fd = open("bucunz", O_RDWR);if(fd == -1){perror("open file");exit(1);}// 关闭文件int ret = close(fd);printf("ret = %d\n", ret);if(ret == -1){perror("close file");exit(1);}
}

这里 open 的第一个参数表示要打开的文件,O_RDWR 表示可读可写,其他还有 O_RDONLY 只读打开以及 O_WRONLY 只写打开,其返回文件描述符,如果出现错误则返回 -1,这里使用 perror 在错误输出之前输出里面的内容。关闭文件使用 close 其参数为文件描述符,如果产生错误返回 -1,成功则返回 0

  • 创建新文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main()
{int fd;// 创建新文件fd = open("myhello", O_RDWR | O_CREAT, 0777);if(fd == -1){perror("open file");exit(1);}printf("fd = %d\n", fd);// 关闭文件int ret = close(fd);printf("ret = %d\n", ret);if(ret == -1){perror("close file");exit(1);}
}

这里通过在 open 中添加参数 O_CREAT 以在没有该文件时生成新文件。

可以看到其 fd 为 3,前面 0~2 为默认打开的,所以从 3 开始分配。

我们之前设置最后参数为 0777 表示赋予文件所有用户所有权限,但我们看到该文件的其他用户并没有写权限,这是因为Linux在赋予文件全选时需要减去一个掩码,可以通过 umask 显示,这里为0002,其取反与赋予的权限通过二进制与操纵产生最终的文件权限。

  • 创建新文件,如果文件已存在则出错返回
    可以在 open 参数添加 O_EXCL实现,即fd = open("myhello", O_RDWR | O_CREAT | O_EXCL, 0777);

  • 如果文件存在,将长度截断为0字节
    可以在 open 参数添加 O_TRUNC 实现,即fd = open("myhello", O_RDWR | O_TRUNC);

read 与 write

read 的函数原型如下:

其返回值有三种可能:

  1. 返回一个大于0的数,表示读了多少字节的数据
  2. 返回一个 0,表示文件已经读完了
  3. 返回一个 -1,表示读文件出错了

write 的函数原型如下:

如果写成功,返回写入的字节数,如果出错则返回 -1。

readwrite 函数均需要指定读取或写入文件的文件描述符,缓冲区,以及读取和写入的字节数。

下面使用 openreadwrite 实现从一个文件读取数据放入另一个文件中:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>int main(void)
{int fd = open("test1.txt", O_RDONLY);if(fd == -1){perror("open file1");exit(1);}int fd2 = open("test2.txt", O_WRONLY | O_CREAT, 0777);if(fd2 == -1){perror("open file2");exit(1);}int size;char buffer[8]; // 缓冲区while((size=read(fd, buffer, 8)) != 0){if(size == -1){perror("read file1");exit(1);}int ret = write(fd2, buffer, size);if(ret == -1){perror("write file2");exit(1);}printf("%d\n", ret);}if(close(fd) == -1){perror("close file1");exit(1);}if(close(fd2) == -1){perror("close file2");exit(1);}return 0;
}


可以看到 test1.txt 一共50个字节,前六次每次读取缓冲区大小的字节数(8),最后一次读取最后2个字节。

lseek

lseek 函数原型如下:


lseek 重新定位文件偏移,offset 表示偏移量,whence 表示偏移位置,即从哪开始偏移。

whence 共有三种选项:

  1. SEEK_SET 文件偏移被设为 offset,即从文件头开始偏移 offset 字节
  2. SEEK_CUR 文件偏移被设为文件指针当前位置+offset偏移字节数,即从当前位置开始偏移offset字节
  3. SEEK_END 文件偏移被设为文件的大小+offset偏移字节数,即从文件尾开始偏移offset字节

这里需要说明的是,当从文件尾开始偏移offset字节时,这时并不会改变文件大小,如果此时在该位置进行写入,那么中间部分会变成空洞,进行占位,除非数据被写入这些空洞,否则返回 \0。空洞的作用有一点就是当我们下载文件时,用空洞进行占位可以需要的空间保留下来。

如果成功返回从文件头开始的偏移字节数,如果产生错误,返回 -1。

#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<stdio.h>int main(void)
{int fd = open("./test1.txt",O_RDWR);if( fd == -1){perror("open test1.txt:");exit(1);}int ret = lseek(fd,0,SEEK_END);printf("file length = %d\n",ret);// 文件扩展ret = lseek(fd,2000,SEEK_END);printf("return value = %d\n",ret);// 实现文件拓展,需要最后一次写操作write(fd,"a",1);close(fd);return 0;
}


可以看到 test1.txt 被扩展为 2051 字节了,且中间有很多空洞 ^@

Linux学习笔记——系统函数IO相关推荐

  1. linux学习笔记 -- 系统编程

    系统编程 相关概念 概念 简易cpu结构 mmu内存管理单元 环境变量 PATH SHELL HOME LANG TERM getenv setenv unsetenv 进程控制 fork函数 get ...

  2. 【Linux学习笔记④】——Shell程序设计【变量 输入与输出 条件表达式 判断语句 循环语句 Shell函数】

    ⌛️ 文章目录 一.Shell 概述 二.Shell 脚本的定义与执行 2.1 Shell 脚本的定义 2.2 Shell 脚本的执行 三.Shell 变量 3.1 用户自定义变量 3.2 环境变量 ...

  3. 嵌入式系统及应用Linux学习笔记(一)——常用命令

    https://blog.csdn.net/qq_40700822/article/details/106080389 Linux学习笔记(一) Linux简介 Linux特点 Linux基本操作 1 ...

  4. linux最小系统 安装教程,Linux 学习笔记 1 使用最小的系统,从分区安装系统开始,...

    Linux 学习笔记 1 使用最小的系统,从分区安装系统开始, 我们常用的linux系统在安装过程中大多都省略了对系统进行分区的操作,以至于后期,不了解什么是分区以及分区当中最基本的一些概念, 我们不 ...

  5. Linux学习笔记之——Linux系统内部相关介绍

    Linux学习笔记之--Linux系统内部相关介绍 摘要:主要记录一些比较有用的能够帮助理解和使用Linux的知识.比如一些相关概念.没兴趣的看看就好.知道有这么个东西.注意事项.和一些常用目录的作用 ...

  6. Linux学习笔记(二)——文件与磁盘系统

    Linux学习笔记(二)--文件与磁盘系统 文件操作 ls [选项][目录名]-l :列出长数据串,包含文件的属性与权限数据等-a :列出全部的文件,连同隐藏文件(开头为.的文件)一起列出来(常用)- ...

  7. Linux学习笔记(二)(Vim文本编辑器 权限管理(用户,用户组) 系统相关(磁盘,进程))

    Linux学习笔记(二) 5.账号管理 概述 一.用户账号管理 概述 1. 添加账号 :useradd(`useradd 选项 用户名`) 2. 删除帐号(`userdel 选项 用户名`) 3. 修 ...

  8. 【黑马程序员新版Linux学习笔记】Linux系统实用操作命令——操作演示

    [黑马程序员新版Linux学习笔记]Linux 零基础快速入门: (一)Linux基本命令--操作演示 (二)Linux用户和权限 -- 操作演示 (三)Linux 小技巧快捷键 (四)Linux系统 ...

  9. Linux学习笔记---移植官方linux步骤(二)

    目录 修改网络驱动 修改 LAN8720 的复位 以及网络时钟 引脚驱动 修改fec1和fec2节点pinctrl-0属性 修改LAN8720A的PHY地址 修改 fec_main.c 文件 配置 L ...

最新文章

  1. 重点:怎样正确的使用QThread类(注:包括推荐使用QThread线程的新方法QObject::moveToThread)...
  2. BI Content、Metadata Repository
  3. Utils 工具 推送
  4. HDU 4857 拓扑排序 优先队列
  5. 帆软报表(finereport)点击事件对话框打开
  6. linux下根据端口查进程,linux根据进程查端口,根据端口查进程
  7. Vue 开发中常见报错与处理
  8. 十大排序算法----堆排序(最后一个非叶子节点的序号是n/2-1的推理)
  9. c语言五子棋中怎么设置开局,五子棋教程:指定开局
  10. freeswitch之G729转码操作配置详解
  11. IDEA配置JDK版本
  12. VB中数组的嵌套循环
  13. NPOIHelper
  14. 手机号与邮箱正则表达式
  15. 2022爱分析・消费品零售数字化厂商全景报告 | 爱分析报告
  16. 【深一点学习】BP网络,结合数学推导的代码实现
  17. 第三章数据链路层测试题
  18. Stable_baselines3 tensorboard可视化
  19. Relation Extraction 关系抽取综述
  20. ArcGIS插件 - 易至天工影像加载插件

热门文章

  1. java写文件用二进制分割_java分割二进制文件
  2. Android简单美观计算器(界面部分)
  3. 2.网页布局之切切豆腐
  4. 前端vue使用XXTEA进行对称加解密。同时对比rsa算法和xxtea算法的优缺点。
  5. linux如何读取华为手机存储文件夹下,安卓手机文件系统的二次革命,F2FS后再看EROFS...
  6. HC-05 蓝牙模块使用
  7. Pixel 手机遇到问题记录
  8. 【Lintcode】562. Backpack IV
  9. Base64与图片之间互相转换
  10. java-php-python-ssm猫咪伤患会诊复查医疗平台计算机毕业设计