首页 > 其他分享 >10.文件IO

10.文件IO

时间:2023-08-21 18:33:54浏览次数:30  
标签:10 函数 文件 int 描述符 fd 内核 IO

10.文件IO

从本章开始学习各种Linux系统函数,这些函数的用法必须结合Linux内核的工作原理来理解,因为系统函数正是内核提供给应用程序的接口,而要理解内核的工作原理,必须熟练掌握C语言,因为内核也是用C语言写的,我们在描述内核工作原理时必然要用“指针”、“结构体”、“链表”这些名词来组织语言,就像只有掌握了英语才能看懂英文书一样,只有学好了C语言才能看懂我描述的内核工作原理。

1.C库IO函数的工作流程


C语言操作文件相关问题:

使用fopen函数打开一个文件,返回一个FILE* fp,这个指针指向的结构体有三个重要的成员。

▶ 文件描述符: 通过文件描述可以找到文件的inode,通过inode可以找到对应的数据块

▶ 文件指针: 读和写共享一个文件指针,读或者写都会引起文件指针的变化

▶ 文件缓冲区: 读或者写会先通过文件缓冲区,主要目的是为了减少对磁盘的读写次数,提高读写磁盘的效率。

备注:

▶ 头文件stdio.h 的第48行处: typedef struct _IO_FILE FILE;

▶ 头文件libio.h 的第241行处: struct _IO_FILE,这个接头体定义中有一个_fileno成员,这个就是文件描述符

2. C库函数与系统函数的关系

系统调用: 由操作系统实现并提供给外部应用程序的编程接口,(Application Programming Interface, API),是应用程序同系统之间数据交互的桥梁。

3.虚拟地址空间

进程的虚拟地址空间分为用户区和内核区,其中内核区是受保护的,用户是不能够对其进行读写操作的;内核区中很重要的一个就是进程管理,进程管理中有一个区域就是PCB(本质是一个结构体);

PCB中有文件描述符表,文件描述符表中存放着打开的文件描述符,涉及到文件的IO操作都会用到这个文件描述符。

man 2 write系统调用

man 3标准C语言函数

命令行参数:

4.pcb和文件描述符表

备注:

pcb:结构体:task_stuct,该结构体在:

/usr/src/linux-headers-4.4.0-97/include/linux/sched.h:1390

一个进程有一个文件描述符表:1024

  • 前三个被占用, 分别是STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO

  • 文件描述符作用:通过文件描述符找到inode, 通过inode找到磁盘数据块.

虚拟地址空间à内核区àPCBà文件描述表à文件描述符à文件IO操作使用文件描述符

作业:

将昨天的add.c sub.c mul.c divd.c编写makefile文件生成库文件;-----makefile的名字为mathmak, 并编写main.c主程序调用库文件的makefile, 名字为mainmak.

5.文件IO

从本章开始学习各种Linux系统函数,这些函数的用法必须结合Linux内核的工作原理来理解, 因为系统函数正是内核提供给应用程序的接口, 而要理解内核的工作原理,必须熟练掌握C语言,因为内核也是用C语言写的,我们在描述内核工作原理时必然要用“指针”、“结构体”、“链表”这些名词来组织语言,就像只有掌握了英语才能看懂英文书一样,只有学好了C语言才能看懂我描述的内核工作原理。

C标准函数与系统函数的区别

什么是系统调用

由操作系统实现并提供给外部应用程序的编程接口。(Application Programming Interface,API)。是应用程序同系统之间数据交互的桥梁。

一个helloworld如何打印到屏幕。

每一个FILE文件流(标准C库函数)都有一个缓冲区buffer,默认大小8192Byte。Linux系统的IO函数默认是没有缓冲区。

open/close

文件描述符

一个进程启动之后,默认打开三个文件描述符:

#define  STDIN_FILENO     		0
#define  STDOUT_FILENO    	    1
#define  STDERR_FILENO    	    2

新打开文件返回文件描述符表中未使用的最小文件描述符,调用open函数可以打开或创建一个文件,得到一个文件描述符

open函数

█函数描述: 打开或者新建一个文件

█函数原型:

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

