文件操作与系统调用

在 Linux 系统中:一切皆文件
它把一切资源都看作是文件,包括硬件设备,通常称为设备文件

文件系统

为了高效地存储和管理数据,文件系统在存储介质上建立了一种组织结构,这些结构包括操作系统引导区、目录和文件,就如同图书馆给不同类的书籍进行分类、编号,放在不同的书架上。

文件描述符

文件描述符(file descriptor)就是内核为了高效管理这些已经被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符来实现

Linux中规定每一个文件对应一个索引,这样要操作文件的时候,我们直接找到索引就可以对其进行操作

Linux还规定系统刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。这意味着如果此时去打开一个新的文件,它的文件描述符会是3

Linux内核对所有打开的文件有一个文件描述符表格,里面存储了每个文件描述符作为索引与一个打开文件相对应的关系,简单理解就是下图这样一个数组,文件描述符(索引)就是文件描述符表这个数组的下标,数组的内容就是指向一个个打开的文件的指针
在这里插入图片描述

Linux内核维护了3个数据结构:

进程级的文件描述符表
系统级的打开文件描述符表
文件系统的i-node表

一个 Linux 进程启动后,会在内核空间中创建一个 PCB 控制块,PCB 内部有一个文件描述符表(File descriptor table),记录着当前进程所有可用的文件描述符,也即当前进程所有打开的文件。进程级的描述符表的每一条记录了单个进程所使用的文件描述符的相关信息,进程之间相互独立,一个进程使用了文件描述符3,另一个进程也可以用3。

除了进程级的文件描述符表,系统还需要维护另外两张表:打开文件表、i-node 表。这两张表存储了每个打开文件的打开文件句柄(open file handle)。一个打开文件句柄存储了与一个打开文件相关的全部信息。

注:
1.同一个进程的不同文件描述符可以指向同一个文件;不同进程可以拥有相同的文件描述符;不同进程的同一个文件描述符可以指向不同的文件(一般也是这样,除了 0、1、2 这三个特殊的文件);不同进程的不同文件描述符也可以指向同一个文件

2.文件描述符是特殊的索引,实际上就是进程中file_struct结构体成员fd_array的数组下标

文件打开模式

主模式:

O_RDONLY:只读模式
O_WRONLY:只写模式
O_RDWR:读写,模式

副模式:

O_CREAT:当文件不存在,需要去创建文件
O_APPEND:追加模式
O_DIRECT:直接IO模式
O_SYNC:同步模式
O_NOBLOCK:非阻塞模式

文件操作(C标准库)

标准库实际是对系统调用再次进行了封装。使用 C 标准库编写的代码,能方便地在不同的系统上移植

fopen 函数
作用:用于打开或创建文件,返回相应的文件流

#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
pathname:用于指定要打开或创建的文件名
mode:用于指定文件的打开方式,注意该参数是一个字符串,输入时需要带双引号
	“r”:以只读方式打开,文件指针位于文件的开头。
	“r+”:以读和写的方式打开,文件指针位于文件的开头。
	“w”:以写的方式打开,不管原文件是否有内容都把原内容清空掉,文件指针位于文件的开头。
	“w+”:同上,不过当文件不存在时,前面的“w”模式会返回错误,而此处的“w+”则会创建新文件。
	“a”:以追加内容的方式打开,若文件不存在会创建新文件,文件指针位于文件的末尾。与“w+”的区别是它不会清空原文件的内容而是追加。
	“a+”:以读和追加的方式打开,其它同上。

fread 函数
作用:从文件流中读取数据

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
ptr :读取到的数据会被存储在 ptr 指向的数组中
nmemb :读取 nmemb 项数据,
size:每项的大小为 size
stream:使用 fopen 打开的文件流,fread 通过它指定要访问的文件

fwrite 函数
作用:把数据写入到文件流
把 ptr 数组中的内容写入到 stream 文件流,写入的项数为 nmemb,每项
大小为 size,返回值为成功写入的项数(项的单位为 size)

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

