2019-10-28 11:31:16 weixin_40608613 阅读数 1307

文件系统概述

操作系统中与管理文件有关的软件和数据
Linux文件系统采用的是树形结构
Linux支持多种文件系统
Linux文件系统提供丰富的文件和目录操作命令

Linux文件系统的特点

1.Linux文件系统采用树形结构,从根目录root(/)开始。
2.Linux的虚拟文件系统允许众多不同类型的文件系统共存,并支持跨文件系统的操作。
3.Linux的文件是无结构字符流式文件,不考虑文件内部的逻辑结构,只把文件简单地看作是一系列字符的序列。
4.Linux的文件可由文件拥有者或超级用户设置相应的访问权限而受到保护。
5.Linux把所有的外部设备都看作文件,可以使用与文件系统相同的系统调用和函数来读写外部设备。

Linux文件系统的组成

在这里插入图片描述

  • /home:用户主目录的基点目录,默认情况每个用户主目录都设在该目录下,如默认情况下用户 user01 的主目录是 /home/user01 ;
  • /lib:存放标准程序设计库目录,又叫动态链接共享库目录,目录中文件类似windows里的.dll文件;
  • /etc:存放系统管理和配置文件目录;
  • /dev:存放设备特殊文件目录,如声卡文件、磁盘文件等;
  • /usr:最庞大的目录,存放应用程序和文件目录;
  • /proc:虚拟目录,是系统内存的映射,可直接访问这个目录来获取系统信息;
  • /root:系统管理员的主目录;
  • /tmp:存放公用临时文件目录。
  • /sbin/usr/sbin:系统级的命令与工具目录;
  • /bin/usr/bin:用户级的命令与工具目录;
  • /usr/include:C语言,内核编译所需要的头文件存放目录;
  • /lib/usr/lib:库文件存放目录,其中有表态库和动态库;
  • /usr/src:Linux源代码目录,编译内核时使用;
  • /var:通常用来存放一些经常变化的内容,比如各种网络服务的工作目录都在这里;

Linux文件系统类型

EXT文件系统

  • Linux的第一个版本是基于Minix文件系统的。当Linux成熟时,引入了扩展文件系统(ext文件系统),ext是第一个专门为Linux设计的文件系统。但由于ext文件系统在稳定性,速度和兼容性方面存在不少缺陷,已经很少使用。
  • Linux在1994年引入了二级扩展文件系统(second extended file system,ext2),相当高效和稳定,是Linux系统默认使用的文件系统。ext2文件系统是非日志文件系统,这在关键行业的应用是一个致命的弱点。
  • ext2文件系统经过逐步改进形成了ext3文件系统,这个新的文件系统在设计时牢记了两点,一是成为一个日志文件系统,二是尽可能与原来的ext2文件系统兼容。

目前ext3文件系统非常稳定可靠,该文件系统具有如下特点:

  1. 高可用性
    Linux系统使用ext3文件系统后,即使非正常关机,系统也不需要检查文件系统。
  2. 数据的完整性
    ext3文件系统能够极大地提高文件系统的完整性,避免了意外关机对文件系统的破坏。
  3. 文件系统的速度
    ext3文件系统的日志功能对磁盘的驱动器读写头进行了优化。
  4. 数据转换
    由ext2文件系统转换成ext3文件系统非常容易。另外,ext3文件系统可以不经任何更改,直接加载成为ext2文件系统。
  5. 多种日志模式

ext3文件系统有三种日志模式EXT3三种日志模式:

  1. Journal模式:将所有数据和元数据的改变都记入日志,这种模式减少丢失每个文件所作修改的机会,但需要很多额外磁盘访问,是最安全和最慢的ext3日志模式;
  2. Ordered模式:只有对文件系统元数据的改变才记入日志,但保证数据在元数据之前被写入磁盘,是缺省的ext3日志模式;
  3. 写回(Writeback)模式:只有对文件系统元数据的改变才记入日志,数据和元数据被写入磁盘的顺序不保证,是在这三种日志模式中最快的模式。

其他文件系统

  • MS-DOS
    MS-DOS文件系统,DOS、Windows和OS/2操作系统上使用的文件系统。
  • UMSDOS
    扩展的MS-DOS文件系统。
  • VFAT
    Microsoft扩展的FAT文件系统,被Windows9x /2000 /XP使用。
  • Proc
    一种基于内存的伪文件系统,不占用磁盘空间,只是以文件的方式为访问Linux内核数据提供接口。
  • ISO9660
    一种针对ISO9660标准的CD-ROM文件系统。
  • SYSV
    System V/Coherent在 Linux 平台上的文件系统。
  • NFS
    Sun公司推出的网络文件系统。
  • SMB
    支持SMB协议的网络文件系统,可用于实现Linux与Windows的文件共享。
  • FAT
    它不是一个单独的文件系统,而是MS-DOS, umsdos 和vfat 文件系统的常用部分。
  • swap
    swap文件系统用于Linux的交换分区。
  • Reiserfs
    是Linux内核2.4.1以后支持的一种全新的日志文件系统。
  • NTFS
    Windows NT文件系统。
  • HPFS
    是Microsoft的LAN Manager中的文件系统,同时也是IBM的LAN Server和OS/2的文件系统,称为高性能文件系统。

 不同版本的Linux系统所支持的文件系统类型和种类都会有所不同。Linux 允许众多不同类型的文件系统共存,并支持跨文件系统的操作,这是由于虚拟文件系统(Virtual File System,简称VFS)的存在。
 虚拟文件系统是Linux内核中的一个软件层,用于给用户空间的程序提供文件系统接口;它也提供了内核中的一个抽象功能,允许不同的文件系统共存。系统中所有的文件系统不但依赖VFS共存,而且也依靠VFS协同工作。