█函数参数:

  ▶pathname参数是要打开或创建的文件名,和fopen一样,pathname既可以是相对路径也可以是绝对路径。

  ▶flags参数有一系列常数值可供选择,可以同时选择多个常数用按位或运算符连接起来,所以这些常数的宏定义都以O_开头,表示or。

   ☆必选项:以下三个常数中必须指定一个,且仅允许指定一个。

     ●O_RDONLY 只读打开

     ●O_WRONLY 只写打开

     ●O_RDWR 可读可写打开

   ☆以下可选项可以同时指定0个或多个,和必选项按位或起来作为flags参数。可选项有很多,这里只介绍几个常用选项:

     ●O_APPEND 表示追加。如果文件已有内容,这次打开文件所写的数据附加到文件的末尾而不覆盖原来的内容。

     ●O_CREAT 若此文件不存在则创建它。使用此选项时需要提供第三个参数mode,表示该文件的访问权限。

      文件最终权限:mode & ~umask

     ●O_EXCL 如果同时指定了O_CREAT,并且文件已存在,则出错返回。

     ●O_TRUNC 如果文件已存在,将其长度截断为为0字节。

     ●O_NONBLOCK 对于设备文件,以O_NONBLOCK方式打开可以做非阻塞I/O(NonblockI/O),非阻塞I/O。

函数返回值:

  ▶成功: 返回一个最小且未被占用的文件描述符

  ▶失败: 返回-1,并设置errno值。

close函数

█函数描述: 关闭文件

█函数原型: int close(int fd);

█函数参数: fd文件描述符

█函数返回值:

  ▶成功返回0

  ▶失败返回-1,并设置errno值。

需要说明的是,当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close关闭,所以即使用户程序不调用close,在终止时内核也会自动关闭它打开的所有文件。但是对于一个长年累月运行的程序(比如网络服务器),打开的文件描述符一定要记得关闭,否则随着打开的文件越来越多,会占用大量文件描述符和系统资源。

read/write

read函数

█函数描述: 从打开的设备或文件中读取数据

█函数原型: ssize_t read(int fd, void *buf, size_t count);

█函数参数:

  ▶fd: 文件描述符

  ▶buf: 读上来的数据保存在缓冲区buf中

  ▶count: buf缓冲区存放的最大字节数

█函数返回值:

  ▶ >0:读取到的字节数

  ▶ =0:文件读取完毕

  ▶ -1: 出错,并设置errno

write

█函数描述: 向打开的设备或文件中写数据

█函数原型: ssize_t write(int fd, const void *buf, size_t count);

█函数参数:

  ▶fd:文件描述符

  ▶buf:缓冲区,要写入文件或设备的数据

  ▶count:buf中数据的长度

█函数返回值:

  ▶成功:返回写入的字节数

  ▶错误:返回-1并设置errno

lseek

所有打开的文件都有一个当前文件偏移量(current file offset),以下简称为cfo。cfo通常是一个非负整数,用于表明文件开始处到文件当前位置的字节数。读写操作通常开始于 cfo,并且使 cfo 增大,增量为读写的字节数。文件被打开时,cfo 会被初始化为 0,除非使用了 O_APPEND。

使用 lseek 函数可以改变文件的 cfo。

#include <sys/types.h>

#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

█函数描述: 移动文件指针

█函数原型: off_t lseek(int fd, off_t offset, int whence);

█函数参数:

  ▶ fd:文件描述符

  ▶ 参数 offset 的含义取决于参数 whence:

   ◆如果 whence 是 SEEK_SET,文件偏移量将设置为 offset。

   ◆如果 whence 是 SEEK_CUR,文件偏移量将被设置为 cfo 加上 offset,offset 可以为正也可以为负。

   ◆如果 whence 是 SEEK_END,文件偏移量将被设置为文件长度加上 offset,offset 可以为正也可以为负。

   ◆函数返回值: 若lseek成功执行, 则返回新的偏移量。

   ◆lseek函数常用操作

  ▶文件指针移动到头部

lseek(fd, 0, SEEK_SET);

  ▶ 获取文件指针当前位置

int len = lseek(fd, 0, SEEK_CUR);

  ▶获取文件长度

int len = lseek(fd, 0, SEEK_END);

  ▶ lseek实现文件拓展

off_t currpos;// 从文件尾部开始向后拓展1000个字节
currpos = lseek(fd, 1000, SEEK_END); // 额外执行一次写操作,否则文件无法完成拓展
write(fd, “a”, 1);    // 数据随便写

练习:

1 编写简单的IO函数读写文件的代码

2 使用lseek函数获取文件大小

3 使用lseek函数实现文件拓展

perror和errno

errno是一个全局变量,当系统调用后若出错会将errno进行设置,perror可以将errno对应的描述信息打印出来。

如:perror("open"); 如果报错的话打印: open:(空格)错误信息

练习:编写简单的例子,测试perror和errno。

