一、一切皆文件
Linux/UNIX操作系统把所有的服务、设备、协议都抽象成文件的形式,提供了一套统一而简单的文件IO的系统调用,简称系统的文件IO
也就是说在UNIX\Linux中任何对象都可以被当做是某种特殊的文件,都可以像访问文件一样,访问这些对象
文件分类:
普通文件 - 包括纯文本文件、二进制文件、各种压缩文件
目录文件 d 必须有读权限才能进入目录
块设备文件 b 保存大块数据的硬件设备,例如磁盘
字符设备文件 c 操作字符相关的设备 例如键盘、鼠标等
socket文件(套接字文件) s 通常用于网络通信
管道文件 p 用于进程间通信
链接文件 l 类似Windows的快捷方式
二、文件相关的系统调用
int open(const char *pathname, int flags);
功能:打开文件
pathname:文件的文件
flags:打开文件的方式
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 读写
O_APPEND 追加
O_CREAT 文件不存在则创建
O_EXCL 配合O_CREAT,如果文件存在则失败
O_TRUNC 文件如果存在,则清空打开
返回值:文件描述符,类似于FILE*,代表了一个打开的文件(>=0),负数表示失败
int open(const char *pathname, int flags, mode_t mode);
功能:创建文件
flags:O_CREAT
mode:
S_IRWXU 00700 拥有者 读写执行权限
S_IRUSR 00400 读权限
S_IWUSR 00200 写权限
S_IXUSR 00100 执行权限
S_IRWXG 00070 同组用户 读写执行权限
S_IRGRP 00040 读
S_IWGRP 00020 写
S_IXGRP 00010 执行
S_IRWXO 00007 其它用户 读写执行权限
S_IROTH 00004 读
S_IWOTH 00002 写
S_IXOTH 00001 执行
注意:可给宏名 也可以给 权限掩码 (0xxx) 八进制
int creat(const char *pathname, mode_t mode);
功能:创建文件
mode:同上
练习:测试fopen的各种打开方式与open的flags的对应情况
strace ./a.out
ssize_t write(int fd, const void *buf, size_t count);
功能:把内存中的数据写入的文件中
fd:文件描述符 open creat的返回值
buf:待写入的内存的首地址
count:要写入的字节数
返回值:成功写入的字节数
ssize_t read(int fd, void *buf, size_t count);
功能:从文件中读取数据到内存中
fd:文件描述符 open creat的返回值
buf:存储读取出来的数据的内存首地址
count:要读取的字节数
返回值:成功读取的字节数
分别使用标准IO和系统IO写入一百万个整数到文件,测试谁的时间更短?为什么?
结论:在同等数据的写入下,使用标准IO要比直接使用系统IO更快
原因:标准IO有缓冲区机制,在执行fwrite写文件时,数据不是直接调用系统IO写入磁盘,而是先存放在内存的缓冲区中,直到缓冲区满后才会调用一次系统IO全部写入到磁盘,因此对系统IO的调用次数、用户态内核态的转换次数都大大降低
而直接使用系统IO是没有缓冲区机制,因此耗时增加
解决:如果给系统IO也加上缓冲区机制,那么它的速度要比标准IO更快
随机读写:
每个打开的文件都有一个记录读写位置的指针,也称文件位置指针,对文件的读写时该指针会自动往后移动,因此顺序读写时无需操作
当需要去文件的任意位置进行读写时,才需要调整该指针的位置
标准IO:
int fseek(FILE *stream, long offset, int whence);
返回值:成功0 失败-1
系统IO:
off_t lseek(int fd, off_t offset, int whence);
返回值:成功返回调整后位置指针的位置 失败-1
系统IO中的文本文件的读写:
在系统IO中没有类似fprintf\fscanf函数,因此没有直接对文本文件读写的操作
但是可以通过把数据转换成字符串进行间接的文本文件读写
写文本文件
任意类型数据对象 sprintf 转换成字符串 write写入
读文本文件
按字符串格式读取到内存 sscanf 解析到任意类型数据中
文件描述符:
1、非负整数,代表了一个打开的文件
2、通过系统调用(open\creat)返回,该数值是被内核使用的
3、它在内核中对应一个内核对象,因为内核不能暴露它真实的地址,因此不能直接返回真实的文件地址,而是使用文件描述符来表示
4、内核中有一张记录了所有已打开的文件的二维表,文件描述符就是访问该表每行的下标,文件描述符也称为句柄,就是访问文件的凭证
5、内核中有三个默认长期打开的文件描述符
0 标准输入 宏STDIN_FILENO FILE* stdin
1 标准输出 STDOUT_FILENO stdout
2 标准错误 STDERR_FILENO stderr
文件描述符的重定向:
int dup(int oldfd);
功能:复制一个已经打开的文件描述符
返回值:返回一个当前进程没有使用的最小的文件描述符,该描述符与oldfd对应一个文件
int dup2(int oldfd, int newfd);
功能:复制oldfd文件描述符为newfd
文件同步:
1、在写入数据时,内存与磁盘之间有一块缓冲区,目的是为了降低磁盘的读写次数、提高读写效率
2、但是这种机制带来的后果是磁盘中的数据与实际写入的数据可能不符合,系统提供了三个函数让缓冲区中的数据立即写入磁盘,称为文件同步
fsync\sync\fdataync
文件属性:
int stat(const char *pathname, struct stat *buf);
功能:根据文件路径获取文件的属性
buf:输出型参数
int fstat(int fd, struct stat *buf);
功能:根据文件描述符获取文件的属性
int lstat(const char *pathname, struct stat *buf);
功能:根据文件路径获取软链接文件的属性
struct stat {
dev_t st_dev; // 设备ID
ino_t st_ino; // inode节点号
mode_t st_mode; // 文件的类型和权限
nlink_t st_nlink; // 硬链接数
uid_t st_uid; // 用户ID
gid_t st_gid; // 组ID
dev_t st_rdev; // 特殊设备ID
off_t st_size; // 总字节数
blksize_t st_blksize; // IO块总字节数
blkcnt_t st_blocks; // 占用大小512字节的内存块数量
struct timespec st_atim; // 最后访问时间
struct timespec st_mtim; // 最后修改时间
struct timespec st_ctim; // 最后状态修改时间
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
文件的权限:
int access(const char *pathname, int mode);
功能:测试当前用户对文件的权限
pathname:想要测试的文件路径
mode:想要测试的权限
F_OK 测试文件是否存在
R_OK 测试文件是否有读权限
W_OK 测试文件是否有写权限
X_OK 测试文件是否有执行权限
返回值:存在返回0 不存在返回-1
int chmod(const char *pathname, mode_t mode);
功能:根据文件路径修改文件权限
mode:由三个八进制数组成的权限掩码
0xxx
0644 0666 普通文件
0755 可执行文件
int fchmod(int fd, mode_t mode);
功能:根据文件描述符修改文件权限
权限屏蔽码:
如果想让新创建(open\creat)的文件不具备某些权限,可以通过设置权限屏蔽码进行屏蔽权限
查看命令 umask 查看当前终端的权限屏蔽码
注意:可以通过chmod命令、函数可以无视权限屏蔽码
修改权限屏蔽码:
命令:umask 0xxx 修改成0xxx
注意:只是当前终端临时有效,如果想要长期有效需要修改配置文件
函数:mode_t umask(mode_t mask);
mask:新的屏蔽码
返回值:原来的屏蔽码
注意:该函数只是当前进程中生效,进程结束后就失效了
修改文件的大小:
int truncate(const char *path, off_t length);
功能:根据文件路径截取文件长度
length:截取后文件的长度(字节)
int ftruncate(int fd, off_t length);
功能:根据文件描述符截取文件长度
文件删除、重命名:
int remove(const char *pathname);
功能:由C标准库提供的删除文件函数
int unlink(const char *pathname);
功能:操作系统提供的删除文件函数
remove底层调用unlink、rmdir函数
1、如果删除的是硬链接文件或者原文件本身且文件是关闭的,则是把该文件的硬链接数减1,然后清除该文件的inode信息
2、当文件的硬链接数减为0时,系统就把该文件的block权限释放,可以用于存储其它文件的数据
3、如果删除软链接文件,则删除的是软链接文件的inode信息,而不会删除原文件的数据,没有任何一个函数可以通过软链接文件删除源文件
int rename(const char *oldpath, const char *newpath);
功能:重命名文件
什么是软硬链接文件?
Linux文件系统会把分区分为两大部分:
inode信息块区:
每块默认128B,记录某个文件的文件权限、大小、所有者、修改时间等属性信息以及该文件的block信息
block数据块区:
每块默认4Kb,记录了文件的真正内容数据、文件名
每个文件有且只有一个inode信息块以及若干个block数据块,读取文件file需要借助所在目录文件的block中记录的file文件的inode号和文件名,来找到file的inode信息块,从而读取file文件的block数据块
硬链接文件:
没有属于自己的inode和block,只是在不同的目录下复制链接的源文件的inode信息块,通过该复制的inode信息块访问源文件的block数据块
软链接文件:
软链接文件会创建自己的新的inode和block块,在软链接文件的block中不存储源文件的数据,而是存储源文件的inode号,从而借助源文件的inode来访问源文件的block
区别:
1、删除源文件,只是删除源文件的inode信息块中的内容,对应的block的数据不会清理,所以硬链接文件依然可以访问,但是软链接文件就无法访问了
2、当文件的硬链接数删除到0时,文件才算被真正的删除了
3、修改硬链接文件的内容,源文件也会随之修改
4、硬链接不能链接目录、软链接可以
5、硬链接不能跨文件系统使用,而软链接可以
int link(const char *oldpath, const char *newpath);
功能:创建硬链接文件
int symlink(const char *target, const char *linkpath);
功能:创建软链接文件 文件类型 l
目录操作:
int mkdir(const char *pathname, mode_t mode);
功能:创建空目录
mode:目录的权限,必须有执行权限才能进入目录
返回值:成功0 失败-1
int rmdir(const char *pathname);
功能:删除空目录
int chdir(const char *path);
功能:修改当前的工作路径为path 相当于cd
char *getcwd(char *buf, size_t size);
功能:获取当前进程的工作路径 相当于pwd
DIR *opendir(const char *name);
功能:根据目录路径打开目录
返回值:成功返回一个目录流对象
DIR *fdopendir(int fd);
功能:根据目录文件描述符打开目录
返回值:成功返回一个目录流对象
注意:DIR目录流对象中记录了该目录中所有文件的信息
struct dirent *readdir(DIR *dirp);
功能:从目录流dirp中读取一条文件信息并返回
struct dirent {
ino_t d_ino; // inode号
off_t d_off; // 距离下一条信息的字节数
unsigned short d_reclen; // 当前信息的字节数
unsigned char d_type; // 文件类型
char d_name[256]; // 文件名
};
注意:readdir读取完一条信息后,会自动的指向下一条信息,只需要继续执行readdir即可读取下一条,直到返回值为NULL读取完毕
标签:文件,const,int,char,mode,操作,权限
From: https://www.cnblogs.com/wangqiuji/p/17617906.html