在这里插入图片描述

文件和目录的基本概念

文件

文件是指由创建者定义的,具有文件名的一组相关元素的集合,文件可以是文本文档、图片、程序等。
Linux系统下的文件名长度根据不同类型的文件系统有所不同,最多可到255个字符。Linux下为文件取名必须遵守以下规则:

  1. 除了“/”外,所有的字符都可以使用;
  2. 转义字符最好不用,这些字符在Linux系统下有特定的含义,如“?”(问号),“*”(星号),“ ”(空格),“$”(货币符),“&”等;
  3. 避免使用“+”、“-”或“.”作为普通文件名的第一个字符(在Linux下以“.”开头的文件是隐藏文件);
  4. Linux系统的文件名大小写敏感,文件Memo和文件memo是两个不同的文件。

Linux操作系统支持多种文件类型,下面介绍常见的几种。

  1. 普通文件
    普通文件也称为常规文件,是 Linux 中最一般格式的文件,包括系统文件、用户文件和库函数。
  2. 目录文件
    目录文件是由文件目录信息构成的特殊文件,目录文件的内容不是应用程序和数据,而是用来检索普通文件的目录信息。
  3. 设备文件
    在Linux中输入输出设备被看做特殊文件,称为设备文件。设备文件分两类,字符设备文件和块设备文件。设备文件存放在Linux的 /dev 目录
    在这里插入图片描述
    • 其中/dev/tty是表示终端设备的文件
    • 字符串“crw-rw-rw-”的第一个字符“c”表示是字符设备文件
    /dev/sda1表示USB移动存储设备的文件
    • 字符串“brw-rw----”的第一个字符是“b”,表示是块设备文件,
  4. 符号链接文件
    符号链接文件是一种特殊类型的文件,它的内容只是一个字符串,该字符串可能指向一个存在的文件也可能什么都不指向。

目录

目录是Linux文件系统中的一种特殊文件,文件系统利用目录完成按名存取及对文件信息的共享和保护。
工作目录与用户主目录

  • 工作目录
    工作目录是用户在登录到Linux系统后所处于的目录,也称为当前目录。
  • 用户主目录
    是系统管理员增加用户时创建的(以后也可以改变),每个用户都有自己的主目录。普通用户的主目录在/home下,root用户作为系统管理员,因为身份特殊所以有自己的主目录,在/root下。

用户刚登录到系统中时,其工作目录便是该用户主目录,通常与用户的登录名相同。如用户的登录名为user,其主目录通常为/home/user

路径

路径定义
路径是指从树型目录中的某个目录层次到某个文件的一条道路。
路径的构成要素
路径是由目录或目录和文件名构成的,中间用“/”分开。

路径分为绝对路径和相对路径两类:

  1. 绝对路径
    Linux系统中,绝对路径是从“/”(根目录)开始的,也称为完全路径,如/home/user01/usr/bin
  2. 相对路径
    Linux系统中,相对路径是从用户工作目录或用户主目录开始的路径,如 ./test../user1~/test。 其中 . 表示用户工作目录, .. 表示工作目录的上级目录, ~ 表示用户主目录。
2020-03-11 21:43:36 huanqingdong 阅读数 55

1. 文件系统是啥

文件系统是操作系统用于明确存储设备(常见的是磁盘,也有基于NAND Flash的固态硬盘)或分区上的文件的方法和数据结构;即在存储设备上组织文件的方法。

— 百度百科

百科解释有点太专业,实际上就是一个帮助操作系统完成文件存储、检索、管理等功能的软件。

2. 基本的Linux文件系统

2.1 ext文件系统

Linux系统中最早引入的文件系统叫扩展文件系统(extended filesystem,简称ext)。它使用虚拟目录来操作硬件设备(一切皆文件),在物理设备上用订场的块存储数据。
ext将磁盘分成两部分:

  • inode区(inode table)
    inode已节点号区分,存放文件属性信息,例如名称、大小、访问权限、指向数据区块的指针
  • 数据区
    按块存储文件数据

2.2 ext2文件系统

对ext进行提升

  • 单文件上限从2G提升至2T(后期版本32T)
  • inode中增加了文件创建时间,修改时间、最后访问时间
  • 按组分配磁盘块,减轻碎片化

ext2文件系统缺陷
由于采用先将文件写入数据区,再更新或添加inode信息,这两步并没有事务保证,因此会存在inode区和数据区会存在数据不一致的情况,导致文件系统崩溃。

