系统文件IO
open
pathname:要打开或创建的目标文件
flags:打开文件时,可以传入多个参数,进行或运算
参数:
* O_RDONLY:只读打开
* O_WRONLY:只写打开
* O_RDWR:读写打开 ;这三个常量必须指定一个并且只能指定一个
* O_CREAT:若文件不存在,就创建。需要使用mode选项,来指明文件的访问权限
* O_APPEND:追加写
返回值:成功就返回新打开的文件描述符,失败返回-1
read
返回值是实际读到的字节数,失败返回-1。buf是代接受数据的缓冲区
write
被进程在内存中打开的是内存问价,没有被打开的是磁盘文件
文件描述符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。