阻塞和非阻塞:

思考: 阻塞和非阻塞是文件的属性还是read函数的属性?

█普通文件:hello.c

 ▶默认是非阻塞的

█终端设备:如 /dev/tty

 ▶默认阻塞

█管道和套接字

 ▶默认阻塞

练习:

1 测试普通文件是阻塞还是非阻塞的?

2 测试终端设备文件/dev/tty是阻塞还是非阻塞的。

得出结论: 阻塞和非阻塞是文件本身的属性, 不是read函数的属性.

标签:10,函数,文件,int,描述符,fd,内核,IO
From: https://www.cnblogs.com/codemagiciant/p/17646766.html

相关文章

  • 11.文件和目录
    11.文件和目录文件操作相关函数stat/lstat函数█函数描述:获取文件属性█函数原型:intstat(constchar*pathname,structstat*buf);intlstat(constchar*pathname,structstat*buf);█函数返回值:  ▶成功返回0  ▶失败返回-1structstat{ dev_t......
  • C# 通过DynamicObject让Dictionary可以通过dynamic进行操作
    测试代码DynamicDictionarydict=newDynamicDictionary();dict.Value["ID"]=1;dict.Value["Name"]="李二";dict.Value["Address"]="李家坡";dynamic......
  • GLPI资产管理系统+fusioninventory
    准备环境系统环境:centos7mariadb:10.5.18php:7.4.33apache:2.4.6#关闭selinuxsetenforce0vim/etc/selinux/config改成如图下selinux=disabled所示#关闭防火墙systemctlstopfirewalldsystemctldisablefirewalld开始安装1.安装所需工具yum-yinstallwgetvim2.更新系统补丁yum-......
  • MS1022高精度时间测量(TDC)电路
    产品简述MS1022是一款高精度时间测量电路,内部集成了模拟比较器、模拟开关、施密特触发器等器件,从而大大简化了外围电路。同时内部增加了第一波检测功能,使抗干扰能力大大提高。通过读取第一个回波脉冲的相对宽度,用户可以获得接收信号的强度提示。通过这个提示,可以判断超声波换......
  • 20天 hot 100 速通计划-day13
    回溯131.分割回文串给你一个字符串s,请你将s分割成一些子串,使每个子串都是回文串。返回s所有可能的分割方案。回文串是正着读和反着读都一样的字符串。示例1:输入:s="aab"输出:[["a","a","b"],["aa","b"]]示例2:输入:s="a"输出:[["a"]]提示:1&......
  • SpringBoot复习:(40)@EnableConofigurationProperties注解的用法
    一、配置文件:server.port=9123二、配置类:packagecn.edu.tju.config;importcom.mysql.fabric.Server;importorg.springframework.boot.autoconfigure.web.ServerProperties;importorg.springframework.boot.context.properties.EnableConfigurationProperties;importorg.......
  • SpringBoot复习:(44)MyBatisAutoConfiguration
    可以看到MyBatisAutoConfiguration引入了MyBatisProperties这个属性:MyBatisAutoConfiguration中配置了一个SqlSessionFactoryBean,代码如下:可以配置mybatis-config.xml,需要配置文件里指定:mybatis.config-locatinotallow=classpath:/mybatis-config.xml同样可配置MyBatis的xml......
  • SpringBoot复习:(55)在service类中的方法上加上@Transactional注解后,Spring底层是怎么生
    SpringBootrun方法代码如下:可以看到它会调用refreshContext方法来刷新Spring容器,这个refreshContext方法最终会调用AbstractApplicationContext的refresh方法,代码如下如上图,refresh方法最终会调用finisheBeanFactoryInitialization方法,代码如下:从上图可以看出,它最终会调用preIn......
  • SpringBoot复习:(48)RedisAutoConfiguration自动配置类
    RedisAutoConfiguration类代码如下:可以看到在这个类中配置了2个bean:redisTemplate和stringRedisTemplate.而它通过@EnableConfigurationProperties(RedisProperties.class)注解,把配置文件中配置的Redis相关的信息引入进来了,RedisProperties代码如下:还可以看到RedisAutoConfigu......
  • SpringBoot复习:(53)TransactionInterceptor是在哪里配置的?
    我们知道SpringBoot的事务(@Transactional)最终是通过TransactionInterceptor的invoke方法调用invokeWithinTransaction方法来开启事务控制的。TransactionInterceptorbean在哪里配置的呢?在ProxyTransactionManagementConfiguration:可以看到这里创建了一个TransactionIntercept......