3. 日志文件系统

日志文件系统先将文件的更改写入到临时文件(称作日志, journal)中。在数据成功写到存储设备和索引节点表之后,再删除对应的日志条目。如果系统在数据被写入存储设备之前崩溃或断电了,日志文件系统下次会读取日志文件并处理上次留下的未写入的数据。
Linux中有3种广泛使用的日志方法,每种的保护等级都不相同
数据模式
索引节点和文件都会被写入日志;丢失数据风险低,但性能差
有序模式
只有索引节点数据会被写入日志,但只有数据成功写入后才删除;在性能和安全性之间取得了良好的折中
回写模式
只有索引节点数据会被写入日志,但不控制文件数据何时写入;丢失数据风险高,但仍比不用日志好

3.1 ext3文件系统

2001年引入Linux内核中,较ext2提升为每个存储设备添加一个日志文件,默认采用有序模式的日志功能,模式可通过命令进行修改。

3.2 ext4文件系统

2008年受Linux官方内核支持
较ext3提升

  • 支持数据压缩和加密
  • 支持区段(extent)的特性
    区段在存储设备上按块分配空间,但在索引节点表中只保存起始块的位置。由于无需列出所有用来存储文件中数据的数据块,它可以在索引节点表中节省一些空间。
  • 引入了块预分配技术( block preallocation)

3.3 XFS文件系统

美国硅图公司( SGI)最初在1994年为其商业化的IRIX Unix系统开发了XFS。 2002年,它被发布到了适用于Linux环境的版本。
XFS文件系统采用回写模式的日志,在提供了高性能的同时也引入了一定的风险,因为实际数据并未存进日志文件。 XFS文件系统支持在线调整文件系统的大小,但只能扩大不能缩小

4.写时复制文件系统

写时复制(copy-on-write,COW)
COW利用快照兼顾了安全性和性能。如果要修改数据,会使用克隆或可写快照。修改过的数据并不会直接覆盖当前数据,而是被放入文件系统中的另一个位置上。即便是数据修改已经完成,之前的旧数据也不会被重写。

4.1 ZFS文件系统

ZFS是由Sun公司于2005年研发的,用于OpenSolaris操作系统,从2008年起开始向Linux移植,最终在2012年投入Linux产品的使用。
ZFS是一个稳定的文件系统,与Resier4、 Btrfs和ext4势均力敌。它最大的弱项就是没有使用GPL许可。自2013年发起的OpenZFS项目有可能改变这种局面。但是,在获得GPL许可之前, ZFS有可能终无法成为Linux默认的文件系统

4.2 Btrf文件系统

Btrfs文件系统是COW的新人,也被称为B树文件系统。它是由Oracle公司于2007年开始研发的。 Btrfs在Reiser4的诸多特性的基础上改进了可靠性。另一些开发人员最终也加入了开发过程,帮助Btrfs快速成为了最流行的文件系统。究其原因,则要归于它的稳定性、易用性以及能够动态调整已挂载文件系统的大小。 OpenSUSE Linux发行版最近将Btrfs作为其默认文件系统。除此之外,该文件系统也出现在了其他Linux发行版中(如RHEL),不过并不是作为默认文件系统。

2019-12-13 10:15:48 qq_39341113 阅读数 67

Linux教程

linux文件系统概述

什么是文件系统

我们有了一个相对形象的概念,文件系统管理着很多文件。而这些文件其实就是数据,这些数据又是存储在磁盘上的。因此,实质上文件系统是管理磁盘的软件系统,它简化了用户对磁盘空间的使用方法,并降低了磁盘空间的使用难度,通过更加形象的方式将磁盘中的数据展示给用户

文件系统是为方便查找和管理计算机中的文件而设立的。它是操作系统为了存储和管理文件,在存储器(包括软硬盘和光盘等)上建立的一些文件组织和结构。一般来说,文件系统主要包括文件目录和文件本身,主要完成三项功能:跟踪记录存储器中被耗用的空间和自由空间,维护目录名和文件名,跟踪记录每一个文件的物理存储位置。不同的文件系统适用于不同的操作系统,有些操作系统只能识别一种文件系统,而有的则可以识别好几种。

Linux文件系统中的文件是数据的集合,文件系统不仅包含着文件中的数据而且还有文件系统的结构,所有Linux 用户和程序看到的文件、目录、软连接及文件保护信息等都存储在其中
文件系统是软件系统的一部分,它的存在使得应用可以方便的使用抽象命名的数据对象和大小可变的空间。

文件系统是操作系统中负责管理和存储文件信息的软件机构。文件系统由三部分组成:文件系统的接口,对对象操纵和管理的软件集合,对象及属性。

为什么选择文件系统

通过文件系统的方式来组织磁盘存储和数据管理。有以下几个方面的好处。

数据的读取、管理操作变得简单
文件系统给用户提供了一个简单的操作界面,用户可以通过对文件系统的简单操作,实现对磁盘的管理。虽然 Linux 系统下也可以直接使用裸设备,但是在读取性能上,裸设备并不比文件系统能高出多少,同时还造成了管理与维护的麻烦,普通用户是无法接受的。

