首页 > 代码库 > 0723------Linux基础----------文件 IO之文件的打开
0723------Linux基础----------文件 IO之文件的打开
1.文件的打开
1.1 open 和 fopen 。open 返回的是文件描述符,而fopen 返回的是文件指针,二者的第二个参数也不同,一个是宏定义的,一个是字符串。因此在书写的时候要特别注意。
int fd = open("test.txt", O_RDONLY);
FILE *fp = fopen("test.txt", "r");
1.2 FILE * 和 fd 之间的转化
1.2.1 从FILE * 转化成 fd。 fileno函数用于将一个文件指针转化成文件描述符,此时就可以使用linux的函数。
FILE *fp = fopen("test.txt", "r"); int fd = fileno(fp);
1.2.2 从fd 转化成 FILE * 。 fdopen函数用于将一个文件描述符转化成指针,此时就可以使用标准的文件函数。这里fp文件指针的打开模式要和fd文件描述符的打开模式吻合,即只读都是只读。
int fd = open("test.txt", O_RDONLY);FILE *fp = fdopen(fd, "r");
1.3 常用的open标志组合:
a) O_RDONLY:代表只读
b) O_WRONLY | O_CREAT:以只写方式打开,文件不存在则创建;
c) O_WRONLY | O_CREAT | O_APPEND:只写打开,文件不存在则创建,文件存在则进行追加;
d) O_WRONLY | O_CREAT | O_TRUNC:只写打开,不存在则创建,文件存在则清空;
e) O_WRONLY | O_CREAT | O_EXCL:只写打开,不存在则创建,文件存在则打开失败。
1.4 文件指针 stdin, stdout,stderr 在内部一定是调用了STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO这三个文件描述符(因为linux底层是用文件描述符实现的)。
2.进程打开一个文件描述符涉及到的数据结构
2.1 Linux进程打开一个文件描述符,涉及到三个数据结构:
a) 进程表项,归单个进程所有,记录该进程打开的所有的文件描述符。
b) 文件表项,由内核管理,可对应一次打开文件操作,里面记录打开文件的标志、文件偏移量以及指向文件的指针,以及该结构的引用计数(此时有多少个fd指向它)。
c) V节点项,每项对应一个文件,记录着该文件的信息(大小、创建时间、修改时间、所有者、权限)。
2.2 打开文件时三个数据结构的不同情形:
a) 一个进程打开两个不同的文件,进程表项 1 个,文件表项 2 个,V结点 2 个;
b) 一个进程两次打开同一个文件,进程表项 1 个,文件表项 2 个(二者不共享文件偏移量,因此读写不会干扰),V结点 1 个;
c) 两个进程打开同一文件,进程表项 2 个, 文件表项 2 个, V结点 1 个;
d) 一个进程打开一个文件,然后用dup 复制了一个文件描述符,此时,进程表项 1 个, 文件表项 1 个(其中引用计数为 2,这与fork一个子进程相同), V结点 1 个。
3.lseek函数
3.1 lseek 函数用于为一个打开的文件设置当前文件偏移量, 这个当前文件偏移量属性存在于文件表项里面,每一个打开的文件描述符都和一个当前文件偏移量相关联。本例用于返回当前偏移量的值。
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0)/* * lseek 函数 * */int main(int argc, const char *argv[]){ int fd = open("test.txt", O_RDONLY); if(fd == -1){ ERR_EXIT("open"); } char buf[1024] = {0}; read(fd, buf, 5); printf("buf: %s\n", buf); //设置文件的偏移量 该偏移量存在于 文件表项中 //返回当前偏移量距离文件头的字节数 //这里相当于 获取当前的 文件偏移量 off_t len = lseek(fd, 0, SEEK_CUR); printf("offset = %d\n", (int)len); return 0;}
3.2 文件通常的读写操作都是从当前文件偏移量开始的。本例中,先将打开的文件的偏移量设置为后移6个字节的值,那么接下来read的时候就从开始当前文件偏移量处(本例中为6)读取。
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0)/* * lseek 函数 * */int main(int argc, const char *argv[]){ int fd = open("test.txt", O_RDONLY); if(fd == -1){ ERR_EXIT("open"); } off_t len = lseek(fd, 6, SEEK_CUR); //将当前的偏移量设置为 当前偏移量+6 printf("offset = %d\n", (int)len); char buf[1024] = {0}; read(fd, buf, 5); printf("buf: %s\n", buf); len = lseek(fd, 0, SEEK_CUR); printf("offset = %d\n", (int)len); return 0;}
3.3 lseek 还可以获取文件的大小。这里用 od -c filename 查看文件的内容 可以看到文件的大小,注意左边的是8进制。
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0)/* * lseek 函数 * 获取文件的大小 */int main(int argc, const char *argv[]){ int fd = open("test.txt", O_RDONLY); if(fd == -1){ ERR_EXIT("open"); } off_t len = lseek(fd, 0, SEEK_END);// 设置偏移量为 SEEK_END printf("offset = %d\n", (int)len); return 0;}
3.4 lseek 设置文件偏移量时,如果当前没有数据,就写0。
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0)/* */int main(int argc, const char *argv[]){ int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC); if(fd == -1){ ERR_EXIT("open"); } write(fd, "hello", strlen("hello")); off_t len = lseek(fd, 10, SEEK_CUR); printf("offset = %d\n", (int)len); write(fd, "world", strlen("world")); close(fd); return 0;}
3.5 文件空洞
3.5.1 什么是文件空洞? 当文件偏移量超过文件大小的时候,就产生了文件空洞,它被记录在文件信息中,但是在磁盘上并不占据过多地的磁盘空间。例如我们在文件中偏移1G的大小,那么在文件信息中就记录了 1G 的空洞,但是在磁盘上并不占据实际空间,如下图所示。
3.5.2 程序示例和程序运行后的文件信息和磁盘信息。
#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0)/* * 文件空洞 * */int main(int argc, const char *argv[]){ int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC); if(fd == -1){ ERR_EXIT("open"); } char buf[] = "hello"; write(fd, buf, strlen(buf)); off_t len = lseek(fd, 1024*1024*1024, SEEK_CUR); printf("OFF_SET = %d\n", (int)len); write(fd, "world", strlen("world")); close(fd);