9.1 文件初探
9.1.1 文件与文件系统的概念
所谓文件,是指一组相关数据的有序集合。在Linux系统中,文件中的数据与数据之间的关系是由使用文件的应用程序建立和解释的。他们仅在一个文件中有关系。
文件系统是只文件数据结构和管理文件的程序集合,除此之外,还包括ext2、ext3等分区格式和某个具体的目录。
9.1.2 文件的属性
在Linux系统中,文件是很重要也是很浮躁的。每一个文件都存在其特有的属性,包括文件类型和文件权限两个方面。
1)文件类型
①根据处理方法不同,分为缓冲区文件和非缓冲区文件
所谓缓冲区文件,是指系统自动地在内存区为每一个正在使用的文件开辟一个缓冲区。而非缓冲区文件是指不自动地开辟确定大小的缓冲区,有程序本身为每个文件设定缓冲区大小。
从内存想磁盘输出数据时,必须先送到内存中的缓冲区,待装满缓冲区后,再讲数据一起送到磁盘。
②根据数据的组织形式不同,分为文本文件和二进制文件
③文件可以根据其存放数据的作用的不同,将其分为普通文件、目录文件、链接文件、设备文件和管道文件。
9.1.3 文件的相关信息
在Linux系统中,每一个文件都存放在一个目录下,通过一个与文件相关联的索引节点保存文件的一些属性信息。与文件相关的信息主要包括文件的目录结构、索引节点和文件中存放的数据
1)文件的目录结构
系统中的所有文件都存放在根目录root(/)下,所谓的目录文件就像一颗大树,从根目录中又会分支出很多子目录,在子目录下又会分出很多下一级目录或者普通文件。系统中的每个目录都处于一定的目录结构中,在这个目录结构中含有所有的目录项的列表,每一个目录项都是由这个目录的名称和索引节点构成的,开发人员可以通过这个目录文件的名称访问该目录项下的内容,然后通过索引节点可以获取该文件自身的一些属性信息。
2)索引节点
前面多次提及通过文件的索引节点(inode)可以获取这个文件自身的一些信息,在Linux系统中,这些索引节点所包含的信息被封装在stat这个结构体中。
struct stat {
dev_t st_dev; /* ID of
device containing file */
ino_t st_ino; /*
inode number */
mode_t st_mode; /*
protection */
nlink_t st_nlink; /* number of
hard links */
uid_t st_uid; /* user ID of
owner */
gid_t st_gid; /* group ID of
owner */
dev_t st_rdev; /* device ID (if
special file) */
off_t st_size; /* total
size, in bytes */
blksize_t st_blksize; /*
blocksize for file system I/O */
blkcnt_t
st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
3)文件中存放的数据
文件是由一组相关数据有序集合而成的。文件中的这些相关数据都存储在由索引节点指定的位置中,但是也有个别特殊文件没有存储文件中数据的硬盘区域,如设备文件。
9.2 文件的相关操作
在Linux系统中,文件的操作有很多种,如文件的I/O操作、修改文件属性的操作、赋值文件描述符操作,以及一些对文件进行控制的相关操作等。在Linux系统中,文件的I/O操作有两种操作模式,一种是基于文件描述符的I/O操作,另一种是基于文件流的I/O操作。
9.2.1 修改文件属性
1)系统提供了chown()和fchown()函数修改指定文件的所有者识别号和用户组识别号,系统调用函数的定义形式如下:
#include<sys/types.h>
#include<unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group)
int fchown(int fd, und_t owner, gid_t group)
这两个系统调用函数都是用于修饰文件的所有者识别号和用户组识别号。其中,函数chown()中参数pathname代表的是文件的绝对路径或相对路径;函数fchown()中的参数fd表示文件的文件描述符。通过这两个参数就制定了需要操作的文件。
2)改变文件的访问权限
在Linux系统中可以通过调用chmod()和fchmod()函数改变文件的访问权限。文件的访问权限就是前面介绍过的读的权限、写的权限和执行的权限。
#include<sys/types.h>
#include<sys/stat.h>
int chmod(const char *path, mode_t mode)
int fchmod(int fildes,mode_t mode)
3)改变文件的名称
在Linux系统中,还提供了系统调用函数rename(),用于修改文件的位置或者文件的名字:
#include<stdio.h>
int rename(const char *oldpath, const char *newpath)
4)改变文件的长度
在Linux系统中存在这样两个系统调用函数,用于将某一个文件修改成指定的长度。
#include<sys/types.h>
#include<unistd.h>
int truncate(const char *path, off_t length)
int ftruncate(int fd, off_t length)
对于ftruncate()函数,文件必须是以写的形式打开的;而对于truncate()函数,文件必须是可写的。
9.2.2 复制文件描述符
在Linux系统中,提供了dup()和dup()两个函数,用于复制文件的描述符。
#include<unistd.h>
int dup(int oldfd)
int dup2(int oldfd, int newfd)
这两个函数主要实现了复制一份参数oldfd表示的文件描述符,冰箭文件描述符返回。
复制出来的文件描述符与原来的文件描述符指的是同一文件,共享所有的锁定、读写位置和各项权限或旗标。
如果函数调用成功,返回值为最小及尚未使用的文件描述符;否则返回值为-1,并设置适当的errno值
dup返回的新文件描述符是该进程未使用的最小文件描述符。dup2可以用newfd参数指定新描述符的数值。如果newfd当前已经打开,则先将其关闭再做dup2操作,如果oldfd等于newfd,则dup2直接返回newfd,而不用先关闭newfd。
9.2.3 获取文件信息
在Linux系统中,提供了三个系统调用函数,用于获取文件的信息。
#include <sys/types.h>
#include
<sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char
*path, struct stat *buf);
上述3个函数主要是通过指针或者文件描述符所指定的文件进行相关信息的获取,然后将获取到的信息写入到参数buf中。
再通过系统调用函数获取文件信息时,即使对该文件没有读取权限,也可以获取到该文件的信息。
对于stat()和lstat(),如果需要获取出于某个目录下的文件信息,则要求对该文件所处的所有上级目录有执行的权限
9.2.4 文件的其他操作
1)将缓冲区数据写回磁盘
系统调用函数fsync()实现了将缓冲区数据写回磁盘
#include<unistd.h>
int fsync(int fd)
2)锁定文件
系统调用函数flock()主要实现了对文件做各种锁定或解除锁定的动作,该函数的定义形式如下:
#include<sys/file.h>
int flock(int fd, int operation)
9.3 特殊文件的操作
9.3.1 目录文件的操作
目录文件是比较特殊的一种文件,用于存放文件名及其相关信息,是内核中用于组织文件系统的基本节点。Linux系统从空间上看,是由文件组成的,每一部分内容都存放到一个指定的目录中。目录文件就像一棵大树,从根处可以分支成许多叉,而Linux系统中的所有文件都存放在根目录下,以”\"表示.
1)获取当前的工作目录
#include<unistd.h>
char *getcwd(char *buf, size_t size)
2)更改当前工作的目录
#include<unistd.h>
int chdir(const char *path)
int fchdir(int fd)
3)创建和删除目录
#include<sys/stat.h>
#include<sys/types.h>
int mkdir(const char *pathname, mode_t mode)
int rmdir(const char *pathname)
4)打开与关闭文件
在Linux系统中,目录文件作为一种特殊的文件,可以被打开、关闭一季度去。系统提供了系统调用函数opendir()和closedir()用于打开和关闭目录文件。就像对普通文件的操作一样,打开之后,当不再使用时,需要及时关闭文件,否则会造成文件的丢失。
#include<sys/types.h>
#include<dirent.h>
DIR *opendir(const char *name)
int closedir(DIR *dir)
5)读取目录文件
对目录文件打开后,必然要对文件进行读取等操作。因此,系统提供了系统调用函数用于读取目录文件中的数据。
#include<sys/types.h>
#include<dirent.h>
struct dirent *readdir(DIR *dir)
参数dir用于存放目录流
readdir()函数返回餐宿dir目录流的下个目录的进入点。
函数的返回值为下个目录的进入点,数据类型为dirent结构体。
9.3.2 连接文件的操作
在Linux系统中,连接文件是一个特殊文件,类似于Windows系统中的快捷方式,是可以快速定位不同目录下文件的方法。系统中存在两种链接文件,一种是硬链接,另一种是符号链接。
1)硬链接
硬链接是依附于索引节点而存在的。在Linux系统中,使用硬链接需要注意一下几点
①目录无法创建硬链接,只有文件才可以创建硬链接
②硬链接不能跨越文件系统,即不能为处在不同分区上的文件创建硬链接。
在Linux系统的终端下,可以通过ln命令创建一个文件的硬链接。连接文件相当于源文件的一个快捷方式,两个文件的索引节点值是一致的。当删除源文件是,硬链接文件依然指向原来的索引节点值,即索引节点没有被删除。因此,想要删除文件的数据,需要将文件以及所有的硬链接一同删除。
在Linux系统中,提供了相关的系统调用函数,用于创建一个新的硬链接和删除一个硬链接。
#include<unistd.h>
int link(const char *oldpath, const char *newpath)
int unlink(const char *pathname)
2)符号链接
符号链接是通过文件名来指向另一个文件的,因此符号链接文件和源文件的索引节点号并不同。一旦将源文件删除,那么符号链接文件就会无效。符号链接较硬连接方便很多,可以给任意类型的文件简历符号链接。
在Linux系统下,提供了系统调用函数symlink()和readlink()用于对符号链接进行创建和打开操作。
#include<unistd.h>
int symlink(const char *oldpath, const char *newpath)
sszie_t readlink(const char *path, char *buf, size_t bufsiz)
删除符号链接文件的系统效用函数与删除硬链接文件的系统调用函数是相同的,都使用unlink()函数。
9.3.3 设备文件
Linux系统与Windows系统不同,其将设备当做文件来处理。因此,在Linux系统中,对文件的读写操作都可以应用到设备文件中,可以把设备文件当做普通文件来处理。在访问外部设备时,不需要系统提供一种标准接口与外部设备相关联,只需要像访问普通文件一样来访问设备文件。
在Linux系统中,很多东西都是以文件的形式存在的,因此设备文件存在一个抽象化的设备目录,如"/dev",关于文件的读写、或者控制操作等,都可以应用到设备文件上。但是,有个别的外部设备文件在操作时需要特别注意,如串口和声卡等外部设备。
在Linux系统下,不仅可以通过C语言编程实现控制终端以及对串口的读写操作,还可以控制扬声器发生和声卡等外部设备播放音频文件等。
9.4 小结
本章主要针对Linux系统中的文件操作进行了详细的讲解。在Linux系统中,不仅包含普通的文件,还包含一些特殊的文件,如目录文件、链接文件、管道文件和设备文件等。因此,本章在开始部分就对系统中的文件和文件系统的概念进行了分析,并对文件的一些属性的相关信息进行了说明。带着对文件的初步了解,深入到Linux下的关于文件的C语言编程中的应用,在本章中结合实例对文件的一些特殊操作进行了详细的讲解。
由于本书的方向是Linux系统下的C语言编程,因此对于Linux中的文件和文件系统的相关知识只是做了初步的介绍。