磁盘分区管理灵活
在文件系统下提供了很多的磁盘分区管理工具,例如 LVM 等,通过这些工具可以灵活地对磁盘分区进行大小的修改,而在裸设备下,必须预先规划好磁盘空间,可能造成空间的不足或者浪费。

支持数据容错机制,数据安全能得到保障
一个好的文件系统提供了对于数据读取和写入的各种容错和保护机制,很好地保障了数据的安全,而裸设备没有这种灵活的管理机制,对于数据安全只能通过手工的备份方式来实现。

文件系统是操作系统和磁盘之间的一个桥梁,因此对磁盘的任何写操作,都要经过文件系统,然后才到磁盘。这可以说是优点,也可以说是个缺点,通过文件系统可以合理简单地组织磁盘数据,但在大量写操作下,文件系统本身也会产生开销,例如对元数据的维护、文件系统缓存等,这个桥梁就成了一个障碍。

不过,现在很多类型的文件系统都在做写入性能的改进,因而相对于文件系统提供的这些优点,缺点基本可以忽略

文件系统优点:保存形式外存,可随时更改;无需知道实际地址;既可顺序存取,也可直接存取;以记录为单位。 
文件系统缺点:造成数据冗余(原因:数据重复;消除方法:分析方法、规范化理论)

文件系统的使用流程

  • 存储介质选择
  • 磁盘分区
  • 文件系统创建
  • 挂载设备
2017-08-10 09:14:20 clusterally 阅读数 280

系统调用(System Call):
所有的操作系统都提供多种服务的入口点,程序由此向内核请求服务。这些可直接进入内核的入口点被称为系统调用。

为什么用户程序不能直接访问内核提供的服务?
在Linux中,为了更好地保护内核空间,程序的运行空间分为内核空间和用户空间(也就是常称的内核态和用户态),它们分别运行在不同的级别上,在逻辑上是相互隔离的。因此,用户进程在通常情况下不允许访问内核数据,也无法使用内核函数,它们只能在用户空间操作用户数据,调用用户空间的函数。

“文件”这个名词不陌生,什么是文件?
系统资源(内存、硬盘、一般设备、进程间通信的通道等)的一个抽象
对系统资源进行访问的一个通用接口。

采用这种“文件”的方式有什么好处?
对资源提供通用的操作接口,可以极大地简化系统编程接口的设计。

文件描述符
所有执行I/O操作的系统调用使用文件描述符来表示打开的文件。
文件描述符是一个非负整数。
文件描述符可以表示各种类型的打开的文件。
对文件的操作只要使用文件描述符即可指定所操作的文件。
只与程序有关,与相应文件无关,每个程序都有自己的文件描述符。
从小到大分配,从当前能用的描述符中最小的开始分配。
从0开始,0(代表标准输入),1(代表标准输出),2(代表标准错误),这三个描述符默认打开。

不带缓存的I/O操作
1.open
open()系统调用可以打开或创建一个文件。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
                    Returns file descriptor on success, or –1 on error

各参数及返回值的含义如下:
pathname:要打开或创建的文件名称。
flags:标志位,指定打开文件的操作方式。
mode:指定新文件的访问权限。(仅当创建新文件时才使用该参数)
返回值:若成功返回文件描述符,否则返回-1并设置变量errno的值。

flag基本取值:
O_RDOWRONLY:以只写方式打开文件。
O_RDWRNLY:以只读方式打开文件。
O_RDWR:以读写方式打开文件。
注意:上述三个常量必须指定且只能指定一个。
可选取值(部分):
O_CREAT:如果被打开的文件不存在,则自动创建这个文件。
O_EXCL:如果O_CREAT标志已经使用,那么当由pathname参数指定的文件已经存在时,open返回失败。
O_TRUNC:如果被打开的文件存在并且是以可写的方式打开的,则清空文件原有的内容。
O_APPEND:新写入的内容将被附加在文件原来的内容之后,即打开后文件的读写位置被置于文件尾。

mode可用八进制数代替,例:0777(可读可写可执行);0666(可读可写可执行)

2.read
read()系统调用从打开的文件中读取数据。

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
            Returns number of bytes read, 0 on EOF, or1 on error

各参数及返回值的含义如下:
fd:要读取的文件的描述符。
buf:读取到的数据要放入的缓冲区。
count:要读取的字节数。
返回值:若成功返回读到的字节数,若已到文件结尾则返回0,若出错则返回-1并设置变量errno的值。
注意:
1. 这里的size_t是无符号整型,ssize_t是有符号整型。
2. buf指向的内存空间必须事先分配好。

3.write
write()系统调用向打开的文件写数据。

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
            Returns number of bytes written, or1 on error  

各参数及返回值的含义如下:
fd:要写入的文件的描述符。
buf:要写入的数据所存放的缓冲区。
count:要写入的字节数。
返回值:若成功返回已写的字节数,出错则返回-1并设置变量errno的值。

