目录
一,系统接口
1,open
函数原型:int open(const char *pathname,int flags)
pathname:要打开或者创建的目标文件。
flags:打开要做什么,基本上有O_WRONLY(只写) O_RDONLY(只读) O_CREAT(创建)O_APPEND(追加)
返回值:成功返回文件描述符
失败返回 -1;
如下代码:
2,write
函数原型:
int write(int _filehandle,const void *_Buf,unsigned int _Maxcharcount)
_filehandle,文件描述符,_Buf写入的字符,_Maxcharcount字符长度
相比较于其他接口,均是如此,如read/close/lseek
但对于open的返回值fd为什么是3呢,此时就牵扯到一个文件描述符的概念。
二,文件描述符
1,0&1&2
通过对open的了解,我们知道文件描述符fd是一个整数。
linux系统会默认打开三个接口,0/1/2,这三个接口分别stdin,stdout,stderr,分别对应键盘,显示器,显示器。
而我们也可以使用如下的方式进行输出,通过读0,输出到1和2
void test2()
{
char buf[1024];
ssize_t s = read(0, buf, sizeof(buf));
if(s > 0)
{
buf[s] = 0;
write(1, buf, strlen(buf));
write(2, buf, strlen(buf));
}
}
2,文件描述符是什么东西
文件描述符就是从0开始的小整数,当我们打开文件后,就类似与创建进程一样,先描述再组织,使用task_struct描述这个文件的信息,被file_struct管理,使用指针指向数组的下标实现,再通过内核去管理这个文件的信息 ,只要知道这个数组下标,就能找到在内核中对应的信息。
3,文件描述符的分配规则
void test3()
{
close(1);
int fd=open("log.txt",O_WRONLY);
printf("i am printf\n");
fprintf(stdout,"i am fileskkkk\n");
fflush(stdout);
close(fd);
}
此时我们关闭1号描述符,打开log.txt,使用输出到屏幕,发现屏幕上没有打印任何数据,打开log.txt,发现 i am printf 和i am fileskkkk都写到文件当中了,所以通过这个我们可以看到,
文件描述符会优先分配到未被使用的最小下标。
三,缓冲概念
一般缓冲分为三种:
无缓冲(系统接口)
行缓冲(常见的对显示器进行刷新数据)(方便人机交互)
全缓冲(对文件的写入使用全缓冲)
缓冲一般由语言层提供,os也有缓冲(为了减少对磁盘的访问)
四,重定向
1,重定向的原理
通过上面程序的演示,我们大概可以知道,关闭标准输出,可以将信息输出到文件中,这就是一种重定向,本质是修改文件描述符fd下标对应的struct_file*里面。
我们再来看看下面的程序;
void test4()
{
close(1);
int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
if(fd < 0)
{
perror("open");
}
printf("fd: %d\n", fd);
fflush(stdout);
close(fd);
}
此时,我们发现,本来应该输出到显示器上的内容,输出到了文件myfile 当中,其中,fd=1。这种现象叫做输出重定向。常见的重定向有:>, >>, <,更加说明了这种现象,
2,重定向的函数
使用dup2系统调用
函数原型:
int dup2(int oldfd,int newfd);
dup2(fd,1),如这个一样,用fd覆盖1号文件描述符
这个函数的功能在于覆盖式的把旧的文件描述符给新的文件描述符,两个文件描述符共享权限。
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
int main()
{
printf("i am printf\n");
fprintf(stdout,"i am fprintf\n");
fputs("i am fputs",stdout);
fork();
return 0;
}
我们发现此时打印了6条语句,这是因为什么呢,
其原因在于父进程打印完以后,fork创建出子进程,父子进程共享一段代码,父子数据发生写时拷贝,因为进程具有独立性,子进程也执行了三条语句。
所以会输出6行。
但当我们使用如下代码时,
int main()
{
const char*msg="I am write\n";
printf("i am printf\n");
fprintf(stdout,"i am fprintf\n");
write(1,msg,strlen(msg));
fork();
return 0;
}
会发现打印了5行数据,
这是因为,当我们在往屏幕上打印数据时,会存在一个缓冲的概念,我们使用C库函数打印的时候,会先缓冲到C提供的缓冲区里面,子进程写时拷贝,也会缓冲到缓冲区,由于系统的接口没有缓冲区,所以会直接刷新到屏幕,再把父子进程的缓冲区刷新到屏幕上,所以打印五行数据。
注意:重定向还是不重定向不会更改进程的缓冲方式
C接口打印两次,osAPI打印1次
五,文件系统
1,文件是由什么构成的
文件=磁盘文件+内存文件,磁盘文件:文件属性和文件内容的集合,Inode存储文件属性
,磁盘存储文件内容。通过映射inode编号,就能找到文件内容在磁盘的那块存储着。
superblock:存放文件系统的结构信息,主要是存储inode还剩多少,data blocks还剩多少,inode table有那些inode被占用,那些没有,data blocks有哪些block被占用,那些没有。
Group descriptor table:描述块组属性信息
Block Bitmap:使用映射的方式,统计那块空间被占用,那块空间未被占用
Inode Bitmap:使用映射的方式,统计inode编码的占用情况。
Inode Table:索引节点表,存储inode编码
Data Blocks:数据存取块,存储数据的地方,与inode存在映射关系。
2,描述一下创建文件的过程,以及写入1KB的过程
1,存储属性:使用inodetable,找到inodetable表空闲的节点存储文件的属性信息
2,存储信息,将文件内容存储在Data Blocks对应的空间中,与inode建立映射关系。
3,记录分配情况,在Block Bitmap存储当前内容的存储情况。
4,添加文件名到目录中,文件名和inode之间的对应关系会将文件和文件属性连接起来
写入:
文件内容写入到datablocks,记录分配情况修改,
超级块更改修改存储datablock的记录分配
Inode文件内容属性发生改变
3,删除文件的过程
将inode位图中所在的位置为0,block bitmap对应的位也置为0,此时,datablock中的数据不用删除,等再次分配的时候,会覆盖式的写文件,避免了删除的过程。所以删除文件很快。
六,软硬链接
1,软连接
指令 ln-s 源文件 目标文件,软连接的作用就相当于是windows下的快捷方式,通过快捷方式也可执行文件
2,硬链接
指令 ln 源文件 目标文件,硬链接的作用就是类似于C++的取别名,其指向相同的地址空间,删除源文件也不会影响硬链接的产生的目标文件
总结
1,语言层的输入输出均封装了系统给的API接口
2,文件描述符就是类似进程,先描述(task_struct),在组织(file_struct),通过file提供的数组下标存文件的指针,然后系统对这个指针管理,只要知道下标,就能在内核中找到信息。
3,重定向就是把本来要输出(输入)通过文件描述符,输出或者输入到我们想让文件去的地方
4,语言有自己的缓冲区,操作系统也有,互不影响
5,inode为文件的索引,其包含文件的属性集合,文件=文件属性(Inode)+文件内容(data Blocks)
6,软连接:快捷方式,硬链接:取别名