当前位置: 首页 > news >正文

Linux应用 文件I/O

Linux系统下文件的存储

​ Linux系统下文件的存储是由两部分存储的,第一部分是存储文件的具体数据,第二部分是inode表,inode 是一个描述文件的结构体,其描述了文件的基本信息和存储位置,通过找到inode的编号就可以找到对应的inode,通过inode就可以找到其存储信息,进而完成对文件的读写。

在这里插入图片描述

ls -i   		//查看当前目录下文件的inode标号
stat file_path  //查看指定文件的inode标号

tips:格式化有时候分为格式化和快速格式化,快速格式化只是删除了磁盘的inode表,所以快速格式化的u盘是可以找回数据的(在原来的存储信息没有被覆盖之前)。

打开一个文件的步骤

1、系统通过文件名称找到对应的inode编号

2、通过inode编号在inode表中找到对应的inode结构体

3、通过inode结构体,找到对应的存储block信息,然后在磁盘中读取文件。

文件在被读取以后,系统会malloc一块内存区域,文件会被存在内存中进行管理、缓存,我们进行操作的话是对内存中的文件进行操作,不是针对磁盘中存放的静态文件。

为什么Linux系统要把文件读取内存中然后进行读写操作呢

1、内存的访问速度是大于外部磁盘的,把文件读取到内存中,能够有效的提高速率

2、Linux对于存储设备的访问是以块为单位的,对内存的访问是以字节为单位的,所以在内存中操作文件比较灵活。

PCB(进程控制块)中有一个指针指向文件描述表,文件描述表中的每一个元素索引代表一个文件表,文件表记录一个文件的相关信息,进程打开的所有文件对应的文件描述符都记录在文件描述符表中,每一个文件描述符都指向一个对应的文件表,当前程对于文件的操作都是基于这个文件对应的文件表的。

在这里插入图片描述

返回错误

Linux系统下对于常见的错误都做了一个编号。

每一个进程都有一个自己维护的Errno变量,是程序中的全局变量,用来存储就近发生的函数执行错误编号。

如何获取系统所维护的Errno变量呢-------->>在文件加上头文件#include <errno.h> ,可以认为这个变量是在这个头文件中声明的。

如何解析这个errno变量呢--------->> 使用c库函数 strerror() 将对应的 errno 转换成适合我们查看的字符串信息

#include <string.h>
char* strerror(int errnum);

有没有直接根据errno值打印对应错误的函数呢-------->>

#include <stdio.h>
void perror(const char*s)			//可以在输出的错误提示字符串之前加入自己的打印信息 也就是%s可以自己传入,然后会在错误之前加上冒号和空格打印。

退出

_exit()和__Exit()

调用_exit()函数会清除其使用的内存空间,并销毁其在内核中的各种数据结构,关闭进程中所有文件描述符,结束进程,移交控制权给操作系统,其实就

_exit() 和__Exit()等价

#include <unistd.h>
void _exit(int status)   //传入0表示正常结束,其他值表示有错误发生,系统调用#include <stdlib.h>
void exit(int status);   //标准c库函数

空洞文件

空洞文件指的是,在还没有使用到这个空间的时候,提前开辟出这个文件空间的大小,没有存数据的那部分就属于是空洞文件。

空洞文件的作用:1、方便多线程同时下载文件

2、虚拟机分配内存空间。

ls查看文件的逻辑大小

du命令查看空洞文件

使用read函数读取文件空洞部分会读到什么呢?

在 Linux 上只要文件系统支持“空洞”(hole,即 sparse file),用 read() 去读空洞时,内核会返回全 0 字节(\0),行为与读普通全-0 区域完全一样;既不会报错,也不会缩短返回长度(除非读到 EOF)

O_TRUNC

在打开文件以后自动将文件清空

O_APPEND

打开文件后自动将写指针指向文件末尾,但不会影响读指针,读指针依旧指向文件开头。

tips

O_TRUNC的优先级大于O_APPEND

多次打开同一个文件

区别在于是否会生成新的文件表

1、如果多次打开同一个文件的话,会得到不同的文件修饰符,也就是open一次就会得到一个文件修饰符, 因为文件修饰符是有限资源,所以需要自己手动释放也就是使用close关闭文件,一个文件描述符对应一个独立的文件表,但是这个文件表指向的是同一个inode和同一个动态文件。