4.close
close()系统调用关闭一个打开的文件。

#include <unistd.h>
int close(int fd);
            Returns 0 on success, or1 on error 

各参数及返回值的含义如下:
fd:要关闭的文件的描述符。
返回值:若成功返回0,出错则返回-1。
注:当一个进程终止时,内核会自动关闭它所有打开的文件。

5.lseek
lseek系统调用可以改变文件偏移量(File Offset)。文件偏移量是一个整数,表示距文件起始处的字节数。

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fildes, off_t offset, int whence);
Returns new file offset if successful, or1 on error

其中,参数whence必需是以下三个常量之一:
SEEK_SET:将文件偏移量设置在距文件开始处offset个字节。
SEEK_CUR:将文件偏移量设置在其当前值加offset,offset可正可负。
SEEK_END:将文件偏移量设置为文件长度加offset,offset可正可负。

复制文件:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define SIZE 1024

int main()
{
    // 打开要读的文件
    int fd1 = open("1.ppt", O_RDONLY);
    if (fd1 == -1)
    {
        perror ("open fd1");
        return -1;
    }


    int fd2 = open("2.ppt", O_WRONLY|O_CREAT, 0777);
    if (fd2 == -1)
    {
        perror ("open fd2");
        return -1;
    }

    int ret = 0;
    char buf[SIZE] = {0};
    while (ret = read (fd1, buf, SIZE))
    {
        if (ret == -1)
        {
            perror("read");
            break;
        }

        write (fd2, buf, ret);
    }
    printf ("文件复制完成\n");
    close (fd1);
    close (fd2);
    return 0;
}

设置文件的偏移指针

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define SIZE 1024

int main()
{
    // 打开要读的文件
    int fd = open("abc2", O_RDWR|O_CREAT, 0777);
    if (fd == -1)
    {
        perror ("open fd1");
        return -1;
    }

    // 设置这个文件的偏移指针
    lseek (fd, 20, SEEK_SET);

    char *buf = "hello";

    write (fd, buf, strlen(buf));

    close (fd);
    return 0;
}

I/O缓冲的解释(以printf()为例)
printf函数向标准输出写入多个字符,所写入的字符被放在一个用户态的缓冲区中,直到碰到一个换行符,系统才调用write函数将缓冲区中的数据写入标准输出,也就是说,在换行符之前写入的字符并不会立即出现在控制台屏幕上。

为什么要采用这种缓冲机制?
为了提高系统进行I/O操作的效率!
系统调用要请求内核的服务,会引发CPU模式的切换,期间会有大量的堆栈数据保存操作,开销比较大。如果频繁地进行系统调用,会降低应用程序的运行效率。有了缓冲机制以后,多个读写操作可以合并为一次系统调用,减少了系统调用的次数,将大大提高程序的运行效率。

所谓的标准I/O函数实际上是对底层系统调用的封装,最终读写设备或文件的操作仍需调用系统I/O函数来完成。

标准I/O函数也是使用文件描述符操作文件?
标准I/O函数并不直接操作文件描述符,而是使用文件指针。文件指针和文件描述符是一一对应的关系,这种对应关系由标准I/O库自己内部维护。文件指针指向的数据类型为FILE型,但应用程序无须关心它的具体内容。
在标准I/O中,一个打开的文件称为流(stream),流可以用于读(输入流)、写(输出流)或读写(输入输出流)。每个进程在启动后就会打开三个流,分别对应:stdin(标准输入流)、stdout(标准输出流)以及stderr(标准错误输出流)。

带缓冲的I/O操作
1.fopen
fopen用于打开一个标准I/O流

#include <stdio.h>
FILE *fopen(const char *path, const char *mode);

fopen打开由path指定的文件,并把它与一个文件流关联起来。mode参数指定文件的打开方式。
fopen执行成功返回一个非空的FILE*指针,失败时返回NULL。

“r”或”rb”:以只读方式打开。
“w”或”wb”:以只写方式打开,并把文件长度截短为零。
“a”或”ab”:以写方式打开,新内容追加在文件尾。
“r+”或”rb+”或”r+b”:以更新方式打开(读和写)。
“w+”或”wb+”或”w+b”:以更新方式打开,并把文件长度截短为零。
“a+”或”ab+”或”a+b”:以更新方式打开,新内容追加在文件尾。

注:字母b表示文件是一个二进制文件而不是文本文件。

2.fread
fread()用于从一个文件流里读取数据。

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

数据从文件流stream读到ptr指向的数据缓冲区里。size参数指定每个数据记录的长度,nmemb给出要传输的记录的个数。函数的返回值是成功读到数据缓冲区里的记录的个数(不是字节数)。

3、fwrite
fwrite()从指定的数据缓冲区里取出数据记录,并把它们写到输出流中。它的返回值是成功写入的记录个数。函数原型如下:

#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);

4、fclose
fclose()函数用于关闭指定的文件流。

#include <stdio.h>
int fclose(FILE *fp);

5、fflush
fflush()用于把文件流里所有未写出的数据立即写出。

#include <stdio.h>
int fflush(FILE *stream);  

