系统文件IO
open
pathname:要打开或创建的目标文件
flags:打开文件时,可以传入多个参数,进行或运算
参数:
- O_RDONLY:只读打开
- O_WRONLY:只写打开
- O_RDWR:读写打开 ;这三个常量必须指定一个并且只能指定一个
- O_CREAT:若文件不存在,就创建。需要使用mode选项,来指明文件的访问权限
- O_APPEND:追加写
返回值:成功就返回新打开的文件描述符,失败返回-1
mode:创建文件时指定的权限
read
返回值是实际读到的字节数,失败返回-1。buf是存储接受到数据的缓冲区
write
被进程在内存中打开的是内存文件,没有被打开的是磁盘文件
close
关闭指定的文件,头文件和write一样。关闭成功返回0,失败返回-1。如果程序终止时没有关闭打开的文件,内核对自动将打开的文件关闭。
文件描述符fd
0&1&2
Linux进程默认情况下有3个缺省打开的文件描述符,分别是标准输入0、标准输出1、标准错误2,对应外设键盘、显示器、显示器
文件描述符从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构开描述目标文件,于是就有了file结构体,表示一个已经打开的文件对象。而open指令,必须让进程和文件关联起来,每个进程都有一个指针指向一张表,该表最重要的部分就是包涵一个指针数组,么个元素都是一个指向被打开文件的指针。所以,fd 的本质就是数组下标。
文件描述符的分配规则
找到没有被占用的最小的文件描述符。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
close(0);
int fd = open("myfile", O_RDONLY);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
close(fd);
return 0;
}
此时fd是0,如果关闭的是1,那么本该输出到显示器上的内容会输出到文件中,就做输出重定向。重定向的本质就是在OS内部,更改fd对应的内容的指向。
使用dup2系统调用
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/types.h>
int main(int argc,char* argv[])
{
if(argc != 2)
return 2;
int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC);
if(fd<0)
{
perror("open");
return 1;
}
dup2(fd,1);// 输出重定向
fprintf(stdout,"%s\n",argv[1]);
close(fd);
return 0;
}
缓冲区
写透模式:亲自去执行,成本高,效率低
写回模式:通过第三方,成本低,效率高
缓冲区刷新策略:立即刷新、行缓冲、全缓冲。
特殊情况:用户强制刷新(fflush),进程退出。缓冲区的刷新策略是一般+特殊
一般而言,显示器是行缓冲,磁盘文件是全缓冲。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
close(1);
int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
if(fd<0)
{
perror("open");
return 1;
}
printf("hello world\n");
fflush(stdout);
close(fd);
// 数据在缓冲区中,但是对应的fd关了,缓冲区无法刷新,就不能打印
return 0;
}
#include <stdio.h>
#include <string.h>
int main()
{
const char *msg0="hello printf\n";
const char *msg1="hello fwrite\n";
const char *msg2="hello write\n";
printf("%s", msg0);
fwrite(msg1, strlen(msg0), 1, stdout);
write(1, msg2, strlen(msg2));
fork();
return 0;
}
运行结果:
如果对运行结果进行重定向到文件中,
发现库函数都输出了两遍,而系统调用只输出了一遍。
- 一般C写入文件时是全缓冲,而写入显示器是行缓冲
- 库函数自带缓冲区,当发生重定向到普通文件时,数据刷新方式就是全缓冲
- 在缓冲区的数据不会立即刷新,fork之后也不会
- 进程退出后,缓冲区会刷新
- fork的时候,发生了写时拷贝,所以有两份数据
- 系统调用没有缓冲区。这里的缓冲区都是指用户级缓冲区,该缓冲区是由C标准库维护的
文件系统
将数据存到磁盘就是将数据存到该数组,所以对磁盘的管理就变成了对数组的管理。
- super block:文件系统的属性信息
- Data bloks:虽然磁盘的基本单位是扇区(512字节),但是操作系统和磁盘进程IO的基本单位是4kb,因为512字节太小,需要多次IO,效率低。而且如果操作系统使用和磁盘一样的大小,万一磁盘的大小变了,OS也需要改。Data blocks就是多个4kb的集合,保存的都是特定文件的内容,也可以存其他块的块号
- inode table:inode是一个大小为128字节的空间,保存的是对应文件的属性。每一个inode块都有一个inode编号,还保存了和他同一个块组的块的编号
- BlockBitmap:比特位和block是一一对应的,其中比特位为1,代表被占用,否则表示可用
- inode Bitmap:和inode一一对应,比特位为1,代表占用,否则表示可用
- GDT:块组描述符,表示块组的信息
创建一个新文件主要的操作:
- 存储属性:找到一个空的i节点,将文件信息存入其中
- 存储数据
- 记录分配情况
- 添加文件名到目录,文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来
软硬链接
ln -s testlink1.txt soft.link // 建立软连接
ln testlink2.txt hard.link // 建立硬连接
软硬链接的本质区别就是有无独立的inode。软链接有,说明软链接是一个独立的文件,类似于快捷方式,内容指向文件对应的路径。硬链接就是在指定的目录下,建立了文件名和指定的inode的映射关系,就是取别名,不是真正的创建新文件。属性中有一个数叫做硬链接数,当我们删除一个文件的时候,并不是把inode删除,而是将这个引用计数递减,当它为0的时候,这个文件才被真正删除,就是没有文件名和这个文件关联。默认创建目录,引用计数是2。