首先这里将对Linux下文件调用相关接口进行介绍,然后对比fd与FILE, 这里也会简单介绍下文件描述符和软硬链接相关的知识。首先复习C文件IO相关的操作。
C文件IO
FILE* fp = fopen("file", "rwa+"); //打开文件
fgetc(fp); fputc() //字符输入输出
fgets(buf,len,fp); fputs(); //字符串输入输出
fscanf(fp,"%s",buf); fprintf(); //流输入输出
fread(buf,size,num,fp); fwrite(); //文件读写
fclose(f); //关闭文件
详见:C语言文件IO相关操作
fwrite()函数:
1.作用:在C语言中fwrite()函数常用语将一块内存区域中的数据写入到本地文本。
注意:返回值随着调用格式的不同而不同:
(1) 调用格式:fwrite(buf,sizeof(buf),1,fp);
成功写入返回值为1(即count)
(2)调用格式:fwrite(buf,1,sizeof(buf),fp);
成功写入则返回实际写入的数据个数(单位为Byte)
注:写完数据后要调用fclose()关闭流,不关闭流的情况下,每次读或写数据后,文件指针都会指向下一个待写或者读数据位置的指针。
fread()函数:
1. 作用:从一个文件流中读取数据
注意:返回值随着调用格式的不同而不同:
(1) 调用格式:fread(buf,sizeof(buf),1,fp);
读取成功时:当读取的数据量正好是sizeof(buf)个Byte时,返回值为1(即count)
否则返回值为0(读取数据量小于sizeof(buf))
(2)调用格式:fread(buf,1,sizeof(buf),fp);
读取成功返回值为实际读回的数据个数(单位为Byte)
实例:
1 //使用fwrite给文件myfile写入数据
#include<stdio.h>
2 #include<string.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5
6 int main()
7 {
8 FILE* fp = fopen("myfile","w");
9 if(!fp)
10 {
11 perror("fopen");
12 exit(1);
13 }
14
15 const char* msg="hello world\n";
16 fwrite(msg, strlen(msg),1,fp);
17 return 0;
18 }
//使用fread读出myfile的数据
#include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<string.h>
5
6 int main()
7 {
8 FILE* fd = fopen("myfile", "r");
9 if(!fd)
10 {
11 perror("fopen");
12 exit(1);
13 }
14
15 char buf[1024];
16 ssize_t s=fread(buf,1,sizeof(buf),fd);
17
18 buf[s]=0;
19 printf("%s",buf);
20 fclose(fd);
21 return 0;
22 }
//3中方式输出信息到显示屏
#include<stdio.h>
2 #include<string.h>
3
4 int main()
5 {
6 const char* msg = "hello fwrite\n";
7 fwrite(msg,strlen(msg),1,stdout);
8 printf("hello printf\n");
9 fprintf(stdout,"hello fprintf\n");
10
11 return 0;
12 }
注:如果在一个函数中实现文件中读入数据在读出需要定义了两个FILE变量,一个用于write,一个用于read,写完后要close掉,然后再打开,然后读。如果直接使用一个FILE变量,会出错!
系统文件IO
1. int open(const char* path, int flags);
功能: 1. 打开文件 2. 创建文件
参数:path :要打开的文件
flag: 打开方式 //前三个只能选择其中一个
O_RDONLY:只读方式打开 、O_WRONLY: 只写方式打开、 O_RDWR: 读写方式打开 、O_TRUNC : 清空文件、O_APPEND: 追加
返回值: 失败-1 成功:文件描述符
//创建文件
int open(const char* path, int flags, mode_t mode);
参数:path: 要创建的文件名 flags: O_CREAT mode: 给文件的权限 0644
创建文件还要受unmask影响。 若文件存在,则不会清空文件。
如果文件存在不创建,如果文件不存在则创建。 flag : O_CREAT | O_RAWR | O_EXCL
2. int read(int fd, char* buf , size_t len);
功能:从fd文件中读取数据到buf所指向的空间,该空间的大小为len 注:没超过len有多少读多少
返回值:实际读取的字符个数
3. int write(int fd, const char* buf, size_t buf)
功能:往fd所指向的文件中写入数据,数据的起始地址为buf, 大小为len
4. int close(int fd )
功能:关闭读取的文件描述符
5. //定位
int lseek(int fd, off_t offset, int whence);
参数:offset : 偏移量 whence :SEEK_SET / SEEK_CUR / SEEK_END
返回值:从文件开头偏移多少字节。
对比FILE与fd
文件描述符
文件描述符就是从0开始的小整数。
当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。又因为每个进程和文件关联起来,所以每个进程都有一个指针*files, 指向一张表files_struct , 该表一个指针数组,每个元素都是一个指向打开文件的指针。本质上,文件描述符就是该数组的下标。
FILE结构体
IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。 所以C库当中的FILE结构体内部,必定封装来了fd. 库函数会自带缓冲区。
struct FILE
{
char *_ptr;//文件输入的下一个位置
int _cnt;//当前缓冲区的相对位置 (减少用户到内核的切换)
char *_base;//指基础位置(文件的起始位置)
int _flag;//文件标志
int _file;//文件的有效性验证
int _charbuf;//检查缓冲区状况,如果缓冲区则不读取
int _bufsiz;//文件的大小
char *_tmpfname;//临时文件名
int _fileno; //封装的文件描述符
};
总结:fd与FILE
1. fd是一个就是一个小整数
2. FILE结构体中,封装了文件描述符。
理解文件系统与inode
从上图可以看到,文件系统将属性和数据分开存放,那我们创建一个新文件又执行了那些操作。
1. 存储属性
内核先找到一个空闲的i结点。内核把文件信息记录到其中
2. 存储数据
该文件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800. 将内核缓冲区的第一块数据复制到300.
3. 记录分配的请况
4. 添加文件名到目录
硬链接和软链接
硬连接
从上面分析可以看出,真正找到磁盘上文件的并不是文件名,而是inode.
注:当我们在删除文件时干了连接事情:
1. 在目录中将对应的记录删除 2. 将硬连接数-1, 如果为0,则将对应的磁盘释放
软链接
硬连接通过inode引用另一个文件,软链接是通过名字引用另外一个文件。(所以两个文件的inode不同)