使用这个函数可以确保在程序继续执行之前重要的数据都已经被写到磁盘上。
调用fclose函数隐含执行了一次fflush操作,所以不必在调用fclose之前调用fflush。

6、fseek
fseek()用于在文件流里为下一次读写操作指定位置。

#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);  

offset和whence参数的含义和取值与lseek函数完全一样,但lseek返回的是一个off_t数值,而fseek返回的是一个整数:0表示成功,-1表示失败并设置errno指出错误。

复制文件:

#include <stdio.h>

#define SIZE 1024

int main()
{
    // FILE *fp = fopen("abc", "rb+");
    FILE *fp = fopen("1.ppt", "ab+");
    if (fp == NULL)
    {
        perror ("fopen");
        return -1;
    }

    FILE *fp1 = fopen("2.ppt", "ab+");
    if (fp1 == NULL)
    {
        perror ("fopen");
        return -1;
    }

    char buf[SIZE] = {0};

    // feof 判断是否读到文件结尾,如果读到文件结尾,它返回一个非0 的值
    int ret;
    while (ret = fread(buf, sizeof(char), SIZE, fp))
    {   
        fwrite(buf, sizeof(char), ret, fp1);
    }

    if(ret == 0 && !feof(fp))
    {
        perror ("fread");
        return -1;
    }

    printf ("文件读取结束\n");

    fclose(fp);
    fclose(fp1);
    return 0;   
}

fgetc()、getc()和getchar()
fgetc()从文件流里取出下一个字节并把它作为一个字符返回。当它到达文件结尾或出现错误时,返回EOF。getc()和fgetc()一样,但它有可能被实现为一个宏。getchar()相当于getc(stdin)。

#include <stdio.h>
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);

fputc()、putc()和putchar()
fputc()把一个字符写到一个输出文件流中,它返回写入的值,如果失败,则返回EOF。类似fgetc()和getc(),putc()的作用也相当于fputc(),但它可能被实现为一个宏。putchar()相当于putc(c, stdout),它把单个字符写到标准输出。

注意:putchar和getchar都是把字符当作int类型而不是char类型来使用的,这就允许文件结尾EOF取值为-1。

#include <stdio.h>
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);

复制文件:

#include <stdio.h>

#define SIZE 1024

int main()
{
    // FILE *fp = fopen("abc", "rb+");
    FILE *fp = fopen("1.ppt", "ab+");
    if (fp == NULL)
    {
        perror ("fopen");
        return -1;
    }

    FILE *fp1 = fopen("2.ppt", "ab+");
    if (fp1 == NULL)
    {
        perror ("fopen");
        return -1;
    }

    char buf[SIZE] = {0};

    // feof 判断是否读到文件结尾,如果读到文件结尾,它返回一个非0 的值
    int ret;
    while (1)
    {   
        int c = fgetc(fp);
        if (c == EOF)
            break;
        fputc(c, fp1);
    }

    fclose(fp);
    fclose(fp1);
    return 0;   
}

读取数据:

#include <stdio.h>

#define SIZE 1024

typedef struct student
{
    int  id;
    char name[20];
}STU;


void write_data(STU *a)
{
    FILE *fp = fopen("student", "ab+");
    if (fp == NULL)
    {
        perror ("fopen");
        return;
    }

    // 要写入个数
    int len = sizeof(a)/sizeof(a[0]);
    fwrite(&len, sizeof(int), 1, fp);
    int i;
    for (i = 0; i < len; i++)
    {
        // 写入数据长度
        len = sizeof(a[i]);
        fwrite(&len, sizeof(int), 1, fp);

        // 写入数据
        fwrite(&a[i], sizeof(STU), 1, fp);
    }

    fclose(fp);
}

void read_data()
{
    FILE *fp = fopen("student", "ab+");
    if (fp == NULL)
    {
        perror ("fopen");
        return;
    }

    // 读记录的个数
    int count;
    fread (&count, sizeof(int), 1, fp);

    printf ("记录个数是:%d\n", count);
    int i;
    STU tmp;
    for (i = 0; i < count; i++)
    {
        int len;
        fread (&len, sizeof(int), 1, fp);

        // 读取数据
        fread (&tmp, len, 1, fp);

        printf ("id = %d, name = %s\n", tmp.id, tmp.name);
    }

    fclose(fp);
}

int main()
{


    int i;
    STU a[20];
    for (i = 0; i < 20; i++)
    {
        a[i].id = i;
        sprintf (a[i].name, "zhang%d", i);
    }

    // 写入数据
    // write_data(a);

    // 读数据
    read_data();

    return 0;   
}
2019-10-14 20:19:45 weixin_42648229 阅读数 249

操作系统的基本功能之一就是文件管理,而文件管理是由文件系统完成的。文件系统的目的就是实现数据的查询和存储,由于使用场景和环境的不同,linux支持多种文件系统,不同的文件系统支持不同的体系。