2、前面说过,在我们打开文件的时候,操作系统会将文件读入内存中,生成文件的动态文件,那么当我们多次打开同一个文件的时候,操作系统会生成多个动态文件吗?答案是不会的,当我们多次打开一个文件时,系统只会在内存中生成一个动态文件。

3、既然系统只在内存中生成一个动态文件,那么对于多个文件修饰符,程序中对文件的读写操作是同时操作一个文件还是分别操作。答案是分别操作,不同的文件描述符对应的读写指针是独立的。

在这里插入图片描述

4、如果打开多个文件的时候FLAG标志都有O_APPEND,那么写指针会一直指在文件尾部,也就是多个文件描述符的写操作都是从文件末尾开始的。

文件描述符的复制

Linux系统中,open获得的文件描述符可以被复制,复制得到的文件描述符,权限与原文件描述符相同。这些文件描述符指向一个文件表。

#include <unistd.h>
int dup(int oldfd)      //复制文件描述符的系统调用 新编号由系统指定
int dup2(int oldfd,int newfd)  //复制文件描述符,可指定新编号

在这里插入图片描述

复制得到的文件描述符指向同一个文件表。所以复制来的文件描述符,所以读写指针是相同的。

文件共享

文件共享指的就是多个不同的文件描述符指向同一个inode节点。

同一个进程中使用Open打开同一个文件。

不同文件描述符指向不同文件表,不同文件表指向同一个inode节点和动态文件。
在这里插入图片描述
)

这个时候,不同的文件描述符对应着对这个文件的不同操作,是分别进行的。

不同的进程中open打开同一个文件

不同文件描述表中的不同文件描述符,指向不同文件描述表,指向同一个inode节点和动态文件。
在这里插入图片描述

和上面一个,指向同一个inode节点,读写操作是分别进行的。

同一个进程中使用dup复制文件描述符

在这里插入图片描述

不同文件描述符,指向同一个文件表,指向同一个inode节点和动态文件。
不同的文件描述符指向同一个节点,对于文件的读写操作是共享的。

原子操作与竞争冒险

竞争冒险出现在两个不同的进程操作同一个文件时,因为两个文件对于这个文件的操作是独立的(不使用O_APPEND flag).所以在同时对文件操作时,会出现其中一方的操作被覆盖,进而导致数据丢失。

原子操作就是将可能会导致竞争冒险的操作原子化,也就是本来多个步骤的操作可能会因为任务切换而导致出错,现在保证这个操作能一气呵成,中间不被打断,这样的操作就叫原子操作。原子操作要么不执行,要执行就要一口气执行完。

O_APPEND标志就会在每次write的时候,将文件的写指针移动到文件末尾,然后进行write,然后在写入数据。这个操作同时也就组成了原子操作。这里的移动指针和写数据是一气呵成的

pread()和Pwrite()用法和功能和平常的read()和write()相同,但是他们是原子操作。

#include <unistd.h>
ssize_t pread(int fd,void*  buf,size_t count,off_t offset)
ssize_t pwrite(int fd,void* buf,size_t count,off_t offset)
//支持指定当前读写的偏移量,但是两个操作不会更改文件的读写指针位置

O_EXCL标志保证了,检查文件是否存在,不存在就创建的原子操作。检查和创建是一气呵成的

fcntl和ioctl

fcntl是操作文件描述符的。
fcntl()函数可以对一个已经打开的文件描述符执行一系列控制操作,譬如复制一个文件描述符(与 dup、dup2 作用相同)、获取/设置文件描述符标志、获取/设置文件状态标志等,类似于一个多功能文件描述符管理工具箱。fcntl()函数原型如下所示(可通过"man 2 fcntl"命令查看)

#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd,int cmd , .../*arg*/) ; 
//1、复制文件标识符cmd=F_DUPFD
//2、获取设置文件描述符标志 cmd=F_GETFD 或 cmd=F_SETFD 只有 O_APPEND O_ASYNC O_DIRECT O_NOATIME O_NONBLOCK可以修改

ioctl是发送和接受命令的,一般都用于接受。
ioctl()可以认为是一个文件 IO 操作的杂物箱,可以处理的事情非常杂、不统一,一般用于操作特殊文件或硬件外设,譬如可以通过 ioctl 获取 LCD 相关信息等,

