- IO就是input和output,文件IO就是文件的读写。文件没有打开时是存放在块设备中的文件系统里的,这样的文件叫做静态文件。
- 操作一个文件一般是先打开(open)一个文件,得到这个文件的文件描述符,然后对文件进行读写(read/write)或其他操作,最后关闭(close)文件。
- 当我们open一个文件的时候,内核在进程中建立一个打开文件的数据结构来记录我们打开的文件,然后在内存中申请一段内存,将静态文件的内容读取到内存中特定地址管理存放,此时内存中的就是动态文件。之后的读写等操作都是针对这份内存中的动态文件,当close关闭动态文件后,内核将内存中动态文件的内容同步更新到静态文件中。
- 文件描述符的概念:一个程序打开一个文件就会得到一个文件描述符(整数),该数字用来区分一个程序打开的多个文件。文件描述符的作用域就是当前进程。
open :open and possibly create a file·
#include
#include
#include
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
read : read from a file descriptor
#include
ssize_t read(int fd, void *buf, size_t count);
write : write to a file descriptor
#include
ssize_t write(int fd, const void *buf, size_t count);
close :close a file descriptor
#include
int close(int fd);
lseek: reposition read/write file offset
#include
#include
off_t lseek(int fd, off_t offset, int whence);
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
open函数中的参数pathname是文件的路径,是一个字符串,例如"./test.txt"。而参数flags指定打开文件后的权限,打开的文件只能按照flags权限来操作。flags分为主类和副类。参数mode:如果文件被新建,指定其权限位(八进制权限码)。
附:
flags主类(这三类是互斥的):
O_RDONLY | O_WRONLY | O_RDWR |
---|---|---|
只读 | 只写 | 可读可写 |
flags副类:
flags | 作用 |
---|---|
O_EXCL | 如果使用O_CREAT选项且文件存在,则返回错误消息 |
O_NOCTTY | 如果文件为终端,那么终端不可以调用open系统调用的那个进程的控制终端 |
O_TRUNC | 如果文件已经存在则清除文件中的原有内容 |
O_APPEND | 若写文件则在文件内容后追加内容 |
主副可以搭配使用,例如:O_RDWR|O_CREAT|O_TRUNC
open函数的返回值是文件描述符(>=0的整数),一般用fd表示。
实例:打开一个文件代码
int fd = -1; //fd
char read_buff[100]={0}; //read buffer
int ret = 0; //return value of read
char* file_path = "./test.txt";
//step1:open file
fd = open(file_path,O_RDWR);
if(fd == -1)
{
printf("open error!\n");
}
else
printf("open success,fd of %s is %d\n",file_name,fd);
ssize_t read(int fd, void *buf, size_t count);
fd表示要读取哪个文件,一般由前面的open返回得到。buf是应用程序自己提供的一段内存缓冲区,用来存储读出的内容。count是要读取的字节数。返回值ssize_t类型是linux内核用typedef重定义的一个类型(其实就是int),表示成功读取的字节数。
实例:读取文件中的内容代码
//read file
ret = read(fd,read_buff,20);
if(ret < 0)
{
printf("read failed!\n");
}
else
{
printf("read success!\n");
printf("ret = %d,content is [%s].\n",ret,read_buff);
}
ssize_t write(int fd, const void *buf, size_t count);
实例:写入内容代码
//write file
char write_buff[20] = "hello linux!";
ret = write(fd,write_buff,strlen(write_buff));
if(ret<0)
{
printf("write failed!\n");
}
else
printf("write success,ret = %d\n",ret);
int close(int fd);
实例:关闭文件代码
//step3:close file
close(fd);
#include
#include
#include
#include
#include
#include
int main(int argc,char**argv)
{
int fd = -1;
char read_buff[100]={0};
int ret = 0;
char* file_name = "test.txt";
char write_buff[20] = "hello linux!";
//step1:open file
fd = open(file_name,O_RDWR);
if(fd == -1)
{
printf("open error!\n");
}
else
printf("open success,fd of %s is %d\n",file_name,fd);
//step2:wr file
#if 0
//read file
ret = read(fd,read_buff,20);
if(ret < 0)
{
printf("read failed!\n");
}
else
{
printf("read success!\n");
printf("ret = %d,content is [%s].\n",ret,read_buff);
}
#endif
#if 1
//write file
ret = write(fd,write_buff,strlen(write_buff));
if(ret<0)
{
printf("write failed!\n");
}
else
printf("write success,ret = %d\n",ret);
#endif
//step3:close file
close(fd);
return 0;
}
- 当文件没有被打开时,文件以一种固定的形式存放在硬盘中的,这时候的文件叫作静态文件。
- 一块硬盘可以分为硬盘内容管理表项和真正存储内容的区域等两大区域。操作系统访问文件时是先去读取硬盘内容管理表,从中找到我们要访问的那个文件的扇区级别的信息,然后再用这个信息去查询真正存储内容的区域,最后得到我们要的文件。
- 操作系统最初拿到的信息是文件名,最终得到的是文件内容。第一步就是去查询硬盘内容管理表,这个管理表中以文件为单位记录了各个文件的各种信息,每一个文件有一个信息列表(我们叫inode,i节点,其实质是一个结构体,这个结构体有很多元素,每个元素记录了这个文件的一些信息,其中就包括文件名、文件在硬盘上对应的扇区号、块号那些东西·····)
- 硬盘管理的时候是以文件为单位的,每个文件一个inode,每个inode有一个数字编号,对应一个结构体,结构体中记录了各种信息。
- 联系平时实践,格式化硬盘(U盘)时发现有:快速格式化和底层格式化。快速格式化非常快,格式化一个32GB的U盘只要1秒钟,普通格式化格式化速度慢。这两个的差异?其实快速格式化就是只删除了U盘中的硬盘内容管理表(其实就是inode),真正存储的内容没有动。这种格式化的内容是有可能被找回的。
- 一个程序的运行就是一个进程,我们在程序中打开的文件就属于某个进程。每个进程都有一个数据结构用来记录这个进程的所有信息(叫进程信息表),表中有一个指针会指向一个文件管理表,文件管理表中记录了当前进程打开的所有文件及其相关信息。文件管理表中用来索引各个打开的文件的index就是文件描述符fd,我们最终找到的就是一个已经被打开的文件的管理结构体vnode。
- 一个vnode中就记录了一个被打开的文件的各种信息,而且我们只要知道这个文件的fd,就可以很容易的找到这个文件的vnode进而对这个文件进行各种操作。