#include<stdio.h>
//fs文件操作:读,写,执行
//文件id的长度,同类项个数,所对应的真实的地址,及属性(单位,所挂载的通知,是否校验,
struct file_item_t
{
	int id;//文件id号
	int real_addr;//真实对应的地址
	int len;//数据的长度
	int same;//同类项的个数
	int attr;//每个字节对应的bit位,表示特定的属性(bit23- bit31:数据的单位;bit16- bit22:该id所挂载的通知;bit8-bit15:所挂载的ram;bit0:数据是否校验;bit1:同类项使能;
			 //bit2:设备页的大小,一般只有操作flash设备的时候,这个属性才会生效
};


//通信相关的参数,存在e2里面
struct file_item_t comm_para[]=
{
	{ 0x1200,0x3450, 4,3,6},
	{ 0x1200,0x3450, 4,3,6 },
	{ 0x1200,0x3450, 4,3,6 },
	{ 0x1200,0x3450, 4,3,6 },
	{ 0x1200,0x3450, 4,3,6 },
};

int comm_para_num = sizeof(comm_para) / sizeof(comm_para[0]); //e2相关的参数总的个数

struct file_item_t ram_para[] =
{
	{ 0x1200,0x3450, 4,3,6 },
	{ 0x1200,0x3450, 4,3,6 },
	{ 0x1200,0x3450, 4,3,6 },
	{ 0x1200,0x3450, 4,3,6 },
	{ 0x1200,0x3450, 4,3,6 },
};
int ram_para_num = sizeof(ram_para) / sizeof(ram_para[0]); //ram相关的总的个数

														   //对id的操作,无外乎就是读,写 ,执行。
struct file_ops_t
{
	int(*read)(int id, int*buf, int len, void *flag); //读操作
	int(*write)(int id, int*buf, int len, void *flag);//写操作
	int(*ctrl)(int id, int*buf, int len, void *flag);//直接控制
};

int e2_read(int id, int*buf, int len, void *flag)
{

}
int e2_write(int id, int*buf, int len, void *flag)
{

}
int e2_ctrl(int id, int*buf, int len, void *flag)
{

}
//对存在e2地址相关的参数的操作
struct file_ops_t e2_fife_ops = 
{
	   e2_read,
	   e2_write,
	   e2_ctrl,
};

int at_read(int id, int*buf, int len, void *flag)
{

}
int at_write(int id, int*buf, int len, void *flag)
{

}
int at_ctrl(int id, int*buf, int len, void *flag)
{

}

int comm_read(int id, int*buf, int len, void *flag)
{

}
int comm_write(int id, int*buf, int len, void *flag)
{

}
int comm_ctrl(int id, int*buf, int len, void *flag)
{

}

struct ram_file_ops_t
{
	int index;
	struct file_ops_t *ram_ops;
};

struct file_ops_t at_ram =
{
	at_read,
	at_write,
	at_ctrl,
};

struct file_ops_t comm_ram =
{
	comm_read,
	comm_write,
	comm_ctrl,
};

//对每个id进行的操作,无外乎读,写,执行。根据具体绑定的驱动,进行实际的操作
struct ram_file_ops_t ram_file_ops[] =
{
	{ 3, &at_ram },
	{ 4, &comm_ram},
};
int  ram_file_ops_num = sizeof(ram_file_ops) / sizeof(ram_file_ops[0]);

struct which_partition_t
{
	struct file_ops_t *ops; //对id的操作进行实际控制
	struct file_item_t *item; //文件id的参数信息
	int *item_num;//文件id的个数 
	int attr;//文件所挂载的驱动  //根据所挂载的驱动,进行实际的读,写,执行操作
};


//文件id总的分类
struct which_partition_t which_partition[] = 
{
	{ &e2_fife_ops,   comm_para,  &comm_para_num,   0},
	{ &ram_file_ops,  ram_para,   &ram_para_num,   1},
};

int which_partition_num = sizeof(which_partition)/sizeof(which_partition[0]);

//文件系统运行时,总的数据结构
struct file_run_item_t 
{
	int id;//文件的id号
	int current_addr;//当前的地址,因为操作备用地址时,是根据当前地址偏移出来的
	int len;//操作e2的长度
	int crc_addr;//备份地址的长度
	int crc_len;//crc备份地址的长度
	struct which_partion_t *which_partition; //id所挂载的驱动和id相关的属性及id的操作(读,写,执行)
	struct items_t         *which_items; //文件类型
	struct device_t        *which_device;//真正挂驱动,挂IIC和spi
};

struct file_run_item_t   file_run_item = 0;//全局的fs结构体为0
struct fifo_t
{
	int *ptr;//申请内存的大小
	int read_ptr;//读指针
	int write_ptr;//写指针
	int buf_size;//内存的大小
};
struct fifo_t **notify = 0;//notify申请的全局变量

int bitmap[16] = {1,2,4,8,0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000,0x8000};//分别为16个字节数据对应的bit位

//所挂载的通知
struct notify_para_t
{
	int notify_value;//通知的值
	int notify_index;//通知的索引
};

struct notify_para_t notify_para[]= 
{
	{0x4000,0x0006 },
	{0x6000,0x0007 },
};