#include <sys/ioctl.h>
int ioctl(int fd,unsigned long request,...);    //给对应的fd文件发送request请求,然后在驱动文件中编写不同request对应不同的操作/*在LEDAPP应用文件中*/
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>#define LED_MAGIC  'L'
#define LED_ON     _IO(LED_MAGIC, 0)
#define LED_OFF    _IO(LED_MAGIC, 1)int main(int argc, char *argv[])
{int fd = open("/dev/myled0", O_RDWR);if (fd < 0) { perror("open"); return 1; }if (argc > 1 && argv[1][0] == '1')ioctl(fd, LED_ON);      /* 亮 */elseioctl(fd, LED_OFF);     /* 灭 */close(fd);return 0;
}/*在led驱动文件中*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>#define LED_MAGIC  'L'
#define LED_ON     _IO(LED_MAGIC, 0)
#define LED_OFF    _IO(LED_MAGIC, 1)   //自定义命令类型static struct gpio_desc *led_gpio;static long led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{switch (cmd) {case LED_ON:gpiod_set_value(led_gpio, 1);return 0;case LED_OFF:gpiod_set_value(led_gpio, 0);return 0;default:return -ENOTTY;}
}static const struct file_operations led_fops = {.owner          = THIS_MODULE,.unlocked_ioctl = led_ioctl,
};static int __init led_probe(struct platform_device *pdev)
{led_gpio = gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);if (IS_ERR(led_gpio)) return PTR_ERR(led_gpio);return register_chrdev(0, "myled", &led_fops); /* 0→动态主设备号 */
}static int __exit led_remove(struct platform_device *pdev)
{gpiod_put(led_gpio);unregister_chrdev(0, "myled");return 0;
}static const struct of_device_id led_of_match[] = {{ .compatible = "foo,myled" },{}
};
MODULE_DEVICE_TABLE(of, led_of_match);static struct platform_driver led_driver = {.probe  = led_probe,.remove = __exit_p(led_remove),.driver = {.name           = "myled",.of_match_table = led_of_match,},
};
module_platform_driver(led_driver);MODULE_LICENSE("GPL");

截断文件

两个函数 truncate()和ftruncate()

#include <unistd.h>
#include <sys/types.h>int truncate(const char* path,off_t length);   //使用文件路径指定文件
int ftruncate(int fd,off_t length);		       //使用文件修饰符指定文件  文件必须是open得到的,且必须有可写权限
//两个文件都可以将指定文件截断成指定大小 多了就砍掉后面的,小了就扩展成那么大的,用'/0'填充
//截断文件不会改变读写指针位置

图片来源 正点原子,个人学习,侵权删

http://www.dtcms.com/a/436173.html

相关文章:

  • 网站建设费的摊销乐陵市seo关键词优化
  • 作业部落 WordPress优化wordpress速度
  • 做网站报价北京鑫旺路桥建设有限公司网站
  • 企业cms网站建设考试题如何建设个人网站
  • 大港油田建设官方网站不会PS怎么建网站
  • 公司做网站域名的好处微信公众号关联网站
  • 学校网站建设 分工湖南省住房与城乡建设厅网站
  • 新网站关键词怎么优化合肥网站排名优化公司
  • Spring Boot 消息队列技术整合
  • 34个行政区划总篇
  • 树的重心与直径 性质
  • 请问做卖东西网站怎么建设网站如何弄好几张网站背景
  • 企业网站建设基本要素做折页的网站
  • SysTick 简单总结
  • 地方网站如何做怎么做网页背景
  • 做网站优化竞价区别wordpress主题安装不成功
  • 福建省建设资格注册与管理中心网站网站开发公司业务员培训
  • 做的比较好的猎头网站系统app定制开发
  • 大学生做的美食网站做网站是什么软件
  • 建设局网站港府名都野望王绩
  • 自己的网站服务器可以免费做推广的网站
  • 四川省建设工程造价信息网站公司实验室设计
  • 《基层建设》官方网站陈村网站设计
  • 吃透大数据算法-数据压缩算法Run Length Encoding(RLE)
  • 宜兴建设公司网站网页设计师联盟网站怎么
  • 上海网站建设seodian莱芜区都市网莱芜杂谈
  • 网站注册搜索引擎的目的是企业网络维护一般多少钱
  • 网站发布平台南宁伯才网络建站如何
  • 广安网站建设推荐h5是什么意思游戏
  • 分类信息的网站如何推广做建筑材料哪个网站好一点