fclose 函数
作用:关闭指定的文件流,关闭时它会把尚未写到文件的内容都写出
因为标准库会对数据进行缓冲,所以需要使用 fclose 来确保数据被写出

#include <unistd.h>
int close(int fd);

fflush 函数
作用:把尚未写到文件的内容立即写出
常用于确保前面操作的数据被写入到磁盘上,fclose 函数本身也包含了 fflush 的操作

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

fseek 函数
作用: 设置下一次读写函数操作的位置

#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
 offset :用于指定位置
 whence:定义了 offset 的意义
	SEEK_SET: offset 是一个绝对位置。
	SEEK_END: offset 是以文件尾为参考点的相对位置。
	SEEK_CUR: offset 是以当前位置为参考点的相对位置。

事例:

#include <stdio.h>
#include <string.h>

//要写入的字符串
const char buf[] = "filesystem_test:Hello World!\n";
//文件描述符 文件流
FILE *fp;
char str[100];


int main(void)
{
    
    
	//创建一个文件
	fp = fopen("filesystem_test.txt", "w+");
	//正常返回文件指针
	//异常返回 NULL
	if(NULL == fp)
	{
    
    
		printf("Fail to Open File\n");
		return 0;
	}
	//将 buf 的内容写入文件
	/每次写入 1 个字节,总长度由 strlen 给出
	fwrite(buf, 1, strlen(buf), fp);
	
	//写入 Embedfire
	//每次写入 1 个字节,总长度由 strlen 给出
	fwrite("Embedfire\n", 1, strlen("Embedfire\n"),fp);
	//把缓冲区的数据立即写入文件
	fflush(fp);
	
	//此时的文件位置指针位于文件的结尾处,使用 fseek 函数使文件指针回到文件头
	fseek(fp, 0, SEEK_SET);
	
	//从文件中读取内容到 str 中
	//每次读取 100 个字节,读取 1 次
	fread(str, 100, 1, fp);
	
	printf("File content:\n%s \n", str);
	//关闭文件流
	fclose(fp);
	
	return 0;
}

文件操作(系统调用)

Linux 提供的文件操作系统调用常用的有 open、 write、 read、 lseek、 close 等

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,int perms)

pathname:要打开或创建的文件名
flag:指定文件的打开方式
mode:当 open 函数的 flag 值设置为 O_CREAT 时,必须使用 mode 参数来设置文件与用户
相关的权限。
当前用户:
	S_IRUSR 用户拥有读权限
	S_IWUSR 用户拥有写权限
	S_IXUSR 用户拥有执行权限
	S_IRWXU 用户拥有读、写、执行权限
返回值:文件描述符

close 函数
作用:关闭该 fd 文件描述符对应的文件

#include <unistd.h>
int close(int fd);

read 函数
作用:从文件中读取若干个字节的数据,保存到数据缓冲区 buf 中,并返回实际读取的字
节数

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
fd:文件对应的文件描述符,可以通过 fopen 函数获得
buf:指向数据缓冲区的指针
count:读取多少个字节的数据

write函数
作用:往文件写入内容,并返回实际写入的字节长度

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
fd:文件对应的文件描述符,可以通过 fopen 函数获得。
buf:指向数据缓冲区的指针;
count:往文件中写入多少个字节。

lseek函数
用于设置文件指针的位置(设置文件读写位置),并返回文件指针相对于文件头的位置

#include <unistd.h>
off_t lseek(int fd,off_t offset,int whence)
offset :用于指定位置
whence:定义了 offset 的意义
	若whence为SEEK_SET,基准点为文件开头
	若whence为SEEK_CUR,基准点为当前位置
	若whence为SEEK_END,基准点为文件末尾

sync函数
作用:页缓存和回写,强制把修改过的页缓存区数据写入磁盘

#include <unistd.h>
void sync(void);

猜你喜欢

转载自blog.csdn.net/qq_53144843/article/details/120162496
今日推荐