int notify_para_num = sizeof(notify_para)/sizeof(notify_para[0]);//notify总的个数


//为每个模块关注的id进行申请内存
int notify_init(void)
{
	int  i = 0;
	int  j = 0;
	int  k = 0;
	int notify_index = 0;
	struct file_item_t *item = 0;
	int module_buf[16] = { 0 };//16个模块,每个模块要分配的内存的大小
	notify = (struct fifo_t**)malloc((sizeof(struct fifo_t*) * 16, 1));//给notify申请全局变量

	//遍历每个id,查找所有挂载此模块的id总的个数
	for (i = 0; i < which_partition_num; i++)
	{
		for (j = 0; j < which_partition[i].item_num; j++) //每个分区id总的个数
		{
			notify_index = (int)(item->attr >> 16);//判断该id是否挂载有通知
			if (notify_index) //挂载有通知
			{
				for (k = 0; k < notify_para_num; k++)
				{
					if (notify_para[k].notify_index == notify_index)
					{
						if (notify_para[k].notify_value && bitmap[i])//挂载有此模块的通知
						{
							module_buf[i]++;
						}				
					}

				}

			}

		}
	}

	//为每一个模块所挂载的通知,分配总的内存
	for (i = 0; i < 16; i++)
	{
		notify[i] = (struct fifo_t *)malloc(sizeof(struct fifo_t),1);
		notify[i]->ptr = (int)malloc(module_buf[i] * 2);//申请的缓存的大小
		notify[i]->read_ptr = 0;
		notify[i]->write_ptr = 0;
		notify[i]->buf_size = module_buf[i];
	}
}

int find_id_result(int id)
{
	int  i = 0, j = 0, k = 0;
	struct file_item_t *item = 0;
	int start = 0, end = 0, middle = 0, result = 0xA5;
	//采用二分法,在总的表格里面查找此id相关的信息
	for (i = 0; i < which_partition_num; i++)
	{
		item = which_partition[i].item;//所有参数相关的信息
		for (j = 0; j < which_partition[i].item_num; j++)
		{
			if ((id >= item->id) && (id <= item->id + which_partition[i].item_num + 1)) //要搜索的id在开始和结束里面,则从此表格中进行搜索
			{
				start = item->id;
				end = item->id + which_partition[i].item_num + 1;
				middle = (start + end) / 2;
				do
				{
					if (id > middle)
					{
						result = 1;//需要往后查询
						start = middle;
						middle = (start + end) / 2;
					}
					else if (id < middle)  //需要往前查询
					{
						result = 1;//需要往后查询
						end = middle;
						middle = (start + end) / 2;
					}
					else if (id == middle)
					{
						result = 0;
						file_run_item.which_items = item;
						file_run_item.which_partition = &which_partition[i];
						//根据id所给定的属性里面的信息,进行查找其绑定的驱动
                   
						return true;//正确查询到此id
					}

				} while (result != 0)//当index= 0时,表示查询到;>1时表示

			}
		}
	}
	return false;//从所有表格中进行查询,都没有查询到
}


//根据要搜索的id,在总的表格进行搜素,并且找到其所挂载的驱动
 int hfp_write(int id, int *buf, int len, void *flag)
{	
	int  i = 0, j = 0;
	struct file_item_t *item = 0;
	int notify_data = 0;
	if( true == find_id_result(id))//在表格中查询得到正确的id
	{
       //根据查询id所绑定的数据项的属性,来找其挂载的驱动
		 item = file_run_item.which_items;
		 int driver_attr = (int)(item->attr >> 16);
		 int notify_attr = (int)(item->attr >> 8);
		 if (driver_attr) //当配置有ram相关的驱动时,则从ram表格里面进行查找,进行执行对应的对应操作
		 {
			 for (i = 0; i < ram_file_ops_num; i++)
			 {
				 if (ram_file_ops_num[i].index == driver_attr) //查询得到对应的ramfs驱动
				 {
					 ram_file_ops_num[i].ram_ops->write(id, buf, len, flag);
				 }
				 return;
			 }		
		 }
		 //不是ram的话,就是e2或flash,直接操作就可以了
		 file_run_item.which_partition->ops->write(id, buf, len, flag); //真实的地址
	}
    //操作完要执行的动作后,进行通知其他模块。将此id写入到
	if (notify_attr) //挂载着通知
	{
		for (i = 0; i < notify_para_num; i++)
		{
			if (notify_attr == notify_para_num[i].notify_index)
			{
				notify_data = notify_para_num[i].notify_value;//获取到该id所关注的模块值
				for (j = 0; j < 16; j++) //看需要通知哪个模块
				{
					if (notify_data & bitmap[i])//需要通知到这个模块,对应的fifo里面写入id
					{
						notify[i]->ptr[wr++] =  id >> 8;
						notify[i]->ptr[wr++] =  id;                   
					}
				}
			}

		}
	}

}

Linux文件系统之二

阅读数 559

Linux文件系统目录

阅读数 1251

Linux文件系统目录

博文 来自: xietansheng

Linux文件系统

阅读数 7246

没有更多推荐了,返回首页